linux_gpib_rs/lowlevel/
multidevice.rs

1#![allow(non_snake_case)]
2use crate::error::{GpibError, IbError};
3use crate::lowlevel::utility::{Addr4882, ThreadIbcnt, ThreadIbcntl};
4use crate::status::IbStatus;
5use crate::types::{IbSendEOI, PrimaryAddress, SecondaryAddress};
6use crate::DEBUG;
7use linux_gpib_sys::Addr4882_t;
8use std::default::Default;
9use std::os::raw::{c_int, c_short, c_void};
10
11/// find devices
12///
13/// FindLstn() will check the primary addresses in the padList array for devices. The GPIB addresses of all devices found will be stored in the resultList array, and ibcnt will be set to the number of devices found. The maxNumResults parameter limits the maximum number of results that will be returned, and is usually set to the number of elements in the resultList array. If more than maxNumResults devices are found, an ETAB error is returned in iberr. The padList should consist of primary addresses only, with no secondary addresses (all possible secondary addresses will be checked as necessary).
14///
15/// Your GPIB board must have the capability to monitor the NDAC bus line in order to use this function (see iblines).
16///
17/// This function has the additional effect of addressing the board as talker for the duration of the Find Listeners protocol, which is beyond what IEEE 488.2 specifies. This is done because some boards cannot reliably read the state of the NDAC bus line unless they are the talker. Being the talker causes the board's gpib transceiver to configure NDAC as an input, so its state can be reliably read from the bus through the transceiver.
18///
19/// See: [Linux GPIB Reference](https://linux-gpib.sourceforge.io/doc_html/reference-function-findlstn.html)
20///
21pub fn FindLstn(board_desc: c_int, padList: Vec<Addr4882>) -> Result<Vec<Addr4882>, GpibError> {
22    let mut result: Vec<Addr4882_t> = Vec::new();
23    result.resize_with(31, || linux_gpib_sys::NOADDR);
24    let mut padList = padList
25        .into_iter()
26        .map(|a| a.addr)
27        .collect::<Vec<Addr4882_t>>();
28    padList.push(linux_gpib_sys::NOADDR);
29    if DEBUG {
30        println!("FindLstn({}, {:?})", board_desc, padList);
31    }
32    unsafe {
33        linux_gpib_sys::FindLstn(
34            board_desc,
35            padList.as_ptr(),
36            result.as_mut_ptr(),
37            padList.len().try_into()?,
38        )
39    };
40    let status = IbStatus::current_thread_local_status();
41    if status.err {
42        let error = IbError::current_thread_local_error()?;
43        match error {
44            IbError::EARG => {
45                eprintln!(
46                    "Invalid primary address at index {} in padlist",
47                    ThreadIbcnt()
48                );
49            }
50            IbError::EBUS => {
51                eprintln!("No devices are connected to the GPIB.");
52            }
53            IbError::ETAB => {
54                eprintln!("The number of devices found on the GPIB exceed limit.");
55            }
56            _ => {}
57        }
58        if DEBUG {
59            println!("-> {:?}", error);
60        }
61        Err(GpibError::DriverError(status, error))
62    } else {
63        let n_values: usize = ThreadIbcntl().try_into()?;
64        result.truncate(n_values);
65        if DEBUG {
66            println!("-> {:?}", result);
67        }
68        Ok(result.into_iter().map(|a| Addr4882 { addr: a }).collect())
69    }
70}
71
72/// Find all listeners on board.
73pub fn FindAllLstn(board_desc: c_int) -> Result<Vec<Addr4882>, GpibError> {
74    let padList = (1..31)
75        .into_iter()
76        .map(|pad| Addr4882::new(PrimaryAddress::new(pad)?, SecondaryAddress::default()))
77        .collect::<Result<Vec<Addr4882>, GpibError>>()?;
78    FindLstn(board_desc, padList)
79}
80
81/// clear a device
82///
83/// DevClear() causes the interface board specified by board_desc to send the clear command to the GPIB addresses specified by address. The results of the serial polls are stored into resultList. If you wish to clear multiple devices simultaneously, use DevClearList()
84pub fn DevClear(board: c_int, address: Addr4882) -> Result<(), GpibError> {
85    if DEBUG {
86        println!("DevClear({}, {})", board, address);
87    }
88    unsafe {
89        linux_gpib_sys::DevClear(board, address.addr);
90    }
91    let status = IbStatus::current_thread_local_status();
92    if status.err {
93        if DEBUG {
94            println!("-> {:?}", status);
95        }
96        Err(GpibError::DriverError(
97            status,
98            IbError::current_thread_local_error()?,
99        ))
100    } else {
101        if DEBUG {
102            println!("-> {:?}", status);
103        }
104        Ok(())
105    }
106}
107
108/// clear multiple devices
109///
110/// DevClear() causes the interface board specified by board_desc to send the clear command simultaneously to all the GPIB addresses specified by the addressList array. If addressList is empty or NULL, then the clear command is sent to all devices on the bus. If you only wish to clear a single device, DevClear() or ibclr() may be slightly more convenient.
111pub fn DevClearList(board: c_int, addresses: &Vec<Addr4882>) -> Result<(), GpibError> {
112    let mut instruments = addresses
113        .iter()
114        .map(|a| a.addr)
115        .collect::<Vec<Addr4882_t>>();
116    instruments.push(linux_gpib_sys::NOADDR);
117    if DEBUG {
118        println!("DevClearList({:?}, {:?})", board, addresses);
119    }
120    unsafe {
121        linux_gpib_sys::DevClearList(board, instruments.as_ptr());
122    }
123    let status = IbStatus::current_thread_local_status();
124    if DEBUG {
125        println!("-> {:?}", status);
126    }
127    if status.err {
128        Err(GpibError::DriverError(
129            status,
130            IbError::current_thread_local_error()?,
131        ))
132    } else {
133        Ok(())
134    }
135}
136
137/// put devices into local mode.
138///
139/// 	EnableLocal() addresses all of the devices in the addressList array as listeners then sends the GTL (go to local) command byte, causing them to enter local mode. This requires that the board is the controller-in-charge. Note that while the REN (remote enable) bus line is asserted, the devices will return to remote mode the next time they are addressed.
140///
141///If addressList is empty or NULL, then the REN line is unasserted and all devices enter local mode. The board must be system controller to change the state of the REN line.
142///
143/// See: [Linux GPIB Reference](https://linux-gpib.sourceforge.io/doc_html/reference-function-enablelocal.html)
144pub fn EnableLocal(board: c_int, addresses: &Vec<Addr4882>) -> Result<(), GpibError> {
145    let mut instruments = addresses
146        .iter()
147        .map(|a| a.addr)
148        .collect::<Vec<Addr4882_t>>();
149    instruments.push(linux_gpib_sys::NOADDR);
150    unsafe {
151        linux_gpib_sys::EnableLocal(board, instruments.as_ptr());
152    }
153    let status = IbStatus::current_thread_local_status();
154    if status.err {
155        Err(GpibError::DriverError(
156            status,
157            IbError::current_thread_local_error()?,
158        ))
159    } else {
160        Ok(())
161    }
162}
163
164/// put devices into remote mode.
165///
166/// EnableRemote() asserts the REN (remote enable) line, and addresses all of the devices in the addressList array as listeners (causing them to enter remote mode). The board must be system controller.
167///
168/// See: [Linux GPIB Reference](https://linux-gpib.sourceforge.io/doc_html/reference-function-enableremote.html)
169pub fn EnableRemote(board: c_int, addresses: &Vec<Addr4882>) -> Result<(), GpibError> {
170    let mut instruments = addresses
171        .iter()
172        .map(|a| a.addr)
173        .collect::<Vec<Addr4882_t>>();
174    instruments.push(linux_gpib_sys::NOADDR);
175    unsafe {
176        linux_gpib_sys::EnableRemote(board, instruments.as_ptr());
177    }
178    let status = IbStatus::current_thread_local_status();
179    if status.err {
180        Err(GpibError::DriverError(
181            status,
182            IbError::current_thread_local_error()?,
183        ))
184    } else {
185        Ok(())
186    }
187}
188
189/// find device requesting service and read its status byte
190///
191/// FindRQS will serial poll the GPIB addresses specified in the addressList array until it finds a device requesting service. The status byte of the device requesting service and its address are returned. If no device requesting service is found, an ETAB error is returned.
192///
193/// See: [Linux GPIB Reference](https://linux-gpib.sourceforge.io/doc_html/reference-function-findrqs.html)
194pub fn FindRQS(board: c_int, addresses: &Vec<Addr4882>) -> Result<(Addr4882, c_short), GpibError> {
195    let mut instruments = addresses
196        .iter()
197        .map(|a| a.addr)
198        .collect::<Vec<Addr4882_t>>();
199    instruments.push(linux_gpib_sys::NOADDR);
200    let mut status_byte: c_short = 0;
201    unsafe {
202        linux_gpib_sys::FindRQS(board, instruments.as_ptr(), &mut status_byte);
203    }
204    let status = IbStatus::current_thread_local_status();
205    if status.err {
206        Err(GpibError::DriverError(
207            status,
208            IbError::current_thread_local_error()?,
209        ))
210    } else {
211        let index: usize = ThreadIbcnt().try_into()?;
212        if index >= addresses.len() {
213            Err(GpibError::ValueError(
214                "index stored in Ibcnt is larger than addresses array length".to_owned(),
215            ))
216        } else {
217            Ok((addresses[index], status_byte))
218        }
219    }
220}
221
222/// make device controller-in-charge
223///
224/// PassControl() causes the board specified by board_desc to pass control to the device specified by address. On success, the device becomes the new controller-in-charge.
225///
226/// See: [Linux GPIB Reference](https://linux-gpib.sourceforge.io/doc_html/reference-function-passcontrol.html)
227pub fn PassControl(board: c_int, address: Addr4882) -> Result<(), GpibError> {
228    unsafe {
229        linux_gpib_sys::PassControl(board, address.addr);
230    }
231    let status = IbStatus::current_thread_local_status();
232    if status.err {
233        Err(GpibError::DriverError(
234            status,
235            IbError::current_thread_local_error()?,
236        ))
237    } else {
238        Ok(())
239    }
240}
241
242/// parallel poll devices
243///
244/// PPoll() is similar to the 'traditional' API function ibrpp(). It causes the interface board to perform a parallel poll, and stores the parallel poll byte in the location specified by result. Bits 0 to 7 of the parallel poll byte correspond to the dio lines 1 to 8, with a 1 indicating the corresponding dio line is asserted. The devices on the bus you wish to poll should be configured beforehand with PPollConfig(). The board must be controller-in-charge to perform a parallel poll.
245///
246/// See: [Linux GPIB Reference](https://linux-gpib.sourceforge.io/doc_html/reference-function-ppoll.html)
247pub fn PPoll(board: c_int) -> Result<c_short, GpibError> {
248    let mut result: c_short = 0;
249    unsafe {
250        linux_gpib_sys::PPoll(board, &mut result);
251    }
252    let status = IbStatus::current_thread_local_status();
253    if status.err {
254        Err(GpibError::DriverError(
255            status,
256            IbError::current_thread_local_error()?,
257        ))
258    } else {
259        Ok(result)
260    }
261}
262
263/// configure a device's parallel poll response
264///
265/// PPollConfig() configures the device specified by address to respond to parallel polls. The dio_line (valid values are 1 through 8) specifies which dio line the device being configured should use to send back its parallel poll response. The line_sense argument specifies the polarity of the response. If line_sense is nonzero, then the specified dio line will be asserted to indicate that the 'individual status bit' (or 'ist') is 1. If sense is zero, then the specified dio line will be asserted when ist is zero.
266///
267/// See: [Linux GPIB Reference](https://linux-gpib.sourceforge.io/doc_html/reference-function-ppollconfig.html)
268pub fn PPollConfig(
269    board: c_int,
270    address: Addr4882,
271    dio_line: c_int,
272    line_sense: c_int,
273) -> Result<(), GpibError> {
274    unsafe {
275        linux_gpib_sys::PPollConfig(board, address.addr, dio_line, line_sense);
276    }
277    let status = IbStatus::current_thread_local_status();
278    if status.err {
279        Err(GpibError::DriverError(
280            status,
281            IbError::current_thread_local_error()?,
282        ))
283    } else {
284        Ok(())
285    }
286}
287
288/// disable devices' parallel poll response
289///
290/// See: [Linux GPIB Reference](https://linux-gpib.sourceforge.io/doc_html/reference-function-ppollunconfig.html)
291pub fn PPollUnconfig(board: c_int, addresses: &Vec<Addr4882>) -> Result<(), GpibError> {
292    let mut instruments = addresses
293        .iter()
294        .map(|a| a.addr)
295        .collect::<Vec<Addr4882_t>>();
296    instruments.push(linux_gpib_sys::NOADDR);
297    unsafe {
298        linux_gpib_sys::PPollUnconfig(board, instruments.as_ptr());
299    }
300    let status = IbStatus::current_thread_local_status();
301    if status.err {
302        Err(GpibError::DriverError(
303            status,
304            IbError::current_thread_local_error()?,
305        ))
306    } else {
307        Ok(())
308    }
309}
310
311/// read data
312///
313/// 	RcvRespMsg() reads data from the bus. A device must have already been addressed as talker (and the board as listener) before calling this function. Addressing may be accomplished with the ReceiveSetup() function.
314///
315/// Up to count bytes are read into the array specified by buffer. The termination argument specifies the 8-bit end-of-string character (which must be a value from 0 to 255) whose reception will terminate a read. termination can also be set to the 'STOPend' constant, in which case no end-of-string character will be used. Assertion of the EOI line will always end a read.
316///
317/// You may find it simpler to use the slightly higher level function Receive(), since it does not require addressing and reading of data to be performed separately.
318///
319/// See: [Linux GPIB Reference](https://linux-gpib.sourceforge.io/doc_html/reference-function-rcvrespmsg.html)
320pub fn RcvRespMsg(board: c_int, buffer: &mut [u8], termination: c_int) -> Result<(), GpibError> {
321    unsafe {
322        linux_gpib_sys::RcvRespMsg(
323            board,
324            buffer.as_mut_ptr() as *mut c_void,
325            buffer.len().try_into()?,
326            termination,
327        );
328    }
329    let status = IbStatus::current_thread_local_status();
330    if status.err {
331        Err(GpibError::DriverError(
332            status,
333            IbError::current_thread_local_error()?,
334        ))
335    } else {
336        Ok(())
337    }
338}
339
340/// serial poll a device
341///
342/// ReadStatusByte() causes the board specified by the board descriptor board_desc to serial poll the GPIB address specified by address. The status byte is stored at the location specified by the result pointer. If you wish to serial poll multiple devices, it may be slightly more efficient to use AllSPoll(). Serial polls may also be conducted with the 'traditional API' function ibrsp().
343///
344/// See: [Linux GPIB Reference](https://linux-gpib.sourceforge.io/doc_html/reference-function-readstatusbyte.html)
345pub fn ReadStatusByte(board: c_int, address: Addr4882) -> Result<c_short, GpibError> {
346    let mut result: c_short = 0;
347    unsafe {
348        linux_gpib_sys::ReadStatusByte(board, address.addr, &mut result);
349    }
350    let status = IbStatus::current_thread_local_status();
351    if status.err {
352        Err(GpibError::DriverError(
353            status,
354            IbError::current_thread_local_error()?,
355        ))
356    } else {
357        Ok(result)
358    }
359}
360
361/// perform receive addressing and read data
362///
363/// Receive() performs the necessary addressing, then reads data from the device specified by address. It is equivalent to a ReceiveSetup() call followed by a RcvRespMsg() call.
364///
365/// See: [Linux GPIB Reference](https://linux-gpib.sourceforge.io/doc_html/reference-function-receive.html)
366pub fn Receive(
367    board: c_int,
368    address: Addr4882,
369    buffer: &mut [u8],
370    termination: c_int,
371) -> Result<(IbStatus, usize), GpibError> {
372    if DEBUG {
373        println!("Receive({:?}, {:?})", board, address);
374    }
375    unsafe {
376        linux_gpib_sys::Receive(
377            board,
378            address.addr,
379            buffer.as_mut_ptr() as *mut c_void,
380            buffer.len().try_into()?,
381            termination,
382        );
383    }
384    let status = IbStatus::current_thread_local_status();
385    if status.err {
386        if DEBUG {
387            println!("-> {:?}", status);
388        }
389        Err(GpibError::DriverError(
390            status,
391            IbError::current_thread_local_error()?,
392        ))
393    } else {
394        let n_read = ThreadIbcntl().try_into()?;
395        if DEBUG {
396            println!(
397                "Receive({:?}, {:?}) -> Read {} bytes",
398                board, address, n_read
399            );
400        }
401        Ok((status, n_read))
402    }
403}
404
405/// perform receive addressing
406///
407/// 	ReceiveSetup() addresses the device specified by address as talker, and addresses the interface board as listener. A subsequent RcvRespMsg() call will read data from the device.
408///
409///You may find it simpler to use the slightly higher level function Receive(), since it does not require addressing and reading of data to be performed separately.
410///
411/// See: [Linux GPIB Reference](https://linux-gpib.sourceforge.io/doc_html/reference-function-receivesetup.html)
412pub fn ReceiveSetup(board: c_int, address: Addr4882) -> Result<(), GpibError> {
413    unsafe {
414        linux_gpib_sys::ReceiveSetup(board, address.addr);
415    }
416    let status = IbStatus::current_thread_local_status();
417    if status.err {
418        Err(GpibError::DriverError(
419            status,
420            IbError::current_thread_local_error()?,
421        ))
422    } else {
423        Ok(())
424    }
425}
426
427/// reset system
428///
429/// ResetSys() has the following effects:
430///
431/// - The remote enable bus line is asserted.
432/// - An interface clear is performed (the interface clear bus line is asserted for at least 100 microseconds).
433/// - The device clear command is sent to all the devices on the bus.
434/// - The *RST message is sent to every device specified in the addressList.
435///
436/// See: [Linux GPIB Reference](https://linux-gpib.sourceforge.io/doc_html/reference-function-resetsys.html)
437pub fn ResetSys(board: c_int, addresses: &Vec<Addr4882>) -> Result<(), GpibError> {
438    let mut instruments = addresses
439        .iter()
440        .map(|a| a.addr)
441        .collect::<Vec<Addr4882_t>>();
442    instruments.push(linux_gpib_sys::NOADDR);
443    unsafe {
444        linux_gpib_sys::ResetSys(board, instruments.as_ptr());
445    }
446    let status = IbStatus::current_thread_local_status();
447    if status.err {
448        Err(GpibError::DriverError(
449            status,
450            IbError::current_thread_local_error()?,
451        ))
452    } else {
453        Ok(())
454    }
455}
456
457/// perform send addressing and write data
458///
459/// Send() addresses the device specified by address as listener, then writes data onto the bus. It is equivalent to a SendList() except it only uses a single GPIB address to specify the listener instead of allowing an array of listeners.
460///
461/// See: [Linux GPIB Reference](https://linux-gpib.sourceforge.io/doc_html/reference-function-send.html)
462pub fn Send(
463    board: c_int,
464    address: Addr4882,
465    buffer: &[u8],
466    eot_mode: IbSendEOI,
467) -> Result<(), GpibError> {
468    if DEBUG {
469        println!("Send({:?}, {:?}, {:?})", board, address, buffer);
470    }
471    unsafe {
472        linux_gpib_sys::Send(
473            board,
474            address.addr,
475            buffer.as_ptr() as *const c_void,
476            buffer.len().try_into()?,
477            eot_mode.as_eot(),
478        );
479    }
480    let status = IbStatus::current_thread_local_status();
481    if DEBUG {
482        println!(
483            "Send({:?}, {:?}, {:?}) -> {:?}",
484            board, address, buffer, status
485        );
486    }
487    if status.err {
488        Err(GpibError::DriverError(
489            status,
490            IbError::current_thread_local_error()?,
491        ))
492    } else {
493        Ok(())
494    }
495}
496
497/// perform interface clear
498///
499/// SendIFC() resets the GPIB bus by asserting the 'interface clear' (IFC) bus line for a duration of at least 100 microseconds. The board specified by board_desc must be the system controller in order to assert IFC. The interface clear causes all devices to untalk and unlisten, puts them into serial poll disabled state (don't worry, you will still be able to conduct serial polls), and the board becomes controller-in-charge.
500pub fn SendIFC(board: c_int) -> Result<(), GpibError> {
501    if DEBUG {
502        println!("SendIFC({})", board);
503    }
504    unsafe {
505        linux_gpib_sys::SendIFC(board);
506    }
507    let status = IbStatus::current_thread_local_status();
508    if DEBUG {
509        println!("endIFC({}) -> {:?}", board, status);
510    }
511    if status.err {
512        Err(GpibError::DriverError(
513            status,
514            IbError::current_thread_local_error()?,
515        ))
516    } else {
517        Ok(())
518    }
519}
520
521/// write data to multiple devices
522///
523/// See: [Linux GPIB Reference](https://linux-gpib.sourceforge.io/doc_html/reference-function-sendlist.html)
524pub fn SendList(
525    board: c_int,
526    addresses: &Vec<Addr4882>,
527    buffer: &[u8],
528    eot_mode: IbSendEOI,
529) -> Result<(), GpibError> {
530    if DEBUG {
531        println!("SendList({:?}, {:?}, {:?})", board, addresses, buffer);
532    }
533    let mut instruments = addresses
534        .iter()
535        .map(|a| a.addr)
536        .collect::<Vec<Addr4882_t>>();
537    instruments.push(linux_gpib_sys::NOADDR);
538    unsafe {
539        linux_gpib_sys::SendList(
540            board,
541            instruments.as_ptr(),
542            buffer.as_ptr() as *const c_void,
543            buffer.len().try_into()?,
544            eot_mode.as_eot(),
545        );
546    }
547    let status = IbStatus::current_thread_local_status();
548    if DEBUG {
549        println!(
550            "SendList({:?}, {:?}, {:?}) -> {:?}",
551            board, addresses, buffer, status
552        );
553    }
554    if status.err {
555        Err(GpibError::DriverError(
556            status,
557            IbError::current_thread_local_error()?,
558        ))
559    } else {
560        Ok(())
561    }
562}
563
564/// put devices into local lockout mode
565///
566/// See: [Linux GPIB Reference](https://linux-gpib.sourceforge.io/doc_html/reference-function-sendllo.html)
567pub fn SendLLO(board: c_int) -> Result<(), GpibError> {
568    unsafe {
569        linux_gpib_sys::SendLLO(board);
570    }
571    let status = IbStatus::current_thread_local_status();
572    if status.err {
573        Err(GpibError::DriverError(
574            status,
575            IbError::current_thread_local_error()?,
576        ))
577    } else {
578        Ok(())
579    }
580}
581
582/// put devices into remote with lockout state
583///
584/// See: [Linux GPIB Reference](https://linux-gpib.sourceforge.io/doc_html/reference-function-setrwls.html)
585pub fn SetRWLS(board: c_int, addresses: &Vec<Addr4882>) -> Result<(), GpibError> {
586    let mut instruments = addresses
587        .iter()
588        .map(|a| a.addr)
589        .collect::<Vec<Addr4882_t>>();
590    instruments.push(linux_gpib_sys::NOADDR);
591    unsafe {
592        linux_gpib_sys::SetRWLS(board, instruments.as_ptr());
593    }
594    let status = IbStatus::current_thread_local_status();
595    if status.err {
596        Err(GpibError::DriverError(
597            status,
598            IbError::current_thread_local_error()?,
599        ))
600    } else {
601        Ok(())
602    }
603}
604
605///  query state of SRQ bus line
606///
607/// Returns true if the SRQ line is asserted, false if it is not asserted.
608///
609/// See: [Linux GPIB Reference](https://linux-gpib.sourceforge.io/doc_html/reference-function-testsrq.html)
610pub fn TestSRQ(board: c_int) -> Result<bool, GpibError> {
611    let mut result: c_short = 10;
612    unsafe {
613        linux_gpib_sys::TestSRQ(board, &mut result);
614    }
615    let status = IbStatus::current_thread_local_status();
616    if status.err {
617        Err(GpibError::DriverError(
618            status,
619            IbError::current_thread_local_error()?,
620        ))
621    } else {
622        match result {
623            0 => Ok(false),
624            1 => Ok(true),
625            other => Err(GpibError::ValueError(format!("Unexpected value {}", other))),
626        }
627    }
628}
629
630/// perform self-test queries on devices
631///
632/// See: [Linux GPIB Reference](https://linux-gpib.sourceforge.io/doc_html/reference-function-testsys.html)
633pub fn TestSys(board: c_int, addresses: &Vec<Addr4882>) -> Result<Vec<c_short>, GpibError> {
634    let mut instruments = addresses
635        .iter()
636        .map(|a| a.addr)
637        .collect::<Vec<Addr4882_t>>();
638    instruments.push(linux_gpib_sys::NOADDR);
639    let mut results: Vec<c_short> = Vec::with_capacity(addresses.len());
640    results.resize(addresses.len(), 0);
641    unsafe {
642        linux_gpib_sys::TestSys(board, instruments.as_ptr(), results.as_mut_ptr());
643    }
644    let status = IbStatus::current_thread_local_status();
645    if status.err {
646        Err(GpibError::DriverError(
647            status,
648            IbError::current_thread_local_error()?,
649        ))
650    } else {
651        Ok(results)
652    }
653}
654
655/// trigger a device
656///
657/// See: [Linux GPIB Reference](https://linux-gpib.sourceforge.io/doc_html/reference-function-trigger.html)
658pub fn Trigger(board: c_int, address: Addr4882) -> Result<(), GpibError> {
659    unsafe {
660        linux_gpib_sys::Trigger(board, address.addr);
661    }
662    let status = IbStatus::current_thread_local_status();
663    if status.err {
664        Err(GpibError::DriverError(
665            status,
666            IbError::current_thread_local_error()?,
667        ))
668    } else {
669        Ok(())
670    }
671}
672
673/// trigger multiple devices
674///
675/// See: [Linux GPIB Reference](https://linux-gpib.sourceforge.io/doc_html/reference-function-triggerlist.html)
676pub fn TriggerList(board: c_int, addresses: &Vec<Addr4882>) -> Result<(), GpibError> {
677    let mut instruments = addresses
678        .iter()
679        .map(|a| a.addr)
680        .collect::<Vec<Addr4882_t>>();
681    instruments.push(linux_gpib_sys::NOADDR);
682    unsafe {
683        linux_gpib_sys::TriggerList(board, instruments.as_ptr());
684    }
685    let status = IbStatus::current_thread_local_status();
686    if status.err {
687        Err(GpibError::DriverError(
688            status,
689            IbError::current_thread_local_error()?,
690        ))
691    } else {
692        Ok(())
693    }
694}
695
696#[cfg(feature = "async-tokio")]
697/// sleep until the SRQ bus line is asserted
698///
699/// See: [Linux GPIB Reference](https://linux-gpib.sourceforge.io/doc_html/reference-function-waitsrq.html)
700pub async fn WaitSRQ(board: c_int) -> Result<c_short, GpibError> {
701    tokio::task::spawn_blocking(move || {
702        let mut result: c_short = 0;
703        unsafe {
704            linux_gpib_sys::WaitSRQ(board, &mut result);
705        }
706        let status = IbStatus::current_thread_local_status();
707        if status.err {
708            Err(GpibError::DriverError(
709                status,
710                IbError::current_thread_local_error()?,
711            ))
712        } else {
713            Ok(result)
714        }
715    })
716    .await?
717}