| // SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause |
| // SPDX-FileCopyrightText: 2022 Linaro Ltd. |
| // SPDX-FileCopyrightText: 2022 Viresh Kumar <viresh.kumar@linaro.org> |
| |
| use std::ptr; |
| |
| use super::{ |
| gpiod, |
| request::{Event, Request}, |
| Error, OperationType, Result, |
| }; |
| |
| /// Line edge events |
| /// |
| /// An iterator over the elements of type `Event`. |
| |
| pub struct Events<'a> { |
| buffer: &'a mut Buffer, |
| read_index: usize, |
| len: usize, |
| } |
| |
| impl<'a> Events<'a> { |
| pub fn new(buffer: &'a mut Buffer, len: usize) -> Self { |
| Self { |
| buffer, |
| read_index: 0, |
| len, |
| } |
| } |
| |
| /// Get the number of contained events in the snapshot, this doesn't change |
| /// on reading events from the iterator. |
| pub fn len(&self) -> usize { |
| self.len |
| } |
| |
| /// Check if buffer is empty. |
| pub fn is_empty(&self) -> bool { |
| self.len == 0 |
| } |
| } |
| |
| impl<'a> Iterator for Events<'a> { |
| type Item = Result<&'a Event>; |
| |
| fn nth(&mut self, n: usize) -> Option<Self::Item> { |
| if self.read_index + n >= self.len { |
| return None; |
| } |
| |
| self.read_index += n + 1; |
| Some(self.buffer.event(self.read_index - 1)) |
| } |
| |
| fn next(&mut self) -> Option<Self::Item> { |
| self.nth(0) |
| } |
| } |
| |
| /// Line edge events buffer |
| #[derive(Debug, Eq, PartialEq)] |
| pub struct Buffer { |
| pub(crate) buffer: *mut gpiod::gpiod_edge_event_buffer, |
| events: Vec<*mut gpiod::gpiod_edge_event>, |
| } |
| |
| impl Buffer { |
| /// Create a new edge event buffer. |
| /// |
| /// If capacity equals 0, it will be set to a default value of 64. If |
| /// capacity is larger than 1024, it will be limited to 1024. |
| pub fn new(capacity: usize) -> Result<Self> { |
| // SAFETY: The `gpiod_edge_event_buffer` returned by libgpiod is guaranteed to live as long |
| // as the `struct Buffer`. |
| let buffer = unsafe { gpiod::gpiod_edge_event_buffer_new(capacity) }; |
| if buffer.is_null() { |
| return Err(Error::OperationFailed( |
| OperationType::EdgeEventBufferNew, |
| errno::errno(), |
| )); |
| } |
| |
| // SAFETY: `gpiod_edge_event_buffer` is guaranteed to be valid here. |
| let capacity = unsafe { gpiod::gpiod_edge_event_buffer_get_capacity(buffer) as usize }; |
| |
| Ok(Self { |
| buffer, |
| events: vec![ptr::null_mut(); capacity], |
| }) |
| } |
| |
| /// Get the capacity of the event buffer. |
| pub fn capacity(&self) -> usize { |
| self.events.len() |
| } |
| |
| /// Get edge events from a line request. |
| /// |
| /// This function will block if no event was queued for the line. |
| pub fn read_edge_events(&mut self, request: &Request) -> Result<Events> { |
| for i in 0..self.events.len() { |
| self.events[i] = ptr::null_mut(); |
| } |
| |
| // SAFETY: `gpiod_line_request` is guaranteed to be valid here. |
| let ret = unsafe { |
| gpiod::gpiod_line_request_read_edge_events( |
| request.request, |
| self.buffer, |
| self.events.len().try_into().unwrap(), |
| ) |
| }; |
| |
| if ret == -1 { |
| Err(Error::OperationFailed( |
| OperationType::LineRequestReadEdgeEvent, |
| errno::errno(), |
| )) |
| } else { |
| let ret = ret as usize; |
| |
| if ret > self.events.len() { |
| Err(Error::TooManyEvents(ret, self.events.len())) |
| } else { |
| Ok(Events::new(self, ret)) |
| } |
| } |
| } |
| |
| /// Read an event stored in the buffer. |
| fn event<'a>(&mut self, index: usize) -> Result<&'a Event> { |
| if self.events[index].is_null() { |
| // SAFETY: The `gpiod_edge_event` returned by libgpiod is guaranteed to live as long |
| // as the `struct Event`. |
| let event = unsafe { |
| gpiod::gpiod_edge_event_buffer_get_event(self.buffer, index.try_into().unwrap()) |
| }; |
| |
| if event.is_null() { |
| return Err(Error::OperationFailed( |
| OperationType::EdgeEventBufferGetEvent, |
| errno::errno(), |
| )); |
| } |
| |
| self.events[index] = event; |
| } |
| |
| // SAFETY: Safe as the underlying events object won't get freed until the time the returned |
| // reference is still used. |
| Ok(unsafe { |
| // This will not lead to `drop(event)`. |
| (self.events.as_ptr().add(index) as *const Event) |
| .as_ref() |
| .unwrap() |
| }) |
| } |
| } |
| |
| impl Drop for Buffer { |
| /// Free the edge event buffer and release all associated resources. |
| fn drop(&mut self) { |
| // SAFETY: `gpiod_edge_event_buffer` is guaranteed to be valid here. |
| unsafe { gpiod::gpiod_edge_event_buffer_free(self.buffer) }; |
| } |
| } |