├── .gitignore ├── Cargo.toml ├── LICENSE ├── README.md ├── examples ├── gpio_edge.rs └── gpio_inout.rs └── src ├── dummy.rs ├── lib.rs └── sysfs.rs /.gitignore: -------------------------------------------------------------------------------- 1 | Cargo.lock 2 | target/ 3 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["Marc Brinkmann "] 3 | description = "Fast GPIO interfaces for Linux" 4 | documentation = "https://docs.rs/gpio" 5 | license = "MIT" 6 | name = "gpio" 7 | repository = "https://github.com/mbr/gpio-rs" 8 | version = "0.4.0" 9 | 10 | [dependencies] 11 | nix = "0.10.0" 12 | quick-error = "1.2.1" 13 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017 Marc Brinkmann 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 7 | of the Software, and to permit persons to whom the Software is furnished to do 8 | so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Rust GPIO 2 | ========= 3 | 4 | Deals with GPIO access on Linux and bare metal embedded systems, through sysfs 5 | and direct memory access. Works on stable Rust. 6 | 7 | Roadmap 8 | ------- 9 | 10 | - [x] GPIO write support 11 | - [x] Read support 12 | - [ ] Interrupt support 13 | 14 | Other libraries 15 | --------------- 16 | 17 | Other libraries [can be found on crates.io](https://crates.io/search?q=gpio). 18 | These include: 19 | 20 | * `sysfs_gpio `_ handles GPIO 21 | only via SysFS, but exposes all features. Slightly lower level. 22 | * `cylus `_ Documentation is dead, does a few 23 | questionable things like unwrapping() 24 | * `cupi `_ Most comprehensive GPIO library, 25 | includes almost all features planned for gpio. Does not use volatile. 26 | 27 | TODO: Benchmark 28 | -------------------------------------------------------------------------------- /examples/gpio_edge.rs: -------------------------------------------------------------------------------- 1 | //! Raspberry Pi 2 GPIO example 2 | //! 3 | //! Reads from one GPIO, while toggling output on the other 4 | 5 | extern crate gpio; 6 | 7 | use gpio::{GpioEdge, GpioIn, GpioOut}; 8 | use std::{thread, time}; 9 | 10 | fn main() { 11 | let mut gpio17 = gpio::sysfs::SysFsGpioInput::open(17).unwrap(); 12 | let mut gpio27 = gpio::sysfs::SysFsGpioOutput::open(27).unwrap(); 13 | 14 | // GPIO27 will be toggled every second in the background by a different thread 15 | let mut value = false; 16 | thread::spawn(move || loop { 17 | gpio27.set_value(value).expect("could not set gpio27"); 18 | println!("GPIO27 set to {:?}", value); 19 | thread::sleep(time::Duration::from_millis(1000)); 20 | value = !value; 21 | }); 22 | 23 | // GPIO17 waits for falling edges and displays the value 24 | gpio17.set_edge(GpioEdge::Rising).expect( 25 | "set edge on gpio17", 26 | ); 27 | for result in gpio::sysfs::SysFsGpioEdgeIter::new() 28 | .expect("create iterator") 29 | .add(&gpio17) 30 | .expect("add gpio 17 to iter") 31 | { 32 | println!("GPIO17: {:?}", result.unwrap().gpio_num()); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /examples/gpio_inout.rs: -------------------------------------------------------------------------------- 1 | //! Raspberry Pi 2 GPIO example 2 | //! 3 | //! Reads from one GPIO, while toggling output on the other 4 | 5 | extern crate gpio; 6 | 7 | use gpio::{GpioIn, GpioOut}; 8 | 9 | use std::{thread, time}; 10 | 11 | fn main() { 12 | let gpio23 = gpio::sysfs::SysFsGpioInput::open(23).expect("could not open GPIO23"); 13 | let mut gpio24 = gpio::sysfs::SysFsGpioOutput::open(24).expect("could not open GPIO24"); 14 | 15 | // start a thread to toggle the gpio on and off at a different rate 16 | let mut value = false; 17 | thread::spawn(move || loop { 18 | gpio24.set_value(value).expect("could not set gpio24"); 19 | thread::sleep(time::Duration::from_millis(1000)); 20 | value = !value; 21 | }); 22 | 23 | loop { 24 | println!( 25 | "GPIO23: {:?}", 26 | gpio23.read_value().expect("could not read gpio23") 27 | ); 28 | 29 | thread::sleep(time::Duration::from_millis(100)); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/dummy.rs: -------------------------------------------------------------------------------- 1 | //! GPIO dummy input/output 2 | //! 3 | //! The dummy module can be used instead of a GPIO implementation tied to 4 | //! hardware to run unit tests or otherwise provide means to test an 5 | //! application when no embedded device is around. 6 | //! 7 | //! It supports the same interface as other GPIOs and its input and output 8 | //! behaviour can be configured in a flexible manner. 9 | //! 10 | //! ## Example 11 | //! 12 | //! The `DummyGpioIn` reads values from a callback: 13 | //! 14 | //! ```rust 15 | //! use std::time; 16 | //! use gpio::{GpioIn, GpioValue}; 17 | //! use gpio::dummy::DummyGpioIn; 18 | //! 19 | //! // a simple dummy gpio that is always `true`/`High` 20 | //! let mut dg = DummyGpioIn::new(|| true); 21 | //! assert_eq!(GpioValue::High, dg.read_value().unwrap()); 22 | //! 23 | //! // another example that flips every second 24 | //! let mut timed_gpio = DummyGpioIn::new(|| { 25 | //! std::time::SystemTime::now() 26 | //! .duration_since(time::UNIX_EPOCH) 27 | //! .unwrap() 28 | //! .as_secs() % 2 == 0 29 | //! }); 30 | //! println!("timed: {:?}", timed_gpio.read_value().unwrap()); 31 | //! ``` 32 | //! 33 | //! Output can simple be swallowed by a dummy output port: 34 | //! 35 | //! ```rust 36 | //! use gpio::{GpioOut}; 37 | //! use gpio::dummy::DummyGpioOut; 38 | //! 39 | //! let mut dg = DummyGpioOut::new(|_| ()); 40 | //! dg.set_value(true); 41 | //! ``` 42 | 43 | use std::{sync, thread, time}; 44 | use super::{GpioEdge, GpioIn, GpioOut, GpioValue}; 45 | 46 | /// Dummy GPIO input pin 47 | #[derive(Clone)] 48 | pub struct DummyGpioIn { 49 | value: sync::Arc GpioValue>, 50 | edge: GpioEdge, 51 | } 52 | 53 | impl DummyGpioIn { 54 | /// Create new dummy pin that returns the value of `value` every it is read 55 | pub fn new(value: F) -> DummyGpioIn 56 | where 57 | V: Into, 58 | F: Fn() -> V + 'static, 59 | { 60 | DummyGpioIn { 61 | value: sync::Arc::new(move || value().into()), 62 | edge: GpioEdge::None, 63 | } 64 | } 65 | } 66 | 67 | impl GpioIn for DummyGpioIn { 68 | type Error = (); 69 | 70 | fn read_value(&self) -> Result { 71 | Ok((self.value)()) 72 | } 73 | 74 | fn set_edge(&mut self, edge: GpioEdge) -> Result<(), Self::Error> { 75 | self.edge = edge; 76 | Ok(()) 77 | } 78 | } 79 | 80 | pub struct DummyEdgeIter<'a> { 81 | timeout: Option, 82 | devs: Vec<(&'a DummyGpioIn, GpioValue)>, 83 | } 84 | 85 | impl<'a> DummyEdgeIter<'a> { 86 | pub fn new() -> Result, ()> { 87 | Ok(DummyEdgeIter { 88 | timeout: None, 89 | devs: Vec::new(), 90 | }) 91 | } 92 | 93 | pub fn timeout_ms(&mut self, timeout_ms: u64) -> &mut Self { 94 | self.timeout = Some(time::Duration::from_millis(timeout_ms)); 95 | self 96 | } 97 | 98 | pub fn add(&mut self, dev: &'a DummyGpioIn) -> Result<&mut Self, ()> { 99 | let val = dev.read_value()?; 100 | self.devs.push((dev, val)); 101 | Ok(self) 102 | } 103 | } 104 | 105 | impl<'a> Iterator for DummyEdgeIter<'a> { 106 | type Item = Result<&'a DummyGpioIn, ()>; 107 | 108 | fn next(&mut self) -> Option> { 109 | let start = time::Instant::now(); 110 | loop { 111 | if self.timeout.map_or(false, |to| start.elapsed() > to) { 112 | return Some(Err(())); 113 | } 114 | for &mut (gpio, ref mut val) in &mut self.devs { 115 | let new_val = (gpio.value)(); 116 | if *val == new_val { 117 | continue; 118 | } 119 | *val = new_val; 120 | match (gpio.edge, new_val) { 121 | (GpioEdge::Both, _) | 122 | (GpioEdge::Rising, GpioValue::High) | 123 | (GpioEdge::Falling, GpioValue::Low) => return Some(Ok(gpio)), 124 | (GpioEdge::None, _) | 125 | (GpioEdge::Rising, GpioValue::Low) | 126 | (GpioEdge::Falling, GpioValue::High) => (), 127 | } 128 | } 129 | thread::sleep(time::Duration::from_millis(1)) 130 | } 131 | } 132 | } 133 | 134 | /// Dummy GPIO output pin 135 | #[derive(Debug)] 136 | pub struct DummyGpioOut { 137 | dest: F, 138 | } 139 | 140 | impl DummyGpioOut { 141 | /// Creates a new dummy pin that passes all set values to `dest`. 142 | pub fn new(dest: F) -> DummyGpioOut { 143 | DummyGpioOut { dest } 144 | } 145 | } 146 | 147 | impl GpioOut for DummyGpioOut 148 | where 149 | F: Fn(GpioValue) -> (), 150 | { 151 | type Error = (); 152 | 153 | fn set_low(&mut self) -> Result<(), Self::Error> { 154 | (self.dest)(GpioValue::Low); 155 | Ok(()) 156 | } 157 | 158 | fn set_high(&mut self) -> Result<(), Self::Error> { 159 | (self.dest)(GpioValue::High); 160 | Ok(()) 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! GPIO interface 2 | //! 3 | //! The GPIO crate allows easy and fast access to GPIO pins. It aims to provide 4 | //! an ergonomic interface while being lower overhead, enabling high-frequency 5 | //! output without complicating simple tasks. 6 | //! 7 | //! The core interface is defined using `GpioValue` and the `GpioOut`/`GpioIn` 8 | //! traits. All backends implement at least some of these traits, making them 9 | //! interchangeable, e.g. for testing. 10 | //! 11 | //! The most commonly used implementation is based on the 12 | //! [Linux GPIO Sysfs](https://www.kernel.org/doc/Documentation/gpio/sysfs.txt) 13 | //! interface, found inside the `sysfs` crate. 14 | //! 15 | //! ## Example: writing and reading 16 | //! 17 | //! ```rust,no_run 18 | //! use gpio::{GpioIn, GpioOut}; 19 | //! use std::{thread, time}; 20 | //! 21 | //! // Let's open GPIO23 and -24, e.g. on a Raspberry Pi 2. 22 | //! let gpio23 = gpio::sysfs::SysFsGpioInput::open(23).unwrap(); 23 | //! let mut gpio24 = gpio::sysfs::SysFsGpioOutput::open(24).unwrap(); 24 | //! 25 | //! // GPIO24 will be toggled every second in the background by a different thread 26 | //! let mut value = false; 27 | //! thread::spawn(move || loop { 28 | //! gpio24.set_value(value).expect("could not set gpio24"); 29 | //! thread::sleep(time::Duration::from_millis(1000)); 30 | //! value = !value; 31 | //! }); 32 | //! 33 | //! // The main thread will simply display the current value of GPIO23 every 100ms. 34 | //! loop { 35 | //! println!("GPIO23: {:?}", gpio23.read_value().unwrap()); 36 | //! thread::sleep(time::Duration::from_millis(100)); 37 | //! } 38 | //! ``` 39 | //! 40 | //! ## Example: waiting for falling edges 41 | //! 42 | //! ```rust,no_run 43 | //! use gpio::{GpioEdge, GpioIn, GpioOut}; 44 | //! use std::{thread, time}; 45 | //! 46 | //! let mut gpio17 = gpio::sysfs::SysFsGpioInput::open(17).unwrap(); 47 | //! let mut gpio27 = gpio::sysfs::SysFsGpioOutput::open(27).unwrap(); 48 | //! 49 | //! // GPIO27 will be toggled every second in the background by a different thread 50 | //! let mut value = false; 51 | //! thread::spawn(move || loop { 52 | //! gpio27.set_value(value).expect("could not set gpio27"); 53 | //! println!("GPIO27 set to {:?}", value); 54 | //! thread::sleep(time::Duration::from_millis(1000)); 55 | //! value = !value; 56 | //! }); 57 | //! 58 | //! // GPIO17 waits for falling edges and displays the value 59 | //! gpio17 60 | //! .set_edge(GpioEdge::Falling) 61 | //! .expect("set edge on gpio17"); 62 | //! for result in gpio::sysfs::SysFsGpioEdgeIter::new() 63 | //! .expect("create iterator") 64 | //! .add(&gpio17) 65 | //! .expect("add gpio 17 to iter") 66 | //! { 67 | //! println!("GPIO17: {:?}", result.unwrap().gpio_num()); 68 | //! } 69 | //! ``` 70 | //! 71 | //! ## TODO 72 | //! 73 | //! * `/dev/mem` interface: Higher frequency port usage 74 | //! 75 | 76 | extern crate nix; 77 | #[macro_use] 78 | extern crate quick_error; 79 | 80 | pub mod sysfs; 81 | pub mod dummy; 82 | 83 | /// A value read from or written to a GPIO port 84 | #[derive(Debug, Copy, Clone, Eq, PartialEq)] 85 | pub enum GpioValue { 86 | /// A low value, usually 0 V 87 | Low, 88 | /// A high value, commonly 3.3V or 5V 89 | High, 90 | } 91 | 92 | /// A setting for signaling an interrupt. 93 | #[derive(Debug, Copy, Clone, Eq, PartialEq)] 94 | pub enum GpioEdge { 95 | /// No interrupt. 96 | None, 97 | /// Interrupt on rising edge, i.e. when going from 0 to 1. 98 | Rising, 99 | /// Interrupt on falling edge, i.e. when going from 1 to 0. 100 | Falling, 101 | /// Interrupt on both edges, i.e. whenever the value changes. 102 | Both, 103 | } 104 | 105 | impl From for GpioValue { 106 | #[inline] 107 | fn from(val: bool) -> GpioValue { 108 | if val { GpioValue::High } else { GpioValue::Low } 109 | } 110 | } 111 | 112 | impl From for GpioValue { 113 | #[inline] 114 | fn from(val: u8) -> GpioValue { 115 | if val != 0 { 116 | GpioValue::High 117 | } else { 118 | GpioValue::Low 119 | } 120 | } 121 | } 122 | 123 | impl From for bool { 124 | #[inline] 125 | fn from(val: GpioValue) -> bool { 126 | match val { 127 | GpioValue::Low => false, 128 | GpioValue::High => true, 129 | } 130 | } 131 | } 132 | 133 | impl From for u8 { 134 | #[inline] 135 | fn from(val: GpioValue) -> u8 { 136 | match val { 137 | GpioValue::Low => 0, 138 | GpioValue::High => 1, 139 | } 140 | } 141 | } 142 | 143 | /// Supports sending `GPIOValue`s 144 | pub trait GpioOut { 145 | /// Errors that can occur during initialization of or writing to GPIO 146 | type Error; 147 | 148 | /// Sets the output value of the GPIO port 149 | #[inline(always)] 150 | fn set_value + Copy>(&mut self, value: T) -> Result<(), Self::Error> { 151 | match value.into() { 152 | GpioValue::High => self.set_high(), 153 | GpioValue::Low => self.set_low(), 154 | } 155 | } 156 | 157 | /// Set the GPIO port to a low output value directly 158 | fn set_low(&mut self) -> Result<(), Self::Error>; 159 | 160 | /// Set the GPIO port to a high output value directly 161 | fn set_high(&mut self) -> Result<(), Self::Error>; 162 | } 163 | 164 | /// Supports reading `GPIOValue`s 165 | pub trait GpioIn { 166 | /// Errors that can occur during initialization of or reading from GPIO 167 | type Error; 168 | 169 | /// Perform a single reading of a GPIO port 170 | fn read_value(&self) -> Result; 171 | 172 | /// Configure the criterion for signaling an interrupt. 173 | fn set_edge(&mut self, edge: GpioEdge) -> Result<(), Self::Error>; 174 | } 175 | -------------------------------------------------------------------------------- /src/sysfs.rs: -------------------------------------------------------------------------------- 1 | //! Linux `/sys`-fs based GPIO control 2 | //! 3 | //! Uses the [Linux GPIO Sysfs](https://www.kernel.org/doc/Documentation/gpio/sysfs.txt) filesystem 4 | //! operations to control GPIO ports. It tries to reduce the otherwise hefty syscall overhead 5 | //! by keeping the sysfs files open, instead of reopening them on each read. 6 | //! 7 | //! Every `open` call to a GPIO pin will automatically export the necessary pin and unexport it 8 | //! on close. 9 | 10 | use nix; 11 | use nix::sys::epoll::{self, EpollEvent, EpollFlags, EpollOp}; 12 | use std::{cell, fs, io, isize}; 13 | use std::io::{Read, Seek, SeekFrom, Write}; 14 | use std::os::unix::io::{AsRawFd, RawFd}; 15 | use super::{GpioEdge, GpioIn, GpioOut, GpioValue}; 16 | 17 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] 18 | enum GpioDirection { 19 | Input, 20 | Output, 21 | } 22 | 23 | quick_error! { 24 | #[derive(Debug)] 25 | pub enum GpioError { 26 | Io(err: io::Error) { 27 | from() 28 | description("io error") 29 | display("I/O error: {}", err) 30 | cause(err) 31 | } 32 | Epoll(err: nix::Error) { 33 | from() 34 | description("epoll error") 35 | display("Epoll error: {}", err) 36 | cause(err) 37 | } 38 | EpollEventCount(count: usize) { 39 | description("epoll_wait returned unexpected event count value") 40 | display("epoll_wait returned unexpected event count value: {}", count) 41 | } 42 | EpollDataValue(val: u64) { 43 | description("epoll_wait returned unexpected data value") 44 | display("epoll_wait returned unexpected data value: {}", val) 45 | } 46 | InvalidData(val: u8) { 47 | description("read a value that was neither '0' nor '1' from Linux sysfs GPIO interface") 48 | display("read value {:?} from Linux sysfs GPIO interface, which is neither '0' nor '1'", 49 | val) 50 | } 51 | } 52 | } 53 | 54 | pub type GpioResult = Result; 55 | 56 | #[inline] 57 | fn export_gpio_if_unexported(gpio_num: u16) -> GpioResult<()> { 58 | // export port first if not exported 59 | if fs::metadata(&format!("/sys/class/gpio/gpio{}", gpio_num)).is_err() { 60 | let mut export_fp = fs::File::create("/sys/class/gpio/export")?; 61 | write!(export_fp, "{}", gpio_num)?; 62 | } 63 | 64 | // ensure we're using '0' as low 65 | fs::File::create(format!("/sys/class/gpio/gpio{}/active_low", gpio_num))? 66 | .write_all(b"0")?; 67 | Ok(()) 68 | } 69 | 70 | #[inline] 71 | fn set_gpio_direction(gpio_num: u16, direction: GpioDirection) -> GpioResult<()> { 72 | fs::File::create(format!("/sys/class/gpio/gpio{}/direction", gpio_num))? 73 | .write_all(match direction { 74 | GpioDirection::Input => b"in", 75 | GpioDirection::Output => b"out", 76 | })?; 77 | Ok(()) 78 | } 79 | 80 | #[inline] 81 | fn open_gpio(gpio_num: u16, direction: GpioDirection) -> GpioResult { 82 | let p = format!("/sys/class/gpio/gpio{}/value", gpio_num); 83 | 84 | Ok(match direction { 85 | GpioDirection::Input => fs::File::open(p), 86 | GpioDirection::Output => fs::File::create(p), 87 | }?) 88 | } 89 | 90 | #[derive(Debug)] 91 | struct SysFsGpio { 92 | gpio_num: u16, 93 | sysfp: cell::RefCell, 94 | } 95 | 96 | impl SysFsGpio { 97 | fn open(gpio_num: u16, direction: GpioDirection) -> GpioResult { 98 | export_gpio_if_unexported(gpio_num)?; 99 | 100 | // ensure we're using '0' as low. 101 | // FIXME: this should be configurable 102 | fs::File::create(format!("/sys/class/gpio/gpio{}/active_low", gpio_num))? 103 | .write_all(b"0")?; 104 | 105 | set_gpio_direction(gpio_num, direction)?; 106 | 107 | // finally, we can open the device 108 | Ok(SysFsGpio { 109 | gpio_num, 110 | sysfp: cell::RefCell::new(open_gpio(gpio_num, direction)?), 111 | }) 112 | } 113 | 114 | #[inline] 115 | fn set_direction(&mut self, direction: GpioDirection) -> GpioResult<()> { 116 | set_gpio_direction(self.gpio_num, direction)?; 117 | self.sysfp = cell::RefCell::new(open_gpio(self.gpio_num, direction)?); 118 | 119 | Ok(()) 120 | } 121 | } 122 | 123 | impl Drop for SysFsGpio { 124 | #[inline] 125 | fn drop(&mut self) { 126 | // unexport the pin, if we have not done so already 127 | // best effort, failures are ignored 128 | let unexport_fp = fs::File::create("/sys/class/gpio/unexport"); 129 | 130 | if let Ok(mut fp) = unexport_fp { 131 | write!(fp, "{}\n", self.gpio_num).ok(); 132 | } 133 | } 134 | } 135 | 136 | /// `/sys`-fs based GPIO output 137 | #[derive(Debug)] 138 | pub struct SysFsGpioOutput { 139 | gpio: SysFsGpio, 140 | } 141 | 142 | impl SysFsGpioOutput { 143 | /// Open a GPIO port for Output. 144 | #[inline] 145 | pub fn open(gpio_num: u16) -> GpioResult { 146 | Ok(SysFsGpioOutput { 147 | gpio: SysFsGpio::open(gpio_num, GpioDirection::Output)?, 148 | }) 149 | } 150 | 151 | #[inline] 152 | pub fn into_input(mut self) -> GpioResult { 153 | self.gpio.set_direction(GpioDirection::Input)?; 154 | SysFsGpioInput::from_gpio(self.gpio) 155 | } 156 | 157 | #[inline] 158 | pub fn gpio_num(&self) -> u16 { 159 | self.gpio.gpio_num 160 | } 161 | } 162 | 163 | impl GpioOut for SysFsGpioOutput { 164 | type Error = GpioError; 165 | 166 | #[inline] 167 | fn set_low(&mut self) -> GpioResult<()> { 168 | self.gpio.sysfp.get_mut().write_all(b"0")?; 169 | Ok(()) 170 | } 171 | 172 | #[inline] 173 | fn set_high(&mut self) -> GpioResult<()> { 174 | self.gpio.sysfp.get_mut().write_all(b"1")?; 175 | Ok(()) 176 | } 177 | } 178 | 179 | /// `/sys`-fs based GPIO output 180 | #[derive(Debug)] 181 | pub struct SysFsGpioInput { 182 | gpio: SysFsGpio, 183 | } 184 | 185 | impl SysFsGpioInput { 186 | /// Open a GPIO port for Output. 187 | #[inline] 188 | pub fn open(gpio_num: u16) -> GpioResult { 189 | Self::from_gpio(SysFsGpio::open(gpio_num, GpioDirection::Input)?) 190 | } 191 | 192 | #[inline] 193 | fn from_gpio(gpio: SysFsGpio) -> GpioResult { 194 | Ok(SysFsGpioInput { gpio }) 195 | } 196 | 197 | #[inline] 198 | pub fn into_output(mut self) -> GpioResult { 199 | self.gpio.set_direction(GpioDirection::Output)?; 200 | Ok(SysFsGpioOutput { gpio: self.gpio }) 201 | } 202 | 203 | #[inline] 204 | pub fn gpio_num(&self) -> u16 { 205 | self.gpio.gpio_num 206 | } 207 | } 208 | 209 | impl GpioIn for SysFsGpioInput { 210 | type Error = GpioError; 211 | 212 | #[inline] 213 | fn read_value(&self) -> Result { 214 | let mut buf: [u8; 1] = [0; 1]; 215 | 216 | // we rewind the file descriptor first, otherwise read will fail 217 | self.gpio.sysfp.borrow_mut().seek(SeekFrom::Start(0))?; 218 | 219 | // we read one byte, the trailing byte is a newline 220 | self.gpio.sysfp.borrow_mut().read_exact(&mut buf)?; 221 | 222 | match buf[0] { 223 | b'0' => Ok(GpioValue::Low), 224 | b'1' => Ok(GpioValue::High), 225 | val => { 226 | println!("BUFFER: {:?}", buf); 227 | Err(GpioError::InvalidData(val)) 228 | } 229 | } 230 | } 231 | 232 | fn set_edge(&mut self, edge: GpioEdge) -> Result<(), Self::Error> { 233 | fs::OpenOptions::new() 234 | .write(true) 235 | .open(format!("/sys/class/gpio/gpio{}/edge", self.gpio.gpio_num))? 236 | .write_all(match edge { 237 | GpioEdge::None => b"none", 238 | GpioEdge::Rising => b"rising", 239 | GpioEdge::Falling => b"falling", 240 | GpioEdge::Both => b"both", 241 | })?; 242 | Ok(()) 243 | } 244 | } 245 | 246 | pub struct SysFsGpioEdgeIter<'a> { 247 | /// The timeout, if any. 248 | timeout: Option, 249 | /// The GPIO devices whose edges will be included in this iterator. 250 | devs: Vec<&'a SysFsGpioInput>, 251 | /// The file descriptor of the epoll instance. 252 | epoll_fd: RawFd, 253 | } 254 | 255 | impl<'a> SysFsGpioEdgeIter<'a> { 256 | pub fn new() -> GpioResult> { 257 | let epoll_fd = epoll::epoll_create()?; 258 | Ok(SysFsGpioEdgeIter { 259 | timeout: None, 260 | devs: Vec::new(), 261 | epoll_fd, 262 | }) 263 | } 264 | 265 | pub fn timeout_ms(&mut self, timeout_ms: u64) -> &mut Self { 266 | self.timeout = Some(timeout_ms); 267 | self 268 | } 269 | 270 | pub fn add(&mut self, dev: &'a SysFsGpioInput) -> GpioResult<&mut Self> { 271 | // We use the device's index in the `devs` vector as the data registered with epoll. 272 | let index = self.devs.len() as u64; 273 | let flags = EpollFlags::EPOLLPRI | EpollFlags::EPOLLET; 274 | let mut event = EpollEvent::new(flags, index); 275 | let dev_fd = dev.gpio.sysfp.borrow().as_raw_fd(); 276 | epoll::epoll_ctl(self.epoll_fd, EpollOp::EpollCtlAdd, dev_fd, &mut event)?; 277 | self.devs.push(dev); 278 | Ok(self) 279 | } 280 | 281 | fn get_next(&mut self) -> GpioResult<&'a SysFsGpioInput> { 282 | let timeout = self.timeout.map_or(isize::MAX, |t| t as isize); 283 | // A dummy event, to be overwritten by `epoll`. 284 | let mut events = [EpollEvent::empty()]; 285 | let event_count = epoll::epoll_wait(self.epoll_fd, &mut events, timeout)?; 286 | if event_count != 1 { 287 | return Err(GpioError::EpollEventCount(event_count)); 288 | } 289 | // Epoll wrote the event data into the array. We used the device's index as the data: 290 | self.devs 291 | .get(events[0].data() as usize) 292 | .map(|d| *d) 293 | .ok_or_else(|| GpioError::EpollDataValue(events[0].data())) 294 | } 295 | } 296 | 297 | impl<'a> Iterator for SysFsGpioEdgeIter<'a> { 298 | type Item = GpioResult<&'a SysFsGpioInput>; 299 | 300 | fn next(&mut self) -> Option> { 301 | Some(self.get_next()) 302 | } 303 | } 304 | --------------------------------------------------------------------------------