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
143
use embedded_hal::i2c;

use riot_sys::{i2c_acquire, i2c_read_bytes, i2c_release, i2c_write_bytes};

use crate::error::{NegativeErrorExt, NumericError};

use super::*;

const I2C_NOSTOP: u8 = riot_sys::i2c_flags_t_I2C_NOSTOP;
const I2C_NOSTART: u8 = riot_sys::i2c_flags_t_I2C_NOSTART;

#[derive(Debug)]
pub struct Error(NumericError);

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

impl i2c::Error for Error {
    fn kind(&self) -> i2c::ErrorKind {
        match -self.0.number() as _ {
            // The list documented with all the RIOT I2C functions
            riot_sys::EIO => i2c::ErrorKind::NoAcknowledge(i2c::NoAcknowledgeSource::Data),
            riot_sys::ENXIO => i2c::ErrorKind::NoAcknowledge(i2c::NoAcknowledgeSource::Address),
            riot_sys::ETIMEDOUT => i2c::ErrorKind::Other,
            riot_sys::EINVAL => i2c::ErrorKind::Other,
            riot_sys::EOPNOTSUPP => i2c::ErrorKind::Other, // We should avoid this at type level,
            // but can't because RIOT is not telling
            // us at setup time.
            riot_sys::EAGAIN => i2c::ErrorKind::ArbitrationLoss,
            _ => i2c::ErrorKind::Other,
        }
    }
}

impl i2c::ErrorType for I2CDevice {
    type Error = Error;
}

fn with_acquire<R>(dev: &mut I2CDevice, f: impl FnOnce(&mut I2CDevice) -> R) -> R {
    unsafe { i2c_acquire(dev.dev) };
    let result = f(dev);
    unsafe { i2c_release(dev.dev) };
    result
}

impl i2c::I2c<i2c::SevenBitAddress> for I2CDevice {
    fn transaction(
        &mut self,
        address: i2c::SevenBitAddress,
        operations: &mut [i2c::Operation<'_>],
    ) -> Result<(), Self::Error> {
        with_acquire(self, |dev| {
            #[derive(PartialEq)]
            enum LastOperation {
                Initial,
                Read,
                Write,
            }
            use LastOperation::*;

            impl LastOperation {
                fn from(op: &i2c::Operation) -> Self {
                    match op {
                        i2c::Operation::Read(_) => Read,
                        i2c::Operation::Write(_) => Write,
                    }
                }
            }

            let mut last_operation = Initial;
            let last_index = operations.len() - 1;

            for (i, op) in operations.iter_mut().enumerate() {
                let this_operation = LastOperation::from(op);
                let is_last = i == last_index;

                let maybe_nostop = if is_last { 0 } else { I2C_NOSTOP };

                // The regular read and write functions in RIOT automatically issue a repeated
                // start condition when called after a I2C_NOSTOP operation.
                if last_operation != this_operation {
                    match op {
                        i2c::Operation::Read(slice) => {
                            slice[0] = 0xff;
                            let result = (unsafe {
                                i2c_read_bytes(
                                    dev.dev,
                                    address as u16,
                                    slice.as_mut_ptr() as _,
                                    slice.len() as _,
                                    maybe_nostop,
                                )
                            })
                            .negative_to_error()?;
                            result
                        }
                        i2c::Operation::Write(slice) => (unsafe {
                            i2c_write_bytes(
                                dev.dev,
                                address as u16,
                                slice.as_ptr() as _,
                                slice.len() as _,
                                maybe_nostop,
                            )
                        })
                        .negative_to_error()?,
                    };
                } else {
                    // No "repeated start", no address, just a different scatter-gather slice
                    match op {
                        i2c::Operation::Read(slice) => (unsafe {
                            slice[0] = 0xff;
                            i2c_read_bytes(
                                dev.dev,
                                0,
                                slice.as_mut_ptr() as _,
                                slice.len() as _,
                                I2C_NOSTART | maybe_nostop,
                            )
                        })
                        .negative_to_error()?,
                        i2c::Operation::Write(slice) => (unsafe {
                            i2c_write_bytes(
                                dev.dev,
                                0,
                                slice.as_ptr() as _,
                                slice.len() as _,
                                I2C_NOSTART | maybe_nostop,
                            )
                        })
                        .negative_to_error()?,
                    };
                }

                last_operation = this_operation;
            }
            Ok(())
        })
    }
}