From 4a69aeb3774dcfff13db6e086226582136f0608c Mon Sep 17 00:00:00 2001 From: Edwin Amsler Date: Sat, 29 Sep 2018 16:01:27 -0500 Subject: [PATCH 1/4] Create skeleton for RFC purposes --- src/core.rs | 16 ++++++++++++++++ src/linux.rs | 25 ++++++++++++++++++++++++- src/mock.rs | 25 ++++++++++++++++++++++++- 3 files changed, 64 insertions(+), 2 deletions(-) diff --git a/src/core.rs b/src/core.rs index 092974b8..c398c810 100644 --- a/src/core.rs +++ b/src/core.rs @@ -110,4 +110,20 @@ pub trait I2CDevice { /// Select a register, send 1 to 31 bytes of data to it, and reads /// 1 to 31 bytes of data from it. fn smbus_process_block(&mut self, register: u8, values: &[u8]) -> Result, Self::Error>; + + /// Send a batch of messages. Useful for when multiple buffers need to be + /// sent or recieved without an I2C `stop` flag. + fn transfer(&self, messages: &[M]) -> Result<(), Self::Error>; } + +/// Messages sent to / from an I2C device as a batch +pub trait I2CMessage { + /// Represents a read operation + fn read(data: &[u8]) -> Self; + + /// Represents a write operation + fn write(&self, data: &[u8]) -> Self; + + /// Represents a custom operation or special opperands + fn custom(&self, data: &[u8], address: u16, flags: u16) -> Self; +} \ No newline at end of file diff --git a/src/linux.rs b/src/linux.rs index 5320d6ab..a3768da8 100644 --- a/src/linux.rs +++ b/src/linux.rs @@ -7,7 +7,10 @@ // except according to those terms. use ffi; -use core::I2CDevice; +use core::{ + I2CDevice, + I2CMessage +}; use std::error::Error; use std::path::Path; use std::fs::File; @@ -220,4 +223,24 @@ impl I2CDevice for LinuxI2CDevice { fn smbus_process_block(&mut self, register: u8, values: &[u8]) -> Result, LinuxI2CError> { ffi::i2c_smbus_process_call_block(self.as_raw_fd(), register, values).map_err(From::from) } + + fn transfer(&self, messages: &[LinuxMessage]) -> Result<(), Self::Error> { + unimplemented!() + } +} + +struct LinuxMessage; + +impl I2CMessage for LinuxMessage { + fn read(data: &[u8]) -> Self { + unimplemented!(); + } + + fn write(&self, data: &[u8]) -> Self { + unimplemented!(); + } + + fn custom(&self, data: &[u8], address: u16, flags: u16) -> Self { + unimplemented!(); + } } diff --git a/src/mock.rs b/src/mock.rs index 54b666d0..e46bea7a 100644 --- a/src/mock.rs +++ b/src/mock.rs @@ -5,7 +5,10 @@ // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. -use core::I2CDevice; +use core::{ + I2CDevice, + I2CMessage, +}; use std::io; pub type I2CResult = io::Result; @@ -54,6 +57,22 @@ impl I2CRegisterMap { } } +struct MockMessage; + +impl I2CMessage for MockMessage { + fn read(data: &[u8]) -> Self { + unimplemented!(); + } + + fn write(&self, data: &[u8]) -> Self { + unimplemented!(); + } + + fn custom(&self, data: &[u8], address: u16, flags: u16) -> Self { + unimplemented!(); + } +} + pub struct MockI2CDevice { pub regmap: I2CRegisterMap, } @@ -99,4 +118,8 @@ impl I2CDevice for MockI2CDevice { fn smbus_write_i2c_block_data(&mut self, _register: u8, _values: &[u8]) -> I2CResult<()> { unimplemented!() } + + fn transfer(&self, messages: &[MockMessage]) -> Result<(), Self::Error> { + unimplemented!() + } } From 3364ab2c4ba9f3c8193c6c694908c1f55e4475ca Mon Sep 17 00:00:00 2001 From: Edwin Amsler Date: Sat, 29 Sep 2018 13:03:08 -0500 Subject: [PATCH 2/4] Upgrade nix to 0.11 --- Cargo.toml | 2 +- src/ffi.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 29ebc79f..9509070f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,7 +18,7 @@ https://www.kernel.org/doc/Documentation/i2c/dev-interface libc = "0.2" bitflags = "1" byteorder = "1" -nix = "0.10" +nix = "0.11" [dev-dependencies] docopt = "0.8" diff --git a/src/ffi.rs b/src/ffi.rs index c4c08f7e..f043d4d6 100644 --- a/src/ffi.rs +++ b/src/ffi.rs @@ -175,8 +175,8 @@ mod ioctl { use super::{I2C_SLAVE, I2C_SMBUS}; pub use super::i2c_smbus_ioctl_data; - ioctl!(bad write_int set_i2c_slave_address with I2C_SLAVE); - ioctl!(bad write_ptr i2c_smbus with I2C_SMBUS; i2c_smbus_ioctl_data); + ioctl_write_int_bad!(set_i2c_slave_address, I2C_SLAVE); + ioctl_write_ptr_bad!(i2c_smbus, I2C_SMBUS, i2c_smbus_ioctl_data); } From d9ee0662314d2a16470d68c80c4b72491bbd73f8 Mon Sep 17 00:00:00 2001 From: Edwin Amsler Date: Mon, 29 Oct 2018 21:51:57 -0500 Subject: [PATCH 3/4] Add ioctl, add associated type --- src/core.rs | 3 ++- src/ffi.rs | 33 +++++++++++++++++++++++++-------- src/linux.rs | 9 +++++---- src/mock.rs | 5 +++-- 4 files changed, 35 insertions(+), 15 deletions(-) mode change 100644 => 100755 src/core.rs mode change 100644 => 100755 src/ffi.rs mode change 100644 => 100755 src/linux.rs mode change 100644 => 100755 src/mock.rs diff --git a/src/core.rs b/src/core.rs old mode 100644 new mode 100755 index c398c810..3673f959 --- a/src/core.rs +++ b/src/core.rs @@ -16,6 +16,7 @@ use std::error::Error; /// Linux i2cdev interface. pub trait I2CDevice { type Error: Error; + type I2CMessage; /// Read data from the device to fill the provided slice fn read(&mut self, data: &mut [u8]) -> Result<(), Self::Error>; @@ -113,7 +114,7 @@ pub trait I2CDevice { /// Send a batch of messages. Useful for when multiple buffers need to be /// sent or recieved without an I2C `stop` flag. - fn transfer(&self, messages: &[M]) -> Result<(), Self::Error>; + fn transfer(&self, messages: &[Self::I2CMessage]) -> Result<(), Self::Error>; } /// Messages sent to / from an I2C device as a batch diff --git a/src/ffi.rs b/src/ffi.rs old mode 100644 new mode 100755 index f043d4d6..2910ef01 --- a/src/ffi.rs +++ b/src/ffi.rs @@ -40,7 +40,7 @@ bitflags! { } #[repr(C)] -struct i2c_msg { +pub struct i2c_msg { /// slave address addr: u16, /// serialized I2CMsgFlags @@ -147,7 +147,7 @@ const I2C_FUNCS: u16 = 0x0705; const I2C_RDWR: u16 = 0x0707; const I2C_PEC: u16 = 0x0708; const I2C_SMBUS: u16 = 0x0720; -const I2C_RDRW_IOCTL_MAX_MSGS: u8 = 42; +const I2C_RDRW_IOCTL_MAX_MSGS: usize = 42; /// This is the structure as used in the I2C_SMBUS ioctl call #[repr(C)] @@ -164,19 +164,21 @@ pub struct i2c_smbus_ioctl_data { /// This is the structure as used in the I2C_RDWR ioctl call #[repr(C)] -struct i2c_rdwr_ioctl_data { +pub struct i2c_rdwr_ioctl_data { // struct i2c_msg __user *msgs; - msgs: *mut i2c_msg, - // __u32 nmsgs; - nmsgs: u32, + msgs: *const i2c_msg, + // __i32 nmsgs; + nmsgs: i32, } mod ioctl { - use super::{I2C_SLAVE, I2C_SMBUS}; - pub use super::i2c_smbus_ioctl_data; + use super::{I2C_SLAVE, I2C_SMBUS, I2C_RDWR}; + use super::i2c_smbus_ioctl_data; + use super::i2c_rdwr_ioctl_data; ioctl_write_int_bad!(set_i2c_slave_address, I2C_SLAVE); ioctl_write_ptr_bad!(i2c_smbus, I2C_SMBUS, i2c_smbus_ioctl_data); + ioctl_write_ptr_bad!(i2c_rdrw, I2C_RDWR, i2c_rdwr_ioctl_data); } @@ -429,3 +431,18 @@ pub fn i2c_smbus_process_call_block(fd: RawFd, register: u8, values: &[u8]) -> R let count = data.block[0]; Ok((&data.block[1..(count + 1) as usize]).to_vec()) } + +#[inline] +pub fn i2c_rdwr(fd: RawFd, values: &[i2c_msg]) -> Result<(), I2CError> { + // TODO: Find out if this is the best course of action or if we should let Linux throw the error + assert!(values.len() <= I2C_RDRW_IOCTL_MAX_MSGS); + + let i2c_data = i2c_rdwr_ioctl_data { + msgs: values.as_ptr(), + nmsgs: values.len() as i32, + }; + + unsafe { + ioctl::i2c_rdrw(fd, &i2c_data).map(drop) + } +} \ No newline at end of file diff --git a/src/linux.rs b/src/linux.rs old mode 100644 new mode 100755 index a3768da8..f2728f1e --- a/src/linux.rs +++ b/src/linux.rs @@ -9,7 +9,7 @@ use ffi; use core::{ I2CDevice, - I2CMessage + I2CMessage, }; use std::error::Error; use std::path::Path; @@ -128,6 +128,7 @@ impl LinuxI2CDevice { impl I2CDevice for LinuxI2CDevice { type Error = LinuxI2CError; + type I2CMessage = LinuxMessage; /// Read data from the device to fill the provided slice fn read(&mut self, data: &mut [u8]) -> Result<(), LinuxI2CError> { @@ -224,12 +225,12 @@ impl I2CDevice for LinuxI2CDevice { ffi::i2c_smbus_process_call_block(self.as_raw_fd(), register, values).map_err(From::from) } - fn transfer(&self, messages: &[LinuxMessage]) -> Result<(), Self::Error> { - unimplemented!() + fn transfer(&self, messages: &[LinuxMessage]) -> Result<(), LinuxI2CError> { + ffi::i2c_rdwr(self.as_raw_fd(), messages).map_err(From::from) } } -struct LinuxMessage; +type LinuxMessage = ffi::i2c_msg; impl I2CMessage for LinuxMessage { fn read(data: &[u8]) -> Self { diff --git a/src/mock.rs b/src/mock.rs old mode 100644 new mode 100755 index e46bea7a..a2b39ca2 --- a/src/mock.rs +++ b/src/mock.rs @@ -57,7 +57,7 @@ impl I2CRegisterMap { } } -struct MockMessage; +pub struct MockMessage; impl I2CMessage for MockMessage { fn read(data: &[u8]) -> Self { @@ -86,6 +86,7 @@ impl MockI2CDevice { impl I2CDevice for MockI2CDevice { type Error = io::Error; + type I2CMessage = MockMessage; fn read(&mut self, data: &mut [u8]) -> I2CResult<()> { self.regmap.read(data) @@ -119,7 +120,7 @@ impl I2CDevice for MockI2CDevice { unimplemented!() } - fn transfer(&self, messages: &[MockMessage]) -> Result<(), Self::Error> { + fn transfer(&self, messages: &[MockMessage]) -> Result<(), Self::Error> { unimplemented!() } } From ef9755db5b79602d7222d5cdfde289d7885ab6e2 Mon Sep 17 00:00:00 2001 From: Edwin Amsler Date: Sun, 11 Nov 2018 14:58:58 -0600 Subject: [PATCH 4/4] Clean up message flags --- src/core.rs | 6 ++++-- src/ffi.rs | 29 ++++------------------------- src/linux.rs | 47 +++++++++++++++++++++++++++++++++++++++++------ src/mock.rs | 10 ++++++---- 4 files changed, 55 insertions(+), 37 deletions(-) diff --git a/src/core.rs b/src/core.rs index 3673f959..e9646df0 100755 --- a/src/core.rs +++ b/src/core.rs @@ -119,12 +119,14 @@ pub trait I2CDevice { /// Messages sent to / from an I2C device as a batch pub trait I2CMessage { + type Flags; + /// Represents a read operation - fn read(data: &[u8]) -> Self; + fn read(&self, data: &[u8]) -> Self; /// Represents a write operation fn write(&self, data: &[u8]) -> Self; /// Represents a custom operation or special opperands - fn custom(&self, data: &[u8], address: u16, flags: u16) -> Self; + fn custom(&self, data: &[u8], address: u16, flags: Self::Flags) -> Self; } \ No newline at end of file diff --git a/src/ffi.rs b/src/ffi.rs index 2910ef01..a1753a81 100755 --- a/src/ffi.rs +++ b/src/ffi.rs @@ -18,37 +18,16 @@ use byteorder::{NativeEndian, ReadBytesExt, WriteBytesExt}; pub type I2CError = nix::Error; -bitflags! { - struct I2CMsgFlags: u16 { - /// this is a ten bit chip address - const I2C_M_TEN = 0x0010; - /// read data, from slave to master - const I2C_M_RD = 0x0001; - /// if I2C_FUNC_PROTOCOL_MANGLING - const I2C_M_STOP = 0x8000; - /// if I2C_FUNC_NOSTART - const I2C_M_NOSTART = 0x4000; - /// if I2C_FUNC_PROTOCOL_MANGLING - const I2C_M_REV_DIR_ADDR = 0x2000; - /// if I2C_FUNC_PROTOCOL_MANGLING - const I2C_M_IGNORE_NAK = 0x1000; - /// if I2C_FUNC_PROTOCOL_MANGLING - const I2C_M_NO_RD_ACK = 0x0800; - /// length will be first received byte - const I2C_M_RECV_LEN = 0x0400; - } -} - #[repr(C)] pub struct i2c_msg { /// slave address - addr: u16, + pub(crate) addr: u16, /// serialized I2CMsgFlags - flags: u16, + pub(crate) flags: u16, /// msg length - len: u16, + pub(crate) len: u16, /// pointer to msg data - buf: *mut u8, + pub(crate) buf: *const u8, } bitflags! { diff --git a/src/linux.rs b/src/linux.rs index f2728f1e..9d0af8d3 100755 --- a/src/linux.rs +++ b/src/linux.rs @@ -232,16 +232,51 @@ impl I2CDevice for LinuxI2CDevice { type LinuxMessage = ffi::i2c_msg; +bitflags! { + /// Various flags used by the i2c_rdwr ioctl on Linux. For details, see + /// https://www.kernel.org/doc/Documentation/i2c/i2c-protocol + /// + /// In general, these are for special cases and should not be needed + pub struct I2CMsgFlags: u16 { + /// Use ten bit addressing on this message + const TenBitAddress = 0x0010; + /// Read data, from slave to master + const Read = 0x0001; + /// Force an I2C stop condition on this message + const Stop = 0x8000; + /// Avoid sending an I2C start condition on this message + const NoStart = 0x4000; + /// If you need to invert a 'read' command bit to a 'write' + const InvertCommand = 0x2000; + /// Force this message to ignore I2C negative acknowlegements + const IgnoreNack = 0x1000; + /// Force message to ignore acknowledgement + const IgnoreAck = 0x0800; + /// Allow the client to specify how many bytes it will send + const UseRecieveLength = 0x0400; + } +} + impl I2CMessage for LinuxMessage { - fn read(data: &[u8]) -> Self { - unimplemented!(); + type Flags = I2CMsgFlags; + + fn custom(&self, data: &[u8], address: u16, flags: I2CMsgFlags) -> LinuxMessage { + // Convert from special type to u16 + let flags = flags.bits(); + + Self { + addr: address, + flags, + len: data.len() as u16, + buf: data.as_ptr(), + } } - fn write(&self, data: &[u8]) -> Self { - unimplemented!(); + fn read(&self, data: &[u8]) -> LinuxMessage { + self.custom(data, self.addr, I2CMsgFlags::Read) } - fn custom(&self, data: &[u8], address: u16, flags: u16) -> Self { - unimplemented!(); + fn write(&self, data: &[u8]) -> LinuxMessage { + self.custom(data, self.addr, I2CMsgFlags::empty()) } } diff --git a/src/mock.rs b/src/mock.rs index a2b39ca2..6fe98835 100755 --- a/src/mock.rs +++ b/src/mock.rs @@ -60,15 +60,17 @@ impl I2CRegisterMap { pub struct MockMessage; impl I2CMessage for MockMessage { - fn read(data: &[u8]) -> Self { + type Flags = (); + + fn read(&self, _data: &[u8]) -> Self { unimplemented!(); } - fn write(&self, data: &[u8]) -> Self { + fn write(&self, _data: &[u8]) -> Self { unimplemented!(); } - fn custom(&self, data: &[u8], address: u16, flags: u16) -> Self { + fn custom(&self, _data: &[u8], _address: u16, _flags: ()) -> Self { unimplemented!(); } } @@ -120,7 +122,7 @@ impl I2CDevice for MockI2CDevice { unimplemented!() } - fn transfer(&self, messages: &[MockMessage]) -> Result<(), Self::Error> { + fn transfer(&self, _messages: &[MockMessage]) -> Result<(), Self::Error> { unimplemented!() } }