gpio_cdev/
lib.rs

1// Copyright (c) 2018 The rust-gpio-cdev Project Developers.
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
6// option. This file may not be copied, modified, or distributed
7// except according to those terms.
8
9//! The `gpio-cdev` crate provides access to the [GPIO character device
10//! ABI](https://www.kernel.org/doc/Documentation/ABI/testing/gpio-cdev).  This API,
11//! stabilized with Linux v4.4, deprecates the legacy sysfs interface to GPIOs that is
12//! planned to be removed from the upstream kernel after
13//! year 2020 (which is coming up quickly).
14//!
15//! This crate attempts to wrap this interface in a moderately direction fashion
16//! while retaining safety and using Rust idioms (where doing so could be mapped
17//! to the underlying abstraction without significant overhead or loss of
18//! functionality).
19//!
20//! For additional context for why the kernel is moving from the sysfs API to the
21//! character device API, please see the main [README on Github].
22//!
23//! # Examples
24//!
25//! The following example reads the state of a GPIO line/pin and writes the matching
26//! state to another line/pin.
27//!
28//! ```no_run
29//! use gpio_cdev::{Chip, LineRequestFlags, EventRequestFlags, EventType};
30//!
31//! // Lines are offset within gpiochip0; see docs for more info on chips/lines
32//! fn mirror_gpio(inputline: u32, outputline: u32) -> Result<(), gpio_cdev::Error> {
33//!     let mut chip = Chip::new("/dev/gpiochip0")?;
34//!     let input = chip.get_line(inputline)?;
35//!     let output = chip.get_line(outputline)?;
36//!     let output_handle = output.request(LineRequestFlags::OUTPUT, 0, "mirror-gpio")?;
37//!     for event in input.events(
38//!         LineRequestFlags::INPUT,
39//!         EventRequestFlags::BOTH_EDGES,
40//!         "mirror-gpio",
41//!     )? {
42//!         let evt = event?;
43//!         println!("{:?}", evt);
44//!         match evt.event_type() {
45//!             EventType::RisingEdge => {
46//!                 output_handle.set_value(1)?;
47//!             }
48//!             EventType::FallingEdge => {
49//!                 output_handle.set_value(0)?;
50//!             }
51//!         }
52//!     }
53//!
54//!     Ok(())
55//! }
56//!
57//! # fn main() -> Result<(), gpio_cdev::Error> {
58//! #     mirror_gpio(0, 1)
59//! # }
60//! ```
61//!
62//! To get the state of a GPIO Line on a given chip:
63//!
64//! ```no_run
65//! use gpio_cdev::{Chip, LineRequestFlags};
66//!
67//! # fn main() -> Result<(), gpio_cdev::Error> {
68//! // Read the state of GPIO4 on a raspberry pi.  /dev/gpiochip0
69//! // maps to the driver for the SoC (builtin) GPIO controller.
70//! // The LineHandle returned by request must be assigned to a
71//! // variable (in this case the variable handle) to ensure that
72//! // the corresponding file descriptor is not closed.
73//! let mut chip = Chip::new("/dev/gpiochip0")?;
74//! let handle = chip
75//!     .get_line(4)?
76//!     .request(LineRequestFlags::INPUT, 0, "read-input")?;
77//! for _ in 1..4 {
78//!     println!("Value: {:?}", handle.get_value()?);
79//! }
80//! # Ok(()) }
81//! ```
82//!
83//! [README on Github]: https://github.com/rust-embedded/rust-gpio-cdev
84
85#![cfg_attr(docsrs, feature(doc_cfg))]
86
87#[macro_use]
88extern crate bitflags;
89#[macro_use]
90extern crate nix;
91
92use std::cmp::min;
93use std::ffi::CStr;
94use std::fs::{read_dir, File, ReadDir};
95use std::io::Read;
96use std::mem;
97use std::ops::Index;
98use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, RawFd};
99use std::path::{Path, PathBuf};
100use std::ptr;
101use std::slice;
102use std::sync::Arc;
103
104#[cfg(feature = "async-tokio")]
105#[cfg_attr(docsrs, doc(cfg(feature = "async-tokio")))]
106mod async_tokio;
107pub mod errors; // pub portion is deprecated
108mod ffi;
109
110#[derive(Debug, Clone, Copy, PartialEq)]
111pub enum IoctlKind {
112    ChipInfo,
113    LineInfo,
114    LineHandle,
115    LineEvent,
116    GetLine,
117    SetLine,
118}
119
120#[cfg(feature = "async-tokio")]
121#[cfg_attr(docsrs, doc(cfg(feature = "async-tokio")))]
122pub use crate::async_tokio::AsyncLineEventHandle;
123pub use errors::*;
124
125unsafe fn rstr_lcpy(dst: *mut libc::c_char, src: &str, length: usize) {
126    let copylen = min(src.len() + 1, length);
127    ptr::copy_nonoverlapping(src.as_bytes().as_ptr().cast(), dst, copylen - 1);
128    slice::from_raw_parts_mut(dst, length)[copylen - 1] = 0;
129}
130
131#[derive(Debug)]
132struct InnerChip {
133    pub path: PathBuf,
134    pub file: File,
135    pub name: String,
136    pub label: String,
137    pub lines: u32,
138}
139
140/// A GPIO Chip maps to the actual device driver instance in hardware that
141/// one interacts with to interact with individual GPIOs.  Often these chips
142/// map to IP chunks on an SoC but could also be enumerated within the kernel
143/// via something like a PCI or USB bus.
144///
145/// The Linux kernel itself enumerates GPIO character devices at two paths:
146/// 1. `/dev/gpiochipN`
147/// 2. `/sys/bus/gpiochipN`
148///
149/// It is best not to assume that a device will always be enumerated in the
150/// same order (especially if it is connected via a bus).  In order to reliably
151/// find the correct chip, there are a few approaches that one could reasonably
152/// take:
153///
154/// 1. Create a udev rule that will match attributes of the device and
155///    setup a symlink to the device.
156/// 2. Iterate over all available chips using the [`chips()`] call to find the
157///    device with matching criteria.
158/// 3. For simple cases, just using the enumerated path is fine (demo work).  This
159///    is discouraged for production.
160///
161/// [`chips()`]: fn.chips.html
162#[derive(Debug)]
163pub struct Chip {
164    inner: Arc<InnerChip>,
165}
166
167/// Iterator over chips
168#[derive(Debug)]
169pub struct ChipIterator {
170    readdir: ReadDir,
171}
172
173impl Iterator for ChipIterator {
174    type Item = Result<Chip>;
175
176    fn next(&mut self) -> Option<Result<Chip>> {
177        for entry in &mut self.readdir {
178            match entry {
179                Ok(entry) => {
180                    if entry
181                        .path()
182                        .as_path()
183                        .to_string_lossy()
184                        .contains("gpiochip")
185                    {
186                        return Some(Chip::new(entry.path()));
187                    }
188                }
189                Err(e) => {
190                    return Some(Err(e.into()));
191                }
192            }
193        }
194
195        None
196    }
197}
198
199/// Iterate over all GPIO chips currently present on this system
200pub fn chips() -> Result<ChipIterator> {
201    Ok(ChipIterator {
202        readdir: read_dir("/dev")?,
203    })
204}
205
206impl Chip {
207    /// Open the GPIO Chip at the provided path (e.g. `/dev/gpiochip<N>`)
208    pub fn new<P: AsRef<Path>>(path: P) -> Result<Self> {
209        let f = File::open(path.as_ref())?;
210        let mut info: ffi::gpiochip_info = unsafe { mem::zeroed() };
211        ffi::gpio_get_chipinfo_ioctl(f.as_raw_fd(), &mut info)?;
212
213        Ok(Self {
214            inner: Arc::new(InnerChip {
215                file: f,
216                path: path.as_ref().to_path_buf(),
217                name: unsafe {
218                    CStr::from_ptr(info.name.as_ptr())
219                        .to_string_lossy()
220                        .into_owned()
221                },
222                label: unsafe {
223                    CStr::from_ptr(info.label.as_ptr())
224                        .to_string_lossy()
225                        .into_owned()
226                },
227                lines: info.lines,
228            }),
229        })
230    }
231
232    /// Get the fs path of this character device (e.g. `/dev/gpiochipN`)
233    pub fn path(&self) -> &Path {
234        self.inner.path.as_path()
235    }
236
237    /// The name of the device driving this GPIO chip in the kernel
238    pub fn name(&self) -> &str {
239        self.inner.name.as_str()
240    }
241
242    /// A functional name for this GPIO chip, such as a product number.  Might
243    /// be an empty string.
244    ///
245    /// As an example, the SoC GPIO chip on a Raspberry Pi is "pinctrl-bcm2835"
246    pub fn label(&self) -> &str {
247        self.inner.label.as_str()
248    }
249
250    /// The number of lines/pins indexable through this chip
251    ///
252    /// Not all of these may be usable depending on how the hardware is
253    /// configured/muxed.
254    pub fn num_lines(&self) -> u32 {
255        self.inner.lines
256    }
257
258    /// Get a handle to the GPIO line at a given offset
259    ///
260    /// The actual physical line corresponding to a given offset
261    /// is completely dependent on how the driver/hardware for
262    /// the chip works as well as the associated board layout.
263    ///
264    /// For a device like the NXP i.mx6 SoC GPIO controller there
265    /// are several banks of GPIOs with each bank containing 32
266    /// GPIOs.  For this hardware and driver something like
267    /// `GPIO2_5` would map to offset 37.
268    pub fn get_line(&mut self, offset: u32) -> Result<Line> {
269        Line::new(self.inner.clone(), offset)
270    }
271
272    /// Get a handle to multiple GPIO line at a given offsets
273    ///
274    /// The group of lines can be manipulated simultaneously.
275    pub fn get_lines(&mut self, offsets: &[u32]) -> Result<Lines> {
276        Lines::new(self.inner.clone(), offsets)
277    }
278
279    /// Get a handle to all the GPIO lines on the chip
280    ///
281    /// The group of lines can be manipulated simultaneously.
282    pub fn get_all_lines(&mut self) -> Result<Lines> {
283        let offsets: Vec<u32> = (0..self.num_lines()).collect();
284        self.get_lines(&offsets)
285    }
286
287    /// Get an interator over all lines that can be potentially access for this
288    /// chip.
289    pub fn lines(&self) -> LineIterator {
290        LineIterator {
291            chip: self.inner.clone(),
292            idx: 0,
293        }
294    }
295}
296
297/// Iterator over GPIO Lines for a given chip.
298#[derive(Debug)]
299pub struct LineIterator {
300    chip: Arc<InnerChip>,
301    idx: u32,
302}
303
304impl Iterator for LineIterator {
305    type Item = Line;
306
307    fn next(&mut self) -> Option<Line> {
308        if self.idx < self.chip.lines {
309            let idx = self.idx;
310            self.idx += 1;
311            // Since we checked the index, we know this will be Ok
312            Some(Line::new(self.chip.clone(), idx).unwrap())
313        } else {
314            None
315        }
316    }
317}
318
319/// Access to a specific GPIO Line
320///
321/// GPIO Lines must be obtained through a parent [`Chip`] and
322/// represent an actual GPIO pin/line accessible via that chip.
323/// Not all accessible lines for a given chip may actually
324/// map to hardware depending on how the board is setup
325/// in the kernel.
326///
327#[derive(Debug, Clone)]
328pub struct Line {
329    chip: Arc<InnerChip>,
330    offset: u32,
331}
332
333/// Information about a specific GPIO Line
334///
335/// Wraps kernel [`struct gpioline_info`].
336///
337/// [`struct gpioline_info`]: https://elixir.bootlin.com/linux/v4.9.127/source/include/uapi/linux/gpio.h#L36
338#[derive(Debug)]
339pub struct LineInfo {
340    line: Line,
341    flags: LineFlags,
342    name: Option<String>,
343    consumer: Option<String>,
344}
345
346bitflags! {
347    /// Line Request Flags
348    ///
349    /// Maps to kernel [`GPIOHANDLE_REQUEST_*`] flags.
350    ///
351    /// [`GPIOHANDLE_REQUEST_*`]: https://elixir.bootlin.com/linux/v4.9.127/source/include/uapi/linux/gpio.h#L58
352    #[derive(Debug, Clone)]
353    pub struct LineRequestFlags: u32 {
354        const INPUT = (1 << 0);
355        const OUTPUT = (1 << 1);
356        const ACTIVE_LOW = (1 << 2);
357        const OPEN_DRAIN = (1 << 3);
358        const OPEN_SOURCE = (1 << 4);
359    }
360}
361
362bitflags! {
363    /// Event request flags
364    ///
365    /// Maps to kernel [`GPIOEVENT_REQEST_*`] flags.
366    ///
367    /// [`GPIOEVENT_REQUEST_*`]: https://elixir.bootlin.com/linux/v4.9.127/source/include/uapi/linux/gpio.h#L109
368    pub struct EventRequestFlags: u32 {
369        const RISING_EDGE = (1 << 0);
370        const FALLING_EDGE = (1 << 1);
371        const BOTH_EDGES = Self::RISING_EDGE.bits() | Self::FALLING_EDGE.bits();
372    }
373}
374
375bitflags! {
376    /// Informational Flags
377    ///
378    /// Maps to kernel [`GPIOLINE_FLAG_*`] flags.
379    ///
380    /// [`GPIOLINE_FLAG_*`]: https://elixir.bootlin.com/linux/v4.9.127/source/include/uapi/linux/gpio.h#L29
381    #[derive(Debug)]
382    pub struct LineFlags: u32 {
383        const KERNEL = (1 << 0);
384        const IS_OUT = (1 << 1);
385        const ACTIVE_LOW = (1 << 2);
386        const OPEN_DRAIN = (1 << 3);
387        const OPEN_SOURCE = (1 << 4);
388    }
389}
390
391/// In or Out
392#[derive(Debug, Clone, Copy, PartialEq)]
393pub enum LineDirection {
394    In,
395    Out,
396}
397
398unsafe fn cstrbuf_to_string(buf: &[libc::c_char]) -> Option<String> {
399    if buf[0] == 0 {
400        None
401    } else {
402        Some(CStr::from_ptr(buf.as_ptr()).to_string_lossy().into_owned())
403    }
404}
405
406impl Line {
407    fn new(chip: Arc<InnerChip>, offset: u32) -> Result<Self> {
408        if offset >= chip.lines {
409            return Err(offset_err(offset));
410        }
411        Ok(Self { chip, offset })
412    }
413
414    /// Get info about the line from the kernel.
415    pub fn info(&self) -> Result<LineInfo> {
416        let mut line_info = ffi::gpioline_info {
417            line_offset: self.offset,
418            flags: 0,
419            name: [0; 32],
420            consumer: [0; 32],
421        };
422        ffi::gpio_get_lineinfo_ioctl(self.chip.file.as_raw_fd(), &mut line_info)?;
423
424        Ok(LineInfo {
425            line: self.clone(),
426            flags: LineFlags::from_bits_truncate(line_info.flags),
427            name: unsafe { cstrbuf_to_string(&line_info.name[..]) },
428            consumer: unsafe { cstrbuf_to_string(&line_info.consumer[..]) },
429        })
430    }
431
432    /// Offset of this line within its parent chip
433    pub fn offset(&self) -> u32 {
434        self.offset
435    }
436
437    /// Get a handle to this chip's parent
438    pub fn chip(&self) -> Chip {
439        Chip {
440            inner: self.chip.clone(),
441        }
442    }
443
444    /// Request access to interact with this line from the kernel
445    ///
446    /// This is similar to the "export" operation present in the sysfs
447    /// API with the key difference that we are also able to configure
448    /// the GPIO with `flags` to specify how the line will be used
449    /// at the time of request.
450    ///
451    /// For an output, the `default` parameter specifies the value
452    /// the line should have when it is configured as an output.  The
453    /// `consumer` string should describe the process consuming the
454    /// line (this will be truncated to 31 characters if too long).
455    ///
456    /// # Errors
457    ///
458    /// The main source of errors here is if the kernel returns an
459    /// error to the ioctl performing the request here.  This will
460    /// result in an [`Error`] being returned with [`ErrorKind::Ioctl`].
461    ///
462    /// One possible cause for an error here would be if the line is
463    /// already in use.  One can check for this prior to making the
464    /// request using [`is_kernel`].
465    ///
466    /// [`Error`]: errors/struct.Error.html
467    /// [`ErrorKind::Ioctl`]: errors/enum.ErrorKind.html#variant.Ioctl
468    /// [`is_kernel`]: struct.Line.html#method.is_kernel
469    pub fn request(
470        &self,
471        flags: LineRequestFlags,
472        default: u8,
473        consumer: &str,
474    ) -> Result<LineHandle> {
475        // prepare the request; the kernel consumes some of these values and will
476        // set the fd for us.
477        let mut request = ffi::gpiohandle_request {
478            lineoffsets: unsafe { mem::zeroed() },
479            flags: flags.bits(),
480            default_values: unsafe { mem::zeroed() },
481            consumer_label: unsafe { mem::zeroed() },
482            lines: 1,
483            fd: 0,
484        };
485        request.lineoffsets[0] = self.offset;
486        request.default_values[0] = default;
487        unsafe {
488            rstr_lcpy(
489                request.consumer_label[..].as_mut_ptr(),
490                consumer,
491                request.consumer_label.len(),
492            );
493        }
494        ffi::gpio_get_linehandle_ioctl(self.chip.file.as_raw_fd(), &mut request)?;
495        Ok(LineHandle {
496            line: self.clone(),
497            flags,
498            file: unsafe { File::from_raw_fd(request.fd) },
499        })
500    }
501
502    /// Get an event handle that can be used as a blocking iterator over
503    /// the events (state changes) for this Line
504    ///
505    /// When used as an iterator, it blocks while there is not another event
506    /// available from the kernel for this line matching the subscription
507    /// criteria specified in the `event_flags`.  The line will be configured
508    /// with the specified `handle_flags` and `consumer` label.
509    ///
510    /// Note that as compared with the sysfs interface, the character
511    /// device interface maintains a queue of events in the kernel so
512    /// events may happen (e.g. a line changing state faster than can
513    /// be picked up in userspace in real-time).  These events will be
514    /// returned on the iterator in order with the event containing the
515    /// associated timestamp attached with high precision within the
516    /// kernel (from an ISR for most drivers).
517    ///
518    /// # Example
519    ///
520    /// ```no_run
521    /// # fn main() -> Result<(), gpio_cdev::Error> {
522    /// use gpio_cdev::{Chip, LineRequestFlags, EventRequestFlags};
523    /// use std::io;
524    ///
525    /// let mut chip = Chip::new("/dev/gpiochip0")?;
526    /// let input = chip.get_line(0)?;
527    ///
528    /// // Show all state changes for this line forever
529    /// for event in input.events(
530    ///     LineRequestFlags::INPUT,
531    ///     EventRequestFlags::BOTH_EDGES,
532    ///     "rust-gpio"
533    /// )? {
534    ///     println!("{:?}", event?);
535    /// }
536    /// # Ok(())
537    /// # }
538    /// ```
539    pub fn events(
540        &self,
541        handle_flags: LineRequestFlags,
542        event_flags: EventRequestFlags,
543        consumer: &str,
544    ) -> Result<LineEventHandle> {
545        let mut request = ffi::gpioevent_request {
546            lineoffset: self.offset,
547            handleflags: handle_flags.bits(),
548            eventflags: event_flags.bits(),
549            consumer_label: unsafe { mem::zeroed() },
550            fd: 0,
551        };
552
553        unsafe {
554            rstr_lcpy(
555                request.consumer_label[..].as_mut_ptr(),
556                consumer,
557                request.consumer_label.len(),
558            );
559        }
560        ffi::gpio_get_lineevent_ioctl(self.chip.file.as_raw_fd(), &mut request)?;
561
562        Ok(LineEventHandle {
563            line: self.clone(),
564            file: unsafe { File::from_raw_fd(request.fd) },
565        })
566    }
567
568    #[cfg(feature = "async-tokio")]
569    #[cfg_attr(docsrs, doc(cfg(feature = "async-tokio")))]
570    pub fn async_events(
571        &self,
572        handle_flags: LineRequestFlags,
573        event_flags: EventRequestFlags,
574        consumer: &str,
575    ) -> Result<AsyncLineEventHandle> {
576        let events = self.events(handle_flags, event_flags, consumer)?;
577        AsyncLineEventHandle::new(events)
578    }
579}
580
581impl LineInfo {
582    /// Get a handle to the line that this info represents
583    pub fn line(&self) -> &Line {
584        &self.line
585    }
586
587    /// Name assigned to this chip if assigned
588    pub fn name(&self) -> Option<&str> {
589        self.name.as_deref()
590    }
591
592    /// The name of this GPIO line, such as the output pin of the line on the
593    /// chip, a rail or a pin header name on a board, as specified by the gpio
594    /// chip.
595    pub fn consumer(&self) -> Option<&str> {
596        self.consumer.as_deref()
597    }
598
599    /// Get the direction of this GPIO if configured
600    ///
601    /// Lines are considered to be inputs if not explicitly
602    /// marked as outputs in the line info flags by the kernel.
603    pub fn direction(&self) -> LineDirection {
604        if self.flags.contains(LineFlags::IS_OUT) {
605            LineDirection::Out
606        } else {
607            LineDirection::In
608        }
609    }
610
611    /// True if the any flags for the device are set (input or output)
612    pub fn is_used(&self) -> bool {
613        !self.flags.is_empty()
614    }
615
616    /// True if this line is being used by something else in the kernel
617    ///
618    /// If another driver or subsystem in the kernel is using the line
619    /// then it cannot be used via the cdev interface. See [relevant kernel code].
620    ///
621    /// [relevant kernel code]: https://elixir.bootlin.com/linux/v4.9.127/source/drivers/gpio/gpiolib.c#L938
622    pub fn is_kernel(&self) -> bool {
623        self.flags.contains(LineFlags::KERNEL)
624    }
625
626    /// True if this line is marked as active low in the kernel
627    pub fn is_active_low(&self) -> bool {
628        self.flags.contains(LineFlags::ACTIVE_LOW)
629    }
630
631    /// True if this line is marked as open drain in the kernel
632    pub fn is_open_drain(&self) -> bool {
633        self.flags.contains(LineFlags::OPEN_DRAIN)
634    }
635
636    /// True if this line is marked as open source in the kernel
637    pub fn is_open_source(&self) -> bool {
638        self.flags.contains(LineFlags::OPEN_SOURCE)
639    }
640}
641
642/// Handle for interacting with a "requested" line
643///
644/// In order for userspace to read/write the value of a GPIO
645/// it must be requested from the chip using [`Line::request`].
646/// On success, the kernel creates an anonymous file descriptor
647/// for interacting with the requested line.  This structure
648/// is the go-between for callers and that file descriptor.
649///
650/// [`Line::request`]: struct.Line.html#method.request
651#[derive(Debug)]
652pub struct LineHandle {
653    line: Line,
654    flags: LineRequestFlags,
655    file: File,
656}
657
658impl LineHandle {
659    /// Request the current state of this Line from the kernel
660    ///
661    /// This call is expected to succeed for both input and output
662    /// lines.  It should be noted, however, that some drivers may
663    /// not be able to give any useful information when the value
664    /// is requested for an output line.
665    ///
666    /// This value should be 0 or 1 which a "1" representing that
667    /// the line is active.  Usually this means that the line is
668    /// at logic-level high but it could mean the opposite if the
669    /// line has been marked as being `ACTIVE_LOW`.
670    pub fn get_value(&self) -> Result<u8> {
671        let mut data: ffi::gpiohandle_data = unsafe { mem::zeroed() };
672        ffi::gpiohandle_get_line_values_ioctl(self.file.as_raw_fd(), &mut data)?;
673        Ok(data.values[0])
674    }
675
676    /// Request that the line be driven to the specified value
677    ///
678    /// The value should be 0 or 1 with 1 representing a request
679    /// to make the line "active".  Usually "active" means
680    /// logic level high unless the line has been marked as `ACTIVE_LOW`.
681    ///
682    /// Calling `set_value` on a line that is not an output will
683    /// likely result in an error (from the kernel).
684    pub fn set_value(&self, value: u8) -> Result<()> {
685        let mut data: ffi::gpiohandle_data = unsafe { mem::zeroed() };
686        data.values[0] = value;
687        ffi::gpiohandle_set_line_values_ioctl(self.file.as_raw_fd(), &mut data)?;
688        Ok(())
689    }
690
691    /// Get the Line information associated with this handle.
692    pub fn line(&self) -> &Line {
693        &self.line
694    }
695
696    /// Get the flags with which this handle was created
697    pub fn flags(&self) -> LineRequestFlags {
698        self.flags.clone()
699    }
700}
701
702impl AsRawFd for LineHandle {
703    /// Gets the raw file descriptor for the `LineHandle`.
704    fn as_raw_fd(&self) -> RawFd {
705        self.file.as_raw_fd()
706    }
707}
708
709/// A collection of lines that can be accesses simultaneously
710///
711/// This is a collection of lines, all from the same GPIO chip that can
712/// all be accessed simultaneously
713#[derive(Debug)]
714pub struct Lines {
715    lines: Vec<Line>,
716}
717
718impl Lines {
719    fn new(chip: Arc<InnerChip>, offsets: &[u32]) -> Result<Self> {
720        let res: Result<Vec<Line>> = offsets
721            .iter()
722            .map(|off| Line::new(chip.clone(), *off))
723            .collect();
724        let lines = res?;
725        Ok(Self { lines })
726    }
727
728    /// Get a handle to the parent chip for the lines
729    pub fn chip(&self) -> Chip {
730        self.lines[0].chip()
731    }
732
733    /// Get the number of lines in the collection
734    pub fn is_empty(&self) -> bool {
735        self.lines.is_empty()
736    }
737
738    /// Get the number of lines in the collection
739    pub fn len(&self) -> usize {
740        self.lines.len()
741    }
742
743    /// Request access to interact with these lines from the kernel
744    ///
745    /// This is similar to the "export" operation present in the sysfs
746    /// API with the key difference that we are also able to configure
747    /// the GPIO with `flags` to specify how the line will be used
748    /// at the time of request.
749    ///
750    /// For an output, the `default` parameter specifies the value
751    /// each line should have when it is configured as an output.  The
752    /// `consumer` string should describe the process consuming the
753    /// line (this will be truncated to 31 characters if too long).
754    ///
755    /// # Errors
756    ///
757    /// The main source of errors here is if the kernel returns an
758    /// error to the ioctl performing the request here.  This will
759    /// result in an [`Error`] being returned with [`ErrorKind::Ioctl`].
760    ///
761    /// One possible cause for an error here would be if the lines are
762    /// already in use.  One can check for this prior to making the
763    /// request using [`is_kernel`].
764    ///
765    /// [`Error`]: errors/struct.Error.html
766    /// [`ErrorKind::Ioctl`]: errors/enum.ErrorKind.html#variant.Ioctl
767    /// [`is_kernel`]: struct.Line.html#method.is_kernel
768    pub fn request(
769        &self,
770        flags: LineRequestFlags,
771        default: &[u8],
772        consumer: &str,
773    ) -> Result<MultiLineHandle> {
774        let n = self.lines.len();
775        if default.len() != n {
776            return Err(invalid_err(n, default.len()));
777        }
778        // prepare the request; the kernel consumes some of these values and will
779        // set the fd for us.
780        let mut request = ffi::gpiohandle_request {
781            lineoffsets: unsafe { mem::zeroed() },
782            flags: flags.bits(),
783            default_values: unsafe { mem::zeroed() },
784            consumer_label: unsafe { mem::zeroed() },
785            lines: n as u32,
786            fd: 0,
787        };
788        #[allow(clippy::needless_range_loop)] // clippy does not understand this loop correctly
789        for i in 0..n {
790            request.lineoffsets[i] = self.lines[i].offset();
791            request.default_values[i] = default[i];
792        }
793        unsafe {
794            rstr_lcpy(
795                request.consumer_label[..].as_mut_ptr(),
796                consumer,
797                request.consumer_label.len(),
798            );
799        }
800        ffi::gpio_get_linehandle_ioctl(self.lines[0].chip().inner.file.as_raw_fd(), &mut request)?;
801        let lines = self.lines.clone();
802        Ok(MultiLineHandle {
803            lines: Self { lines },
804            file: unsafe { File::from_raw_fd(request.fd) },
805        })
806    }
807}
808
809impl Index<usize> for Lines {
810    type Output = Line;
811
812    fn index(&self, i: usize) -> &Line {
813        &self.lines[i]
814    }
815}
816
817/// Handle for interacting with a "requested" line
818///
819/// In order for userspace to read/write the value of a GPIO
820/// it must be requested from the chip using [`Line::request`].
821/// On success, the kernel creates an anonymous file descriptor
822/// for interacting with the requested line.  This structure
823/// is the go-between for callers and that file descriptor.
824///
825/// [`Line::request`]: struct.Line.html#method.request
826#[derive(Debug)]
827pub struct MultiLineHandle {
828    lines: Lines,
829    file: File,
830}
831
832impl MultiLineHandle {
833    /// Request the current state of this Line from the kernel
834    ///
835    /// This call is expected to succeed for both input and output
836    /// lines.  It should be noted, however, that some drivers may
837    /// not be able to give any useful information when the value
838    /// is requested for an output line.
839    ///
840    /// This value should be 0 or 1 which a "1" representing that
841    /// the line is active.  Usually this means that the line is
842    /// at logic-level high but it could mean the opposite if the
843    /// line has been marked as being `ACTIVE_LOW`.
844    pub fn get_values(&self) -> Result<Vec<u8>> {
845        let mut data: ffi::gpiohandle_data = unsafe { mem::zeroed() };
846        ffi::gpiohandle_get_line_values_ioctl(self.file.as_raw_fd(), &mut data)?;
847        let n = self.num_lines();
848        let values: Vec<u8> = (0..n).map(|i| data.values[i]).collect();
849        Ok(values)
850    }
851
852    /// Request that the line be driven to the specified value
853    ///
854    /// The value should be 0 or 1 with 1 representing a request
855    /// to make the line "active".  Usually "active" means
856    /// logic level high unless the line has been marked as `ACTIVE_LOW`.
857    ///
858    /// Calling `set_value` on a line that is not an output will
859    /// likely result in an error (from the kernel).
860    pub fn set_values(&self, values: &[u8]) -> Result<()> {
861        let n = self.num_lines();
862        if values.len() != n {
863            return Err(invalid_err(n, values.len()));
864        }
865        let mut data: ffi::gpiohandle_data = unsafe { mem::zeroed() };
866        data.values[..n].clone_from_slice(&values[..n]);
867        ffi::gpiohandle_set_line_values_ioctl(self.file.as_raw_fd(), &mut data)?;
868        Ok(())
869    }
870
871    /// Get the number of lines associated with this handle
872    pub fn num_lines(&self) -> usize {
873        self.lines.len()
874    }
875
876    /// Get the Line information associated with this handle.
877    pub fn lines(&self) -> &Lines {
878        &self.lines
879    }
880}
881
882impl AsRawFd for MultiLineHandle {
883    /// Gets the raw file descriptor for the `LineHandle`.
884    fn as_raw_fd(&self) -> RawFd {
885        self.file.as_raw_fd()
886    }
887}
888
889/// Did the Line rise (go active) or fall (go inactive)?
890///
891/// Maps to kernel [`GPIOEVENT_EVENT_*`] definitions.
892///
893/// [`GPIOEVENT_EVENT_*`]: https://elixir.bootlin.com/linux/v4.9.127/source/include/uapi/linux/gpio.h#L136
894#[derive(Debug, Clone, Copy, PartialEq)]
895pub enum EventType {
896    RisingEdge,
897    FallingEdge,
898}
899
900/// Information about a change to the state of a Line
901///
902/// Wraps kernel [`struct gpioevent_data`].
903///
904/// [`struct gpioevent_data`]: https://elixir.bootlin.com/linux/v4.9.127/source/include/uapi/linux/gpio.h#L142
905pub struct LineEvent(ffi::gpioevent_data);
906
907impl std::fmt::Debug for LineEvent {
908    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
909        write!(
910            f,
911            "LineEvent {{ timestamp: {:?}, event_type: {:?} }}",
912            self.timestamp(),
913            self.event_type()
914        )
915    }
916}
917
918impl LineEvent {
919    /// Best estimate of event occurrence time, in nanoseconds
920    ///
921    /// In most cases, the timestamp for the event is captured
922    /// in an interrupt handler so it should be very accurate.
923    ///
924    /// The nanosecond timestamp value should are captured
925    /// using the `CLOCK_MONOTONIC` offsets in the kernel and
926    /// should be compared against `CLOCK_MONOTONIC` values.
927    /// Note that kernel versions prior to 5.7 used
928    /// `CLOCK_REALTIME` offsets instead.
929    pub fn timestamp(&self) -> u64 {
930        self.0.timestamp
931    }
932
933    /// Was this a rising or a falling edge?
934    pub fn event_type(&self) -> EventType {
935        if self.0.id == 0x01 {
936            EventType::RisingEdge
937        } else {
938            EventType::FallingEdge
939        }
940    }
941}
942
943/// Handle for retrieving events from the kernel for a line
944///
945/// In order for userspace to retrieve incoming events on a GPIO,
946/// an event handle must be requested from the chip using
947/// [`Line::events`].
948/// On success, the kernel creates an anonymous file descriptor
949/// for reading events. This structure is the go-between for callers
950/// and that file descriptor.
951///
952/// [`Line::events`]: struct.Line.html#method.events
953#[derive(Debug)]
954pub struct LineEventHandle {
955    line: Line,
956    file: File,
957}
958
959impl LineEventHandle {
960    /// Retrieve the next event from the kernel for this line
961    ///
962    /// This blocks while there is not another event available from the
963    /// kernel for the line which matches the subscription criteria
964    /// specified in the `event_flags` when the handle was created.
965    pub fn get_event(&mut self) -> Result<LineEvent> {
966        match self.read_event() {
967            Ok(Some(event)) => Ok(event),
968            Ok(None) => Err(event_err(nix::errno::Errno::EIO)),
969            Err(e) => Err(e.into()),
970        }
971    }
972
973    /// Request the current state of this Line from the kernel
974    ///
975    /// This value should be 0 or 1 which a "1" representing that
976    /// the line is active.  Usually this means that the line is
977    /// at logic-level high but it could mean the opposite if the
978    /// line has been marked as being `ACTIVE_LOW`.
979    pub fn get_value(&self) -> Result<u8> {
980        let mut data: ffi::gpiohandle_data = unsafe { mem::zeroed() };
981        ffi::gpiohandle_get_line_values_ioctl(self.file.as_raw_fd(), &mut data)?;
982        Ok(data.values[0])
983    }
984
985    /// Get the Line information associated with this handle.
986    pub fn line(&self) -> &Line {
987        &self.line
988    }
989
990    pub fn file(&self) -> &File {
991        &self.file
992    }
993
994    pub fn file2(&mut self) -> &File {
995        &self.file
996    }
997
998    /// Helper function which returns the line event if a complete event was read, Ok(None) if not
999    /// enough data was read or the error returned by `read()`.
1000    pub(crate) fn read_event(&mut self) -> std::io::Result<Option<LineEvent>> {
1001        let mut data: ffi::gpioevent_data = unsafe { mem::zeroed() };
1002        let data_as_buf = unsafe {
1003            slice::from_raw_parts_mut(
1004                (&mut data as *mut ffi::gpioevent_data).cast(),
1005                mem::size_of::<ffi::gpioevent_data>(),
1006            )
1007        };
1008        let bytes_read = self.file.read(data_as_buf)?;
1009        if bytes_read == mem::size_of::<ffi::gpioevent_data>() {
1010            Ok(Some(LineEvent(data)))
1011        } else {
1012            Ok(None)
1013        }
1014    }
1015}
1016
1017impl AsRawFd for LineEventHandle {
1018    /// Gets the raw file descriptor for the `LineEventHandle`.
1019    fn as_raw_fd(&self) -> RawFd {
1020        self.file.as_raw_fd()
1021    }
1022}
1023
1024impl AsFd for LineEventHandle {
1025    /// Gets the raw file descriptor for the `LineEventHandle`.
1026    fn as_fd(&self) -> BorrowedFd<'_> {
1027        self.file.as_fd()
1028    }
1029}
1030
1031impl Iterator for LineEventHandle {
1032    type Item = Result<LineEvent>;
1033
1034    fn next(&mut self) -> Option<Result<LineEvent>> {
1035        match self.read_event() {
1036            Ok(None) => None,
1037            Ok(Some(event)) => Some(Ok(event)),
1038            Err(e) => Some(Err(e.into())),
1039        }
1040    }
1041}