use std::fmt;
pub struct CdevPin(pub gpio_cdev::LineHandle, gpio_cdev::LineInfo);
impl CdevPin {
pub fn new(handle: gpio_cdev::LineHandle) -> Result<Self, gpio_cdev::errors::Error> {
let info = handle.line().info()?;
Ok(CdevPin(handle, info))
}
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 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
}
pub fn into_input_pin(self) -> Result<CdevPin, gpio_cdev::errors::Error> {
if self.1.direction() == gpio_cdev::LineDirection::In {
return Ok(self);
}
let line = self.0.line().clone();
let input_flags = self.get_input_flags();
let consumer = self.1.consumer().unwrap_or("").to_owned();
std::mem::drop(self);
CdevPin::new(line.request(input_flags, 0, &consumer)?)
}
pub fn into_output_pin(
self,
state: embedded_hal::digital::PinState,
) -> Result<CdevPin, gpio_cdev::errors::Error> {
if self.1.direction() == gpio_cdev::LineDirection::Out {
return Ok(self);
}
let line = self.0.line().clone();
let output_flags = self.get_output_flags();
let consumer = self.1.consumer().unwrap_or("").to_owned();
std::mem::drop(self);
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,
)?)
}
}
fn state_to_value(state: embedded_hal::digital::PinState, is_active_low: bool) -> u8 {
if is_active_low {
match state {
embedded_hal::digital::PinState::High => 0,
embedded_hal::digital::PinState::Low => 1,
}
} else {
match state {
embedded_hal::digital::PinState::High => 1,
embedded_hal::digital::PinState::Low => 0,
}
}
}
#[derive(Debug)]
pub struct CdevPinError {
err: gpio_cdev::errors::Error,
}
impl CdevPinError {
pub fn inner(&self) -> &gpio_cdev::errors::Error {
&self.err
}
}
impl From<gpio_cdev::errors::Error> for CdevPinError {
fn from(err: gpio_cdev::errors::Error) -> Self {
Self { err }
}
}
impl fmt::Display for CdevPinError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.err)
}
}
impl std::error::Error for CdevPinError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
Some(&self.err)
}
}
impl embedded_hal::digital::Error for CdevPinError {
fn kind(&self) -> embedded_hal::digital::ErrorKind {
use embedded_hal::digital::ErrorKind;
ErrorKind::Other
}
}
impl embedded_hal::digital::ErrorType for CdevPin {
type Error = CdevPinError;
}
impl embedded_hal::digital::OutputPin for CdevPin {
fn set_low(&mut self) -> Result<(), Self::Error> {
self.0
.set_value(state_to_value(
embedded_hal::digital::PinState::Low,
self.1.is_active_low(),
))
.map_err(CdevPinError::from)
}
fn set_high(&mut self) -> Result<(), Self::Error> {
self.0
.set_value(state_to_value(
embedded_hal::digital::PinState::High,
self.1.is_active_low(),
))
.map_err(CdevPinError::from)
}
}
impl embedded_hal::digital::InputPin for CdevPin {
fn is_high(&mut self) -> Result<bool, Self::Error> {
self.0
.get_value()
.map(|val| {
val == state_to_value(
embedded_hal::digital::PinState::High,
self.1.is_active_low(),
)
})
.map_err(CdevPinError::from)
}
fn is_low(&mut self) -> Result<bool, Self::Error> {
self.is_high().map(|val| !val)
}
}
impl core::ops::Deref for CdevPin {
type Target = gpio_cdev::LineHandle;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl core::ops::DerefMut for CdevPin {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}