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