libgpiod/
event_buffer.rs

1// SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause
2// SPDX-FileCopyrightText: 2022 Linaro Ltd.
3// SPDX-FileCopyrightText: 2022 Viresh Kumar <viresh.kumar@linaro.org>
4
5use std::ptr;
6
7use super::{
8    Error, OperationType, Result, gpiod,
9    request::{Event, Request},
10};
11
12/// Line edge events
13///
14/// An iterator over the elements of type `Event`.
15pub struct Events<'a> {
16    buffer: &'a mut Buffer,
17    read_index: usize,
18    len: usize,
19}
20
21impl<'a> Events<'a> {
22    pub fn new(buffer: &'a mut Buffer, len: usize) -> Self {
23        Self {
24            buffer,
25            read_index: 0,
26            len,
27        }
28    }
29
30    /// Get the number of contained events in the snapshot, this doesn't change
31    /// on reading events from the iterator.
32    pub fn len(&self) -> usize {
33        self.len
34    }
35
36    /// Check if buffer is empty.
37    pub fn is_empty(&self) -> bool {
38        self.len == 0
39    }
40}
41
42impl<'a> Iterator for Events<'a> {
43    type Item = Result<&'a Event>;
44
45    fn nth(&mut self, n: usize) -> Option<Self::Item> {
46        if self.read_index + n >= self.len {
47            return None;
48        }
49
50        self.read_index += n + 1;
51        Some(self.buffer.event(self.read_index - 1))
52    }
53
54    fn next(&mut self) -> Option<Self::Item> {
55        // clippy false-positive, fixed in next clippy release:
56        // https://github.com/rust-lang/rust-clippy/issues/9820
57        #[allow(clippy::iter_nth_zero)]
58        self.nth(0)
59    }
60}
61
62/// Line edge events buffer
63#[derive(Debug, Eq, PartialEq)]
64pub struct Buffer {
65    pub(crate) buffer: *mut gpiod::gpiod_edge_event_buffer,
66    events: Vec<*mut gpiod::gpiod_edge_event>,
67}
68
69// SAFETY: Buffer models an owned gpiod_edge_event_buffer. However, there may
70// be events tied to it. Concurrent access from multiple threads to a buffer
71// and its associated events is not allowed by the C lib.
72// In Rust, those events will always be borrowed from a buffer instance. Thus,
73// either Rust prevents the user to move the Buffer while there are still
74// borrowed events, or we can safely send the the Buffer.
75unsafe impl Send for Buffer {}
76
77impl Buffer {
78    /// Create a new edge event buffer.
79    ///
80    /// If capacity equals 0, it will be set to a default value of 64. If
81    /// capacity is larger than 1024, it will be limited to 1024.
82    pub fn new(capacity: usize) -> Result<Self> {
83        // SAFETY: The `gpiod_edge_event_buffer` returned by libgpiod is guaranteed to live as long
84        // as the `struct Buffer`.
85        let buffer = unsafe { gpiod::gpiod_edge_event_buffer_new(capacity) };
86        if buffer.is_null() {
87            return Err(Error::OperationFailed(
88                OperationType::EdgeEventBufferNew,
89                errno::errno(),
90            ));
91        }
92
93        // SAFETY: `gpiod_edge_event_buffer` is guaranteed to be valid here.
94        let capacity = unsafe { gpiod::gpiod_edge_event_buffer_get_capacity(buffer) };
95
96        Ok(Self {
97            buffer,
98            events: vec![ptr::null_mut(); capacity],
99        })
100    }
101
102    /// Get the capacity of the event buffer.
103    pub fn capacity(&self) -> usize {
104        self.events.len()
105    }
106
107    /// Get edge events from a line request.
108    ///
109    /// This function will block if no event was queued for the line.
110    pub fn read_edge_events<'a>(&'a mut self, request: &Request) -> Result<Events<'a>> {
111        for i in 0..self.events.len() {
112            self.events[i] = ptr::null_mut();
113        }
114
115        // SAFETY: `gpiod_line_request` is guaranteed to be valid here.
116        let ret = unsafe {
117            gpiod::gpiod_line_request_read_edge_events(
118                request.request,
119                self.buffer,
120                self.events.len(),
121            )
122        };
123
124        if ret == -1 {
125            Err(Error::OperationFailed(
126                OperationType::LineRequestReadEdgeEvent,
127                errno::errno(),
128            ))
129        } else {
130            let ret = ret as usize;
131
132            if ret > self.events.len() {
133                Err(Error::TooManyEvents(ret, self.events.len()))
134            } else {
135                Ok(Events::new(self, ret))
136            }
137        }
138    }
139
140    /// Read an event stored in the buffer.
141    fn event<'a>(&mut self, index: usize) -> Result<&'a Event> {
142        if self.events[index].is_null() {
143            // SAFETY: The `gpiod_edge_event` returned by libgpiod is guaranteed to live as long
144            // as the `struct Event`.
145            let event = unsafe {
146                gpiod::gpiod_edge_event_buffer_get_event(self.buffer, index.try_into().unwrap())
147            };
148
149            if event.is_null() {
150                return Err(Error::OperationFailed(
151                    OperationType::EdgeEventBufferGetEvent,
152                    errno::errno(),
153                ));
154            }
155
156            self.events[index] = event;
157        }
158
159        // SAFETY: Safe as the underlying events object won't get freed until the time the returned
160        // reference is still used.
161        Ok(unsafe {
162            // This will not lead to `drop(event)`.
163            (self.events.as_ptr().add(index) as *const Event)
164                .as_ref()
165                .unwrap()
166        })
167    }
168}
169
170impl Drop for Buffer {
171    /// Free the edge event buffer and release all associated resources.
172    fn drop(&mut self) {
173        // SAFETY: `gpiod_edge_event_buffer` is guaranteed to be valid here.
174        unsafe { gpiod::gpiod_edge_event_buffer_free(self.buffer) };
175    }
176}