1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
//! Implementation of [`embedded-hal`] digital input/output traits using a Linux Sysfs pin
//!
//! [`embedded-hal`]: https://docs.rs/embedded-hal

use std::fmt;
use std::path::Path;

/// Newtype around [`sysfs_gpio::Pin`] that implements the `embedded-hal` traits
///
/// [`sysfs_gpio::Pin`]: https://docs.rs/sysfs_gpio/0.6.0/sysfs_gpio/struct.Pin.html
pub struct SysfsPin(pub sysfs_gpio::Pin);

impl SysfsPin {
    /// See [`sysfs_gpio::Pin::new`][0] for details.
    ///
    /// [0]: https://docs.rs/sysfs_gpio/0.6.0/sysfs_gpio/struct.Pin.html#method.new
    pub fn new(pin_num: u64) -> Self {
        SysfsPin(sysfs_gpio::Pin::new(pin_num))
    }

    /// See [`sysfs_gpio::Pin::from_path`][0] for details.
    ///
    /// [0]: https://docs.rs/sysfs_gpio/0.6.0/sysfs_gpio/struct.Pin.html#method.from_path
    pub fn from_path<P>(path: P) -> sysfs_gpio::Result<Self>
    where
        P: AsRef<Path>,
    {
        sysfs_gpio::Pin::from_path(path).map(SysfsPin)
    }

    /// Convert this pin to an input pin
    pub fn into_input_pin(self) -> Result<SysfsPin, sysfs_gpio::Error> {
        self.set_direction(sysfs_gpio::Direction::In)?;
        Ok(self)
    }

    /// Convert this pin to an output pin
    pub fn into_output_pin(
        self,
        state: embedded_hal::digital::PinState,
    ) -> Result<SysfsPin, sysfs_gpio::Error> {
        self.set_direction(match state {
            embedded_hal::digital::PinState::High => sysfs_gpio::Direction::High,
            embedded_hal::digital::PinState::Low => sysfs_gpio::Direction::Low,
        })?;
        Ok(self)
    }
}

/// Error type wrapping [sysfs_gpio::Error](sysfs_gpio::Error) to implement [embedded_hal::digital::Error]
#[derive(Debug)]
pub struct SysfsPinError {
    err: sysfs_gpio::Error,
}

impl SysfsPinError {
    /// Fetch inner (concrete) [`sysfs_gpio::Error`]
    pub fn inner(&self) -> &sysfs_gpio::Error {
        &self.err
    }
}

impl From<sysfs_gpio::Error> for SysfsPinError {
    fn from(err: sysfs_gpio::Error) -> Self {
        Self { err }
    }
}

impl fmt::Display for SysfsPinError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", self.err)
    }
}

impl std::error::Error for SysfsPinError {
    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
        Some(&self.err)
    }
}

impl embedded_hal::digital::Error for SysfsPinError {
    fn kind(&self) -> embedded_hal::digital::ErrorKind {
        use embedded_hal::digital::ErrorKind;
        ErrorKind::Other
    }
}

impl embedded_hal::digital::ErrorType for SysfsPin {
    type Error = SysfsPinError;
}

impl embedded_hal::digital::OutputPin for SysfsPin {
    fn set_low(&mut self) -> Result<(), Self::Error> {
        if self.0.get_active_low().map_err(SysfsPinError::from)? {
            self.0.set_value(1).map_err(SysfsPinError::from)
        } else {
            self.0.set_value(0).map_err(SysfsPinError::from)
        }
    }

    fn set_high(&mut self) -> Result<(), Self::Error> {
        if self.0.get_active_low().map_err(SysfsPinError::from)? {
            self.0.set_value(0).map_err(SysfsPinError::from)
        } else {
            self.0.set_value(1).map_err(SysfsPinError::from)
        }
    }
}

impl embedded_hal::digital::InputPin for SysfsPin {
    fn is_high(&mut self) -> Result<bool, Self::Error> {
        if !self.0.get_active_low().map_err(SysfsPinError::from)? {
            self.0
                .get_value()
                .map(|val| val != 0)
                .map_err(SysfsPinError::from)
        } else {
            self.0
                .get_value()
                .map(|val| val == 0)
                .map_err(SysfsPinError::from)
        }
    }

    fn is_low(&mut self) -> Result<bool, Self::Error> {
        self.is_high().map(|val| !val).map_err(SysfsPinError::from)
    }
}

impl core::ops::Deref for SysfsPin {
    type Target = sysfs_gpio::Pin;

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

impl core::ops::DerefMut for SysfsPin {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.0
    }
}