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}