linux_embedded_hal/
cdev_pin.rs

1//! Implementation of [`embedded-hal`] digital input/output traits using a Linux CDev pin
2//!
3//! [`embedded-hal`]: https://docs.rs/embedded-hal
4
5use std::fmt;
6
7/// Newtype around [`gpio_cdev::LineHandle`] that implements the `embedded-hal` traits
8///
9/// [`gpio_cdev::LineHandle`]: https://docs.rs/gpio-cdev/0.5.0/gpio_cdev/struct.LineHandle.html
10pub struct CdevPin(pub gpio_cdev::LineHandle, gpio_cdev::LineInfo);
11
12impl CdevPin {
13    /// See [`gpio_cdev::Line::request`][0] for details.
14    ///
15    /// [0]: https://docs.rs/gpio-cdev/0.5.0/gpio_cdev/struct.Line.html#method.request
16    pub fn new(handle: gpio_cdev::LineHandle) -> Result<Self, gpio_cdev::errors::Error> {
17        let info = handle.line().info()?;
18        Ok(CdevPin(handle, info))
19    }
20
21    fn get_input_flags(&self) -> gpio_cdev::LineRequestFlags {
22        if self.1.is_active_low() {
23            return gpio_cdev::LineRequestFlags::INPUT | gpio_cdev::LineRequestFlags::ACTIVE_LOW;
24        }
25        gpio_cdev::LineRequestFlags::INPUT
26    }
27
28    fn get_output_flags(&self) -> gpio_cdev::LineRequestFlags {
29        let mut flags = gpio_cdev::LineRequestFlags::OUTPUT;
30        if self.1.is_active_low() {
31            flags.insert(gpio_cdev::LineRequestFlags::ACTIVE_LOW);
32        }
33        if self.1.is_open_drain() {
34            flags.insert(gpio_cdev::LineRequestFlags::OPEN_DRAIN);
35        } else if self.1.is_open_source() {
36            flags.insert(gpio_cdev::LineRequestFlags::OPEN_SOURCE);
37        }
38        flags
39    }
40
41    /// Set this pin to input mode
42    pub fn into_input_pin(self) -> Result<CdevPin, gpio_cdev::errors::Error> {
43        if self.1.direction() == gpio_cdev::LineDirection::In {
44            return Ok(self);
45        }
46        let line = self.0.line().clone();
47        let input_flags = self.get_input_flags();
48        let consumer = self.1.consumer().unwrap_or("").to_owned();
49
50        // Drop self to free the line before re-requesting it in a new mode.
51        std::mem::drop(self);
52
53        CdevPin::new(line.request(input_flags, 0, &consumer)?)
54    }
55
56    /// Set this pin to output mode
57    pub fn into_output_pin(
58        self,
59        state: embedded_hal::digital::PinState,
60    ) -> Result<CdevPin, gpio_cdev::errors::Error> {
61        if self.1.direction() == gpio_cdev::LineDirection::Out {
62            return Ok(self);
63        }
64
65        let line = self.0.line().clone();
66        let output_flags = self.get_output_flags();
67        let consumer = self.1.consumer().unwrap_or("").to_owned();
68
69        // Drop self to free the line before re-requesting it in a new mode.
70        std::mem::drop(self);
71
72        let is_active_low = output_flags.intersects(gpio_cdev::LineRequestFlags::ACTIVE_LOW);
73        CdevPin::new(line.request(
74            output_flags,
75            state_to_value(state, is_active_low),
76            &consumer,
77        )?)
78    }
79}
80
81/// Converts a pin state to the gpio_cdev compatible numeric value, accounting
82/// for the active_low condition.
83fn state_to_value(state: embedded_hal::digital::PinState, is_active_low: bool) -> u8 {
84    if is_active_low {
85        match state {
86            embedded_hal::digital::PinState::High => 0,
87            embedded_hal::digital::PinState::Low => 1,
88        }
89    } else {
90        match state {
91            embedded_hal::digital::PinState::High => 1,
92            embedded_hal::digital::PinState::Low => 0,
93        }
94    }
95}
96
97/// Error type wrapping [gpio_cdev::errors::Error](gpio_cdev::errors::Error) to implement [embedded_hal::digital::Error]
98#[derive(Debug)]
99pub struct CdevPinError {
100    err: gpio_cdev::errors::Error,
101}
102
103impl CdevPinError {
104    /// Fetch inner (concrete) [`gpio_cdev::errors::Error`]
105    pub fn inner(&self) -> &gpio_cdev::errors::Error {
106        &self.err
107    }
108}
109
110impl From<gpio_cdev::errors::Error> for CdevPinError {
111    fn from(err: gpio_cdev::errors::Error) -> Self {
112        Self { err }
113    }
114}
115
116impl fmt::Display for CdevPinError {
117    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
118        write!(f, "{}", self.err)
119    }
120}
121
122impl std::error::Error for CdevPinError {
123    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
124        Some(&self.err)
125    }
126}
127
128impl embedded_hal::digital::Error for CdevPinError {
129    fn kind(&self) -> embedded_hal::digital::ErrorKind {
130        use embedded_hal::digital::ErrorKind;
131        ErrorKind::Other
132    }
133}
134
135impl embedded_hal::digital::ErrorType for CdevPin {
136    type Error = CdevPinError;
137}
138
139impl embedded_hal::digital::OutputPin for CdevPin {
140    fn set_low(&mut self) -> Result<(), Self::Error> {
141        self.0
142            .set_value(state_to_value(
143                embedded_hal::digital::PinState::Low,
144                self.1.is_active_low(),
145            ))
146            .map_err(CdevPinError::from)
147    }
148
149    fn set_high(&mut self) -> Result<(), Self::Error> {
150        self.0
151            .set_value(state_to_value(
152                embedded_hal::digital::PinState::High,
153                self.1.is_active_low(),
154            ))
155            .map_err(CdevPinError::from)
156    }
157}
158
159impl embedded_hal::digital::InputPin for CdevPin {
160    fn is_high(&mut self) -> Result<bool, Self::Error> {
161        self.0
162            .get_value()
163            .map(|val| {
164                val == state_to_value(
165                    embedded_hal::digital::PinState::High,
166                    self.1.is_active_low(),
167                )
168            })
169            .map_err(CdevPinError::from)
170    }
171
172    fn is_low(&mut self) -> Result<bool, Self::Error> {
173        self.is_high().map(|val| !val)
174    }
175}
176
177impl core::ops::Deref for CdevPin {
178    type Target = gpio_cdev::LineHandle;
179
180    fn deref(&self) -> &Self::Target {
181        &self.0
182    }
183}
184
185impl core::ops::DerefMut for CdevPin {
186    fn deref_mut(&mut self) -> &mut Self::Target {
187        &mut self.0
188    }
189}