industrial_io/
channel.rs

1// libiio-sys/src/channel.rs
2//
3// Copyright (c) 2018-2025, Frank Pagliughi
4//
5// Licensed under the MIT license:
6//   <LICENSE or http://opensource.org/licenses/MIT>
7// This file may not be copied, modified, or distributed except according
8// to those terms.
9//
10//! Industrial I/O Channels
11//!
12
13use super::*;
14use crate::{ffi, ATTR_BUF_SIZE};
15use std::{
16    any::TypeId,
17    collections::HashMap,
18    ffi::CString,
19    mem::{self, size_of, size_of_val},
20    os::raw::{c_char, c_int, c_longlong, c_uint, c_void},
21};
22
23/// The channel direction
24#[derive(Debug, Clone, Copy, PartialEq, Eq)]
25pub enum Direction {
26    /// Channel is input
27    Input,
28    /// Channel is output
29    Output,
30}
31
32/// The type of data associated with a channel.
33#[allow(missing_docs)]
34#[repr(u32)]
35#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
36pub enum ChannelType {
37    Voltage = ffi::iio_chan_type_IIO_VOLTAGE,
38    Current = ffi::iio_chan_type_IIO_CURRENT,
39    Power = ffi::iio_chan_type_IIO_POWER,
40    Accel = ffi::iio_chan_type_IIO_ACCEL,
41    AnglVel = ffi::iio_chan_type_IIO_ANGL_VEL,
42    Magn = ffi::iio_chan_type_IIO_MAGN,
43    Light = ffi::iio_chan_type_IIO_LIGHT,
44    Intensity = ffi::iio_chan_type_IIO_INTENSITY,
45    Proximity = ffi::iio_chan_type_IIO_PROXIMITY,
46    Temp = ffi::iio_chan_type_IIO_TEMP,
47    Incli = ffi::iio_chan_type_IIO_INCLI,
48    Rot = ffi::iio_chan_type_IIO_ROT,
49    Angl = ffi::iio_chan_type_IIO_ANGL,
50    Timestamp = ffi::iio_chan_type_IIO_TIMESTAMP,
51    Capacitance = ffi::iio_chan_type_IIO_CAPACITANCE,
52    AltVoltage = ffi::iio_chan_type_IIO_ALTVOLTAGE,
53    Cct = ffi::iio_chan_type_IIO_CCT,
54    Pressure = ffi::iio_chan_type_IIO_PRESSURE,
55    HumidityRelative = ffi::iio_chan_type_IIO_HUMIDITYRELATIVE,
56    Activity = ffi::iio_chan_type_IIO_ACTIVITY,
57    Steps = ffi::iio_chan_type_IIO_STEPS,
58    Energy = ffi::iio_chan_type_IIO_ENERGY,
59    Distance = ffi::iio_chan_type_IIO_DISTANCE,
60    Velocity = ffi::iio_chan_type_IIO_VELOCITY,
61    Concentration = ffi::iio_chan_type_IIO_CONCENTRATION,
62    Resistance = ffi::iio_chan_type_IIO_RESISTANCE,
63    Ph = ffi::iio_chan_type_IIO_PH,
64    UvIndex = ffi::iio_chan_type_IIO_UVINDEX,
65    ElectricalConductivity = ffi::iio_chan_type_IIO_ELECTRICALCONDUCTIVITY,
66    Count = ffi::iio_chan_type_IIO_COUNT,
67    Index = ffi::iio_chan_type_IIO_INDEX,
68    Gravity = ffi::iio_chan_type_IIO_GRAVITY,
69    Unknown = ffi::iio_chan_type_IIO_CHAN_TYPE_UNKNOWN,
70}
71
72impl From<u32> for ChannelType {
73    fn from(val: u32) -> ChannelType {
74        use ChannelType::*;
75
76        match val {
77            ffi::iio_chan_type_IIO_VOLTAGE => Voltage,
78            ffi::iio_chan_type_IIO_CURRENT => Current,
79            ffi::iio_chan_type_IIO_POWER => Power,
80            ffi::iio_chan_type_IIO_ACCEL => Accel,
81            ffi::iio_chan_type_IIO_ANGL_VEL => AnglVel,
82            ffi::iio_chan_type_IIO_MAGN => Magn,
83            ffi::iio_chan_type_IIO_LIGHT => Light,
84            ffi::iio_chan_type_IIO_INTENSITY => Intensity,
85            ffi::iio_chan_type_IIO_PROXIMITY => Proximity,
86            ffi::iio_chan_type_IIO_TEMP => Temp,
87            ffi::iio_chan_type_IIO_INCLI => Incli,
88            ffi::iio_chan_type_IIO_ROT => Rot,
89            ffi::iio_chan_type_IIO_ANGL => Angl,
90            ffi::iio_chan_type_IIO_TIMESTAMP => Timestamp,
91            ffi::iio_chan_type_IIO_CAPACITANCE => Capacitance,
92            ffi::iio_chan_type_IIO_ALTVOLTAGE => AltVoltage,
93            ffi::iio_chan_type_IIO_CCT => Cct,
94            ffi::iio_chan_type_IIO_PRESSURE => Pressure,
95            ffi::iio_chan_type_IIO_HUMIDITYRELATIVE => HumidityRelative,
96            ffi::iio_chan_type_IIO_ACTIVITY => Activity,
97            ffi::iio_chan_type_IIO_STEPS => Steps,
98            ffi::iio_chan_type_IIO_ENERGY => Energy,
99            ffi::iio_chan_type_IIO_DISTANCE => Distance,
100            ffi::iio_chan_type_IIO_VELOCITY => Velocity,
101            ffi::iio_chan_type_IIO_CONCENTRATION => Concentration,
102            ffi::iio_chan_type_IIO_RESISTANCE => Resistance,
103            ffi::iio_chan_type_IIO_PH => Ph,
104            ffi::iio_chan_type_IIO_UVINDEX => UvIndex,
105            ffi::iio_chan_type_IIO_ELECTRICALCONDUCTIVITY => ElectricalConductivity,
106            ffi::iio_chan_type_IIO_COUNT => Count,
107            ffi::iio_chan_type_IIO_INDEX => Index,
108            ffi::iio_chan_type_IIO_GRAVITY => Gravity,
109            _ => Unknown,
110        }
111    }
112}
113
114/// The format of a data sample.
115#[derive(Debug, Copy, Clone)]
116pub struct DataFormat {
117    /// The data format struct from the C library
118    data_fmt: ffi::iio_data_format,
119}
120
121impl DataFormat {
122    /// Creates a new data format from the underlyinh library type.
123    fn new(data_fmt: ffi::iio_data_format) -> Self {
124        Self { data_fmt }
125    }
126
127    /// Gets total length of the sample, in bits.
128    pub fn length(&self) -> u32 {
129        u32::from(self.data_fmt.length)
130    }
131
132    /// Gets the length of valid data in the sample, in bits.
133    pub fn bits(&self) -> u32 {
134        u32::from(self.data_fmt.bits)
135    }
136
137    /// Right-shift to apply when converting sample.
138    pub fn shift(&self) -> u32 {
139        u32::from(self.data_fmt.shift)
140    }
141
142    /// Determines if the sample is signed
143    pub fn is_signed(&self) -> bool {
144        self.data_fmt.is_signed
145    }
146
147    /// Determines if the sample is fully defined, sign extended, etc.
148    pub fn is_fully_defined(&self) -> bool {
149        self.data_fmt.is_fully_defined
150    }
151
152    /// Determines if the sample is in big-endian format
153    pub fn is_big_endian(&self) -> bool {
154        self.data_fmt.is_be
155    }
156
157    /// Determinesif the sample should be scaled when converted
158    pub fn with_scale(&self) -> bool {
159        self.data_fmt.with_scale
160    }
161
162    /// Contains the scale to apply if `with_scale` is set
163    pub fn scale(&self) -> f64 {
164        self.data_fmt.scale
165    }
166
167    /// Number of times length repeats
168    pub fn repeat(&self) -> u32 {
169        u32::from(self.data_fmt.repeat)
170    }
171
172    /// The number of bytes required to hold a single sample from the channel.
173    pub fn byte_length(&self) -> usize {
174        let nbytes = (self.length() / 8) * self.repeat();
175        nbytes as usize
176    }
177
178    /// Gets the `TypeId` for a single sample from the channel.
179    ///
180    /// This will get the `TypeId` for a sample if it can fit into a standard
181    /// integer type, signed or unsigned, of 8, 16, 32, or 64 bits.
182    pub fn type_of(&self) -> Option<TypeId> {
183        let nbytes = self.byte_length();
184
185        if self.is_signed() {
186            match nbytes {
187                1 => Some(TypeId::of::<i8>()),
188                2 => Some(TypeId::of::<i16>()),
189                4 => Some(TypeId::of::<i32>()),
190                8 => Some(TypeId::of::<i64>()),
191                _ => None,
192            }
193        }
194        else {
195            match nbytes {
196                1 => Some(TypeId::of::<u8>()),
197                2 => Some(TypeId::of::<u16>()),
198                4 => Some(TypeId::of::<u32>()),
199                8 => Some(TypeId::of::<u64>()),
200                _ => None,
201            }
202        }
203    }
204}
205
206/// An Industrial I/O Device Channel
207#[derive(Debug, Clone)]
208pub struct Channel {
209    /// Pointer to the underlying IIO channel object
210    pub(crate) chan: *mut ffi::iio_channel,
211    #[allow(dead_code)]
212    /// Holder for the Device's lifetime for libiio safety.
213    pub(crate) ctx: Context,
214}
215
216impl Channel {
217    /// Retrieves the name of the channel (e.g. <b><i>vccint</i></b>)
218    pub fn name(&self) -> Option<String> {
219        let pstr = unsafe { ffi::iio_channel_get_name(self.chan) };
220        cstring_opt(pstr)
221    }
222
223    /// Retrieve the channel ID (e.g. <b><i>voltage0</i></b>)
224    pub fn id(&self) -> Option<String> {
225        let pstr = unsafe { ffi::iio_channel_get_id(self.chan) };
226        cstring_opt(pstr)
227    }
228
229    /// Determines if this is an output channel.
230    #[inline]
231    pub fn is_output(&self) -> bool {
232        unsafe { ffi::iio_channel_is_output(self.chan) }
233    }
234
235    /// Determines if this is an input channel.
236    #[inline]
237    pub fn is_input(&self) -> bool {
238        !self.is_output()
239    }
240
241    /// Determines the direction of the channel
242    pub fn direction(&self) -> Direction {
243        match self.is_output() {
244            true => Direction::Output,
245            false => Direction::Input,
246        }
247    }
248
249    /// Determines if the channel is a scan element
250    ///
251    /// A scan element is a channel that can generate samples (for an
252    /// input  channel) or receive samples (for an output channel) after
253    /// being enabled.
254    pub fn is_scan_element(&self) -> bool {
255        unsafe { ffi::iio_channel_is_scan_element(self.chan) }
256    }
257
258    /// Gets the index of the channel in the device
259    pub fn index(&self) -> Result<usize> {
260        let ret = unsafe { ffi::iio_channel_get_index(self.chan) };
261        sys_result(ret as i32, ret as usize)
262    }
263
264    /// Determines if the channel has any attributes
265    pub fn has_attrs(&self) -> bool {
266        unsafe { ffi::iio_channel_get_attrs_count(self.chan) > 0 }
267    }
268
269    /// Gets the number of attributes for the channel
270    pub fn num_attrs(&self) -> usize {
271        let n = unsafe { ffi::iio_channel_get_attrs_count(self.chan) };
272        n as usize
273    }
274
275    /// Determines if the channel has the specified attribute.
276    pub fn has_attr(&self, attr: &str) -> bool {
277        let attr = cstring_or_bail_false!(attr);
278        unsafe { !ffi::iio_channel_find_attr(self.chan, attr.as_ptr()).is_null() }
279    }
280
281    /// Gets the channel-specific attribute at the index
282    pub fn get_attr(&self, idx: usize) -> Result<String> {
283        let pstr = unsafe { ffi::iio_channel_get_attr(self.chan, idx as c_uint) };
284        cstring_opt(pstr).ok_or(Error::InvalidIndex)
285    }
286
287    /// Try to find the channel-specific attribute by name.
288    pub fn find_attr(&self, name: &str) -> Option<String> {
289        let cname = cstring_or_bail!(name);
290        let pstr = unsafe { ffi::iio_channel_find_attr(self.chan, cname.as_ptr()) };
291        cstring_opt(pstr)
292    }
293
294    /// Reads a channel-specific attribute
295    ///
296    /// `attr` The name of the attribute
297    pub fn attr_read<T: FromAttribute>(&self, attr: &str) -> Result<T> {
298        let sval = self.attr_read_str(attr)?;
299        T::from_attr(&sval)
300    }
301
302    /// Reads a channel-specific attribute as a string
303    ///
304    /// `attr` The name of the attribute
305    pub fn attr_read_str(&self, attr: &str) -> Result<String> {
306        let mut buf = vec![0 as c_char; ATTR_BUF_SIZE];
307        let attr = CString::new(attr)?;
308        let ret = unsafe {
309            ffi::iio_channel_attr_read(self.chan, attr.as_ptr(), buf.as_mut_ptr(), buf.len())
310        };
311        sys_result(ret as i32, ())?;
312        let s = unsafe {
313            CStr::from_ptr(buf.as_ptr())
314                .to_str()
315                .map_err(|_| Error::StringConversionError)?
316        };
317        Ok(s.into())
318    }
319
320    /// Reads a channel-specific attribute as a boolean
321    /// `attr` The name of the attribute
322    pub fn attr_read_bool(&self, attr: &str) -> Result<bool> {
323        let mut val: bool = false;
324        let attr = CString::new(attr)?;
325        let ret = unsafe { ffi::iio_channel_attr_read_bool(self.chan, attr.as_ptr(), &mut val) };
326        sys_result(ret, val)
327    }
328
329    /// Reads a channel-specific attribute as an integer (i64)
330    ///
331    /// `attr` The name of the attribute
332    pub fn attr_read_int(&self, attr: &str) -> Result<i64> {
333        let mut val: c_longlong = 0;
334        let attr = CString::new(attr)?;
335        let ret =
336            unsafe { ffi::iio_channel_attr_read_longlong(self.chan, attr.as_ptr(), &mut val) };
337        sys_result(ret, val as i64)
338    }
339
340    /// Reads a channel-specific attribute as a floating-point (f64) number
341    ///
342    /// `attr` The name of the attribute
343    pub fn attr_read_float(&self, attr: &str) -> Result<f64> {
344        let mut val: f64 = 0.0;
345        let attr = CString::new(attr)?;
346        let ret = unsafe { ffi::iio_channel_attr_read_double(self.chan, attr.as_ptr(), &mut val) };
347        sys_result(ret, val)
348    }
349
350    // Callback from the C lib to extract the collection of all
351    // channel-specific attributes. See attr_read_all().
352    unsafe extern "C" fn attr_read_all_cb(
353        _chan: *mut ffi::iio_channel,
354        attr: *const c_char,
355        val: *const c_char,
356        _len: usize,
357        pmap: *mut c_void,
358    ) -> c_int {
359        if attr.is_null() || val.is_null() || pmap.is_null() {
360            return -1;
361        }
362
363        let attr = CStr::from_ptr(attr).to_string_lossy().to_string();
364        // TODO: We could/should check val[len-1] == '\x0'
365        let val = CStr::from_ptr(val).to_string_lossy().to_string();
366        let map: &mut HashMap<String, String> = &mut *pmap.cast();
367        map.insert(attr, val);
368        0
369    }
370
371    /// Reads all the channel-specific attributes.
372    /// This is especially useful when using the network backend to
373    /// retrieve all the attributes with a single call.
374    pub fn attr_read_all(&self) -> Result<HashMap<String, String>> {
375        let mut map = HashMap::new();
376        let pmap = (&mut map as *mut HashMap<_, _>).cast();
377        let ret = unsafe {
378            ffi::iio_channel_attr_read_all(self.chan, Some(Channel::attr_read_all_cb), pmap)
379        };
380        sys_result(ret, map)
381    }
382
383    /// Writes a channel-specific attribute
384    ///
385    /// `attr` The name of the attribute
386    /// `val` The value to write
387    pub fn attr_write<T: ToAttribute>(&self, attr: &str, val: T) -> Result<()> {
388        let sval = T::to_attr(&val)?;
389        self.attr_write_str(attr, &sval)
390    }
391
392    /// Writes a channel-specific attribute as a string
393    ///
394    /// `attr` The name of the attribute
395    /// `val` The value to write
396    pub fn attr_write_str(&self, attr: &str, val: &str) -> Result<()> {
397        let attr = CString::new(attr)?;
398        let sval = CString::new(val)?;
399        let ret = unsafe { ffi::iio_channel_attr_write(self.chan, attr.as_ptr(), sval.as_ptr()) };
400        sys_result(ret as i32, ())
401    }
402
403    /// Writes a channel-specific attribute as a boolean
404    ///
405    /// `attr` The name of the attribute
406    /// `val` The value to write
407    pub fn attr_write_bool(&self, attr: &str, val: bool) -> Result<()> {
408        let attr = CString::new(attr)?;
409        let ret = unsafe { ffi::iio_channel_attr_write_bool(self.chan, attr.as_ptr(), val) };
410        sys_result(ret, ())
411    }
412
413    /// Writes a channel-specific attribute as an integer (i64)
414    ///
415    /// `attr` The name of the attribute
416    /// `val` The value to write
417    pub fn attr_write_int(&self, attr: &str, val: i64) -> Result<()> {
418        let attr = CString::new(attr)?;
419        let ret = unsafe { ffi::iio_channel_attr_write_longlong(self.chan, attr.as_ptr(), val) };
420        sys_result(ret, ())
421    }
422
423    /// Writes a channel-specific attribute as a floating-point (f64) number
424    ///
425    /// `attr` The name of the attribute
426    /// `val` The value to write
427    pub fn attr_write_float(&self, attr: &str, val: f64) -> Result<()> {
428        let attr = CString::new(attr)?;
429        let ret = unsafe { ffi::iio_channel_attr_write_double(self.chan, attr.as_ptr(), val) };
430        sys_result(ret, ())
431    }
432
433    /// Gets an iterator for the attributes of the channel
434    pub fn attrs(&self) -> AttrIterator<'_> {
435        AttrIterator { chan: self, idx: 0 }
436    }
437
438    /// Enable the channel
439    ///
440    /// Before creating a buffer, at least one channel of the device
441    /// must be enabled.
442    pub fn enable(&self) {
443        unsafe { ffi::iio_channel_enable(self.chan) };
444    }
445
446    /// Disable the channel
447    pub fn disable(&self) {
448        unsafe { ffi::iio_channel_disable(self.chan) };
449    }
450
451    /// Determines if the channel is enabled
452    pub fn is_enabled(&self) -> bool {
453        unsafe { ffi::iio_channel_is_enabled(self.chan) }
454    }
455
456    // ----- Data Type and Conversion -----
457
458    /// Gets the data format for the channel
459    pub fn data_format(&self) -> DataFormat {
460        unsafe {
461            let pfmt = ffi::iio_channel_get_data_format(self.chan);
462            DataFormat::new(*pfmt)
463        }
464    }
465
466    /// Gets the `TypeId` for a single sample from the channel.
467    ///
468    /// This will get the `TypeId` for a sample if it can fit into a standard
469    /// integer type, signed or unsigned, of 8, 16, 32, or 64 bits.
470    pub fn type_of(&self) -> Option<TypeId> {
471        let dfmt = self.data_format();
472        dfmt.type_of()
473    }
474
475    /// Gets the type of data associated with the channel
476    pub fn channel_type(&self) -> ChannelType {
477        // TODO: We're trusting that the lib returns a valid enum.
478        unsafe {
479            let n = ffi::iio_channel_get_type(self.chan);
480            mem::transmute(n)
481        }
482    }
483
484    /// Converts a single sample from the hardware format to the host format.
485    ///
486    /// To be properly converted, the value must be the same type as that of
487    /// the channel, including size and sign. If not, the original value is
488    /// returned.
489    pub fn convert<T>(&self, val: T) -> T
490    where
491        T: Copy + 'static,
492    {
493        let mut retval = val;
494        if self.type_of() == Some(TypeId::of::<T>()) {
495            unsafe {
496                ffi::iio_channel_convert(
497                    self.chan,
498                    (&mut retval as *mut T).cast(),
499                    (&val as *const T).cast(),
500                );
501            }
502        }
503        retval
504    }
505
506    /// Converts a sample from the host format to the hardware format.
507    ///
508    /// To be properly converted, the value must be the same type as that of
509    /// the channel, including size and sign. If not, the original value is
510    /// returned.
511    pub fn convert_inverse<T>(&self, val: T) -> T
512    where
513        T: Copy + 'static,
514    {
515        let mut retval = val;
516        if self.type_of() == Some(TypeId::of::<T>()) {
517            unsafe {
518                ffi::iio_channel_convert_inverse(
519                    self.chan,
520                    (&mut retval as *mut T).cast(),
521                    (&val as *const T).cast(),
522                );
523            }
524        }
525        retval
526    }
527
528    /// Demultiplex and convert the samples of a given channel.
529    pub fn read<T>(&self, buf: &Buffer) -> Result<Vec<T>>
530    where
531        T: Default + Copy + 'static,
532    {
533        if self.type_of() != Some(TypeId::of::<T>()) {
534            return Err(Error::WrongDataType);
535        }
536
537        let n = buf.capacity();
538        let sz_item = size_of::<T>();
539        let sz_in = n * sz_item;
540
541        let mut v = vec![T::default(); n];
542        let sz = unsafe { ffi::iio_channel_read(self.chan, buf.buf, v.as_mut_ptr().cast(), sz_in) };
543
544        if sz > sz_in {
545            return Err(Error::BadReturnSize); // This should never happen.
546        }
547
548        if sz < sz_in {
549            v.truncate(sz / sz_item);
550        }
551        Ok(v)
552    }
553
554    /// Demultiplex the samples of a given channel.
555    pub fn read_raw<T>(&self, buf: &Buffer) -> Result<Vec<T>>
556    where
557        T: Default + Copy + 'static,
558    {
559        if self.type_of() != Some(TypeId::of::<T>()) {
560            return Err(Error::WrongDataType);
561        }
562
563        let n = buf.capacity();
564        let sz_item = size_of::<T>();
565        let sz_in = n * sz_item;
566
567        let mut v = vec![T::default(); n];
568        let sz =
569            unsafe { ffi::iio_channel_read_raw(self.chan, buf.buf, v.as_mut_ptr().cast(), sz_in) };
570
571        if sz > sz_in {
572            return Err(Error::BadReturnSize); // This should never happen.
573        }
574
575        if sz < sz_in {
576            v.truncate(sz / sz_item);
577        }
578        Ok(v)
579    }
580
581    /// Convert and multiplex the samples of a given channel.
582    /// Returns the number of items written.
583    pub fn write<T>(&self, buf: &Buffer, data: &[T]) -> Result<usize>
584    where
585        T: Default + Copy + 'static,
586    {
587        if self.type_of() != Some(TypeId::of::<T>()) {
588            return Err(Error::WrongDataType);
589        }
590
591        let sz_item = size_of::<T>();
592        let sz_in = size_of_val(data);
593
594        let sz = unsafe { ffi::iio_channel_write(self.chan, buf.buf, data.as_ptr().cast(), sz_in) };
595
596        Ok(sz / sz_item)
597    }
598
599    /// Multiplex the samples of a given channel.
600    /// Returns the number of items written.
601    pub fn write_raw<T>(&self, buf: &Buffer, data: &[T]) -> Result<usize>
602    where
603        T: Default + Copy + 'static,
604    {
605        if self.type_of() != Some(TypeId::of::<T>()) {
606            return Err(Error::WrongDataType);
607        }
608
609        let sz_item = size_of::<T>();
610        let sz_in = size_of_val(data);
611
612        let sz = unsafe { ffi::iio_channel_write(self.chan, buf.buf, data.as_ptr().cast(), sz_in) };
613
614        Ok(sz / sz_item)
615    }
616}
617
618impl PartialEq for Channel {
619    /// Two channels are the same if they refer to the same underlying
620    /// object in the library.
621    fn eq(&self, other: &Self) -> bool {
622        self.chan == other.chan
623    }
624}
625
626/// Iterator over the attributes of a Channel
627#[derive(Debug)]
628pub struct AttrIterator<'a> {
629    /// Reference to the Channel that we're scanning for attributes
630    chan: &'a Channel,
631    /// Index for the next Channel attribute from the iterator
632    idx: usize,
633}
634
635impl Iterator for AttrIterator<'_> {
636    type Item = String;
637
638    /// Gets the next Channel attribute from the iterator
639    fn next(&mut self) -> Option<Self::Item> {
640        match self.chan.get_attr(self.idx) {
641            Ok(name) => {
642                self.idx += 1;
643                Some(name)
644            }
645            Err(_) => None,
646        }
647    }
648}
649
650// --------------------------------------------------------------------------
651//                              Unit Tests
652// --------------------------------------------------------------------------
653
654// Note: These tests assume that the IIO Dummy kernel module is loaded
655// locally with a device created. See the `load_dummy.sh` script.
656
657#[cfg(test)]
658mod tests {
659    use super::*;
660
661    const DEV_ID: &str = "dummydev";
662
663    // See that we get the default context.
664    #[test]
665    fn default_context() {
666        let ctx = Context::new().unwrap();
667        let dev = ctx.find_device(DEV_ID).unwrap();
668
669        let idx_chan = dev.get_channel(0).unwrap();
670        let id = idx_chan.id().unwrap();
671        let dir = idx_chan.direction();
672
673        let id_chan = dev.find_channel(&id, dir).unwrap();
674        assert_eq!(id_chan, idx_chan);
675    }
676}