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
#![deny(missing_docs)] #![doc(html_root_url = "http://arcnmx.github.io/i2c-rs/")] //! Generic traits encompassing operations on an I2C bus. //! //! Implementation of these traits are left up to downstream crates such as: //! //! - [i2c-linux](https://crates.io/crates/i2c-linux) //! - [i2cdev](https://crates.io/crates/i2c-i2cdev) #[macro_use] extern crate bitflags; use std::io::{self, Read, Write}; mod smbus; pub use smbus::SmbusReadWrite; /// Indicates an ability to communicate with the I2C protocol. pub trait Master { /// The error type returned by I2C operations. type Error; } /// An I2C master can address different slaves on an I2C bus. pub trait Address: Master { /// Sets the current slave to address. /// /// This should *not* be shifted to include the read/write bit, and /// therefore should be only 7 bits wide normally. fn set_slave_address(&mut self, addr: u16, tenbit: bool) -> Result<(), Self::Error>; } /// An I2C master that can communicate using the standard Read/Write traits. /// /// The `i2c_read`/`i2c_write` methods are only provided to expose the original /// error type, and should otherwise be identical to `read`/`write`. pub trait ReadWrite: Master { /// Initiate an isolated read transfer on the I2C bus, followed by a STOP. fn i2c_read(&mut self, value: &mut [u8]) -> Result<usize, Self::Error>; /// Initiate an isolated write transfer on the I2C bus, followed by a STOP. fn i2c_write(&mut self, value: &[u8]) -> Result<(), Self::Error>; } impl<T: Master + Read + Write> ReadWrite for T where T::Error: From<io::Error> { fn i2c_read(&mut self, value: &mut [u8]) -> Result<usize, Self::Error> { self.read(value).map_err(From::from) } fn i2c_write(&mut self, value: &[u8]) -> Result<(), Self::Error> { self.write(value) .and_then(|len| if len != value.len() { Err(io::Error::new(io::ErrorKind::Interrupted, format!("I2C write was truncated to {} bytes", len))) } else { Ok(()) }).map_err(From::from) } } /// SMBus operations pub trait Smbus: Master { /// Sends a single bit to the device, in the place of the rd/wr address bit. fn smbus_write_quick(&mut self, value: bool) -> Result<(), Self::Error>; /// Reads a single byte from a device without specifying a register. fn smbus_read_byte(&mut self) -> Result<u8, Self::Error>; /// Sends a single byte to the device fn smbus_write_byte(&mut self, value: u8) -> Result<(), Self::Error>; /// Reads a byte from the designated register. fn smbus_read_byte_data(&mut self, command: u8) -> Result<u8, Self::Error>; /// Writes a byte to the designated register. fn smbus_write_byte_data(&mut self, command: u8, value: u8) -> Result<(), Self::Error>; /// Reads a 16-bit word from the designated register. fn smbus_read_word_data(&mut self, command: u8) -> Result<u16, Self::Error>; /// Writes a 16-bit word to the designated register. fn smbus_write_word_data(&mut self, command: u8, value: u16) -> Result<(), Self::Error>; /// Writes a 16-bit word to the specified register, then reads a 16-bit word /// in response. fn smbus_process_call(&mut self, command: u8, value: u16) -> Result<u16, Self::Error>; /// Reads up to 32 bytes from the designated device register. /// /// Returns the number of bytes read, which may be larger than `value.len()` /// if the read was truncated. fn smbus_read_block_data(&mut self, command: u8, value: &mut [u8]) -> Result<usize, Self::Error>; /// Writes up to 32 bytes to the designated device register. fn smbus_write_block_data(&mut self, command: u8, value: &[u8]) -> Result<(), Self::Error>; } /// SMBus 2.0 operations pub trait Smbus20: Smbus { /// Sends up to 31 bytes of data to the designated register, and reads up to /// 31 bytes in return. /// /// Returns the number of bytes read, which may be larger than `read.len()` /// if the read was truncated. fn smbus_process_call_block(&mut self, command: u8, write: &[u8], read: &mut [u8]) -> Result<usize, Self::Error>; } /// SMBus Packet Error Checking pub trait SmbusPec: Smbus { /// Enables or disables SMBus Packet Error Checking fn smbus_set_pec(&mut self, pec: bool) -> Result<(), Self::Error>; } /// Basic I2C transfer without including length prefixes associated with SMBus. pub trait BlockTransfer: Master { /// Reads a block of bytes from the designated device register. /// /// Unlike `smbus_read_block_data` this does not receive a data length. fn i2c_read_block_data(&mut self, command: u8, value: &mut [u8]) -> Result<usize, Self::Error>; /// Writes a block of bytes to the designated device register. /// /// Unlike `smbus_write_block_data` this does not transfer the data length. fn i2c_write_block_data(&mut self, command: u8, value: &[u8]) -> Result<(), Self::Error>; } /// Advanced I2C transfer queues that support repeated START operations. pub trait BulkTransfer: Master { /// Specifies the flags that this implementation supports. fn i2c_transfer_support(&mut self) -> Result<(ReadFlags, WriteFlags), Self::Error>; /// Executes a queue of I2C transfers, separated by repeated START /// conditions. Data buffers are truncated to the actual read length on /// completion. fn i2c_transfer(&mut self, messages: &mut [Message]) -> Result<(), Self::Error>; } /// Part of a combined I2C transaction. pub enum Message<'a> { /// I2C read command Read { /// The slave address of the device to read from. address: u16, /// A data buffer to read into. data: &'a mut [u8], /// Additional flags can modify the operation to work around device quirks. flags: ReadFlags, }, /// I2C write command Write { /// The slave address of the device to write to. address: u16, /// The data to write. data: &'a [u8], /// Additional flags can modify the operation to work around device quirks. flags: WriteFlags, }, } impl<'a> Message<'a> { /// Byte length of the message data buffer. pub fn len(&self) -> usize { match *self { Message::Read { ref data, .. } => data.len(), Message::Write { ref data, .. } => data.len(), } } /// Address of the message's slave. pub fn address(&self) -> u16 { match *self { Message::Read { address, .. } => address, Message::Write { address, .. } => address, } } /// The data buffer of the message. pub fn data(&self) -> &[u8] { match *self { Message::Read { ref data, .. } => data, Message::Write { data, .. } => data, } } } bitflags! { /// Flags to work around device quirks. #[derive(Default)] pub struct ReadFlags: u16 { /// The first received byte will indicate the remaining length of the transfer. const RECEIVE_LEN = 0x01; /// NACK bit is generated for this read. const NACK = 0x02; /// Flips the meaning of the read/write address bit for misbehaving devices. const REVERSE_RW = 0x04; /// Do not generate a START condition or the address start byte. When /// used for the first message, a START condition is still generated. /// /// This can be used to combine multiple buffers into a single I2C transfer, /// usually without a direction change. const NO_START = 0x08; /// Force a STOP condition after this message. const STOP = 0x10; } } bitflags! { /// Flags to work around device quirks. #[derive(Default)] pub struct WriteFlags: u16 { /// Treat NACK as an ACK and prevent it from interrupting the transfer. const IGNORE_NACK = 0x01; /// Flips the meaning of the read/write address bit for misbehaving devices. const REVERSE_RW = 0x02; /// Do not generate a START condition or the address start byte. When /// used for the first message, a START condition is still generated. /// /// This can be used to combine multiple buffers into a single I2C transfer, /// usually without a direction change. const NO_START = 0x04; /// Force a STOP condition after this message. const STOP = 0x08; } }