Skip to content

Commit c79024c

Browse files
committed
Rework i2cbus implementation
1 parent e7d2b75 commit c79024c

File tree

5 files changed

+125
-144
lines changed

5 files changed

+125
-144
lines changed

examples/pca9956b.rs

Lines changed: 13 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@ extern crate i2cdev;
1010
extern crate docopt;
1111

1212
#[cfg(any(target_os = "linux", target_os = "android"))]
13-
use i2cdev::linux::{LinuxI2CBus, I2CMsg};
13+
use i2cdev::core::{I2CBus, I2CMessage};
1414
#[cfg(any(target_os = "linux", target_os = "android"))]
15-
use i2cdev::core::I2CBus;
15+
use i2cdev::linux::{LinuxI2CBus, LinuxI2CMessage};
1616

1717
use std::env::args;
1818
use docopt::Docopt;
@@ -44,7 +44,7 @@ fn main() {
4444
Ok(bus) => bus,
4545
Err(_e) => {
4646
println!("Error opening I2C Bus {} {}", path, _e);
47-
return
47+
return;
4848
}
4949
};
5050
println!("Opened I2C Bus OK: {}", path);
@@ -53,30 +53,26 @@ fn main() {
5353
// 1) Write the MODE1 register address, with top bit indcating auto-
5454
// increment should be enabled
5555
// 2) Read 10 bytes from the current register onwards
56-
let mut dataw: Vec<u8> = vec![0b1000_0000];
57-
let mut data: Vec<u8> = vec![0; 10];
58-
let mut msgs: Vec<I2CMsg> = Vec::new();
59-
msgs.push(I2CMsg::new(ADDR, &mut dataw));
60-
msgs.push(I2CMsg::new(ADDR, &mut data));
61-
msgs[1].set_read();
56+
let mut data = [0; 10];
57+
let mut msgs = [
58+
LinuxI2CMessage::write(ADDR, &[0b1000_0000]),
59+
LinuxI2CMessage::read(ADDR, &mut data),
60+
];
6261

6362
// Send the messages to the kernel to process
64-
match bus.rdwr(&mut msgs) {
65-
Ok(rc) => {
66-
println!("Successful RDWR call: {} messages processed", rc)
67-
},
63+
match bus.transfer(&mut msgs) {
64+
Ok(rc) => println!("Successful transfer call: {} messages processed", rc),
6865
Err(_e) => {
6966
println!("Error reading/writing {}", _e);
70-
return
71-
},
67+
return;
68+
}
7269
}
7370

7471
// Print the data read from the device. A recently reset PCA9956B should
7572
// return:
7673
// 0x8005000000000000ff00
7774
let mut output = "Result: 0x".to_string();
78-
let data = msgs[1].data();
79-
for byte in data {
75+
for byte in &data {
8076
output = format!("{}{:02x}", output, byte);
8177
}
8278
println!("{}", output);

src/core.rs

Lines changed: 8 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -125,89 +125,19 @@ pub trait I2CDevice {
125125
/// in use. The trait is based on the Linux i2cdev interface.
126126
pub trait I2CBus {
127127
type Error: Error;
128+
type Message: I2CMessage;
128129

129130
// Performs multiple serially chained I2C read/write transactions. On
130131
// success the return code is the number of successfully executed
131132
// transactions
132-
fn rdwr<'a>(&mut self, msgs: &mut Vec<I2CMsg<'a>>) -> Result<i32, Self::Error>;
133+
fn transfer(&mut self, msgs: &mut [Self::Message]) -> Result<u32, Self::Error>;
133134
}
134135

135-
bitflags! {
136-
pub struct I2CMsgFlags: u16 {
137-
/// this is a ten bit chip address
138-
const I2C_M_TEN = 0x0010;
139-
/// read data, from slave to master
140-
const I2C_M_RD = 0x0001;
141-
/// if I2C_FUNC_PROTOCOL_MANGLING
142-
const I2C_M_STOP = 0x8000;
143-
/// if I2C_FUNC_NOSTART
144-
const I2C_M_NOSTART = 0x4000;
145-
/// if I2C_FUNC_PROTOCOL_MANGLING
146-
const I2C_M_REV_DIR_ADDR = 0x2000;
147-
/// if I2C_FUNC_PROTOCOL_MANGLING
148-
const I2C_M_IGNORE_NAK = 0x1000;
149-
/// if I2C_FUNC_PROTOCOL_MANGLING
150-
const I2C_M_NO_RD_ACK = 0x0800;
151-
/// length will be first received byte
152-
const I2C_M_RECV_LEN = 0x0400;
153-
}
154-
}
155-
156-
/// Rust version of i2c_msg
157-
pub struct I2CMsg<'a> {
158-
/// slave address
159-
pub(crate) addr: u16,
160-
/// serialized I2CMsgFlags
161-
pub(crate) flags: u16,
162-
163-
/// msg length comes from msg Vector length
164-
165-
/// msg data to be sent/received
166-
pub(crate) data: &'a mut Vec<u8>,
167-
}
136+
/// Read/Write I2C message
137+
pub trait I2CMessage {
138+
/// Read data from device
139+
fn read(slave_address: u16, data: &mut [u8]) -> Self;
168140

169-
impl<'a> I2CMsg<'a> {
170-
/// Create I2CMsg from address and data buffer
171-
pub fn new(addr: u16, data: &'a mut Vec<u8>) -> Self {
172-
I2CMsg {
173-
addr,
174-
flags: 0,
175-
data
176-
}
177-
}
178-
179-
/// Set flags
180-
pub fn set_flags(&mut self, flags: u16) {
181-
self.flags = flags;
182-
}
183-
184-
/// Get flags
185-
pub fn flags(&mut self) -> u16 {
186-
self.flags
187-
}
188-
189-
/// Set addr
190-
pub fn set_addr(&mut self, addr: u16) {
191-
self.addr = addr;
192-
}
193-
194-
/// Get addr
195-
pub fn addr(&mut self) -> u16 {
196-
self.addr
197-
}
198-
199-
/// Set data
200-
pub fn set_data(&mut self, data: &'a mut Vec<u8>) {
201-
self.data = data;
202-
}
203-
204-
/// Get addr
205-
pub fn data(&mut self) -> Vec<u8> {
206-
self.data.clone()
207-
}
208-
209-
/// Sets the read flag
210-
pub fn set_read(&mut self) {
211-
self.flags |= I2CMsgFlags::I2C_M_RD.bits;
212-
}
141+
/// Write data to device
142+
fn write(slave_address: u16, data: &[u8]) -> Self;
213143
}

src/ffi.rs

Lines changed: 14 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -16,38 +16,21 @@ use std::io::Cursor;
1616
use std::os::unix::prelude::*;
1717
use std::marker::PhantomData;
1818
use byteorder::{NativeEndian, ReadBytesExt, WriteBytesExt};
19-
use core::{I2CMsg};
2019
use libc::c_int;
2120

2221
pub type I2CError = nix::Error;
2322

2423
#[repr(C)]
25-
#[derive(Debug)]
26-
/// C version of i2c_msg structure
27-
// See linux/i2c.h
28-
struct i2c_msg<'a> {
24+
pub struct i2c_msg {
2925
/// slave address
30-
addr: u16,
26+
pub(crate) addr: u16,
3127
/// serialized I2CMsgFlags
32-
flags: u16,
28+
pub(crate) flags: u16,
3329
/// msg length
34-
len: u16,
30+
pub(crate) len: u16,
3531
/// pointer to msg data
36-
buf: *mut u8,
37-
_phantom: PhantomData<&'a mut u8>,
38-
}
39-
40-
impl<'a, 'b> From<&'b mut I2CMsg<'a>> for i2c_msg<'a> {
41-
fn from(msg: &mut I2CMsg) -> Self {
42-
i2c_msg {
43-
addr: msg.addr,
44-
flags: msg.flags,
45-
len: msg.data.len() as u16,
46-
buf: msg.data.as_mut_ptr(),
47-
_phantom: PhantomData
32+
pub(crate) buf: *const u8,
4833
}
49-
}
50-
}
5134

5235
bitflags! {
5336
struct I2CFunctions: u32 {
@@ -163,9 +146,9 @@ pub struct i2c_smbus_ioctl_data {
163146
/// This is the structure as used in the I2C_RDWR ioctl call
164147
// see linux/i2c-dev.h
165148
#[repr(C)]
166-
pub struct i2c_rdwr_ioctl_data<'a> {
149+
pub struct i2c_rdwr_ioctl_data {
167150
// struct i2c_msg __user *msgs;
168-
msgs: *mut i2c_msg<'a>,
151+
msgs: *mut i2c_msg,
169152
// __u32 nmsgs;
170153
nmsgs: u32,
171154
}
@@ -430,30 +413,15 @@ pub fn i2c_smbus_process_call_block(fd: RawFd, register: u8, values: &[u8]) -> R
430413
Ok((&data.block[1..(count + 1) as usize]).to_vec())
431414
}
432415

433-
// Returns the number of messages succesfully processed
434-
unsafe fn i2c_rdwr_access(fd: RawFd,
435-
msgs: *mut i2c_msg,
436-
nmsgs: usize)
437-
-> Result<(c_int), I2CError> {
438-
let args = i2c_rdwr_ioctl_data {
439-
msgs,
440-
nmsgs: nmsgs as u32,
416+
#[inline]
417+
pub fn i2c_rdwr(fd: RawFd, values: &mut [i2c_msg]) -> Result<u32, I2CError> {
418+
let i2c_data = i2c_rdwr_ioctl_data {
419+
msgs: values.as_mut_ptr(),
420+
nmsgs: values.len() as u32,
441421
};
442422

443-
ioctl::i2c_rdwr(fd, &args)
444-
}
445-
446-
pub fn i2c_rdwr_read_write(fd: RawFd,
447-
msgs: &mut Vec<I2CMsg>) -> Result<(i32), I2CError> {
448-
// Building the msgs to push is safe.
449-
// We have to use iter_mut as buf needs to be mutable.
450-
let mut cmsgs = msgs.iter_mut().map(<i2c_msg>::from).collect::<Vec<_>>();
451-
452-
// But calling the ioctl is definitely not!
453423
unsafe {
454-
i2c_rdwr_access(fd,
455-
cmsgs.as_mut_ptr(),
456-
cmsgs.len())
424+
let n = ioctl::i2c_rdwr(fd, &i2c_data)?;
425+
Ok(n as u32)
457426
}
458427
}
459-

src/lib.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,33 @@
4141
//! }
4242
//! }
4343
//! ```
44+
//!
45+
//! ```rust,no_run
46+
//! extern crate i2cdev;
47+
//!
48+
//! use std::thread;
49+
//! use std::time::Duration;
50+
//!
51+
//! use i2cdev::core::*;
52+
//! use i2cdev::linux::{LinuxI2CDevice, LinuxI2CError, LinuxI2CMessage};
53+
//!
54+
//! const SLAVE_ADDR: u16 = 0x57;
55+
//!
56+
//! fn write_read_transaction() -> Result<(), LinuxI2CError> {
57+
//! let mut dev = LinuxI2CBus::new("/dev/i2c-1")?;
58+
//!
59+
//! let mut read_data = [0; 2];
60+
//! let mut msgs = [
61+
//! LinuxI2CMessage::write(SLAVE_ADDR, &[0x01]),
62+
//! LinuxI2CMessage::read(SLAVE_ADDR, &mut read_data)
63+
//! ];
64+
//! dev.transfer(&mut msgs)?;
65+
//! thread::sleep(Duration::from_millis(100));
66+
//!
67+
//! println!("Reading: {:?}", read_data);
68+
//! Ok(())
69+
//! }
70+
//! ```
4471
4572
#![crate_name = "i2cdev"]
4673
#![crate_type = "lib"]

src/linux.rs

Lines changed: 63 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ use std::io::prelude::*;
1919
use std::os::unix::prelude::*;
2020

2121
// Expose these core structs from this module
22-
pub use core::{I2CMsgFlags, I2CMsg};
22+
pub use core::I2CMessage;
2323

2424
pub struct LinuxI2CDevice {
2525
devfile: File,
@@ -250,11 +250,71 @@ impl LinuxI2CBus {
250250
}
251251
}
252252

253+
/// Linux I2C message
254+
pub type LinuxI2CMessage = ffi::i2c_msg;
255+
253256
impl I2CBus for LinuxI2CBus {
254257
type Error = LinuxI2CError;
258+
type Message = LinuxI2CMessage;
255259

256260
/// Issue the provided sequence of I2C transactions
257-
fn rdwr(&mut self, msgs: &mut Vec<I2CMsg>) -> Result<i32, LinuxI2CError> {
258-
ffi::i2c_rdwr_read_write(self.as_raw_fd(), msgs).map_err(From::from)
261+
fn transfer(&mut self, msgs: &mut [Self::Message]) -> Result<u32, LinuxI2CError> {
262+
ffi::i2c_rdwr(self.as_raw_fd(), msgs).map_err(From::from)
263+
}
264+
}
265+
266+
bitflags! {
267+
/// Various flags used by the i2c_rdwr ioctl on Linux. For details, see
268+
/// https://www.kernel.org/doc/Documentation/i2c/i2c-protocol
269+
///
270+
/// In general, these are for special cases and should not be needed
271+
pub struct I2CMessageFlags: u16 {
272+
/// Use ten bit addressing on this message
273+
const TEN_BIT_ADDRESS = 0x0010;
274+
/// Read data, from slave to master
275+
const READ = 0x0001;
276+
/// Force an I2C stop condition on this message
277+
const STOP = 0x8000;
278+
/// Avoid sending an I2C start condition on this message
279+
const NO_START = 0x4000;
280+
/// If you need to invert a 'read' command bit to a 'write'
281+
const INVERT_COMMAND = 0x2000;
282+
/// Force this message to ignore I2C negative acknowlegements
283+
const IGNORE_NACK = 0x1000;
284+
/// Force message to ignore acknowledgement
285+
const IGNORE_ACK = 0x0800;
286+
/// Allow the client to specify how many bytes it will send
287+
const USE_RECEIVE_LENGTH = 0x0400;
288+
}
289+
}
290+
291+
impl I2CMessage for LinuxI2CMessage {
292+
fn read(slave_address: u16, data: &mut [u8]) -> LinuxI2CMessage {
293+
Self {
294+
addr: slave_address,
295+
flags: I2CMessageFlags::READ.bits(),
296+
len: data.len() as u16,
297+
buf: data.as_ptr(),
298+
}
299+
}
300+
301+
fn write(slave_address: u16, data: &[u8]) -> LinuxI2CMessage {
302+
Self {
303+
addr: slave_address,
304+
flags: I2CMessageFlags::empty().bits(),
305+
len: data.len() as u16,
306+
buf: data.as_ptr(),
307+
}
308+
}
309+
}
310+
311+
impl LinuxI2CMessage {
312+
pub fn with_flags(self, flags: I2CMessageFlags) -> Self {
313+
Self {
314+
addr: self.addr,
315+
flags: flags.bits(),
316+
len: self.len,
317+
buf: self.buf,
318+
}
259319
}
260320
}

0 commit comments

Comments
 (0)