freebsd_embedded_hal/
i2c.rs

1//! Implementation of [`embedded-hal`] I2C traits using the FreeBSD iic(4) device interface
2//!
3//! [`embedded-hal`]: https://docs.rs/embedded-hal
4
5use std::{
6    error, fmt,
7    fs::OpenOptions,
8    io,
9    os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd},
10};
11
12pub struct I2cBus(RawFd);
13
14/// An i2c wrapper around std::io::Error.
15///
16/// NOTE: values will be super wrong without https://reviews.freebsd.org/D33707
17#[derive(Debug)]
18pub struct I2cError(io::Error);
19
20impl fmt::Display for I2cError {
21    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
22        write!(f, "{}", self.0)
23    }
24}
25
26impl From<io::Error> for I2cError {
27    fn from(err: io::Error) -> I2cError {
28        I2cError(err)
29    }
30}
31
32impl error::Error for I2cError {
33    fn source(&self) -> Option<&(dyn error::Error + 'static)> {
34        Some(&self.0)
35    }
36}
37
38impl embedded_hal::i2c::Error for I2cError {
39    fn kind(&self) -> embedded_hal::i2c::ErrorKind {
40        use embedded_hal::i2c::ErrorKind::*;
41        match self.0.raw_os_error() {
42            Some(libc::EALREADY) => Bus,
43            Some(libc::EOVERFLOW) => Overrun, // I guess
44            // Unfortunately both IIC_ENOACK and lots of other things translate to EIO
45            _ => Other,
46        }
47    }
48}
49
50impl FromRawFd for I2cBus {
51    unsafe fn from_raw_fd(fd: RawFd) -> Self {
52        I2cBus(fd)
53    }
54}
55
56impl IntoRawFd for I2cBus {
57    fn into_raw_fd(self) -> RawFd {
58        self.0
59    }
60}
61
62impl AsRawFd for I2cBus {
63    fn as_raw_fd(&self) -> RawFd {
64        self.0
65    }
66}
67
68impl Drop for I2cBus {
69    fn drop(&mut self) {
70        unsafe { libc::close(self.0) };
71    }
72}
73
74impl I2cBus {
75    pub fn from_unit(unit: u32) -> Result<I2cBus, I2cError> {
76        Self::from_path(format!("/dev/iic{}", unit))
77    }
78
79    pub fn from_path<P: AsRef<std::path::Path>>(path: P) -> Result<I2cBus, I2cError> {
80        OpenOptions::new()
81            .read(true)
82            .write(true)
83            .open(path)
84            .map(|f| I2cBus(f.into_raw_fd()))
85            .map_err(|e| e.into())
86    }
87}
88
89impl embedded_hal::i2c::blocking::Read for I2cBus {
90    type Error = I2cError;
91
92    fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error> {
93        rdwr(
94            self.0,
95            &[iic_msg {
96                addr: (address as u16) << 1,
97                flags: IIC_M_RD,
98                len: buffer.len() as u16,
99                buf: buffer as *const _ as *mut _,
100            }],
101        )
102    }
103}
104
105impl embedded_hal::i2c::blocking::Write for I2cBus {
106    type Error = I2cError;
107
108    fn write(&mut self, address: u8, bytes: &[u8]) -> Result<(), Self::Error> {
109        rdwr(
110            self.0,
111            &[iic_msg {
112                addr: (address as u16) << 1,
113                flags: IIC_M_WR,
114                len: bytes.len() as u16,
115                buf: bytes as *const _ as *mut _,
116            }],
117        )
118    }
119}
120
121impl embedded_hal::i2c::blocking::WriteIter for I2cBus {
122    type Error = I2cError;
123
124    fn write_iter<B>(&mut self, address: u8, bytes: B) -> Result<(), Self::Error>
125    where
126        B: IntoIterator<Item = u8>,
127    {
128        use embedded_hal::i2c::blocking::Write;
129        self.write(address, &mut bytes.into_iter().collect::<Vec<_>>())
130    }
131}
132
133impl embedded_hal::i2c::blocking::WriteRead for I2cBus {
134    type Error = I2cError;
135
136    fn write_read(
137        &mut self,
138        address: u8,
139        bytes: &[u8],
140        buffer: &mut [u8],
141    ) -> Result<(), Self::Error> {
142        rdwr(
143            self.0,
144            &[
145                iic_msg {
146                    addr: (address as u16) << 1,
147                    flags: IIC_M_WR | IIC_M_NOSTOP,
148                    len: bytes.len() as u16,
149                    buf: bytes as *const _ as *mut _,
150                },
151                iic_msg {
152                    addr: (address as u16) << 1,
153                    flags: IIC_M_RD,
154                    len: buffer.len() as u16,
155                    buf: buffer as *const _ as *mut _,
156                },
157            ],
158        )
159    }
160}
161
162impl embedded_hal::i2c::blocking::WriteIterRead for I2cBus {
163    type Error = I2cError;
164
165    fn write_iter_read<B>(
166        &mut self,
167        address: u8,
168        bytes: B,
169        buffer: &mut [u8],
170    ) -> Result<(), Self::Error>
171    where
172        B: IntoIterator<Item = u8>,
173    {
174        use embedded_hal::i2c::blocking::WriteRead;
175        self.write_read(address, &mut bytes.into_iter().collect::<Vec<_>>(), buffer)
176    }
177}
178
179#[derive(PartialEq)]
180enum OpState {
181    First,
182    WasRead,
183    WasWrite,
184}
185
186impl embedded_hal::i2c::blocking::Transactional for I2cBus {
187    type Error = I2cError;
188
189    fn exec<'a>(
190        &mut self,
191        address: u8,
192        operations: &mut [embedded_hal::i2c::blocking::Operation<'a>],
193    ) -> Result<(), Self::Error> {
194        let mut st = OpState::First;
195        let mut msgs = Vec::with_capacity(operations.len());
196        let mut it = operations.into_iter().peekable();
197
198        while let Some(op) = it.next() {
199            use embedded_hal::i2c::blocking::Operation;
200
201            msgs.push(match op {
202                Operation::Read(buffer) => {
203                    let prev_was_read = st == OpState::WasRead;
204                    st = OpState::WasRead;
205                    iic_msg {
206                        addr: (address as u16) << 1,
207                        flags: IIC_M_RD
208                            | if it.peek().is_some() { IIC_M_NOSTOP } else { 0 }
209                            | if prev_was_read { IIC_M_NOSTART } else { 0 },
210                        len: buffer.len() as u16,
211                        buf: buffer as *const _ as *mut _,
212                    }
213                },
214                Operation::Write(bytes) => {
215                    let prev_was_write = st == OpState::WasWrite;
216                    st = OpState::WasWrite;
217                    iic_msg {
218                        addr: (address as u16) << 1,
219                        flags: IIC_M_WR
220                            | if it.peek().is_some() { IIC_M_NOSTOP } else { 0 }
221                            | if prev_was_write { IIC_M_NOSTART } else { 0 },
222                        len: bytes.len() as u16,
223                        buf: bytes as *const _ as *mut _,
224                    }
225                },
226            });
227        }
228
229        rdwr(self.0, &msgs[..])
230    }
231}
232
233impl embedded_hal::i2c::blocking::TransactionalIter for I2cBus {
234    type Error = I2cError;
235
236    fn exec_iter<'a, O>(&mut self, address: u8, operations: O) -> Result<(), Self::Error>
237    where
238        O: IntoIterator<Item = embedded_hal::i2c::blocking::Operation<'a>>,
239    {
240        // Not trying to execute on-the-fly, because
241        // *lots* of hardware has automatic start-stop handling,
242        // for which the drivers implement validation of nostart/nostop flags
243        // by looking behind in the list of operations.
244        use embedded_hal::i2c::blocking::Transactional;
245        self.exec(address, &mut operations.into_iter().collect::<Vec<_>>())
246    }
247}
248
249const IIC_M_WR: u16 = 0x00;
250const IIC_M_RD: u16 = 0x01;
251const IIC_M_NOSTOP: u16 = 0x02;
252const IIC_M_NOSTART: u16 = 0x04;
253
254#[repr(C)]
255#[allow(non_camel_case_types)]
256struct iic_msg {
257    addr: u16,
258    flags: u16,
259    len: u16,
260    buf: *mut u8,
261}
262
263#[repr(C)]
264#[allow(non_camel_case_types)]
265struct iic_rdwr_data {
266    msgs: *const iic_msg,
267    nmsgs: u32,
268}
269
270fn rdwr(fd: RawFd, msgs: &[iic_msg]) -> Result<(), I2cError> {
271    let mut dat = iic_rdwr_data { msgs: msgs.as_ptr(), nmsgs: msgs.len() as u32 };
272    let res = unsafe {
273        libc::ioctl(fd, 0x80106906 /*I2CRDWR*/, &mut dat as *mut _)
274    };
275    if res == -1 {
276        return Err(I2cError(io::Error::last_os_error()));
277    }
278    Ok(())
279}