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]
88 extern crate bitflags;
89 #[macro_use]
90 extern crate nix;
91 
92 use std::cmp::min;
93 use std::ffi::CStr;
94 use std::fs::{read_dir, File, ReadDir};
95 use std::io::Read;
96 use std::mem;
97 use std::ops::Index;
98 use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, RawFd};
99 use std::path::{Path, PathBuf};
100 use std::ptr;
101 use std::slice;
102 use std::sync::Arc;
103 
104 #[cfg(feature = "async-tokio")]
105 #[cfg_attr(docsrs, doc(cfg(feature = "async-tokio")))]
106 mod async_tokio;
107 pub mod errors; // pub portion is deprecated
108 mod ffi;
109 
110 #[derive(Debug, Clone, Copy, PartialEq)]
111 pub 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")))]
122 pub use crate::async_tokio::AsyncLineEventHandle;
123 pub use errors::*;
124 
rstr_lcpy(dst: *mut libc::c_char, src: &str, length: usize)125 unsafe 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)]
132 struct 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)]
163 pub struct Chip {
164     inner: Arc<InnerChip>,
165 }
166 
167 /// Iterator over chips
168 #[derive(Debug)]
169 pub struct ChipIterator {
170     readdir: ReadDir,
171 }
172 
173 impl Iterator for ChipIterator {
174     type Item = Result<Chip>;
175 
next(&mut self) -> Option<Result<Chip>>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
chips() -> Result<ChipIterator>200 pub fn chips() -> Result<ChipIterator> {
201     Ok(ChipIterator {
202         readdir: read_dir("/dev")?,
203     })
204 }
205 
206 impl Chip {
207     /// Open the GPIO Chip at the provided path (e.g. `/dev/gpiochip<N>`)
new<P: AsRef<Path>>(path: P) -> Result<Self>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`)
path(&self) -> &Path233     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
name(&self) -> &str238     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"
label(&self) -> &str246     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.
num_lines(&self) -> u32254     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.
get_line(&mut self, offset: u32) -> Result<Line>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.
get_lines(&mut self, offsets: &[u32]) -> Result<Lines>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.
get_all_lines(&mut self) -> Result<Lines>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.
lines(&self) -> LineIterator289     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)]
299 pub struct LineIterator {
300     chip: Arc<InnerChip>,
301     idx: u32,
302 }
303 
304 impl Iterator for LineIterator {
305     type Item = Line;
306 
next(&mut self) -> Option<Line>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)]
328 pub 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)]
339 pub struct LineInfo {
340     line: Line,
341     flags: LineFlags,
342     name: Option<String>,
343     consumer: Option<String>,
344 }
345 
346 bitflags! {
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 
362 bitflags! {
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 
375 bitflags! {
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)]
393 pub enum LineDirection {
394     In,
395     Out,
396 }
397 
cstrbuf_to_string(buf: &[libc::c_char]) -> Option<String>398 unsafe 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 
406 impl Line {
new(chip: Arc<InnerChip>, offset: u32) -> Result<Self>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.
info(&self) -> Result<LineInfo>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
offset(&self) -> u32433     pub fn offset(&self) -> u32 {
434         self.offset
435     }
436 
437     /// Get a handle to this chip's parent
chip(&self) -> Chip438     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
request( &self, flags: LineRequestFlags, default: u8, consumer: &str, ) -> Result<LineHandle>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     /// ```
events( &self, handle_flags: LineRequestFlags, event_flags: EventRequestFlags, consumer: &str, ) -> Result<LineEventHandle>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")))]
async_events( &self, handle_flags: LineRequestFlags, event_flags: EventRequestFlags, consumer: &str, ) -> Result<AsyncLineEventHandle>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 
581 impl LineInfo {
582     /// Get a handle to the line that this info represents
line(&self) -> &Line583     pub fn line(&self) -> &Line {
584         &self.line
585     }
586 
587     /// Name assigned to this chip if assigned
name(&self) -> Option<&str>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.
consumer(&self) -> Option<&str>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.
direction(&self) -> LineDirection603     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)
is_used(&self) -> bool612     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
is_kernel(&self) -> bool622     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
is_active_low(&self) -> bool627     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
is_open_drain(&self) -> bool632     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
is_open_source(&self) -> bool637     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)]
652 pub struct LineHandle {
653     line: Line,
654     flags: LineRequestFlags,
655     file: File,
656 }
657 
658 impl 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`.
get_value(&self) -> Result<u8>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).
set_value(&self, value: u8) -> Result<()>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.
line(&self) -> &Line692     pub fn line(&self) -> &Line {
693         &self.line
694     }
695 
696     /// Get the flags with which this handle was created
flags(&self) -> LineRequestFlags697     pub fn flags(&self) -> LineRequestFlags {
698         self.flags.clone()
699     }
700 }
701 
702 impl AsRawFd for LineHandle {
703     /// Gets the raw file descriptor for the `LineHandle`.
as_raw_fd(&self) -> RawFd704     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)]
714 pub struct Lines {
715     lines: Vec<Line>,
716 }
717 
718 impl Lines {
new(chip: Arc<InnerChip>, offsets: &[u32]) -> Result<Self>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
chip(&self) -> Chip729     pub fn chip(&self) -> Chip {
730         self.lines[0].chip()
731     }
732 
733     /// Get the number of lines in the collection
is_empty(&self) -> bool734     pub fn is_empty(&self) -> bool {
735         self.lines.is_empty()
736     }
737 
738     /// Get the number of lines in the collection
len(&self) -> usize739     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
request( &self, flags: LineRequestFlags, default: &[u8], consumer: &str, ) -> Result<MultiLineHandle>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 
809 impl Index<usize> for Lines {
810     type Output = Line;
811 
index(&self, i: usize) -> &Line812     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)]
827 pub struct MultiLineHandle {
828     lines: Lines,
829     file: File,
830 }
831 
832 impl 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`.
get_values(&self) -> Result<Vec<u8>>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).
set_values(&self, values: &[u8]) -> Result<()>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
num_lines(&self) -> usize872     pub fn num_lines(&self) -> usize {
873         self.lines.len()
874     }
875 
876     /// Get the Line information associated with this handle.
lines(&self) -> &Lines877     pub fn lines(&self) -> &Lines {
878         &self.lines
879     }
880 }
881 
882 impl AsRawFd for MultiLineHandle {
883     /// Gets the raw file descriptor for the `LineHandle`.
as_raw_fd(&self) -> RawFd884     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)]
895 pub 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
905 pub struct LineEvent(ffi::gpioevent_data);
906 
907 impl std::fmt::Debug for LineEvent {
fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result908     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 
918 impl 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.
timestamp(&self) -> u64929     pub fn timestamp(&self) -> u64 {
930         self.0.timestamp
931     }
932 
933     /// Was this a rising or a falling edge?
event_type(&self) -> EventType934     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)]
954 pub struct LineEventHandle {
955     line: Line,
956     file: File,
957 }
958 
959 impl 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.
get_event(&mut self) -> Result<LineEvent>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`.
get_value(&self) -> Result<u8>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.
line(&self) -> &Line986     pub fn line(&self) -> &Line {
987         &self.line
988     }
989 
file(&self) -> &File990     pub fn file(&self) -> &File {
991         &self.file
992     }
993 
file2(&mut self) -> &File994     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()`.
read_event(&mut self) -> std::io::Result<Option<LineEvent>>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 
1017 impl AsRawFd for LineEventHandle {
1018     /// Gets the raw file descriptor for the `LineEventHandle`.
as_raw_fd(&self) -> RawFd1019     fn as_raw_fd(&self) -> RawFd {
1020         self.file.as_raw_fd()
1021     }
1022 }
1023 
1024 impl AsFd for LineEventHandle {
1025     /// Gets the raw file descriptor for the `LineEventHandle`.
as_fd(&self) -> BorrowedFd<'_>1026     fn as_fd(&self) -> BorrowedFd<'_> {
1027         self.file.as_fd()
1028     }
1029 }
1030 
1031 impl Iterator for LineEventHandle {
1032     type Item = Result<LineEvent>;
1033 
next(&mut self) -> Option<Result<LineEvent>>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 }
1042