industrial_io/
lib.rs

1// industrial-io/src/lib.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//!
11//! The Rust Industrial I/O crate for Linux.
12//!
13//! This is a Rust wrapper for _libiio_, a library for high-performance
14//! analog I/O from Linux user-space. It interacts with Linux Industrial I/O
15//! (IIO) devices such as A/D's, D/A's, accelerometers, pressure and
16//! temperature sensors, magnetometers, and so on.
17//!
18//! For more information, see:
19//!
20//!   [IIO Wiki](https://wiki.analog.com/software/linux/docs/iio/iio)
21//!
22//!   [libiio Wiki](https://wiki.analog.com/resources/tools-software/linux-software/libiio)
23//!
24//!
25//! #### Default Features ####
26//!
27//! * **utilities** - Whether to build the utility applications
28//! * **libiio_v0_24** - Use the bindings for _libiio_ v0.24
29//!
30//! #### Optional Features ####
31//!
32//! Note, if using alternate bindings for _libiio_, you must disable the
33//! default features, and only select one version.
34//!
35//! * **libiio_v0_25** - Use the bindings for _libiio_ v0.25
36//! * **libiio_v0_24** - Use the bindings for _libiio_ v0.24
37//! * **libiio_v0_23** - Use the bindings for _libiio_ v0.23
38//! * **libiio_v0_21** - Use the bindings for _libiio_ v0.21
39//! * **libiio_v0_19** - Use the bindings for _libiio_ v0.19
40//!
41
42// Lints
43#![deny(
44    missing_docs,
45    missing_debug_implementations,
46    missing_copy_implementations,
47    unstable_features,
48    unused_import_braces,
49    unused_qualifications
50)]
51#![warn(rustdoc::broken_intra_doc_links)]
52// Conversions from "C" types (c_int, etc) may not be useless on all targets.
53#![allow(clippy::useless_conversion)]
54
55use std::{
56    collections::HashMap,
57    ffi::{CStr, CString},
58    fmt,
59    os::raw::{c_char, c_int, c_uint, c_void},
60    slice,
61    str::{self, FromStr},
62};
63
64use libiio_sys::{self as ffi};
65use nix::errno::Errno;
66
67pub use crate::buffer::{AttrIterator as BufferAttrIterator, Buffer};
68pub use crate::channel::{
69    AttrIterator as ChannelAttrIterator, Channel, ChannelType, DataFormat, Direction,
70};
71pub use crate::context::{
72    AttrIterator as ContextAttrIterator, Backend, Context, DeviceIterator, InnerContext,
73};
74pub use crate::device::{AttrIterator as DeviceAttrIterator, ChannelIterator, Device};
75pub use crate::errors::{Error, Result};
76
77#[cfg(not(feature = "libiio_v0_19"))]
78pub use crate::scan_context::{ScanContext, ScanContextIterator};
79
80mod macros;
81
82pub mod buffer;
83pub mod channel;
84pub mod context;
85pub mod device;
86pub mod errors;
87
88#[cfg(not(feature = "libiio_v0_19"))]
89pub mod scan_context;
90
91/// According to the IIO samples, internal buffers need to be big enough
92/// for attributes coming back from the kernel.
93const ATTR_BUF_SIZE: usize = 16384;
94
95// --------------------------------------------------------------------------
96
97/// Gets an optional string value from a C const char pointer.
98/// If the pointer is NULL, this returns `None` otherwise it converts the
99/// string and returns it.
100fn cstring_opt(pstr: *const c_char) -> Option<String> {
101    unsafe {
102        pstr.as_ref().map(|_| {
103            let name = CStr::from_ptr(pstr);
104            name.to_str().unwrap_or_default().to_string()
105        })
106    }
107}
108
109pub(crate) fn sys_result<T>(ret: i32, result: T) -> Result<T> {
110    match ret {
111        ret if ret < 0 => Err(Errno::from_raw(-ret).into()),
112        _ => Ok(result),
113    }
114}
115
116/// Trait to convert a value to a proper attribute string.
117pub trait ToAttribute: fmt::Display {
118    /// Converts the attribute name and value to an attribute string that
119    /// can be sent to the C library.
120    ///
121    /// `attr` The name of the attribute
122    /// `val` The value to write.
123    fn to_attr(&self) -> Result<String> {
124        Ok(format!("{}", self))
125    }
126}
127
128/// Trait to convert an attribute string to a typed value.
129pub trait FromAttribute: FromStr {
130    /// Converts a string attribute to a value type.
131    fn from_attr(s: &str) -> Result<Self> {
132        let val = Self::from_str(s).map_err(|_| Error::StringConversionError)?;
133        Ok(val)
134    }
135}
136
137/// Attribute conversion for the bool type.
138///
139/// The bool type needs a special implementation of the attribute conversion
140/// trait because it's default Rust string counterparts are "true" and "false".
141/// However, sysfs expects these to be "1" or "0".
142impl ToAttribute for bool {
143    fn to_attr(&self) -> Result<String> {
144        Ok((if *self { "1" } else { "0" }).into())
145    }
146}
147
148impl FromAttribute for bool {
149    fn from_attr(s: &str) -> Result<Self> {
150        Ok(s.trim() != "0")
151    }
152}
153
154// Default trait implementations for the types in the IIO lib
155impl ToAttribute for i32 {}
156impl ToAttribute for u32 {}
157impl ToAttribute for i64 {}
158impl ToAttribute for u64 {}
159impl ToAttribute for i128 {}
160impl ToAttribute for u128 {}
161impl ToAttribute for f64 {}
162impl ToAttribute for str {}
163impl ToAttribute for &str {}
164impl ToAttribute for String {}
165
166impl FromAttribute for i32 {}
167impl FromAttribute for u32 {}
168impl FromAttribute for i64 {}
169impl FromAttribute for u64 {}
170impl FromAttribute for i128 {}
171impl FromAttribute for u128 {}
172impl FromAttribute for f64 {}
173impl FromAttribute for String {}
174
175// Callback from the C lib to extract the collection of all
176// device-specific attributes. See attr_read_all().
177pub(crate) unsafe extern "C" fn attr_read_all_cb(
178    _chan: *mut ffi::iio_device,
179    attr: *const c_char,
180    val: *const c_char,
181    _len: usize,
182    pmap: *mut c_void,
183) -> c_int {
184    if attr.is_null() || val.is_null() || pmap.is_null() {
185        return -1;
186    }
187
188    let attr = CStr::from_ptr(attr).to_string_lossy().to_string();
189    // TODO: We could/should check val[len-1] == '\x0'
190    let val = CStr::from_ptr(val).to_string_lossy().to_string();
191    let map: &mut HashMap<String, String> = &mut *pmap.cast();
192    map.insert(attr, val);
193    0
194}
195
196// --------------------------------------------------------------------------
197
198/// A struct to hold version numbers
199#[derive(Debug, PartialEq, Eq)]
200pub struct Version {
201    /// The Major version number
202    pub major: u32,
203    /// The Minor version number
204    pub minor: u32,
205    /// The git tag for the release
206    pub git_tag: String,
207}
208
209impl fmt::Display for Version {
210    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
211        write!(f, "{}.{} tag: {}", self.major, self.minor, self.git_tag)
212    }
213}
214
215// --------------------------------------------------------------------------
216
217/// Gets the library version as (Major, Minor, Git Tag)
218pub fn library_version() -> Version {
219    let mut major: c_uint = 0;
220    let mut minor: c_uint = 0;
221
222    const BUF_SZ: usize = 8;
223    let mut buf = vec![' ' as c_char; BUF_SZ];
224    let pbuf = buf.as_mut_ptr();
225
226    unsafe { ffi::iio_library_get_version(&mut major, &mut minor, pbuf) };
227
228    let sgit = unsafe {
229        if buf.contains(&0) {
230            CStr::from_ptr(pbuf).to_owned()
231        }
232        else {
233            let slc = str::from_utf8(slice::from_raw_parts(pbuf.cast(), BUF_SZ)).unwrap();
234            CString::new(slc).unwrap()
235        }
236    };
237    Version {
238        major: major as u32,
239        minor: minor as u32,
240        git_tag: sgit.to_string_lossy().into_owned(),
241    }
242}
243
244// --------------------------------------------------------------------------
245
246#[cfg(test)]
247mod tests {
248    use super::*;
249    use std::ptr;
250
251    // Just make sure version gives a consistent result.
252    #[test]
253    fn version() {
254        let v1 = library_version();
255        let v2 = library_version();
256        assert!(v1 == v2);
257    }
258
259    #[test]
260    fn test_cstring_opt() {
261        assert_eq!(cstring_opt(ptr::null()), None);
262
263        let s: &[u8] = &[b'h', b'e', b'l', b'l', b'o', 0];
264        let p = s.as_ptr() as *const c_char;
265        assert_eq!(cstring_opt(p), Some(String::from("hello")));
266    }
267
268    #[test]
269    fn test_sys_result() {
270        assert!(sys_result(-1, "hello").is_err());
271        assert!(matches!(sys_result(1, "hello"), Ok("hello")));
272    }
273
274    #[test]
275    fn val_from_attr_str() {
276        let val: i32 = i32::from_attr("123").unwrap();
277        assert_eq!(val, 123);
278
279        let val = bool::from_attr("1").unwrap();
280        assert!(val);
281
282        let val: bool = FromAttribute::from_attr(" 0 \n").unwrap();
283        assert!(!val);
284
285        let val: String = String::from_attr("hello").unwrap();
286        assert_eq!(&val, "hello");
287    }
288
289    #[test]
290    fn val_to_attr_string() {
291        let s = i32::to_attr(&123).unwrap();
292        assert_eq!(&s, "123");
293
294        let s = bool::to_attr(&true).unwrap();
295        assert_eq!(&s, "1");
296
297        let s = bool::to_attr(&false).unwrap();
298        assert_eq!(&s, "0");
299
300        let s = ToAttribute::to_attr("hello").unwrap();
301        assert_eq!(&s, "hello");
302
303        let s = String::to_attr(&"hello".to_string()).unwrap();
304        assert_eq!(s.as_str(), "hello");
305    }
306}