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 //! Wrapper for asynchronous programming using Tokio.
10 
11 use futures::ready;
12 use futures::stream::Stream;
13 use futures::task::{Context, Poll};
14 use tokio::io::unix::{AsyncFd, TryIoError};
15 
16 use std::os::unix::io::AsRawFd;
17 use std::pin::Pin;
18 
19 use super::event_err;
20 use super::{LineEvent, LineEventHandle, Result};
21 
22 /// Wrapper around a `LineEventHandle` which implements a `futures::stream::Stream` for interrupts.
23 ///
24 /// # Example
25 ///
26 /// The following example waits for state changes on an input line.
27 ///
28 /// ```no_run
29 /// use futures::stream::StreamExt;
30 /// use gpio_cdev::{AsyncLineEventHandle, Chip, EventRequestFlags, LineRequestFlags};
31 ///
32 /// async fn print_events(line: u32) -> Result<(), gpio_cdev::Error> {
33 ///     let mut chip = Chip::new("/dev/gpiochip0")?;
34 ///     let line = chip.get_line(line)?;
35 ///     let mut events = AsyncLineEventHandle::new(line.events(
36 ///         LineRequestFlags::INPUT,
37 ///         EventRequestFlags::BOTH_EDGES,
38 ///         "gpioevents",
39 ///     )?)?;
40 ///
41 ///     loop {
42 ///         match events.next().await {
43 ///             Some(event) => println!("{:?}", event?),
44 ///             None => break,
45 ///         };
46 ///     }
47 ///
48 ///     Ok(())
49 /// }
50 ///
51 /// # #[tokio::main]
52 /// # async fn main() {
53 /// #     print_events(42).await.unwrap();
54 /// # }
55 /// ```
56 pub struct AsyncLineEventHandle {
57     asyncfd: AsyncFd<LineEventHandle>,
58 }
59 
60 impl AsyncLineEventHandle {
61     /// Wraps the specified `LineEventHandle`.
62     ///
63     /// # Arguments
64     ///
65     /// * `handle` - handle to be wrapped.
new(handle: LineEventHandle) -> Result<AsyncLineEventHandle>66     pub fn new(handle: LineEventHandle) -> Result<AsyncLineEventHandle> {
67         // The file descriptor needs to be configured for non-blocking I/O for PollEvented to work.
68         let fd = handle.file.as_raw_fd();
69         unsafe {
70             let flags = libc::fcntl(fd, libc::F_GETFL, 0);
71             libc::fcntl(fd, libc::F_SETFL, flags | libc::O_NONBLOCK);
72         }
73 
74         Ok(AsyncLineEventHandle {
75             asyncfd: AsyncFd::new(handle)?,
76         })
77     }
78 }
79 
80 impl Stream for AsyncLineEventHandle {
81     type Item = Result<LineEvent>;
82 
poll_next(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<Option<Self::Item>>83     fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<Option<Self::Item>> {
84         loop {
85             let mut guard = ready!(self.asyncfd.poll_read_ready_mut(cx))?;
86             match guard.try_io(|inner| inner.get_mut().read_event()) {
87                 Err(TryIoError { .. }) => {
88                     // Continue
89                 }
90                 Ok(Ok(Some(event))) => return Poll::Ready(Some(Ok(event))),
91                 Ok(Ok(None)) => return Poll::Ready(Some(Err(event_err(nix::errno::Errno::EIO)))),
92                 Ok(Err(err)) => return Poll::Ready(Some(Err(err.into()))),
93             }
94         }
95     }
96 }
97 
98 impl AsRef<LineEventHandle> for AsyncLineEventHandle {
as_ref(&self) -> &LineEventHandle99     fn as_ref(&self) -> &LineEventHandle {
100         self.asyncfd.get_ref()
101     }
102 }
103