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
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
// Licensed under the Apache License, Version 2.0 or the MIT License.
// SPDX-License-Identifier: Apache-2.0 OR MIT
// Copyright Tock Contributors 2022.

//! Interface for I2C master and slave peripherals.

use crate::ErrorCode;

use core::fmt;
use core::fmt::{Display, Formatter};

/// The type of error encountered during I2C communication.
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum Error {
    /// The slave did not acknowledge the chip address. Most likely the address
    /// is incorrect or the slave is not properly connected.
    AddressNak,

    /// The data was not acknowledged by the slave.
    DataNak,

    /// Arbitration lost, meaning the state of the data line does not correspond
    /// to the data driven onto it. This can happen, for example, when a
    /// higher-priority transmission is in progress by a different master.
    ArbitrationLost,

    /// A start condition was received before received data has been read
    /// from the receive register.
    Overrun,

    /// The requested operation wasn't supported.
    NotSupported,

    /// The underlying device has another request in progress
    Busy,
}

impl Into<ErrorCode> for Error {
    fn into(self) -> ErrorCode {
        match self {
            Self::AddressNak | Self::DataNak => ErrorCode::NOACK,
            Self::ArbitrationLost => ErrorCode::RESERVE,
            Self::Overrun => ErrorCode::SIZE,
            Self::NotSupported => ErrorCode::NOSUPPORT,
            Self::Busy => ErrorCode::BUSY,
        }
    }
}

impl Display for Error {
    fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
        let display_str = match *self {
            Error::AddressNak => "I2C Address Not Acknowledged",
            Error::DataNak => "I2C Data Not Acknowledged",
            Error::ArbitrationLost => "I2C Bus Arbitration Lost",
            Error::Overrun => "I2C receive overrun",
            Error::NotSupported => "I2C/SMBus command not supported",
            Error::Busy => "I2C/SMBus is busy",
        };
        write!(fmt, "{}", display_str)
    }
}

/// This specifies what type of transmission just finished from a Master device.
#[derive(Copy, Clone, Debug)]
pub enum SlaveTransmissionType {
    Write,
    Read,
}

/// Interface for an I2C Master hardware driver.
pub trait I2CMaster<'a> {
    fn set_master_client(&self, master_client: &'a dyn I2CHwMasterClient);
    fn enable(&self);
    fn disable(&self);
    fn write_read(
        &self,
        addr: u8,
        data: &'static mut [u8],
        write_len: usize,
        read_len: usize,
    ) -> Result<(), (Error, &'static mut [u8])>;
    fn write(
        &self,
        addr: u8,
        data: &'static mut [u8],
        len: usize,
    ) -> Result<(), (Error, &'static mut [u8])>;
    fn read(
        &self,
        addr: u8,
        buffer: &'static mut [u8],
        len: usize,
    ) -> Result<(), (Error, &'static mut [u8])>;
}

/// Interface for an SMBus Master hardware driver.
/// The device implementing this will also seperately implement
/// I2CMaster.
pub trait SMBusMaster<'a>: I2CMaster<'a> {
    /// Write data then read data via the I2C Master device in an SMBus
    /// compatible way.
    ///
    /// This function will use the I2C master to write data to a device and
    /// then read data from the device in a SMBus compatible way. This will be
    /// a best effort attempt to match the SMBus specification based on what
    /// the hardware can support.
    /// This function is expected to make any hardware changes required to
    /// support SMBus and then revert those changes to support future I2C.
    ///
    /// addr: The address of the device to write to
    /// data: The buffer to write the data from and read back to
    /// write_len: The length of the write operation
    /// read_len: The length of the read operation
    fn smbus_write_read(
        &self,
        addr: u8,
        data: &'static mut [u8],
        write_len: usize,
        read_len: usize,
    ) -> Result<(), (Error, &'static mut [u8])>;

    /// Write data via the I2C Master device in an SMBus compatible way.
    ///
    /// This function will use the I2C master to write data to a device in a
    /// SMBus compatible way. This will be a best effort attempt to match the
    /// SMBus specification based on what the hardware can support.
    /// This function is expected to make any hardware changes required to
    /// support SMBus and then revert those changes to support future I2C.
    ///
    /// addr: The address of the device to write to
    /// data: The buffer to write the data from
    /// len: The length of the operation
    fn smbus_write(
        &self,
        addr: u8,
        data: &'static mut [u8],
        len: usize,
    ) -> Result<(), (Error, &'static mut [u8])>;

    /// Read data via the I2C Master device in an SMBus compatible way.
    ///
    /// This function will use the I2C master to read data from a device in a
    /// SMBus compatible way. This will be a best effort attempt to match the
    /// SMBus specification based on what the hardware can support.
    /// This function is expected to make any hardware changes required to
    /// support SMBus and then revert those changes to support future I2C.
    ///
    /// addr: The address of the device to read from
    /// buffer: The buffer to store the data to
    /// len: The length of the operation
    fn smbus_read(
        &self,
        addr: u8,
        buffer: &'static mut [u8],
        len: usize,
    ) -> Result<(), (Error, &'static mut [u8])>;
}

/// Interface for an I2C Slave hardware driver.
pub trait I2CSlave<'a> {
    fn set_slave_client(&self, slave_client: &'a dyn I2CHwSlaveClient);
    fn enable(&self);
    fn disable(&self);
    fn set_address(&self, addr: u8) -> Result<(), Error>;
    fn write_receive(
        &self,
        data: &'static mut [u8],
        max_len: usize,
    ) -> Result<(), (Error, &'static mut [u8])>;
    fn read_send(
        &self,
        data: &'static mut [u8],
        max_len: usize,
    ) -> Result<(), (Error, &'static mut [u8])>;
    fn listen(&self);
}

/// Convenience type for capsules that need hardware that supports both
/// Master and Slave modes.
pub trait I2CMasterSlave<'a>: I2CMaster<'a> + I2CSlave<'a> {}
// Provide blanket implementations for trait group
// impl<T: I2CMaster + I2CSlave> I2CMasterSlave for T {}

/// Client interface for capsules that use I2CMaster devices.
pub trait I2CHwMasterClient {
    /// Called when an I2C command completed. The `error` denotes whether the command completed
    /// successfully or if an error occurred.
    fn command_complete(&self, buffer: &'static mut [u8], status: Result<(), Error>);
}

/// Client interface for capsules that use I2CSlave devices.
pub trait I2CHwSlaveClient {
    /// Called when an I2C command completed.
    fn command_complete(
        &self,
        buffer: &'static mut [u8],
        length: usize,
        transmission_type: SlaveTransmissionType,
    );

    /// Called from the I2C slave hardware to say that a Master has sent us
    /// a read message, but the driver did not have a buffer containing data
    /// setup, and therefore cannot respond. The I2C slave hardware will stretch
    /// the clock while waiting for the upper layer capsule to provide data
    /// to send to the remote master. Call `I2CSlave::read_send()` to provide
    /// data.
    fn read_expected(&self);

    /// Called from the I2C slave hardware to say that a Master has sent us
    /// a write message, but there was no buffer setup to read the bytes into.
    /// The HW will stretch the clock while waiting for the user to call
    /// `I2CSlave::write_receive()` with a buffer.
    fn write_expected(&self);
}

/// Higher-level interface for I2C Master commands that wraps in the I2C
/// address. It gives an interface for communicating with a specific I2C
/// device.
pub trait I2CDevice {
    fn enable(&self);
    fn disable(&self);
    fn write_read(
        &self,
        data: &'static mut [u8],
        write_len: usize,
        read_len: usize,
    ) -> Result<(), (Error, &'static mut [u8])>;
    fn write(&self, data: &'static mut [u8], len: usize) -> Result<(), (Error, &'static mut [u8])>;
    fn read(&self, buffer: &'static mut [u8], len: usize)
        -> Result<(), (Error, &'static mut [u8])>;
}

pub trait SMBusDevice: I2CDevice {
    /// Write data then read data to a slave device in an SMBus
    /// compatible way.
    ///
    /// This function will use the I2C master to write data to a device and
    /// then read data from the device in a SMBus compatible way. This will be
    /// a best effort attempt to match the SMBus specification based on what
    /// the hardware can support.
    /// This function is expected to make any hardware changes required to
    /// support SMBus and then revert those changes to support future I2C.
    ///
    /// data: The buffer to write the data from and read back to
    /// write_len: The length of the write operation
    /// read_len: The length of the read operation
    fn smbus_write_read(
        &self,
        data: &'static mut [u8],
        write_len: usize,
        read_len: usize,
    ) -> Result<(), (Error, &'static mut [u8])>;

    /// Write data to a slave device in an SMBus compatible way.
    ///
    /// This function will use the I2C master to write data to a device in a
    /// SMBus compatible way. This will be a best effort attempt to match the
    /// SMBus specification based on what the hardware can support.
    /// This function is expected to make any hardware changes required to
    /// support SMBus and then revert those changes to support future I2C.
    ///
    /// data: The buffer to write the data from
    /// len: The length of the operation
    fn smbus_write(
        &self,
        data: &'static mut [u8],
        len: usize,
    ) -> Result<(), (Error, &'static mut [u8])>;

    /// Read data from a slave device in an SMBus compatible way.
    ///
    /// This function will use the I2C master to read data from a device in a
    /// SMBus compatible way. This will be a best effort attempt to match the
    /// SMBus specification based on what the hardware can support.
    /// This function is expected to make any hardware changes required to
    /// support SMBus and then revert those changes to support future I2C.
    ///
    /// buffer: The buffer to store the data to
    /// len: The length of the operation
    fn smbus_read(
        &self,
        buffer: &'static mut [u8],
        len: usize,
    ) -> Result<(), (Error, &'static mut [u8])>;
}

/// Client interface for I2CDevice implementations.
pub trait I2CClient {
    /// Called when an I2C command completed. The `error` denotes whether the command completed
    /// successfully or if an error occured.
    fn command_complete(&self, buffer: &'static mut [u8], status: Result<(), Error>);
}

pub struct NoSMBus;

impl<'a> I2CMaster<'a> for NoSMBus {
    fn set_master_client(&self, _master_client: &'a dyn I2CHwMasterClient) {}
    fn enable(&self) {}
    fn disable(&self) {}
    fn write_read(
        &self,
        _addr: u8,
        data: &'static mut [u8],
        _write_len: usize,
        _read_len: usize,
    ) -> Result<(), (Error, &'static mut [u8])> {
        Err((Error::NotSupported, data))
    }
    fn write(
        &self,
        _addr: u8,
        data: &'static mut [u8],
        _len: usize,
    ) -> Result<(), (Error, &'static mut [u8])> {
        Err((Error::NotSupported, data))
    }
    fn read(
        &self,
        _addr: u8,
        buffer: &'static mut [u8],
        _len: usize,
    ) -> Result<(), (Error, &'static mut [u8])> {
        Err((Error::NotSupported, buffer))
    }
}

impl<'a> SMBusMaster<'a> for NoSMBus {
    fn smbus_write_read(
        &self,
        _addr: u8,
        data: &'static mut [u8],
        _write_len: usize,
        _read_len: usize,
    ) -> Result<(), (Error, &'static mut [u8])> {
        Err((Error::NotSupported, data))
    }

    fn smbus_write(
        &self,
        _addr: u8,
        data: &'static mut [u8],
        _len: usize,
    ) -> Result<(), (Error, &'static mut [u8])> {
        Err((Error::NotSupported, data))
    }

    fn smbus_read(
        &self,
        _addr: u8,
        buffer: &'static mut [u8],
        _len: usize,
    ) -> Result<(), (Error, &'static mut [u8])> {
        Err((Error::NotSupported, buffer))
    }
}