linux_embedded_hal/
i2c.rs

1//! Implementation of [`embedded-hal`] I2C traits
2//!
3//! [`embedded-hal`]: https://docs.rs/embedded-hal
4
5use std::fmt;
6use std::ops;
7use std::path::{Path, PathBuf};
8
9use embedded_hal::i2c::NoAcknowledgeSource;
10
11/// Newtype around [`i2cdev::linux::LinuxI2CDevice`] that implements the `embedded-hal` traits
12///
13/// [`i2cdev::linux::LinuxI2CDevice`]: https://docs.rs/i2cdev/0.5.0/i2cdev/linux/struct.LinuxI2CDevice.html
14pub struct I2cdev {
15    inner: i2cdev::linux::LinuxI2CDevice,
16    path: PathBuf,
17    address: Option<u16>,
18}
19
20impl I2cdev {
21    /// See [`i2cdev::linux::LinuxI2CDevice::new`][0] for details.
22    ///
23    /// [0]: https://docs.rs/i2cdev/0.5.0/i2cdev/linux/struct.LinuxI2CDevice.html#method.new
24    pub fn new<P>(path: P) -> Result<Self, i2cdev::linux::LinuxI2CError>
25    where
26        P: AsRef<Path>,
27    {
28        let dev = I2cdev {
29            path: path.as_ref().to_path_buf(),
30            inner: i2cdev::linux::LinuxI2CDevice::new(path, 0)?,
31            address: None,
32        };
33        Ok(dev)
34    }
35
36    fn set_address(&mut self, address: u16) -> Result<(), i2cdev::linux::LinuxI2CError> {
37        if self.address != Some(address) {
38            self.inner = i2cdev::linux::LinuxI2CDevice::new(&self.path, address)?;
39            self.address = Some(address);
40        }
41        Ok(())
42    }
43}
44
45impl ops::Deref for I2cdev {
46    type Target = i2cdev::linux::LinuxI2CDevice;
47
48    fn deref(&self) -> &Self::Target {
49        &self.inner
50    }
51}
52
53impl ops::DerefMut for I2cdev {
54    fn deref_mut(&mut self) -> &mut Self::Target {
55        &mut self.inner
56    }
57}
58
59mod embedded_hal_impl {
60    use super::*;
61    use embedded_hal::i2c::ErrorType;
62    use embedded_hal::i2c::{I2c, Operation as I2cOperation, SevenBitAddress, TenBitAddress};
63    use i2cdev::core::{I2CMessage, I2CTransfer};
64    use i2cdev::linux::LinuxI2CMessage;
65    impl ErrorType for I2cdev {
66        type Error = I2CError;
67    }
68
69    impl I2c<TenBitAddress> for I2cdev {
70        fn transaction(
71            &mut self,
72            address: u16,
73            operations: &mut [I2cOperation],
74        ) -> Result<(), Self::Error> {
75            // Map operations from generic to linux objects
76            let mut messages: Vec<_> = operations
77                .as_mut()
78                .iter_mut()
79                .map(|a| match a {
80                    I2cOperation::Write(w) => LinuxI2CMessage::write(w),
81                    I2cOperation::Read(r) => LinuxI2CMessage::read(r),
82                })
83                .collect();
84
85            self.set_address(address)?;
86            self.inner
87                .transfer(&mut messages)
88                .map(drop)
89                .map_err(|err| I2CError { err })
90        }
91    }
92
93    impl I2c<SevenBitAddress> for I2cdev {
94        fn transaction(
95            &mut self,
96            address: u8,
97            operations: &mut [I2cOperation],
98        ) -> Result<(), Self::Error> {
99            I2c::<TenBitAddress>::transaction(self, u16::from(address), operations)
100        }
101    }
102}
103
104/// Error type wrapping [LinuxI2CError](i2cdev::linux::LinuxI2CError) to implement [embedded_hal::i2c::ErrorKind]
105#[derive(Debug)]
106pub struct I2CError {
107    err: i2cdev::linux::LinuxI2CError,
108}
109
110impl I2CError {
111    /// Fetch inner (concrete) [`LinuxI2CError`]
112    pub fn inner(&self) -> &i2cdev::linux::LinuxI2CError {
113        &self.err
114    }
115}
116
117impl From<i2cdev::linux::LinuxI2CError> for I2CError {
118    fn from(err: i2cdev::linux::LinuxI2CError) -> Self {
119        Self { err }
120    }
121}
122
123impl fmt::Display for I2CError {
124    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
125        write!(f, "{}", self.err)
126    }
127}
128
129impl std::error::Error for I2CError {
130    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
131        Some(&self.err)
132    }
133}
134
135impl embedded_hal::i2c::Error for I2CError {
136    fn kind(&self) -> embedded_hal::i2c::ErrorKind {
137        use embedded_hal::i2c::ErrorKind;
138        use nix::errno::Errno::*;
139
140        let errno = match &self.err {
141            i2cdev::linux::LinuxI2CError::Errno(e) => nix::Error::from_i32(*e),
142            i2cdev::linux::LinuxI2CError::Io(e) => match e.raw_os_error() {
143                Some(r) => nix::Error::from_i32(r),
144                None => return ErrorKind::Other,
145            },
146        };
147
148        // https://www.kernel.org/doc/html/latest/i2c/fault-codes.html
149        match errno {
150            EBUSY | EINVAL | EIO => ErrorKind::Bus,
151            EAGAIN => ErrorKind::ArbitrationLoss,
152            ENODEV => ErrorKind::NoAcknowledge(NoAcknowledgeSource::Data),
153            ENXIO => ErrorKind::NoAcknowledge(NoAcknowledgeSource::Address),
154            _ => ErrorKind::Other,
155        }
156    }
157}