sysfs_gpio/
lib.rs

1// Copyright 2015, Paul Osborne <osbpau@gmail.com>
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// http://www.apache.org/license/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// Portions of this implementation are based on work by Nat Pryce:
10// https://github.com/npryce/rusty-pi/blob/master/src/pi/gpio.rs
11
12//! GPIO access under Linux using the GPIO sysfs interface
13//!
14//! The methods exposed by this library are centered around
15//! the `Pin` struct and map pretty directly the API exposed
16//! by the kernel in syfs <https://www.kernel.org/doc/Documentation/gpio/sysfs.txt>.
17//!
18//! # Examples
19//!
20//! Typical usage for systems where one wants to ensure that
21//! the pins in use are unexported upon completion looks like
22//! the following:
23//!
24//! ```no_run
25//! use sysfs_gpio::{Direction, Pin};
26//! use std::thread::sleep;
27//! use std::time::Duration;
28//!
29//! fn main() {
30//!     let my_led = Pin::new(127); // number depends on chip, etc.
31//!     my_led.with_exported(|| {
32//!         my_led.set_direction(Direction::Out).unwrap();
33//!         loop {
34//!             my_led.set_value(0).unwrap();
35//!             sleep(Duration::from_millis(200));
36//!             my_led.set_value(1).unwrap();
37//!             sleep(Duration::from_millis(200));
38//!         }
39//!     }).unwrap();
40//! }
41//! ```
42
43#![cfg_attr(feature = "async-tokio", allow(deprecated))]
44
45#[cfg(feature = "async-tokio")]
46extern crate futures;
47#[cfg(feature = "mio-evented")]
48extern crate mio;
49#[cfg(not(target_os = "wasi"))]
50extern crate nix;
51#[cfg(feature = "async-tokio")]
52extern crate tokio;
53
54use std::io;
55use std::io::prelude::*;
56#[cfg(any(target_os = "linux", target_os = "android", feature = "async-tokio"))]
57use std::io::SeekFrom;
58#[cfg(not(target_os = "wasi"))]
59use std::os::unix::prelude::*;
60use std::path::Path;
61use std::{fs, fs::File};
62
63#[cfg(feature = "async-tokio")]
64use futures::{ready, Stream};
65#[cfg(feature = "mio-evented")]
66use mio::event::Source;
67#[cfg(feature = "mio-evented")]
68use mio::unix::SourceFd;
69#[cfg(any(target_os = "linux", target_os = "android"))]
70use nix::sys::epoll::*;
71#[cfg(not(target_os = "wasi"))]
72use nix::unistd::close;
73#[cfg(feature = "async-tokio")]
74use std::task::Poll;
75#[cfg(feature = "async-tokio")]
76use tokio::io::unix::AsyncFd;
77
78pub use error::Error;
79
80mod error;
81
82#[derive(Clone, Copy, Debug, PartialEq, Eq)]
83pub struct Pin {
84    pin_num: u64,
85}
86
87#[derive(Clone, Copy, Debug, PartialEq, Eq)]
88pub enum Direction {
89    In,
90    Out,
91    High,
92    Low,
93}
94
95#[derive(Clone, Copy, Debug, PartialEq, Eq)]
96pub enum Edge {
97    NoInterrupt,
98    RisingEdge,
99    FallingEdge,
100    BothEdges,
101}
102
103#[macro_export]
104macro_rules! try_unexport {
105    ($gpio:ident, $e:expr) => {
106        match $e {
107            Ok(res) => res,
108            Err(e) => {
109                $gpio.unexport()?;
110                return Err(e);
111            }
112        }
113    };
114}
115
116pub type Result<T> = ::std::result::Result<T, error::Error>;
117
118/// Flush up to max bytes from the provided files input buffer
119///
120/// Typically, one would just use seek() for this sort of thing,
121/// but for certain files (e.g. in sysfs), you need to actually
122/// read it.
123#[cfg(any(target_os = "linux", target_os = "android"))]
124fn flush_input_from_file(dev_file: &mut File, max: usize) -> io::Result<usize> {
125    let mut s = String::with_capacity(max);
126    dev_file.read_to_string(&mut s)
127}
128
129/// Get the pin value from the provided file
130#[cfg(any(target_os = "linux", target_os = "android", feature = "async-tokio"))]
131fn get_value_from_file(dev_file: &mut File) -> Result<u8> {
132    let mut s = String::with_capacity(10);
133    dev_file.seek(SeekFrom::Start(0))?;
134    dev_file.read_to_string(&mut s)?;
135    match s[..1].parse::<u8>() {
136        Ok(n) => Ok(n),
137        Err(_) => Err(Error::Unexpected(format!(
138            "Unexpected value file contents: {:?}",
139            s
140        ))),
141    }
142}
143
144impl Pin {
145    /// Write all of the provided contents to the specified devFile
146    fn write_to_device_file(&self, dev_file_name: &str, value: &str) -> io::Result<()> {
147        let gpio_path = format!("/sys/class/gpio/gpio{}/{}", self.pin_num, dev_file_name);
148        let mut dev_file = File::create(&gpio_path)?;
149        dev_file.write_all(value.as_bytes())?;
150        Ok(())
151    }
152
153    fn read_from_device_file(&self, dev_file_name: &str) -> io::Result<String> {
154        let gpio_path = format!("/sys/class/gpio/gpio{}/{}", self.pin_num, dev_file_name);
155        let mut dev_file = File::open(&gpio_path)?;
156        let mut s = String::new();
157        dev_file.read_to_string(&mut s)?;
158        Ok(s)
159    }
160
161    /// Create a new Pin with the provided `pin_num`
162    ///
163    /// This function does not export the provided pin_num.
164    pub fn new(pin_num: u64) -> Pin {
165        Pin { pin_num }
166    }
167
168    /// Create a new Pin with the provided path
169    ///
170    /// This form is useful when there are other scripts which may
171    /// have already exported the GPIO and created a symlink with a
172    /// nice name that you already have reference to.  Otherwise, it
173    /// is generally preferrable to use `new` directly.
174    ///
175    /// The provided path must be either the already exported
176    /// directory for a GPIO or a symlink to one.  If the directory
177    /// does not look sufficiently like this (i.e. does not resolve to
178    /// a path starting with /sys/class/gpioXXX), then this function
179    /// will return an error.
180    pub fn from_path<T: AsRef<Path>>(path: T) -> Result<Pin> {
181        // Resolve all symbolic links in the provided path
182        let pb = fs::canonicalize(path.as_ref())?;
183
184        // determine if this is valid and figure out the pin_num
185        if !fs::metadata(&pb)?.is_dir() {
186            return Err(Error::Unexpected(
187                "Provided path not a directory or symlink to \
188                                          a directory"
189                    .to_owned(),
190            ));
191        }
192        let num = Pin::extract_pin_from_path(&pb)?;
193        Ok(Pin::new(num))
194    }
195
196    /// Extract pin number from paths like /sys/class/gpio/gpioXXX
197    fn extract_pin_from_path<P: AsRef<Path>>(path: P) -> Result<u64> {
198        path.as_ref()
199            .file_name()
200            .and_then(|filename| filename.to_str())
201            .and_then(|filename_str| filename_str.trim_start_matches("gpio").parse::<u64>().ok())
202            .ok_or_else(|| Error::InvalidPath(format!("{:?}", path.as_ref())))
203    }
204
205    /// Get the pin number
206    pub fn get_pin_num(&self) -> u64 {
207        self.pin_num
208    }
209
210    /// Run a closure with the GPIO exported
211    ///
212    /// Prior to the provided closure being executed, the GPIO
213    /// will be exported.  After the closure execution is complete,
214    /// the GPIO will be unexported.
215    ///
216    /// # Example
217    ///
218    /// ```no_run
219    /// use sysfs_gpio::{Pin, Direction};
220    ///
221    /// let gpio = Pin::new(24);
222    /// let res = gpio.with_exported(|| {
223    ///     println!("At this point, the Pin is exported");
224    ///     gpio.set_direction(Direction::Low)?;
225    ///     gpio.set_value(1)?;
226    ///     // ...
227    ///     Ok(())
228    /// });
229    /// ```
230    #[inline]
231    pub fn with_exported<F: FnOnce() -> Result<()>>(&self, closure: F) -> Result<()> {
232        self.export()?;
233        match closure() {
234            Ok(()) => {
235                self.unexport()?;
236                Ok(())
237            }
238            Err(err) => {
239                self.unexport()?;
240                Err(err)
241            }
242        }
243    }
244
245    /// Determines whether the GPIO is exported
246    ///
247    /// This function will error out if the kernel does not support the GPIO
248    /// sysfs interface (i.e. `/sys/class/gpio` does not exist).
249    pub fn is_exported(&self) -> bool {
250        fs::metadata(&format!("/sys/class/gpio/gpio{}", self.pin_num)).is_ok()
251    }
252
253    /// Export the GPIO
254    ///
255    /// This is equivalent to `echo N > /sys/class/gpio/export` with
256    /// the exception that the case where the GPIO is already exported
257    /// is not an error.
258    ///
259    /// # Errors
260    ///
261    /// The main cases in which this function will fail and return an
262    /// error are the following:
263    /// 1. The system does not support the GPIO sysfs interface
264    /// 2. The requested GPIO is out of range and cannot be exported
265    /// 3. The requested GPIO is in use by the kernel and cannot
266    ///    be exported by use in userspace
267    ///
268    /// # Example
269    /// ```no_run
270    /// use sysfs_gpio::Pin;
271    ///
272    /// let gpio = Pin::new(24);
273    /// match gpio.export() {
274    ///     Ok(()) => println!("Gpio {} exported!", gpio.get_pin()),
275    ///     Err(err) => println!("Gpio {} could not be exported: {}", gpio.get_pin(), err),
276    /// }
277    /// ```
278    pub fn export(&self) -> Result<()> {
279        if fs::metadata(&format!("/sys/class/gpio/gpio{}", self.pin_num)).is_err() {
280            let mut export_file = File::create("/sys/class/gpio/export")?;
281            export_file.write_all(format!("{}", self.pin_num).as_bytes())?;
282        }
283        Ok(())
284    }
285
286    /// Unexport the GPIO
287    ///
288    /// This function will unexport the provided by from syfs if
289    /// it is currently exported.  If the pin is not currently
290    /// exported, it will return without error.  That is, whenever
291    /// this function returns Ok, the GPIO is not exported.
292    pub fn unexport(&self) -> Result<()> {
293        if fs::metadata(&format!("/sys/class/gpio/gpio{}", self.pin_num)).is_ok() {
294            let mut unexport_file = File::create("/sys/class/gpio/unexport")?;
295            unexport_file.write_all(format!("{}", self.pin_num).as_bytes())?;
296        }
297        Ok(())
298    }
299
300    /// Get the pin number for the Pin
301    pub fn get_pin(&self) -> u64 {
302        self.pin_num
303    }
304
305    /// Get the direction of the Pin
306    pub fn get_direction(&self) -> Result<Direction> {
307        match self.read_from_device_file("direction") {
308            Ok(s) => match s.trim() {
309                "in" => Ok(Direction::In),
310                "out" => Ok(Direction::Out),
311                "high" => Ok(Direction::High),
312                "low" => Ok(Direction::Low),
313                other => Err(Error::Unexpected(format!(
314                    "direction file contents {}",
315                    other
316                ))),
317            },
318            Err(e) => Err(::std::convert::From::from(e)),
319        }
320    }
321
322    /// Set this GPIO as either an input or an output
323    ///
324    /// The basic values allowed here are `Direction::In` and
325    /// `Direction::Out` which set the Pin as either an input
326    /// or output respectively.  In addition to those, two
327    /// additional settings of `Direction::High` and
328    /// `Direction::Low`.  These both set the Pin as an output
329    /// but do so with an initial value of high or low respectively.
330    /// This allows for glitch-free operation.
331    ///
332    /// Note that this entry may not exist if the kernel does
333    /// not support changing the direction of a pin in userspace.  If
334    /// this is the case, you will get an error.
335    pub fn set_direction(&self, dir: Direction) -> Result<()> {
336        self.write_to_device_file(
337            "direction",
338            match dir {
339                Direction::In => "in",
340                Direction::Out => "out",
341                Direction::High => "high",
342                Direction::Low => "low",
343            },
344        )?;
345
346        Ok(())
347    }
348
349    /// Get the value of the Pin (0 or 1)
350    ///
351    /// If successful, 1 will be returned if the pin is high
352    /// and 0 will be returned if the pin is low (this may or may
353    /// not match the signal level of the actual signal depending
354    /// on the GPIO "active_low" entry).
355    pub fn get_value(&self) -> Result<u8> {
356        match self.read_from_device_file("value") {
357            Ok(s) => match s.trim() {
358                "1" => Ok(1),
359                "0" => Ok(0),
360                other => Err(Error::Unexpected(format!("value file contents {}", other))),
361            },
362            Err(e) => Err(::std::convert::From::from(e)),
363        }
364    }
365
366    /// Set the value of the Pin
367    ///
368    /// This will set the value of the pin either high or low.
369    /// A 0 value will set the pin low and any other value will
370    /// set the pin high (1 is typical).
371    pub fn set_value(&self, value: u8) -> Result<()> {
372        self.write_to_device_file(
373            "value",
374            match value {
375                0 => "0",
376                _ => "1",
377            },
378        )?;
379
380        Ok(())
381    }
382
383    /// Get the currently configured edge for this pin
384    ///
385    /// This value will only be present if the Pin allows
386    /// for interrupts.
387    pub fn get_edge(&self) -> Result<Edge> {
388        match self.read_from_device_file("edge") {
389            Ok(s) => match s.trim() {
390                "none" => Ok(Edge::NoInterrupt),
391                "rising" => Ok(Edge::RisingEdge),
392                "falling" => Ok(Edge::FallingEdge),
393                "both" => Ok(Edge::BothEdges),
394                other => Err(Error::Unexpected(format!(
395                    "Unexpected file contents {}",
396                    other
397                ))),
398            },
399            Err(e) => Err(::std::convert::From::from(e)),
400        }
401    }
402
403    /// Set the edge on which this GPIO will trigger when polled
404    ///
405    /// The configured edge determines what changes to the Pin will
406    /// result in `poll()` returning.  This call will return an Error
407    /// if the pin does not allow interrupts.
408    pub fn set_edge(&self, edge: Edge) -> Result<()> {
409        self.write_to_device_file(
410            "edge",
411            match edge {
412                Edge::NoInterrupt => "none",
413                Edge::RisingEdge => "rising",
414                Edge::FallingEdge => "falling",
415                Edge::BothEdges => "both",
416            },
417        )?;
418
419        Ok(())
420    }
421
422    /// Get polarity of the Pin (`true` is active low)
423    pub fn get_active_low(&self) -> Result<bool> {
424        match self.read_from_device_file("active_low") {
425            Ok(s) => match s.trim() {
426                "1" => Ok(true),
427                "0" => Ok(false),
428                other => Err(Error::Unexpected(format!(
429                    "active_low file contents {}",
430                    other
431                ))),
432            },
433            Err(e) => Err(::std::convert::From::from(e)),
434        }
435    }
436
437    /// Set the polarity of the Pin (`true` is active low)
438    ///
439    /// This will affect "rising" and "falling" edge triggered
440    /// configuration.
441    pub fn set_active_low(&self, active_low: bool) -> Result<()> {
442        self.write_to_device_file(
443            "active_low",
444            match active_low {
445                true => "1",
446                false => "0",
447            },
448        )?;
449
450        Ok(())
451    }
452
453    /// Get a PinPoller object for this pin
454    ///
455    /// This pin poller object will register an interrupt with the
456    /// kernel and allow you to poll() on it and receive notifications
457    /// that an interrupt has occured with minimal delay.
458    #[cfg(not(target_os = "wasi"))]
459    pub fn get_poller(&self) -> Result<PinPoller> {
460        PinPoller::new(self.pin_num)
461    }
462
463    /// Get an AsyncPinPoller object for this pin
464    ///
465    /// The async pin poller object can be used with the `mio` crate. You should probably call
466    /// `set_edge()` before using this.
467    ///
468    /// This method is only available when the `mio-evented` crate feature is enabled.
469    #[cfg(feature = "mio-evented")]
470    pub fn get_async_poller(&self) -> Result<AsyncPinPoller> {
471        AsyncPinPoller::new(self.pin_num)
472    }
473
474    /// Get a Stream of pin interrupts for this pin
475    ///
476    /// The PinStream object can be used with the `tokio` crate. You should probably call
477    /// `set_edge()` before using this.
478    ///
479    /// This method is only available when the `async-tokio` crate feature is enabled.
480    #[cfg(feature = "async-tokio")]
481    pub fn get_stream(&self) -> Result<PinStream> {
482        PinStream::init(*self)
483    }
484
485    /// Get a Stream of pin values for this pin
486    ///
487    /// The PinStream object can be used with the `tokio` crate. You should probably call
488    /// `set_edge(Edge::BothEdges)` before using this.
489    ///
490    /// Note that the values produced are the value of the pin as soon as we get to handling the
491    /// interrupt in userspace.  Each time this stream produces a value, a change has occurred, but
492    /// it could end up producing the same value multiple times if the value has changed back
493    /// between when the interrupt occurred and when the value was read.
494    ///
495    /// This method is only available when the `async-tokio` crate feature is enabled.
496    #[cfg(feature = "async-tokio")]
497    pub fn get_value_stream(&self) -> Result<PinValueStream> {
498        Ok(PinValueStream(PinStream::init(*self)?))
499    }
500}
501
502#[test]
503fn extract_pin_fom_path_test() {
504    let tok1 = Pin::extract_pin_from_path(&"/sys/class/gpio/gpio951");
505    assert_eq!(951, tok1.unwrap());
506    let tok2 = Pin::extract_pin_from_path(&"/sys/CLASS/gpio/gpio951/");
507    assert_eq!(951, tok2.unwrap());
508    let tok3 = Pin::extract_pin_from_path(&"../../devices/soc0/gpiochip3/gpio/gpio124");
509    assert_eq!(124, tok3.unwrap());
510    let err1 = Pin::extract_pin_from_path(&"/sys/CLASS/gpio/gpio");
511    assert!(err1.is_err());
512    let err2 = Pin::extract_pin_from_path(&"/sys/class/gpio/gpioSDS");
513    assert!(err2.is_err());
514}
515#[cfg(not(target_os = "wasi"))]
516#[derive(Debug)]
517pub struct PinPoller {
518    pin_num: u64,
519    epoll_fd: RawFd,
520    devfile: File,
521}
522#[cfg(not(target_os = "wasi"))]
523impl PinPoller {
524    /// Get the pin associated with this PinPoller
525    ///
526    /// Note that this will be a new Pin object with the
527    /// proper pin number.
528    pub fn get_pin(&self) -> Pin {
529        Pin::new(self.pin_num)
530    }
531
532    /// Create a new PinPoller for the provided pin number
533    #[cfg(any(target_os = "linux", target_os = "android"))]
534    pub fn new(pin_num: u64) -> Result<PinPoller> {
535        let devfile: File = File::open(&format!("/sys/class/gpio/gpio{}/value", pin_num))?;
536        let devfile_fd = devfile.as_raw_fd();
537        let epoll_fd = epoll_create()?;
538        let mut event = EpollEvent::new(EpollFlags::EPOLLPRI | EpollFlags::EPOLLET, 0u64);
539
540        match epoll_ctl(epoll_fd, EpollOp::EpollCtlAdd, devfile_fd, &mut event) {
541            Ok(_) => Ok(PinPoller {
542                pin_num,
543                devfile,
544                epoll_fd,
545            }),
546            Err(err) => {
547                let _ = close(epoll_fd); // cleanup
548                Err(::std::convert::From::from(err))
549            }
550        }
551    }
552
553    #[cfg(not(any(target_os = "linux", target_os = "android")))]
554    pub fn new(_pin_num: u64) -> Result<PinPoller> {
555        Err(Error::Unsupported("PinPoller".into()))
556    }
557
558    /// Block until an interrupt occurs
559    ///
560    /// This call will block until an interrupt occurs.  The types
561    /// of interrupts which may result in this call returning
562    /// may be configured by calling `set_edge()` prior to
563    /// making this call.  This call makes use of epoll under the
564    /// covers.  To poll on multiple GPIOs or other event sources,
565    /// poll asynchronously using the integration with either `mio`
566    /// or `tokio`.
567    ///
568    /// This function will return Some(value) of the pin if a change is
569    /// detected or None if a timeout occurs.  Note that the value provided
570    /// is the value of the pin as soon as we get to handling the interrupt
571    /// in userspace.  Each time this function returns with a value, a change
572    /// has occurred, but you could end up reading the same value multiple
573    /// times as the value has changed back between when the interrupt
574    /// occurred and the current time.
575    #[cfg(any(target_os = "linux", target_os = "android"))]
576    pub fn poll(&mut self, timeout_ms: isize) -> Result<Option<u8>> {
577        flush_input_from_file(&mut self.devfile, 255)?;
578        let dummy_event = EpollEvent::new(EpollFlags::EPOLLPRI | EpollFlags::EPOLLET, 0u64);
579        let mut events: [EpollEvent; 1] = [dummy_event];
580        let cnt = epoll_wait(self.epoll_fd, &mut events, timeout_ms)?;
581        Ok(match cnt {
582            0 => None, // timeout
583            _ => Some(get_value_from_file(&mut self.devfile)?),
584        })
585    }
586
587    #[cfg(not(any(target_os = "linux", target_os = "android")))]
588    pub fn poll(&mut self, _timeout_ms: isize) -> Result<Option<u8>> {
589        Err(Error::Unsupported("PinPoller".into()))
590    }
591}
592
593#[cfg(not(target_os = "wasi"))]
594impl Drop for PinPoller {
595    fn drop(&mut self) {
596        // we implement drop to close the underlying epoll fd as
597        // it does not implement drop itself.  This is similar to
598        // how mio works
599        close(self.epoll_fd).unwrap(); // panic! if close files
600    }
601}
602
603#[cfg(feature = "mio-evented")]
604#[derive(Debug)]
605pub struct AsyncPinPoller {
606    devfile: File,
607}
608
609#[cfg(feature = "mio-evented")]
610impl AsyncPinPoller {
611    fn new(pin_num: u64) -> Result<Self> {
612        let devfile = File::open(&format!("/sys/class/gpio/gpio{}/value", pin_num))?;
613        Ok(AsyncPinPoller { devfile })
614    }
615}
616
617#[cfg(feature = "mio-evented")]
618impl Source for AsyncPinPoller {
619    fn register(
620        &mut self,
621        poll: &mio::Registry,
622        token: mio::Token,
623        interest: mio::Interest,
624    ) -> io::Result<()> {
625        SourceFd(&self.as_raw_fd()).register(poll, token, interest)
626    }
627
628    fn reregister(
629        &mut self,
630        poll: &mio::Registry,
631        token: mio::Token,
632        interest: mio::Interest,
633    ) -> io::Result<()> {
634        SourceFd(&self.as_raw_fd()).reregister(poll, token, interest)
635    }
636
637    fn deregister(&mut self, poll: &mio::Registry) -> io::Result<()> {
638        SourceFd(&self.as_raw_fd()).deregister(poll)
639    }
640}
641
642#[cfg(any(feature = "async-tokio", feature = "mio-evented"))]
643impl AsRawFd for AsyncPinPoller {
644    fn as_raw_fd(&self) -> RawFd {
645        self.devfile.as_raw_fd()
646    }
647}
648
649#[cfg(feature = "async-tokio")]
650pub struct PinStream {
651    evented: AsyncFd<AsyncPinPoller>,
652    skipped_first_event: bool,
653}
654
655#[cfg(feature = "async-tokio")]
656impl PinStream {
657    pub fn init(pin: Pin) -> Result<Self> {
658        Ok(PinStream {
659            evented: AsyncFd::new(pin.get_async_poller()?)?,
660            skipped_first_event: false,
661        })
662    }
663}
664
665#[cfg(feature = "async-tokio")]
666impl Stream for PinStream {
667    type Item = Result<()>;
668
669    fn poll_next(
670        mut self: std::pin::Pin<&mut Self>,
671        cx: &mut std::task::Context<'_>,
672    ) -> Poll<Option<Self::Item>> {
673        loop {
674            let mut guard = ready!(self.evented.poll_read_ready(cx))?;
675            guard.clear_ready();
676            if self.skipped_first_event {
677                return Poll::Ready(Some(Ok(())));
678            } else {
679                self.skipped_first_event = true;
680            }
681        }
682    }
683}
684
685#[cfg(feature = "async-tokio")]
686pub struct PinValueStream(PinStream);
687
688#[cfg(feature = "async-tokio")]
689impl PinValueStream {
690    #[inline]
691    fn get_value(&mut self) -> Result<u8> {
692        get_value_from_file(&mut self.0.evented.get_mut().devfile)
693    }
694}
695
696#[cfg(feature = "async-tokio")]
697impl Stream for PinValueStream {
698    type Item = Result<u8>;
699
700    fn poll_next(
701        mut self: std::pin::Pin<&mut Self>,
702        cx: &mut std::task::Context<'_>,
703    ) -> Poll<Option<Self::Item>> {
704        ready!(std::pin::Pin::new(&mut self.0).poll_next(cx));
705        Poll::Ready(Some(Ok(self.get_value()?)))
706    }
707}