industrial_io/
buffer.rs

1// libiio-sys/src/buffer.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 Buffers.
11//!
12//! The process of capturing samples from or uploading samples to hardware is
13//! managed using [`Buffer`] and related methods.
14//!
15//! It is important to keep in mind that an instance of [`Buffer`] is always
16//! coupled to exactly **one instance of [`Device`]**, and vice-versa.
17//! [`Buffer`]s are allocated on a per-[`Device`] basis, and not per
18//! [`Channel`]. In order to control which [`Channel`]s to capture in a
19//! [`Buffer`], the respective [`Channel`]s must be [enabled][enable_chan] or
20//! [disabled][disable_chan].
21//!
22//! The very first step when working with [`Buffer`]s is to
23//! [enable][enable_chan] the capture [`Channel`]s that we want to use, and
24//! [disable][disable_chan] those that we don't need. This is done with the
25//! functions [`Channel::enable()`] and [`Channel::disable()`]. Note that the
26//! [`Channel`]s will really be enabled or disabled when the [`Buffer`]-object
27//! is created.
28//!
29//! Also, not all [`Channel`]s can be enabled. To know whether or not one
30//! [`Channel`] can be enabled, use [`Channel::is_scan_element()`].
31//!
32//! Once the [`Channel`]s have been enabled, and [triggers assigned] (for
33//! triggered [`Buffer`]s) the [`Buffer`] object can be created from the
34//! [`Device`] object that will be used, with the function
35//! [`Device::create_buffer()`]. This call will fail if no [`Channel`]s have
36//! been enabled, or for triggered buffers, if the trigger has not been
37//! assigned.
38//!
39//! [`Buffer`] objects are automatically dropped when their scope ends.
40//!
41//! For additional information on actually working with [`Buffer`]s, including
42//! some examples, refer to the [`Buffer` documentation][`Buffer`].
43//!
44//! Most parts of the documentation for this module were taken from the [libiio
45//! documentation](https://analogdevicesinc.github.io/libiio/master/libiio/index.html)
46//!
47//! [enable_chan]: crate::channel::Channel::enable()
48//! [disable_chan]: crate::channel::Channel::disable()
49//! [triggers assigned]: crate::device::Device::set_trigger()
50
51use std::{
52    collections::HashMap,
53    marker::PhantomData,
54    mem::size_of,
55    os::raw::{c_int, c_longlong},
56};
57
58use super::*;
59use crate::ffi;
60
61/// An Industrial I/O input or output buffer.
62///
63/// See [here][crate::buffer] for a detailed explanation of how buffers work.
64///
65/// # Examples
66///
67#[derive(Debug)]
68pub struct Buffer {
69    /// The underlying buffer from the C library
70    pub(crate) buf: *mut ffi::iio_buffer,
71    /// The buffer capacity (# samples from each channel)
72    pub(crate) cap: usize,
73    /// Copy of the device to which this device is attached.
74    pub(crate) dev: Device,
75}
76
77impl Buffer {
78    /// Get the buffer size.
79    ///
80    /// Get the buffer capacity in number of samples from each channel that
81    /// the buffer can hold.
82    pub fn capacity(&self) -> usize {
83        self.cap
84    }
85
86    /// Gets a reference to the device to which this buffer is attached.
87    pub fn device(&self) -> &Device {
88        &self.dev
89    }
90
91    /// Gets a pollable file descriptor for the buffer.
92    ///
93    /// This can be used to determine when [`Buffer::refill()`] or
94    /// [`Buffer::push()`] can be called without blocking.
95    pub fn poll_fd(&self) -> Result<c_int> {
96        let ret = unsafe { ffi::iio_buffer_get_poll_fd(self.buf) };
97        sys_result(i32::from(ret), ret)
98    }
99
100    /// Make calls to [`push()`](Buffer::push) or [`refill()`](Buffer::refill)
101    /// blocking or not.
102    ///
103    /// A [`Device`] is blocking by default.
104    pub fn set_blocking_mode(&self, blocking: bool) -> Result<()> {
105        let ret = unsafe { ffi::iio_buffer_set_blocking_mode(self.buf, blocking) };
106        sys_result(ret, ())
107    }
108
109    /// Fetch more samples from the hardware.
110    ///
111    /// This is only valid for input buffers.
112    pub fn refill(&mut self) -> Result<usize> {
113        let ret = unsafe { ffi::iio_buffer_refill(self.buf) };
114        sys_result(ret as i32, ret as usize)
115    }
116
117    /// Send the samples to the hardware.
118    ///
119    /// This is only valid for output buffers.
120    pub fn push(&self) -> Result<usize> {
121        let ret = unsafe { ffi::iio_buffer_push(self.buf) };
122        sys_result(ret as i32, ret as usize)
123    }
124
125    /// Send a given number of samples to the hardware.
126    ///
127    /// This is only valid for output buffers. Note that the number of samples
128    /// explicitly doesn't refer to their size in bytes, but the actual number
129    /// of samples, regardless of the sample size in memory.
130    pub fn push_partial(&self, num_samples: usize) -> Result<usize> {
131        let ret = unsafe { ffi::iio_buffer_push_partial(self.buf, num_samples) };
132        sys_result(ret as i32, ret as usize)
133    }
134
135    /// Cancel all buffer operations.
136    ///
137    /// This function cancels all outstanding [`Buffer`] operations
138    /// previously scheduled. This means any pending [`push()`](Buffer::push)
139    /// or [`refill()`](Buffer::refill) operation will abort and return
140    /// immediately, any further invocations of these functions on the same
141    /// buffer will return immediately with an error.
142    ///
143    /// Usually [`push()`](Buffer::push) and [`refill()`](Buffer::refill)
144    /// will block until either all data has been transferred or a timeout
145    /// occurs. This can, depending on the configuration, take a significant
146    /// amount of time. [`cancel()`](Buffer::cancel) is useful to bypass these
147    /// conditions if the [`Buffer`] operation is supposed to be stopped in
148    /// response to an external event (e.g. user input).
149    ///
150    /// To be able to capture additional data after calling this function the
151    /// buffer should be destroyed and then re-created.
152    ///
153    /// This function can be called multiple times for the same buffer, but all
154    /// but the first invocation will be without additional effect.
155    pub fn cancel(&self) {
156        unsafe {
157            ffi::iio_buffer_cancel(self.buf);
158        }
159    }
160
161    /// Determines if the device has any buffer-specific attributes
162    pub fn has_attrs(&self) -> bool {
163        unsafe { ffi::iio_device_get_buffer_attrs_count(self.dev.dev) > 0 }
164    }
165
166    /// Gets the number of buffer-specific attributes
167    pub fn num_attrs(&self) -> usize {
168        unsafe { ffi::iio_device_get_buffer_attrs_count(self.dev.dev) as usize }
169    }
170
171    /// Gets the name of the buffer-specific attribute at the index
172    pub fn get_attr(&self, idx: usize) -> Result<String> {
173        let pstr = unsafe { ffi::iio_device_get_buffer_attr(self.dev.dev, idx as c_uint) };
174        cstring_opt(pstr).ok_or(Error::InvalidIndex)
175    }
176
177    /// Try to find a buffer-specific attribute by its name
178    pub fn find_attr(&self, name: &str) -> Option<String> {
179        let cname = cstring_or_bail!(name);
180        let pstr = unsafe { ffi::iio_device_find_buffer_attr(self.dev.dev, cname.as_ptr()) };
181        cstring_opt(pstr)
182    }
183
184    /// Determines if a buffer-specific attribute exists
185    pub fn has_attr(&self, name: &str) -> bool {
186        let cname = cstring_or_bail_false!(name);
187        let pstr = unsafe { ffi::iio_device_find_buffer_attr(self.dev.dev, cname.as_ptr()) };
188        !pstr.is_null()
189    }
190
191    /// Reads a buffer-specific attribute
192    ///
193    /// `attr` The name of the attribute
194    pub fn attr_read<T: FromAttribute>(&self, attr: &str) -> Result<T> {
195        let sval = self.attr_read_str(attr)?;
196        T::from_attr(&sval)
197    }
198
199    /// Reads a buffer-specific attribute as a string
200    ///
201    /// `attr` The name of the attribute
202    pub fn attr_read_str(&self, attr: &str) -> Result<String> {
203        let mut buf = vec![0 as c_char; ATTR_BUF_SIZE];
204        let attr = CString::new(attr)?;
205        let ret = unsafe {
206            ffi::iio_device_buffer_attr_read(
207                self.dev.dev,
208                attr.as_ptr(),
209                buf.as_mut_ptr(),
210                buf.len(),
211            )
212        };
213        sys_result(ret as i32, ())?;
214        let s = unsafe {
215            CStr::from_ptr(buf.as_ptr())
216                .to_str()
217                .map_err(|_| Error::StringConversionError)?
218        };
219        Ok(s.into())
220    }
221
222    /// Reads a buffer-specific attribute as a boolean
223    ///
224    /// `attr` The name of the attribute
225    pub fn attr_read_bool(&self, attr: &str) -> Result<bool> {
226        let mut val: bool = false;
227        let attr = CString::new(attr)?;
228        let ret =
229            unsafe { ffi::iio_device_buffer_attr_read_bool(self.dev.dev, attr.as_ptr(), &mut val) };
230        sys_result(ret, val)
231    }
232
233    /// Reads a buffer-specific attribute as an integer (i64)
234    ///
235    /// `attr` The name of the attribute
236    pub fn attr_read_int(&self, attr: &str) -> Result<i64> {
237        let mut val: c_longlong = 0;
238        let attr = CString::new(attr)?;
239        let ret = unsafe {
240            ffi::iio_device_buffer_attr_read_longlong(self.dev.dev, attr.as_ptr(), &mut val)
241        };
242        sys_result(ret, val as i64)
243    }
244
245    /// Reads a buffer-specific attribute as a floating-point (f64) number
246    ///
247    /// `attr` The name of the attribute
248    pub fn attr_read_float(&self, attr: &str) -> Result<f64> {
249        let mut val: f64 = 0.0;
250        let attr = CString::new(attr)?;
251        let ret = unsafe {
252            ffi::iio_device_buffer_attr_read_double(self.dev.dev, attr.as_ptr(), &mut val)
253        };
254        sys_result(ret, val)
255    }
256
257    /// Reads all the buffer-specific attributes.
258    /// This is especially useful when using the network backend to
259    /// retrieve all the attributes with a single call.
260    pub fn attr_read_all(&self) -> Result<HashMap<String, String>> {
261        let mut map = HashMap::new();
262        let pmap = (&mut map as *mut HashMap<_, _>).cast();
263        let ret = unsafe {
264            ffi::iio_device_buffer_attr_read_all(self.dev.dev, Some(attr_read_all_cb), pmap)
265        };
266        sys_result(ret, map)
267    }
268
269    /// Writes a buffer-specific attribute
270    ///
271    /// `attr` The name of the attribute
272    /// `val` The value to write
273    pub fn attr_write<T: ToAttribute>(&self, attr: &str, val: T) -> Result<()> {
274        let sval = T::to_attr(&val)?;
275        self.attr_write_str(attr, &sval)
276    }
277
278    /// Writes a buffer-specific attribute as a string
279    ///
280    /// `attr` The name of the attribute
281    /// `val` The value to write
282    pub fn attr_write_str(&self, attr: &str, val: &str) -> Result<()> {
283        let attr = CString::new(attr)?;
284        let sval = CString::new(val)?;
285        let ret = unsafe {
286            ffi::iio_device_buffer_attr_write(self.dev.dev, attr.as_ptr(), sval.as_ptr())
287        };
288        sys_result(ret as i32, ())
289    }
290
291    /// Writes a buffer-specific attribute as a boolean
292    ///
293    /// `attr` The name of the attribute
294    /// `val` The value to write
295    pub fn attr_write_bool(&self, attr: &str, val: bool) -> Result<()> {
296        let attr = CString::new(attr)?;
297        let ret =
298            unsafe { ffi::iio_device_buffer_attr_write_bool(self.dev.dev, attr.as_ptr(), val) };
299        sys_result(ret, ())
300    }
301
302    /// Writes a buffer-specific attribute as an integer (i64)
303    ///
304    /// `attr` The name of the attribute
305    /// `val` The value to write
306    pub fn attr_write_int(&self, attr: &str, val: i64) -> Result<()> {
307        let attr = CString::new(attr)?;
308        let ret =
309            unsafe { ffi::iio_device_buffer_attr_write_longlong(self.dev.dev, attr.as_ptr(), val) };
310        sys_result(ret, ())
311    }
312
313    /// Writes a buffer-specific attribute as a floating-point (f64) number
314    ///
315    /// `attr` The name of the attribute
316    /// `val` The value to write
317    pub fn attr_write_float(&self, attr: &str, val: f64) -> Result<()> {
318        let attr = CString::new(attr)?;
319        let ret =
320            unsafe { ffi::iio_device_buffer_attr_write_double(self.dev.dev, attr.as_ptr(), val) };
321        sys_result(ret, ())
322    }
323
324    /// Gets an iterator for the buffer attributes in the device
325    pub fn attributes(&self) -> AttrIterator<'_> {
326        AttrIterator { buf: self, idx: 0 }
327    }
328
329    /// Gets an iterator for the data from a channel.
330    pub fn channel_iter<T>(&self, chan: &Channel) -> Iter<'_, T> {
331        Iter::new(self, chan)
332    }
333
334    /// Gets a mutable iterator for the data to a channel.
335    pub fn channel_iter_mut<T>(&mut self, chan: &Channel) -> IterMut<'_, T> {
336        IterMut::new(self, chan)
337    }
338}
339
340/// Destroy the underlying buffer when the object scope ends.
341impl Drop for Buffer {
342    fn drop(&mut self) {
343        unsafe { ffi::iio_buffer_destroy(self.buf) }
344    }
345}
346
347/// An iterator that moves channel data out of a buffer.
348#[derive(Debug)]
349pub struct Iter<'a, T: 'a> {
350    _phantom: PhantomData<&'a T>,
351    // Pointer to the current sample for a channel
352    ptr: *const T,
353    // Pointer to the end of the buffer
354    end: *const T,
355    // The offset to the next sample for the channel
356    step: isize,
357}
358
359impl<T> Iter<'_, T> {
360    /// Create an iterator to move channel data out of a buffer.
361    pub fn new(buf: &Buffer, chan: &Channel) -> Self {
362        unsafe {
363            let begin = ffi::iio_buffer_first(buf.buf, chan.chan).cast();
364            let end = ffi::iio_buffer_end(buf.buf).cast();
365            let ptr = begin;
366            let step: isize = ffi::iio_buffer_step(buf.buf) / size_of::<T>() as isize;
367
368            Self {
369                _phantom: PhantomData,
370                ptr,
371                end,
372                step,
373            }
374        }
375    }
376}
377
378impl<'a, T: 'a> Iterator for Iter<'a, T> {
379    type Item = &'a T;
380
381    fn next(&mut self) -> Option<Self::Item> {
382        unsafe {
383            if self.ptr.cast() >= self.end {
384                None
385            }
386            else {
387                let prev = self.ptr;
388                self.ptr = self.ptr.offset(self.step);
389                Some(&*prev)
390            }
391        }
392    }
393}
394
395/// A mutable iterator to move channel data into a buffer.
396#[derive(Debug)]
397pub struct IterMut<'a, T: 'a> {
398    // Reference to the buffer lifetime
399    _phantom: PhantomData<&'a mut T>,
400    // Pointer to the current sample for a channel
401    ptr: *mut T,
402    // Pointer to the end of the buffer
403    end: *const T,
404    // The offset to the next sample for the channel
405    step: isize,
406}
407
408impl<'a, T: 'a> IterMut<'a, T> {
409    /// Create a mutable iterator to move channel data into a buffer.
410    pub fn new(buf: &'a mut Buffer, chan: &Channel) -> Self {
411        unsafe {
412            let begin = ffi::iio_buffer_first(buf.buf, chan.chan).cast();
413            let end = ffi::iio_buffer_end(buf.buf).cast();
414            let ptr = begin;
415            let step: isize = ffi::iio_buffer_step(buf.buf) / size_of::<T>() as isize;
416
417            Self {
418                _phantom: PhantomData,
419                ptr,
420                end,
421                step,
422            }
423        }
424    }
425}
426
427impl<'a, T: 'a> Iterator for IterMut<'a, T> {
428    type Item = &'a mut T;
429
430    fn next(&mut self) -> Option<Self::Item> {
431        if self.ptr as *const T >= self.end {
432            None
433        }
434        else {
435            unsafe {
436                let prev = self.ptr;
437                self.ptr = self.ptr.offset(self.step);
438                Some(&mut *prev)
439            }
440        }
441    }
442}
443
444/// Iterator over the buffer attributes
445/// 'a Lifetime of the Buffer
446#[derive(Debug)]
447pub struct AttrIterator<'a> {
448    /// Reference to the Buffer that we're scanning for attributes
449    buf: &'a Buffer,
450    /// Index to the next Buffer attribute from the iterator
451    idx: usize,
452}
453
454impl Iterator for AttrIterator<'_> {
455    type Item = String;
456
457    /// Gets the next Buffer attribute from the iterator
458    fn next(&mut self) -> Option<Self::Item> {
459        match self.buf.get_attr(self.idx) {
460            Ok(name) => {
461                self.idx += 1;
462                Some(name)
463            }
464            Err(_) => None,
465        }
466    }
467}
468
469// --------------------------------------------------------------------------
470//                              Unit Tests
471// --------------------------------------------------------------------------
472
473// Note: These tests assume that the IIO Dummy kernel module is loaded
474// locally with a device created. See the `load_dummy.sh` script.
475
476#[cfg(test)]
477mod tests {
478    //use super::*;
479
480    //const DEV_ID: &str = "dummydev";
481}