Skip to content

Commit 28c852d

Browse files
authored
Merge pull request #51 from eldruin/rdwr-ioctl-2
Transactional read/write via I2C_RDWR ioctl - take 2
2 parents c2611e2 + 3693a09 commit 28c852d

File tree

6 files changed

+354
-33
lines changed

6 files changed

+354
-33
lines changed

examples/pca9956b.rs

+79
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
// Copyright 2018, Piers Finlayson <[email protected]>
2+
//
3+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4+
// http://www.apache.org/license/LICENSE-2.0> or the MIT license
5+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
6+
// option. This file may not be copied, modified, or distributed
7+
// except according to those terms.extern crate i2cdev;
8+
9+
extern crate i2cdev;
10+
extern crate docopt;
11+
12+
#[cfg(any(target_os = "linux", target_os = "android"))]
13+
use i2cdev::core::{I2CTransfer, I2CMessage};
14+
#[cfg(any(target_os = "linux", target_os = "android"))]
15+
use i2cdev::linux::{LinuxI2CBus, LinuxI2CMessage};
16+
17+
use std::env::args;
18+
use docopt::Docopt;
19+
20+
const USAGE: &str = "
21+
Reads registers from a PCA9956B IC via Linux i2cdev.
22+
23+
Assumes the PCA9956B is using address 0x20.
24+
25+
Usage:
26+
pca9956b <device>
27+
pca9956b (-h | --help)
28+
pca9956b --version
29+
30+
Options:
31+
-h --help Show this help text.
32+
--version Show version.
33+
";
34+
35+
const ADDR: u16 = 0x20;
36+
37+
#[cfg(any(target_os = "linux", target_os = "android"))]
38+
fn main() {
39+
let args = Docopt::new(USAGE)
40+
.and_then(|d| d.argv(args()).parse())
41+
.unwrap_or_else(|e| e.exit());
42+
let path = args.get_str("<device>");
43+
let mut bus = match LinuxI2CBus::new(path) {
44+
Ok(bus) => bus,
45+
Err(_e) => {
46+
println!("Error opening I2C Bus {} {}", path, _e);
47+
return;
48+
}
49+
};
50+
println!("Opened I2C Bus OK: {}", path);
51+
52+
// Build two I2C messages:
53+
// 1) Write the MODE1 register address, with top bit indcating auto-
54+
// increment should be enabled
55+
// 2) Read 10 bytes from the current register onwards
56+
let mut data = [0; 10];
57+
let mut msgs = [
58+
LinuxI2CMessage::write(&[0b1000_0000]).with_address(ADDR),
59+
LinuxI2CMessage::read(&mut data).with_address(ADDR),
60+
];
61+
62+
// Send the messages to the kernel to process
63+
match bus.transfer(&mut msgs) {
64+
Ok(rc) => println!("Successful transfer call: {} messages processed", rc),
65+
Err(_e) => {
66+
println!("Error reading/writing {}", _e);
67+
return;
68+
}
69+
}
70+
71+
// Print the data read from the device. A recently reset PCA9956B should
72+
// return:
73+
// 0x8005000000000000ff00
74+
let mut output = "Result: 0x".to_string();
75+
for byte in &data {
76+
output = format!("{}{:02x}", output, byte);
77+
}
78+
println!("{}", output);
79+
}

src/core.rs

+30
Original file line numberDiff line numberDiff line change
@@ -111,3 +111,33 @@ pub trait I2CDevice {
111111
/// 1 to 31 bytes of data from it.
112112
fn smbus_process_block(&mut self, register: u8, values: &[u8]) -> Result<Vec<u8>, Self::Error>;
113113
}
114+
115+
/// Interface to an I2C Bus from an I2C Master
116+
///
117+
/// This is used when the client wants to interact directly with the bus
118+
/// without specifying an I2C slave address up-front, either because it needs
119+
/// to communicate with multiple addresses without creatings separate
120+
/// I2CDevice objects, or because it wants to make used of the I2C_RDWR ioctl
121+
/// which allows the client to send and transmit multiple sets I2C data in a
122+
/// single operation, potentially to different I2C slave addresses.
123+
///
124+
/// Typical implementations will store state with references to the bus
125+
/// in use. The trait is based on the Linux i2cdev interface.
126+
pub trait I2CTransfer<'a> {
127+
type Error: Error;
128+
type Message: I2CMessage<'a>;
129+
130+
// Performs multiple serially chained I2C read/write transactions. On
131+
// success the return code is the number of successfully executed
132+
// transactions
133+
fn transfer(&mut self, msgs: &'a mut [Self::Message]) -> Result<u32, Self::Error>;
134+
}
135+
136+
/// Read/Write I2C message
137+
pub trait I2CMessage<'a> {
138+
/// Read data from device
139+
fn read(data: &'a mut [u8]) -> Self;
140+
141+
/// Write data to device
142+
fn write(data: &'a [u8]) -> Self;
143+
}

src/ffi.rs

+26-29
Original file line numberDiff line numberDiff line change
@@ -14,41 +14,22 @@ use std::mem;
1414
use std::ptr;
1515
use std::io::Cursor;
1616
use std::os::unix::prelude::*;
17+
use std::marker::PhantomData;
1718
use byteorder::{NativeEndian, ReadBytesExt, WriteBytesExt};
19+
use libc::c_int;
1820

1921
pub type I2CError = nix::Error;
2022

21-
bitflags! {
22-
struct I2CMsgFlags: u16 {
23-
/// this is a ten bit chip address
24-
const I2C_M_TEN = 0x0010;
25-
/// read data, from slave to master
26-
const I2C_M_RD = 0x0001;
27-
/// if I2C_FUNC_PROTOCOL_MANGLING
28-
const I2C_M_STOP = 0x8000;
29-
/// if I2C_FUNC_NOSTART
30-
const I2C_M_NOSTART = 0x4000;
31-
/// if I2C_FUNC_PROTOCOL_MANGLING
32-
const I2C_M_REV_DIR_ADDR = 0x2000;
33-
/// if I2C_FUNC_PROTOCOL_MANGLING
34-
const I2C_M_IGNORE_NAK = 0x1000;
35-
/// if I2C_FUNC_PROTOCOL_MANGLING
36-
const I2C_M_NO_RD_ACK = 0x0800;
37-
/// length will be first received byte
38-
const I2C_M_RECV_LEN = 0x0400;
39-
}
40-
}
41-
4223
#[repr(C)]
43-
struct i2c_msg {
24+
pub struct i2c_msg {
4425
/// slave address
45-
addr: u16,
26+
pub(crate) addr: u16,
4627
/// serialized I2CMsgFlags
47-
flags: u16,
28+
pub(crate) flags: u16,
4829
/// msg length
49-
len: u16,
30+
pub(crate) len: u16,
5031
/// pointer to msg data
51-
buf: *mut u8,
32+
pub(crate) buf: *const u8,
5233
}
5334

5435
bitflags! {
@@ -163,23 +144,25 @@ pub struct i2c_smbus_ioctl_data {
163144
}
164145

165146
/// This is the structure as used in the I2C_RDWR ioctl call
147+
// see linux/i2c-dev.h
166148
#[repr(C)]
167-
struct i2c_rdwr_ioctl_data {
149+
pub struct i2c_rdwr_ioctl_data {
168150
// struct i2c_msg __user *msgs;
169151
msgs: *mut i2c_msg,
170152
// __u32 nmsgs;
171153
nmsgs: u32,
172154
}
173155

174156
mod ioctl {
175-
use super::{I2C_SLAVE, I2C_SMBUS};
157+
use super::{I2C_SLAVE, I2C_SMBUS, I2C_RDWR};
176158
pub use super::i2c_smbus_ioctl_data;
159+
pub use super::i2c_rdwr_ioctl_data;
177160

178161
ioctl_write_int_bad!(set_i2c_slave_address, I2C_SLAVE);
179162
ioctl_write_ptr_bad!(i2c_smbus, I2C_SMBUS, i2c_smbus_ioctl_data);
163+
ioctl_write_ptr_bad!(i2c_rdwr, I2C_RDWR, i2c_rdwr_ioctl_data);
180164
}
181165

182-
183166
pub fn i2c_set_slave_address(fd: RawFd, slave_address: u16) -> Result<(), nix::Error> {
184167
unsafe {
185168
ioctl::set_i2c_slave_address(fd, slave_address as i32)?;
@@ -429,3 +412,17 @@ pub fn i2c_smbus_process_call_block(fd: RawFd, register: u8, values: &[u8]) -> R
429412
let count = data.block[0];
430413
Ok((&data.block[1..(count + 1) as usize]).to_vec())
431414
}
415+
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,
421+
};
422+
423+
let n;
424+
unsafe {
425+
n = ioctl::i2c_rdwr(fd, &i2c_data)?;
426+
}
427+
Ok(n as u32)
428+
}

src/lib.rs

+54-2
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,15 @@
1414
//! https://www.kernel.org/doc/Documentation/i2c/dev-interface
1515
//! ```rust,no_run
1616
//! extern crate i2cdev;
17-
//!
17+
//!
1818
//! use std::thread;
1919
//! use std::time::Duration;
2020
//!
2121
//! use i2cdev::core::*;
2222
//! use i2cdev::linux::{LinuxI2CDevice, LinuxI2CError};
2323
//!
2424
//! const NUNCHUCK_SLAVE_ADDR: u16 = 0x52;
25-
//!
25+
//!
2626
//! // real code should probably not use unwrap()
2727
//! fn i2cfun() -> Result<(), LinuxI2CError> {
2828
//! let mut dev = LinuxI2CDevice::new("/dev/i2c-1", NUNCHUCK_SLAVE_ADDR)?;
@@ -41,6 +41,58 @@
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 = LinuxI2CDevice::new("/dev/i2c-1", SLAVE_ADDR)?;
58+
//!
59+
//! let mut read_data = [0; 2];
60+
//! let mut msgs = [
61+
//! LinuxI2CMessage::write(&[0x01]),
62+
//! LinuxI2CMessage::read(&mut read_data)
63+
//! ];
64+
//! dev.transfer(&mut msgs)?;
65+
//!
66+
//! println!("Reading: {:?}", read_data);
67+
//! Ok(())
68+
//! }
69+
//! ```
70+
//!
71+
//! ```rust,no_run
72+
//! extern crate i2cdev;
73+
//!
74+
//! use std::thread;
75+
//! use std::time::Duration;
76+
//!
77+
//! use i2cdev::core::*;
78+
//! use i2cdev::linux::{LinuxI2CBus, LinuxI2CError, LinuxI2CMessage};
79+
//!
80+
//! const SLAVE_ADDR: u16 = 0x57;
81+
//!
82+
//! fn write_read_transaction_using_bus() -> Result<(), LinuxI2CError> {
83+
//! let mut dev = LinuxI2CBus::new("/dev/i2c-1")?;
84+
//!
85+
//! let mut read_data = [0; 2];
86+
//! let mut msgs = [
87+
//! LinuxI2CMessage::write(&[0x01]).with_address(SLAVE_ADDR),
88+
//! LinuxI2CMessage::read(&mut read_data).with_address(SLAVE_ADDR)
89+
//! ];
90+
//! dev.transfer(&mut msgs)?;
91+
//!
92+
//! println!("Reading: {:?}", read_data);
93+
//! Ok(())
94+
//! }
95+
//! ```
4496
4597
#![crate_name = "i2cdev"]
4698
#![crate_type = "lib"]

0 commit comments

Comments
 (0)