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