From e7d2b75042896c18ab8e45d4730e573d07a04648 Mon Sep 17 00:00:00 2001 From: Diego Barrios Romero Date: Thu, 7 Nov 2019 18:34:55 +0100 Subject: [PATCH 1/4] Merge branch 'pf-i2cbus' --- examples/pca9956b.rs | 83 +++++++++++++++++++++++++++++++++++ src/core.rs | 100 +++++++++++++++++++++++++++++++++++++++++++ src/ffi.rs | 80 +++++++++++++++++++++++----------- src/linux.rs | 39 ++++++++++++++++- 4 files changed, 275 insertions(+), 27 deletions(-) create mode 100644 examples/pca9956b.rs diff --git a/examples/pca9956b.rs b/examples/pca9956b.rs new file mode 100644 index 00000000..03a7a7e7 --- /dev/null +++ b/examples/pca9956b.rs @@ -0,0 +1,83 @@ +// Copyright 2018, Piers Finlayson +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms.extern crate i2cdev; + +extern crate i2cdev; +extern crate docopt; + +#[cfg(any(target_os = "linux", target_os = "android"))] +use i2cdev::linux::{LinuxI2CBus, I2CMsg}; +#[cfg(any(target_os = "linux", target_os = "android"))] +use i2cdev::core::I2CBus; + +use std::env::args; +use docopt::Docopt; + +const USAGE: &str = " +Reads registers from a PCA9956B IC via Linux i2cdev. + +Assumes the PCA9956B is using address 0x20. + +Usage: + pca9956b + pca9956b (-h | --help) + pca9956b --version + +Options: + -h --help Show this help text. + --version Show version. +"; + +const ADDR: u16 = 0x20; + +#[cfg(any(target_os = "linux", target_os = "android"))] +fn main() { + let args = Docopt::new(USAGE) + .and_then(|d| d.argv(args()).parse()) + .unwrap_or_else(|e| e.exit()); + let path = args.get_str(""); + let mut bus = match LinuxI2CBus::new(path) { + Ok(bus) => bus, + Err(_e) => { + println!("Error opening I2C Bus {} {}", path, _e); + return + } + }; + println!("Opened I2C Bus OK: {}", path); + + // Build two I2C messages: + // 1) Write the MODE1 register address, with top bit indcating auto- + // increment should be enabled + // 2) Read 10 bytes from the current register onwards + let mut dataw: Vec = vec![0b1000_0000]; + let mut data: Vec = vec![0; 10]; + let mut msgs: Vec = Vec::new(); + msgs.push(I2CMsg::new(ADDR, &mut dataw)); + msgs.push(I2CMsg::new(ADDR, &mut data)); + msgs[1].set_read(); + + // Send the messages to the kernel to process + match bus.rdwr(&mut msgs) { + Ok(rc) => { + println!("Successful RDWR call: {} messages processed", rc) + }, + Err(_e) => { + println!("Error reading/writing {}", _e); + return + }, + } + + // Print the data read from the device. A recently reset PCA9956B should + // return: + // 0x8005000000000000ff00 + let mut output = "Result: 0x".to_string(); + let data = msgs[1].data(); + for byte in data { + output = format!("{}{:02x}", output, byte); + } + println!("{}", output); +} diff --git a/src/core.rs b/src/core.rs index 092974b8..90771232 100644 --- a/src/core.rs +++ b/src/core.rs @@ -111,3 +111,103 @@ pub trait I2CDevice { /// 1 to 31 bytes of data from it. fn smbus_process_block(&mut self, register: u8, values: &[u8]) -> Result, Self::Error>; } + +/// Interface to an I2C Bus from an I2C Master +/// +/// This is used when the client wants to interact directly with the bus +/// without specifying an I2C slave address up-front, either because it needs +/// to communicate with multiple addresses without creatings separate +/// I2CDevice objects, or because it wants to make used of the I2C_RDWR ioctl +/// which allows the client to send and transmit multiple sets I2C data in a +/// single operation, potentially to different I2C slave addresses. +/// +/// Typical implementations will store state with references to the bus +/// in use. The trait is based on the Linux i2cdev interface. +pub trait I2CBus { + type Error: Error; + + // Performs multiple serially chained I2C read/write transactions. On + // success the return code is the number of successfully executed + // transactions + fn rdwr<'a>(&mut self, msgs: &mut Vec>) -> Result; +} + +bitflags! { + pub 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; + } +} + +/// Rust version of i2c_msg +pub struct I2CMsg<'a> { + /// slave address + pub(crate) addr: u16, + /// serialized I2CMsgFlags + pub(crate) flags: u16, + + /// msg length comes from msg Vector length + + /// msg data to be sent/received + pub(crate) data: &'a mut Vec, +} + +impl<'a> I2CMsg<'a> { + /// Create I2CMsg from address and data buffer + pub fn new(addr: u16, data: &'a mut Vec) -> Self { + I2CMsg { + addr, + flags: 0, + data + } + } + + /// Set flags + pub fn set_flags(&mut self, flags: u16) { + self.flags = flags; + } + + /// Get flags + pub fn flags(&mut self) -> u16 { + self.flags + } + + /// Set addr + pub fn set_addr(&mut self, addr: u16) { + self.addr = addr; + } + + /// Get addr + pub fn addr(&mut self) -> u16 { + self.addr + } + + /// Set data + pub fn set_data(&mut self, data: &'a mut Vec) { + self.data = data; + } + + /// Get addr + pub fn data(&mut self) -> Vec { + self.data.clone() + } + + /// Sets the read flag + pub fn set_read(&mut self) { + self.flags |= I2CMsgFlags::I2C_M_RD.bits; + } +} diff --git a/src/ffi.rs b/src/ffi.rs index f043d4d6..e8e4dab2 100644 --- a/src/ffi.rs +++ b/src/ffi.rs @@ -14,33 +14,18 @@ use std::mem; use std::ptr; use std::io::Cursor; use std::os::unix::prelude::*; +use std::marker::PhantomData; use byteorder::{NativeEndian, ReadBytesExt, WriteBytesExt}; +use core::{I2CMsg}; +use libc::c_int; 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)] -struct i2c_msg { +#[derive(Debug)] +/// C version of i2c_msg structure +// See linux/i2c.h +struct i2c_msg<'a> { /// slave address addr: u16, /// serialized I2CMsgFlags @@ -49,6 +34,19 @@ struct i2c_msg { len: u16, /// pointer to msg data buf: *mut u8, + _phantom: PhantomData<&'a mut u8>, +} + +impl<'a, 'b> From<&'b mut I2CMsg<'a>> for i2c_msg<'a> { + fn from(msg: &mut I2CMsg) -> Self { + i2c_msg { + addr: msg.addr, + flags: msg.flags, + len: msg.data.len() as u16, + buf: msg.data.as_mut_ptr(), + _phantom: PhantomData + } + } } bitflags! { @@ -163,23 +161,25 @@ pub struct i2c_smbus_ioctl_data { } /// This is the structure as used in the I2C_RDWR ioctl call +// see linux/i2c-dev.h #[repr(C)] -struct i2c_rdwr_ioctl_data { +pub struct i2c_rdwr_ioctl_data<'a> { // struct i2c_msg __user *msgs; - msgs: *mut i2c_msg, + msgs: *mut i2c_msg<'a>, // __u32 nmsgs; nmsgs: u32, } mod ioctl { - use super::{I2C_SLAVE, I2C_SMBUS}; + use super::{I2C_SLAVE, I2C_SMBUS, I2C_RDWR}; pub use super::i2c_smbus_ioctl_data; + pub 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_rdwr, I2C_RDWR, i2c_rdwr_ioctl_data); } - pub fn i2c_set_slave_address(fd: RawFd, slave_address: u16) -> Result<(), nix::Error> { unsafe { ioctl::set_i2c_slave_address(fd, slave_address as i32)?; @@ -429,3 +429,31 @@ 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()) } + +// Returns the number of messages succesfully processed +unsafe fn i2c_rdwr_access(fd: RawFd, + msgs: *mut i2c_msg, + nmsgs: usize) + -> Result<(c_int), I2CError> { + let args = i2c_rdwr_ioctl_data { + msgs, + nmsgs: nmsgs as u32, + }; + + ioctl::i2c_rdwr(fd, &args) +} + +pub fn i2c_rdwr_read_write(fd: RawFd, + msgs: &mut Vec) -> Result<(i32), I2CError> { + // Building the msgs to push is safe. + // We have to use iter_mut as buf needs to be mutable. + let mut cmsgs = msgs.iter_mut().map(::from).collect::>(); + + // But calling the ioctl is definitely not! + unsafe { + i2c_rdwr_access(fd, + cmsgs.as_mut_ptr(), + cmsgs.len()) + } +} + diff --git a/src/linux.rs b/src/linux.rs index 5320d6ab..7e53aa52 100644 --- a/src/linux.rs +++ b/src/linux.rs @@ -7,7 +7,7 @@ // except according to those terms. use ffi; -use core::I2CDevice; +use core::{I2CDevice, I2CBus}; use std::error::Error; use std::path::Path; use std::fs::File; @@ -18,11 +18,18 @@ use std::fs::OpenOptions; use std::io::prelude::*; use std::os::unix::prelude::*; +// Expose these core structs from this module +pub use core::{I2CMsgFlags, I2CMsg}; + pub struct LinuxI2CDevice { devfile: File, slave_address: u16, } +pub struct LinuxI2CBus { + devfile: File, +} + #[derive(Debug)] pub enum LinuxI2CError { Nix(nix::Error), @@ -88,6 +95,12 @@ impl AsRawFd for LinuxI2CDevice { } } +impl AsRawFd for LinuxI2CBus { + fn as_raw_fd(&self) -> RawFd { + self.devfile.as_raw_fd() + } +} + impl LinuxI2CDevice { /// Create a new I2CDevice for the specified path pub fn new>(path: P, @@ -221,3 +234,27 @@ impl I2CDevice for LinuxI2CDevice { ffi::i2c_smbus_process_call_block(self.as_raw_fd(), register, values).map_err(From::from) } } + +impl LinuxI2CBus { + /// Create a new LinuxI2CBus for the specified path + pub fn new>(path: P) + -> Result { + let file = OpenOptions::new() + .read(true) + .write(true) + .open(path)?; + let bus = LinuxI2CBus { + devfile: file, + }; + Ok(bus) + } +} + +impl I2CBus for LinuxI2CBus { + type Error = LinuxI2CError; + + /// Issue the provided sequence of I2C transactions + fn rdwr(&mut self, msgs: &mut Vec) -> Result { + ffi::i2c_rdwr_read_write(self.as_raw_fd(), msgs).map_err(From::from) + } +} From f1656813f3f08c74d176be9bce10ab36d7a37e2b Mon Sep 17 00:00:00 2001 From: Diego Barrios Romero Date: Thu, 7 Nov 2019 21:41:22 +0100 Subject: [PATCH 2/4] Rework i2cbus implementation --- examples/pca9956b.rs | 30 +++++++--------- src/core.rs | 86 +++++--------------------------------------- src/ffi.rs | 60 ++++++++----------------------- src/lib.rs | 27 ++++++++++++++ src/linux.rs | 66 ++++++++++++++++++++++++++++++++-- 5 files changed, 125 insertions(+), 144 deletions(-) diff --git a/examples/pca9956b.rs b/examples/pca9956b.rs index 03a7a7e7..df36d007 100644 --- a/examples/pca9956b.rs +++ b/examples/pca9956b.rs @@ -10,9 +10,9 @@ extern crate i2cdev; extern crate docopt; #[cfg(any(target_os = "linux", target_os = "android"))] -use i2cdev::linux::{LinuxI2CBus, I2CMsg}; +use i2cdev::core::{I2CBus, I2CMessage}; #[cfg(any(target_os = "linux", target_os = "android"))] -use i2cdev::core::I2CBus; +use i2cdev::linux::{LinuxI2CBus, LinuxI2CMessage}; use std::env::args; use docopt::Docopt; @@ -44,7 +44,7 @@ fn main() { Ok(bus) => bus, Err(_e) => { println!("Error opening I2C Bus {} {}", path, _e); - return + return; } }; println!("Opened I2C Bus OK: {}", path); @@ -53,30 +53,26 @@ fn main() { // 1) Write the MODE1 register address, with top bit indcating auto- // increment should be enabled // 2) Read 10 bytes from the current register onwards - let mut dataw: Vec = vec![0b1000_0000]; - let mut data: Vec = vec![0; 10]; - let mut msgs: Vec = Vec::new(); - msgs.push(I2CMsg::new(ADDR, &mut dataw)); - msgs.push(I2CMsg::new(ADDR, &mut data)); - msgs[1].set_read(); + let mut data = [0; 10]; + let mut msgs = [ + LinuxI2CMessage::write(ADDR, &[0b1000_0000]), + LinuxI2CMessage::read(ADDR, &mut data), + ]; // Send the messages to the kernel to process - match bus.rdwr(&mut msgs) { - Ok(rc) => { - println!("Successful RDWR call: {} messages processed", rc) - }, + match bus.transfer(&mut msgs) { + Ok(rc) => println!("Successful transfer call: {} messages processed", rc), Err(_e) => { println!("Error reading/writing {}", _e); - return - }, + return; + } } // Print the data read from the device. A recently reset PCA9956B should // return: // 0x8005000000000000ff00 let mut output = "Result: 0x".to_string(); - let data = msgs[1].data(); - for byte in data { + for byte in &data { output = format!("{}{:02x}", output, byte); } println!("{}", output); diff --git a/src/core.rs b/src/core.rs index 90771232..ac242722 100644 --- a/src/core.rs +++ b/src/core.rs @@ -125,89 +125,19 @@ pub trait I2CDevice { /// in use. The trait is based on the Linux i2cdev interface. pub trait I2CBus { type Error: Error; + type Message: I2CMessage; // Performs multiple serially chained I2C read/write transactions. On // success the return code is the number of successfully executed // transactions - fn rdwr<'a>(&mut self, msgs: &mut Vec>) -> Result; + fn transfer(&mut self, msgs: &mut [Self::Message]) -> Result; } -bitflags! { - pub 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; - } -} - -/// Rust version of i2c_msg -pub struct I2CMsg<'a> { - /// slave address - pub(crate) addr: u16, - /// serialized I2CMsgFlags - pub(crate) flags: u16, - - /// msg length comes from msg Vector length - - /// msg data to be sent/received - pub(crate) data: &'a mut Vec, -} +/// Read/Write I2C message +pub trait I2CMessage { + /// Read data from device + fn read(slave_address: u16, data: &mut [u8]) -> Self; -impl<'a> I2CMsg<'a> { - /// Create I2CMsg from address and data buffer - pub fn new(addr: u16, data: &'a mut Vec) -> Self { - I2CMsg { - addr, - flags: 0, - data - } - } - - /// Set flags - pub fn set_flags(&mut self, flags: u16) { - self.flags = flags; - } - - /// Get flags - pub fn flags(&mut self) -> u16 { - self.flags - } - - /// Set addr - pub fn set_addr(&mut self, addr: u16) { - self.addr = addr; - } - - /// Get addr - pub fn addr(&mut self) -> u16 { - self.addr - } - - /// Set data - pub fn set_data(&mut self, data: &'a mut Vec) { - self.data = data; - } - - /// Get addr - pub fn data(&mut self) -> Vec { - self.data.clone() - } - - /// Sets the read flag - pub fn set_read(&mut self) { - self.flags |= I2CMsgFlags::I2C_M_RD.bits; - } + /// Write data to device + fn write(slave_address: u16, data: &[u8]) -> Self; } diff --git a/src/ffi.rs b/src/ffi.rs index e8e4dab2..1afbddb3 100644 --- a/src/ffi.rs +++ b/src/ffi.rs @@ -16,38 +16,21 @@ use std::io::Cursor; use std::os::unix::prelude::*; use std::marker::PhantomData; use byteorder::{NativeEndian, ReadBytesExt, WriteBytesExt}; -use core::{I2CMsg}; use libc::c_int; pub type I2CError = nix::Error; #[repr(C)] -#[derive(Debug)] -/// C version of i2c_msg structure -// See linux/i2c.h -struct i2c_msg<'a> { +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, - _phantom: PhantomData<&'a mut u8>, -} - -impl<'a, 'b> From<&'b mut I2CMsg<'a>> for i2c_msg<'a> { - fn from(msg: &mut I2CMsg) -> Self { - i2c_msg { - addr: msg.addr, - flags: msg.flags, - len: msg.data.len() as u16, - buf: msg.data.as_mut_ptr(), - _phantom: PhantomData + pub(crate) buf: *const u8, } - } -} bitflags! { struct I2CFunctions: u32 { @@ -163,9 +146,9 @@ pub struct i2c_smbus_ioctl_data { /// This is the structure as used in the I2C_RDWR ioctl call // see linux/i2c-dev.h #[repr(C)] -pub struct i2c_rdwr_ioctl_data<'a> { +pub struct i2c_rdwr_ioctl_data { // struct i2c_msg __user *msgs; - msgs: *mut i2c_msg<'a>, + msgs: *mut i2c_msg, // __u32 nmsgs; nmsgs: u32, } @@ -430,30 +413,15 @@ pub fn i2c_smbus_process_call_block(fd: RawFd, register: u8, values: &[u8]) -> R Ok((&data.block[1..(count + 1) as usize]).to_vec()) } -// Returns the number of messages succesfully processed -unsafe fn i2c_rdwr_access(fd: RawFd, - msgs: *mut i2c_msg, - nmsgs: usize) - -> Result<(c_int), I2CError> { - let args = i2c_rdwr_ioctl_data { - msgs, - nmsgs: nmsgs as u32, +#[inline] +pub fn i2c_rdwr(fd: RawFd, values: &mut [i2c_msg]) -> Result { + let i2c_data = i2c_rdwr_ioctl_data { + msgs: values.as_mut_ptr(), + nmsgs: values.len() as u32, }; - ioctl::i2c_rdwr(fd, &args) -} - -pub fn i2c_rdwr_read_write(fd: RawFd, - msgs: &mut Vec) -> Result<(i32), I2CError> { - // Building the msgs to push is safe. - // We have to use iter_mut as buf needs to be mutable. - let mut cmsgs = msgs.iter_mut().map(::from).collect::>(); - - // But calling the ioctl is definitely not! unsafe { - i2c_rdwr_access(fd, - cmsgs.as_mut_ptr(), - cmsgs.len()) + let n = ioctl::i2c_rdwr(fd, &i2c_data)?; + Ok(n as u32) } } - diff --git a/src/lib.rs b/src/lib.rs index 2080255e..d1996c64 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -41,6 +41,33 @@ //! } //! } //! ``` +//! +//! ```rust,no_run +//! extern crate i2cdev; +//! +//! use std::thread; +//! use std::time::Duration; +//! +//! use i2cdev::core::*; +//! use i2cdev::linux::{LinuxI2CBus, LinuxI2CError, LinuxI2CMessage}; +//! +//! const SLAVE_ADDR: u16 = 0x57; +//! +//! fn write_read_transaction() -> Result<(), LinuxI2CError> { +//! let mut dev = LinuxI2CBus::new("/dev/i2c-1")?; +//! +//! let mut read_data = [0; 2]; +//! let mut msgs = [ +//! LinuxI2CMessage::write(SLAVE_ADDR, &[0x01]), +//! LinuxI2CMessage::read(SLAVE_ADDR, &mut read_data) +//! ]; +//! dev.transfer(&mut msgs)?; +//! thread::sleep(Duration::from_millis(100)); +//! +//! println!("Reading: {:?}", read_data); +//! Ok(()) +//! } +//! ``` #![crate_name = "i2cdev"] #![crate_type = "lib"] diff --git a/src/linux.rs b/src/linux.rs index 7e53aa52..ea422034 100644 --- a/src/linux.rs +++ b/src/linux.rs @@ -19,7 +19,7 @@ use std::io::prelude::*; use std::os::unix::prelude::*; // Expose these core structs from this module -pub use core::{I2CMsgFlags, I2CMsg}; +pub use core::I2CMessage; pub struct LinuxI2CDevice { devfile: File, @@ -250,11 +250,71 @@ impl LinuxI2CBus { } } +/// Linux I2C message +pub type LinuxI2CMessage = ffi::i2c_msg; + impl I2CBus for LinuxI2CBus { type Error = LinuxI2CError; + type Message = LinuxI2CMessage; /// Issue the provided sequence of I2C transactions - fn rdwr(&mut self, msgs: &mut Vec) -> Result { - ffi::i2c_rdwr_read_write(self.as_raw_fd(), msgs).map_err(From::from) + fn transfer(&mut self, msgs: &mut [Self::Message]) -> Result { + ffi::i2c_rdwr(self.as_raw_fd(), msgs).map_err(From::from) + } +} + +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 I2CMessageFlags: u16 { + /// Use ten bit addressing on this message + const TEN_BIT_ADDRESS = 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 NO_START = 0x4000; + /// If you need to invert a 'read' command bit to a 'write' + const INVERT_COMMAND = 0x2000; + /// Force this message to ignore I2C negative acknowlegements + const IGNORE_NACK = 0x1000; + /// Force message to ignore acknowledgement + const IGNORE_ACK = 0x0800; + /// Allow the client to specify how many bytes it will send + const USE_RECEIVE_LENGTH = 0x0400; + } +} + +impl I2CMessage for LinuxI2CMessage { + fn read(slave_address: u16, data: &mut [u8]) -> LinuxI2CMessage { + Self { + addr: slave_address, + flags: I2CMessageFlags::READ.bits(), + len: data.len() as u16, + buf: data.as_ptr(), + } + } + + fn write(slave_address: u16, data: &[u8]) -> LinuxI2CMessage { + Self { + addr: slave_address, + flags: I2CMessageFlags::empty().bits(), + len: data.len() as u16, + buf: data.as_ptr(), + } + } +} + +impl LinuxI2CMessage { + pub fn with_flags(self, flags: I2CMessageFlags) -> Self { + Self { + addr: self.addr, + flags: flags.bits(), + len: self.len, + buf: self.buf, + } } } From 70965e580415244704ae710fcdf85f24d621d3bd Mon Sep 17 00:00:00 2001 From: Diego Barrios Romero Date: Thu, 7 Nov 2019 21:55:16 +0100 Subject: [PATCH 3/4] Enforce data lifetimes --- src/core.rs | 12 ++++++------ src/linux.rs | 16 ++++++++-------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/core.rs b/src/core.rs index ac242722..f211edb9 100644 --- a/src/core.rs +++ b/src/core.rs @@ -123,21 +123,21 @@ pub trait I2CDevice { /// /// Typical implementations will store state with references to the bus /// in use. The trait is based on the Linux i2cdev interface. -pub trait I2CBus { +pub trait I2CBus<'a> { type Error: Error; - type Message: I2CMessage; + type Message: I2CMessage<'a>; // Performs multiple serially chained I2C read/write transactions. On // success the return code is the number of successfully executed // transactions - fn transfer(&mut self, msgs: &mut [Self::Message]) -> Result; + fn transfer(&mut self, msgs: &'a mut [Self::Message]) -> Result; } /// Read/Write I2C message -pub trait I2CMessage { +pub trait I2CMessage<'a> { /// Read data from device - fn read(slave_address: u16, data: &mut [u8]) -> Self; + fn read(slave_address: u16, data: &'a mut [u8]) -> Self; /// Write data to device - fn write(slave_address: u16, data: &[u8]) -> Self; + fn write(slave_address: u16, data: &'a [u8]) -> Self; } diff --git a/src/linux.rs b/src/linux.rs index ea422034..266c0a7e 100644 --- a/src/linux.rs +++ b/src/linux.rs @@ -251,14 +251,14 @@ impl LinuxI2CBus { } /// Linux I2C message -pub type LinuxI2CMessage = ffi::i2c_msg; +pub type LinuxI2CMessage<'a> = ffi::i2c_msg; -impl I2CBus for LinuxI2CBus { +impl<'a> I2CBus<'a> for LinuxI2CBus { type Error = LinuxI2CError; - type Message = LinuxI2CMessage; + type Message = LinuxI2CMessage<'a>; /// Issue the provided sequence of I2C transactions - fn transfer(&mut self, msgs: &mut [Self::Message]) -> Result { + fn transfer(&mut self, msgs: &'a mut [Self::Message]) -> Result { ffi::i2c_rdwr(self.as_raw_fd(), msgs).map_err(From::from) } } @@ -288,8 +288,8 @@ bitflags! { } } -impl I2CMessage for LinuxI2CMessage { - fn read(slave_address: u16, data: &mut [u8]) -> LinuxI2CMessage { +impl<'a> I2CMessage<'a> for LinuxI2CMessage<'a> { + fn read(slave_address: u16, data: &'a mut [u8]) -> LinuxI2CMessage { Self { addr: slave_address, flags: I2CMessageFlags::READ.bits(), @@ -298,7 +298,7 @@ impl I2CMessage for LinuxI2CMessage { } } - fn write(slave_address: u16, data: &[u8]) -> LinuxI2CMessage { + fn write(slave_address: u16, data: &'a [u8]) -> LinuxI2CMessage { Self { addr: slave_address, flags: I2CMessageFlags::empty().bits(), @@ -308,7 +308,7 @@ impl I2CMessage for LinuxI2CMessage { } } -impl LinuxI2CMessage { +impl<'a> LinuxI2CMessage<'a> { pub fn with_flags(self, flags: I2CMessageFlags) -> Self { Self { addr: self.addr, From 7552959c532cc30007af5ec2b98ba060dad1002a Mon Sep 17 00:00:00 2001 From: Diego Barrios Romero Date: Fri, 8 Nov 2019 07:05:46 +0100 Subject: [PATCH 4/4] Rename trait for further reuse --- examples/pca9956b.rs | 2 +- src/core.rs | 2 +- src/linux.rs | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/pca9956b.rs b/examples/pca9956b.rs index df36d007..6d852afc 100644 --- a/examples/pca9956b.rs +++ b/examples/pca9956b.rs @@ -10,7 +10,7 @@ extern crate i2cdev; extern crate docopt; #[cfg(any(target_os = "linux", target_os = "android"))] -use i2cdev::core::{I2CBus, I2CMessage}; +use i2cdev::core::{I2CTransfer, I2CMessage}; #[cfg(any(target_os = "linux", target_os = "android"))] use i2cdev::linux::{LinuxI2CBus, LinuxI2CMessage}; diff --git a/src/core.rs b/src/core.rs index f211edb9..5716aecf 100644 --- a/src/core.rs +++ b/src/core.rs @@ -123,7 +123,7 @@ pub trait I2CDevice { /// /// Typical implementations will store state with references to the bus /// in use. The trait is based on the Linux i2cdev interface. -pub trait I2CBus<'a> { +pub trait I2CTransfer<'a> { type Error: Error; type Message: I2CMessage<'a>; diff --git a/src/linux.rs b/src/linux.rs index 266c0a7e..77aef5a7 100644 --- a/src/linux.rs +++ b/src/linux.rs @@ -7,7 +7,7 @@ // except according to those terms. use ffi; -use core::{I2CDevice, I2CBus}; +use core::{I2CDevice, I2CTransfer}; use std::error::Error; use std::path::Path; use std::fs::File; @@ -253,7 +253,7 @@ impl LinuxI2CBus { /// Linux I2C message pub type LinuxI2CMessage<'a> = ffi::i2c_msg; -impl<'a> I2CBus<'a> for LinuxI2CBus { +impl<'a> I2CTransfer<'a> for LinuxI2CBus { type Error = LinuxI2CError; type Message = LinuxI2CMessage<'a>;