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}