diff --git a/Cargo.toml b/Cargo.toml index 26d4ad8..161f606 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,8 +14,8 @@ edition = "2018" [features] gpio_sysfs = ["sysfs_gpio"] -gpio_cdev = ["gpio-cdev"] -async-tokio = ["gpio-cdev/async-tokio", "dep:embedded-hal-async", "tokio/time", "tokio/rt", "gpiocdev/async_tokio"] +gpio_cdev = ["dep:gpiocdev"] +async-tokio = ["dep:embedded-hal-async", "tokio/time", "tokio/rt", "gpiocdev?/async_tokio"] i2c = ["i2cdev"] spi = ["spidev"] @@ -25,8 +25,7 @@ default = [ "gpio_cdev", "gpio_sysfs", "i2c", "spi" ] embedded-hal = "1" embedded-hal-nb = "1" embedded-hal-async = { version = "1", optional = true } -gpio-cdev = { version = "0.6.0", optional = true } -gpiocdev = { version = "0.6.0" } +gpiocdev = { version = "0.6.1", optional = true } sysfs_gpio = { version = "0.6.1", optional = true } i2cdev = { version = "0.6.0", optional = true } nb = "1" diff --git a/examples/gpio-wait.rs b/examples/gpio-wait.rs index 05b0468..fe65d14 100644 --- a/examples/gpio-wait.rs +++ b/examples/gpio-wait.rs @@ -3,24 +3,28 @@ use std::time::Duration; use embedded_hal::digital::{InputPin, OutputPin, PinState}; use embedded_hal_async::digital::Wait; -use gpio_cdev::{Chip, LineRequestFlags}; +use gpiocdev::Request; use linux_embedded_hal::CdevPin; use tokio::time::{sleep, timeout}; // This example assumes that input/output pins are shorted. +const CHIP: &str = "/dev/gpiochip0"; const INPUT_LINE: u32 = 4; const OUTPUT_LINE: u32 = 17; #[tokio::main] async fn main() -> Result<(), Box> { - let mut chip = Chip::new("/dev/gpiochip0")?; - let input = chip.get_line(INPUT_LINE)?; - let output = chip.get_line(OUTPUT_LINE)?; - - let mut input_pin = - CdevPin::new(input.request(LineRequestFlags::INPUT, 0, "")?)?.into_input_pin()?; - let mut output_pin = CdevPin::new(output.request(LineRequestFlags::OUTPUT, 0, "")?)? - .into_output_pin(PinState::Low)?; + let input = Request::builder() + .on_chip(CHIP) + .with_line(INPUT_LINE) + .request()?; + let output = Request::builder() + .on_chip(CHIP) + .with_line(OUTPUT_LINE) + .request()?; + + let mut input_pin = CdevPin::new(input)?.into_input_pin()?; + let mut output_pin = CdevPin::new(output)?.into_output_pin(PinState::Low)?; timeout(Duration::from_secs(10), async move { let set_output = tokio::spawn(async move { @@ -30,7 +34,6 @@ async fn main() -> Result<(), Box> { }); println!("Waiting for input to go high."); - input_pin.wait_for_high().await?; assert!(input_pin.is_high()?); diff --git a/src/cdev_pin.rs b/src/cdev_pin.rs index 17bbc72..1fc6c64 100644 --- a/src/cdev_pin.rs +++ b/src/cdev_pin.rs @@ -6,166 +6,146 @@ use std::fmt; use embedded_hal::digital::InputPin; #[cfg(feature = "async-tokio")] -use gpiocdev::{line::EdgeDetection, request::Request, tokio::AsyncRequest}; +use gpiocdev::{line::EdgeDetection, tokio::AsyncRequest}; +use gpiocdev::{ + line::{Offset, Value}, + request::{Config, Request}, +}; /// Newtype around [`gpio_cdev::LineHandle`] that implements the `embedded-hal` traits /// /// [`gpio_cdev::LineHandle`]: https://docs.rs/gpio-cdev/0.5.0/gpio_cdev/struct.LineHandle.html #[derive(Debug)] -pub struct CdevPin(pub Option, gpio_cdev::LineInfo); - -#[cfg(feature = "async-tokio")] -#[derive(Debug)] -struct CdevPinEdgeWaiter<'a> { - pin: &'a mut CdevPin, - edge: EdgeDetection, +pub struct CdevPin { + req: Option, + config: Config, + line: Offset, } -#[cfg(feature = "async-tokio")] -impl<'a> CdevPinEdgeWaiter<'a> { - pub fn new(pin: &'a mut CdevPin, edge: EdgeDetection) -> Result { - Ok(Self { pin, edge }) - } - - pub async fn wait(self) -> Result<(), gpiocdev::Error> { - let line_handle = self.pin.0.take().unwrap(); - let line_info = &self.pin.1; - - let line = line_handle.line().clone(); - let flags = line_handle.flags(); - let chip = line.chip().path().to_owned(); - let offset = line.offset(); - let consumer = line_info.consumer().unwrap_or("").to_owned(); - let edge = self.edge; - - // Close line handle. - drop(line_handle); - - let req = Request::builder() - .on_chip(chip) - .with_line(offset) - .as_is() - .with_consumer(consumer.clone()) - .with_edge_detection(edge) - .request()?; - - let req = AsyncRequest::new(req); - let event = req.read_edge_event().await; - drop(req); +impl CdevPin { + /// See [`gpiocdev::request::Request`] for details. + /// + /// # Panics + /// + /// Panics if the `Request` does not contain exactly one line. + pub fn new(req: Request) -> Result { + let config = req.config(); + let lines = config.lines(); - // Recreate line handle. - self.pin.0 = Some(line.request(flags, 0, &consumer).unwrap()); + assert!( + lines.len() == 1, + "A `CdevPin` must correspond to a single GPIO line." + ); + let line = lines[0]; - event?; + let config = req.config(); - Ok(()) + Ok(CdevPin { + req: Some(req), + config, + line, + }) } -} -impl CdevPin { - /// See [`gpio_cdev::Line::request`][0] for details. - /// - /// [0]: https://docs.rs/gpio-cdev/0.5.0/gpio_cdev/struct.Line.html#method.request - pub fn new(handle: gpio_cdev::LineHandle) -> Result { - let info = handle.line().info()?; - Ok(CdevPin(Some(handle), info)) + fn request(&mut self) -> Result<&Request, gpiocdev::Error> { + if self.req.is_some() { + return Ok(self.req.as_ref().unwrap()); + } + + let req = Request::from_config(self.config.clone()).request()?; + Ok(self.req.insert(req)) } - fn line_handle(&self) -> &gpio_cdev::LineHandle { - self.0.as_ref().unwrap() + fn config(&self) -> &Config { + &self.config } - fn get_input_flags(&self) -> gpio_cdev::LineRequestFlags { - if self.1.is_active_low() { - return gpio_cdev::LineRequestFlags::INPUT | gpio_cdev::LineRequestFlags::ACTIVE_LOW; - } - gpio_cdev::LineRequestFlags::INPUT + fn is_active_low(&self) -> bool { + self.line_config().active_low } - fn get_output_flags(&self) -> gpio_cdev::LineRequestFlags { - let mut flags = gpio_cdev::LineRequestFlags::OUTPUT; - if self.1.is_active_low() { - flags.insert(gpio_cdev::LineRequestFlags::ACTIVE_LOW); - } - if self.1.is_open_drain() { - flags.insert(gpio_cdev::LineRequestFlags::OPEN_DRAIN); - } else if self.1.is_open_source() { - flags.insert(gpio_cdev::LineRequestFlags::OPEN_SOURCE); - } - flags + fn line_config(&self) -> &gpiocdev::line::Config { + // Unwrapping is fine, since `self.line` comes from a `Request` and is guaranteed to exist. + self.config().line_config(self.line).unwrap() } /// Set this pin to input mode - pub fn into_input_pin(self) -> Result { - if self.1.direction() == gpio_cdev::LineDirection::In { + pub fn into_input_pin(mut self) -> Result { + let line_config = self.line_config(); + + if line_config.direction == Some(gpiocdev::line::Direction::Output) { return Ok(self); } - let line = self.line_handle().line().clone(); - let input_flags = self.get_input_flags(); - let consumer = self.1.consumer().unwrap_or("").to_owned(); - // Drop self to free the line before re-requesting it in a new mode. - std::mem::drop(self); + drop(self.req.take()); - CdevPin::new(line.request(input_flags, 0, &consumer)?) + CdevPin::new(Request::from_config(self.config).as_input().request()?) } /// Set this pin to output mode pub fn into_output_pin( - self, + mut self, state: embedded_hal::digital::PinState, - ) -> Result { - if self.1.direction() == gpio_cdev::LineDirection::Out { + ) -> Result { + let line_config = self.line_config(); + let is_active_low = line_config.active_low; + + if line_config.direction == Some(gpiocdev::line::Direction::Output) { return Ok(self); } - let line = self.line_handle().line().clone(); - let output_flags = self.get_output_flags(); - let consumer = self.1.consumer().unwrap_or("").to_owned(); + drop(self.req.take()); + + CdevPin::new( + Request::from_config(self.config) + .as_output(state_to_value(state, is_active_low)) + .request()?, + ) + } + + #[cfg(feature = "async-tokio")] + async fn wait_for_edge(&mut self, edge: EdgeDetection) -> Result<(), CdevPinError> { + let config = if let Some(req) = self.req.take() { + req.config() + } else { + self.config.clone() + }; - // Drop self to free the line before re-requesting it in a new mode. - std::mem::drop(self); + let req = Request::from_config(config) + .with_edge_detection(edge) + .request()?; - let is_active_low = output_flags.intersects(gpio_cdev::LineRequestFlags::ACTIVE_LOW); - CdevPin::new(line.request( - output_flags, - state_to_value(state, is_active_low), - &consumer, - )?) + let req = AsyncRequest::new(req); + req.read_edge_event().await?; + + Ok(()) } } /// Converts a pin state to the gpio_cdev compatible numeric value, accounting /// for the active_low condition. -fn state_to_value(state: embedded_hal::digital::PinState, is_active_low: bool) -> u8 { +fn state_to_value(state: embedded_hal::digital::PinState, is_active_low: bool) -> Value { if is_active_low { match state { - embedded_hal::digital::PinState::High => 0, - embedded_hal::digital::PinState::Low => 1, + embedded_hal::digital::PinState::High => Value::Inactive, + embedded_hal::digital::PinState::Low => Value::Active, } } else { match state { - embedded_hal::digital::PinState::High => 1, - embedded_hal::digital::PinState::Low => 0, + embedded_hal::digital::PinState::High => Value::Active, + embedded_hal::digital::PinState::Low => Value::Inactive, } } } -/// Error type wrapping [gpio_cdev::errors::Error](gpio_cdev::errors::Error) to implement [embedded_hal::digital::Error] +/// Error type wrapping [`gpiocdev::Error`] to implement [`embedded_hal::digital::Error`]. #[derive(Debug)] pub struct CdevPinError { - err: gpio_cdev::errors::Error, -} - -impl CdevPinError { - /// Fetch inner (concrete) [`gpio_cdev::errors::Error`] - pub fn inner(&self) -> &gpio_cdev::errors::Error { - &self.err - } + err: gpiocdev::Error, } -impl From for CdevPinError { - fn from(err: gpio_cdev::errors::Error) -> Self { +impl From for CdevPinError { + fn from(err: gpiocdev::Error) -> Self { Self { err } } } @@ -195,33 +175,37 @@ impl embedded_hal::digital::ErrorType for CdevPin { impl embedded_hal::digital::OutputPin for CdevPin { fn set_low(&mut self) -> Result<(), Self::Error> { - self.line_handle() - .set_value(state_to_value( - embedded_hal::digital::PinState::Low, - self.1.is_active_low(), - )) + let line = self.line; + let is_active_low = self.is_active_low(); + self.request()? + .set_value( + line, + state_to_value(embedded_hal::digital::PinState::Low, is_active_low), + ) + .map(|_| ()) .map_err(CdevPinError::from) } fn set_high(&mut self) -> Result<(), Self::Error> { - self.line_handle() - .set_value(state_to_value( - embedded_hal::digital::PinState::High, - self.1.is_active_low(), - )) + let line = self.line; + let is_active_low = self.is_active_low(); + self.request()? + .set_value( + line, + state_to_value(embedded_hal::digital::PinState::High, is_active_low), + ) + .map(|_| ()) .map_err(CdevPinError::from) } } impl InputPin for CdevPin { fn is_high(&mut self) -> Result { - self.line_handle() - .get_value() + let line = self.line; + self.request()? + .value(line) .map(|val| { - val == state_to_value( - embedded_hal::digital::PinState::High, - self.1.is_active_low(), - ) + val == state_to_value(embedded_hal::digital::PinState::High, self.is_active_low()) }) .map_err(CdevPinError::from) } @@ -231,20 +215,6 @@ impl InputPin for CdevPin { } } -impl core::ops::Deref for CdevPin { - type Target = gpio_cdev::LineHandle; - - fn deref(&self) -> &Self::Target { - self.line_handle() - } -} - -impl core::ops::DerefMut for CdevPin { - fn deref_mut(&mut self) -> &mut Self::Target { - self.0.as_mut().unwrap() - } -} - #[cfg(feature = "async-tokio")] impl embedded_hal_async::digital::Wait for CdevPin { async fn wait_for_high(&mut self) -> Result<(), Self::Error> { @@ -264,20 +234,14 @@ impl embedded_hal_async::digital::Wait for CdevPin { } async fn wait_for_rising_edge(&mut self) -> Result<(), Self::Error> { - let waiter = CdevPinEdgeWaiter::new(self, EdgeDetection::RisingEdge).unwrap(); - waiter.wait().await.unwrap(); - Ok(()) + self.wait_for_edge(EdgeDetection::RisingEdge).await } async fn wait_for_falling_edge(&mut self) -> Result<(), Self::Error> { - let waiter = CdevPinEdgeWaiter::new(self, EdgeDetection::FallingEdge).unwrap(); - waiter.wait().await.unwrap(); - Ok(()) + self.wait_for_edge(EdgeDetection::FallingEdge).await } async fn wait_for_any_edge(&mut self) -> Result<(), Self::Error> { - let waiter = CdevPinEdgeWaiter::new(self, EdgeDetection::BothEdges).unwrap(); - waiter.wait().await.unwrap(); - Ok(()) + self.wait_for_edge(EdgeDetection::BothEdges).await } } diff --git a/src/lib.rs b/src/lib.rs index ee52767..e406986 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -22,8 +22,6 @@ pub use spidev; #[cfg(feature = "gpio_sysfs")] pub use sysfs_gpio; -#[cfg(feature = "gpio_cdev")] -pub use gpio_cdev; #[cfg(feature = "gpio_sysfs")] /// Sysfs Pin wrapper module mod sysfs_pin;