On Thu, Dec 2, 2021 at 12:23 PM Viresh Kumar viresh.kumar@linaro.org wrote:
Add rust wrappers around FFI bindings to provide a convenient interface for the users.
Signed-off-by: Viresh Kumar viresh.kumar@linaro.org
Hi Viresh, thanks for the hard work.
Obviously this is not something we can merge yet but it's a good base to continue the work.
General comment on the naming convention:
The line from one of the examples: 'use libgpiod::chip::GpiodChip;' looks like it has a lot of redundancy in it. How about calling the crate gpiod and dropping the chip package entirely? Basically follow what C++ and python bindings do by having `use gpiod::Chip` etc.?
[snip]
+impl GpiodChipInternal {
- /// Find a GPIO chip by path.
- pub(crate) fn open(path: &str) -> Result<Self> {
let chip = unsafe { bindings::gpiod_chip_open(path.as_ptr() as *const c_char) };
if chip.is_null() {
return Err(Error::OperationFailed("GpiodChip open", IoError::last()));
One of my concerns last time was error handling. Does this now translate the error codes from the underlying C code to some kind of rust errors? Can you elaborate on how that works? I see it always returns IoError. In my WIP C++ and Python code I try to translate the errnos into some meaningful exceptions - for instance EINVAL -> ::std::invalid_argument etc. Can we have a similar thing here?
[snip]
- /// Get the GPIO chip name as represented in the kernel.
- pub fn get_name(&self) -> Result<&str> {
// SAFETY: The string returned by libgpiod is guaranteed to live as long
// as the `struct GpiodChip`.
let name = unsafe { bindings::gpiod_chip_get_name(self.ichip.chip()) };
if name.is_null() {
This is not possible, the string can be empty at the very least but never null. Same for label and path.
match ret {
-1 => Err(Error::OperationFailed(
"GpiodChip info-event-wait",
IoError::last(),
)),
0 => Err(Error::OperationTimedOut),
Does it have to be an Error? It's pretty normal for an operation to time out, no?
_ => Ok(()),
}
- }
- /// Read a single line status change event from this chip. If no events are
- /// pending, this function will block.
- pub fn info_event_read(&self) -> Result<GpiodInfoEvent> {
GpiodInfoEvent::new(&self.ichip.clone())
Would you mind explaining what the clone() function does here to ichip and why it's needed.
+impl GpiodEdgeEvent {
- /// Get an event stored in the buffer.
- pub fn new(buffer: &GpiodEdgeEventBuffer, index: u64) -> Result<Self> {
let event = unsafe { bindings::gpiod_edge_event_buffer_get_event(buffer.buffer(), index) };
if event.is_null() {
return Err(Error::OperationFailed(
"GpiodEdgeEvent buffer-get-event",
IoError::last(),
));
}
Ok(Self { event })
- }
- /// Get the event type.
- pub fn get_event_type(&self) -> Result<EdgeEvent> {
EdgeEvent::new(unsafe { bindings::gpiod_edge_event_get_event_type(self.event) } as u32)
- }
- /// Get the timestamp of the event.
- pub fn get_timestamp(&self) -> Duration {
Duration::from_nanos(unsafe { bindings::gpiod_edge_event_get_timestamp(self.event) })
- }
- /// Get the offset of the line on which the event was triggered.
- pub fn get_line_offset(&self) -> u32 {
unsafe { bindings::gpiod_edge_event_get_line_offset(self.event) }
- }
- /// Get the global sequence number of this event.
- ///
- /// Returns sequence number of the event relative to all lines in the
- /// associated line request.
- pub fn get_global_seqno(&self) -> u64 {
unsafe { bindings::gpiod_edge_event_get_global_seqno(self.event) }
- }
- /// Get the event sequence number specific to concerned line.
- ///
- /// Returns sequence number of the event relative to this line within the
- /// lifetime of the associated line request.
- pub fn get_line_seqno(&self) -> u64 {
unsafe { bindings::gpiod_edge_event_get_line_seqno(self.event) }
- }
+}
AFAICT this object will not survive the parent buffer. Take a look at the current development version of the C++ bindings where I play with copy constructors to ensure that.
[snip]
I'll address the config objects once we actually have the final version in the C API.
Bart