#![doc(html_root_url = "https://docs.rs/i2c-linux-sys/0.2.1/")]
#[macro_use]
extern crate bitflags;
extern crate byteorder;
extern crate libc;
use std::{fmt, io, ptr, mem, cmp};
use std::os::unix::io::RawFd;
use byteorder::{NativeEndian, ByteOrder};
use libc::c_int;
bitflags! {
pub struct Flags: u16 {
const RD = I2C_M_RD;
const TEN = I2C_M_TEN;
const RECV_LEN = I2C_M_RECV_LEN;
const NO_RD_ACK = I2C_M_NO_RD_ACK;
const IGNORE_NACK = I2C_M_IGNORE_NAK;
const REV_DIR_ADDR = I2C_M_REV_DIR_ADDR;
const NO_START = I2C_M_NOSTART;
const STOP = I2C_M_STOP;
}
}
pub const I2C_M_RD: u16 = 0x0001;
pub const I2C_M_TEN: u16 = 0x0010;
pub const I2C_M_RECV_LEN: u16 = 0x0400;
pub const I2C_M_NO_RD_ACK: u16 = 0x0800;
pub const I2C_M_IGNORE_NAK: u16 = 0x1000;
pub const I2C_M_REV_DIR_ADDR: u16 = 0x2000;
pub const I2C_M_NOSTART: u16 = 0x4000;
pub const I2C_M_STOP: u16 = 0x8000;
bitflags! {
pub struct Functionality: u32 {
const I2C = I2C_FUNC_I2C;
const TENBIT_ADDR = I2C_FUNC_10BIT_ADDR;
const PROTOCOL_MANGLING = I2C_FUNC_PROTOCOL_MANGLING;
const SMBUS_PEC = I2C_FUNC_SMBUS_PEC;
const NO_START = I2C_FUNC_NOSTART;
const SLAVE = I2C_FUNC_SLAVE;
const SMBUS_BLOCK_PROC_CALL = I2C_FUNC_SMBUS_BLOCK_PROC_CALL;
const SMBUS_QUICK = I2C_FUNC_SMBUS_QUICK;
const SMBUS_READ_BYTE = I2C_FUNC_SMBUS_READ_BYTE;
const SMBUS_WRITE_BYTE = I2C_FUNC_SMBUS_WRITE_BYTE;
const SMBUS_READ_BYTE_DATA = I2C_FUNC_SMBUS_READ_BYTE_DATA;
const SMBUS_WRITE_BYTE_DATA = I2C_FUNC_SMBUS_WRITE_BYTE_DATA;
const SMBUS_READ_WORD_DATA = I2C_FUNC_SMBUS_READ_WORD_DATA;
const SMBUS_WRITE_WORD_DATA = I2C_FUNC_SMBUS_WRITE_WORD_DATA;
const SMBUS_PROC_CALL = I2C_FUNC_SMBUS_PROC_CALL;
const SMBUS_READ_BLOCK_DATA = I2C_FUNC_SMBUS_READ_BLOCK_DATA;
const SMBUS_WRITE_BLOCK_DATA = I2C_FUNC_SMBUS_WRITE_BLOCK_DATA;
const SMBUS_READ_I2C_BLOCK = I2C_FUNC_SMBUS_READ_I2C_BLOCK;
const SMBUS_WRITE_I2C_BLOCK = I2C_FUNC_SMBUS_WRITE_I2C_BLOCK;
const SMBUS_HOST_NOTIFY = I2C_FUNC_SMBUS_HOST_NOTIFY;
const SMBUS_BYTE = I2C_FUNC_SMBUS_BYTE;
const SMBUS_BYTE_DATA = I2C_FUNC_SMBUS_BYTE_DATA;
const SMBUS_WORD_DATA = I2C_FUNC_SMBUS_WORD_DATA;
const SMBUS_BLOCK_DATA = I2C_FUNC_SMBUS_BLOCK_DATA;
const SMBUS_I2C_BLOCK = I2C_FUNC_SMBUS_I2C_BLOCK;
const SMBUS_EMUL = I2C_FUNC_SMBUS_EMUL;
}
}
pub const I2C_FUNC_I2C: u32 = 0x00000001;
pub const I2C_FUNC_10BIT_ADDR: u32 = 0x00000002;
pub const I2C_FUNC_PROTOCOL_MANGLING: u32 = 0x00000004;
pub const I2C_FUNC_SMBUS_PEC: u32 = 0x00000008;
pub const I2C_FUNC_NOSTART: u32 = 0x00000010;
pub const I2C_FUNC_SLAVE: u32 = 0x00000020;
pub const I2C_FUNC_SMBUS_BLOCK_PROC_CALL: u32 = 0x00008000;
pub const I2C_FUNC_SMBUS_QUICK: u32 = 0x00010000;
pub const I2C_FUNC_SMBUS_READ_BYTE: u32 = 0x00020000;
pub const I2C_FUNC_SMBUS_WRITE_BYTE: u32 = 0x00040000;
pub const I2C_FUNC_SMBUS_READ_BYTE_DATA: u32 = 0x00080000;
pub const I2C_FUNC_SMBUS_WRITE_BYTE_DATA: u32 = 0x00100000;
pub const I2C_FUNC_SMBUS_READ_WORD_DATA: u32 = 0x00200000;
pub const I2C_FUNC_SMBUS_WRITE_WORD_DATA: u32 = 0x00400000;
pub const I2C_FUNC_SMBUS_PROC_CALL: u32 = 0x00800000;
pub const I2C_FUNC_SMBUS_READ_BLOCK_DATA: u32 = 0x01000000;
pub const I2C_FUNC_SMBUS_WRITE_BLOCK_DATA: u32 = 0x02000000;
pub const I2C_FUNC_SMBUS_READ_I2C_BLOCK: u32 = 0x04000000;
pub const I2C_FUNC_SMBUS_WRITE_I2C_BLOCK: u32 = 0x08000000;
pub const I2C_FUNC_SMBUS_HOST_NOTIFY: u32 = 0x10000000;
pub const I2C_FUNC_SMBUS_BYTE: u32 = I2C_FUNC_SMBUS_READ_BYTE | I2C_FUNC_SMBUS_WRITE_BYTE;
pub const I2C_FUNC_SMBUS_BYTE_DATA: u32 = I2C_FUNC_SMBUS_READ_BYTE_DATA | I2C_FUNC_SMBUS_WRITE_BYTE_DATA;
pub const I2C_FUNC_SMBUS_WORD_DATA: u32 = I2C_FUNC_SMBUS_READ_WORD_DATA | I2C_FUNC_SMBUS_WRITE_WORD_DATA;
pub const I2C_FUNC_SMBUS_BLOCK_DATA: u32 = I2C_FUNC_SMBUS_READ_BLOCK_DATA | I2C_FUNC_SMBUS_WRITE_BLOCK_DATA;
pub const I2C_FUNC_SMBUS_I2C_BLOCK: u32 = I2C_FUNC_SMBUS_READ_I2C_BLOCK | I2C_FUNC_SMBUS_WRITE_I2C_BLOCK;
pub const I2C_FUNC_SMBUS_EMUL: u32 = I2C_FUNC_SMBUS_QUICK |
I2C_FUNC_SMBUS_BYTE | I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA |
I2C_FUNC_SMBUS_PROC_CALL |
I2C_FUNC_SMBUS_WRITE_BLOCK_DATA | I2C_FUNC_SMBUS_I2C_BLOCK |
I2C_FUNC_SMBUS_PEC;
#[derive(Copy, Clone, Debug, PartialOrd, Ord, PartialEq, Eq, Hash)]
#[repr(u8)]
pub enum SmbusReadWrite {
Read = 1,
Write = 0,
}
pub const I2C_SMBUS_READ: SmbusReadWrite = SmbusReadWrite::Read;
pub const I2C_SMBUS_WRITE: SmbusReadWrite = SmbusReadWrite::Write;
#[derive(Copy, Clone, Debug, PartialOrd, Ord, PartialEq, Eq, Hash)]
#[repr(u32)]
pub enum SmbusTransaction {
Quick = I2C_SMBUS_QUICK,
Byte = I2C_SMBUS_BYTE,
ByteData = I2C_SMBUS_BYTE_DATA,
WordData = I2C_SMBUS_WORD_DATA,
ProcCall = I2C_SMBUS_PROC_CALL,
BlockData = I2C_SMBUS_BLOCK_DATA,
I2cBlockBroken = I2C_SMBUS_I2C_BLOCK_BROKEN,
BlockProcCall = I2C_SMBUS_BLOCK_PROC_CALL,
I2cBlockData = I2C_SMBUS_I2C_BLOCK_DATA,
}
pub const I2C_SMBUS_QUICK: u32 = 0;
pub const I2C_SMBUS_BYTE: u32 = 1;
pub const I2C_SMBUS_BYTE_DATA: u32 = 2;
pub const I2C_SMBUS_WORD_DATA: u32 = 3;
pub const I2C_SMBUS_PROC_CALL: u32 = 4;
pub const I2C_SMBUS_BLOCK_DATA: u32 = 5;
pub const I2C_SMBUS_I2C_BLOCK_BROKEN: u32 = 6;
pub const I2C_SMBUS_BLOCK_PROC_CALL: u32 = 7;
pub const I2C_SMBUS_I2C_BLOCK_DATA: u32 = 8;
pub const I2C_SMBUS_BLOCK_MAX: usize = 32;
#[repr(C)]
#[derive(Copy, Clone, Debug)]
pub struct i2c_msg {
pub addr: u16,
pub flags: Flags,
pub len: u16,
pub buf: *mut u8,
}
#[repr(C)]
#[derive(Copy, Clone, Debug)]
pub struct i2c_rdwr_ioctl_data {
pub msgs: *mut i2c_msg,
pub nmsgs: c_int,
}
#[repr(C)]
#[derive(Copy, Clone)]
pub struct i2c_smbus_data {
_alignment: [u16; 0],
pub block: [u8; I2C_SMBUS_BLOCK_MAX + 2],
}
impl i2c_smbus_data {
pub fn from_byte(v: u8) -> Self {
let mut data = Self::default();
data.set_byte(v);
data
}
pub fn from_word(v: u16) -> Self {
let mut data = Self::default();
data.set_word(v);
data
}
pub fn from_block(v: &[u8]) -> Self {
let mut data = Self::default();
data.set_block(v);
data
}
pub fn byte(&self) -> u8 {
self.block[0]
}
pub fn set_byte(&mut self, v: u8) {
self.block[0] = v;
}
pub fn word(&self) -> u16 {
NativeEndian::read_u16(&self.block[..2])
}
pub fn set_word(&mut self, v: u16) {
NativeEndian::write_u16(&mut self.block[..2], v)
}
pub fn block(&self) -> Option<&[u8]> {
let len = self.block[0] as usize;
if len <= I2C_SMBUS_BLOCK_MAX {
Some(&self.block[1..1 + len])
} else {
None
}
}
pub fn block_mut(&mut self) -> Option<&mut [u8]> {
let len = self.block[0] as usize;
if len <= I2C_SMBUS_BLOCK_MAX {
Some(&mut self.block[1..1 + len])
} else {
None
}
}
pub fn set_block(&mut self, v: &[u8]) {
assert!(v.len() <= I2C_SMBUS_BLOCK_MAX);
self.block[0] = v.len() as u8;
self.block[1..1 + v.len()].copy_from_slice(v);
}
}
impl fmt::Debug for i2c_smbus_data {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("i2c_smbus_data")
.field("byte", &self.byte())
.field("word", &self.word())
.field("block", &self.block())
.finish()
}
}
impl Default for i2c_smbus_data {
fn default() -> Self {
unsafe { mem::zeroed() }
}
}
#[repr(C)]
#[derive(Copy, Clone, Debug)]
pub struct i2c_smbus_ioctl_data {
pub read_write: SmbusReadWrite,
pub command: u8,
pub size: SmbusTransaction,
pub data: *mut i2c_smbus_data,
}
pub const I2C_RETRIES: u16 = 0x0701;
pub const I2C_TIMEOUT: u16 = 0x0702;
pub const I2C_SLAVE: u16 = 0x0703;
pub const I2C_SLAVE_FORCE: u16 = 0x0706;
pub const I2C_TENBIT: u16 = 0x0704;
pub const I2C_FUNCS: u16 = 0x0705;
pub const I2C_RDWR: u16 = 0x0707;
pub const I2C_PEC: u16 = 0x0708;
pub const I2C_SMBUS: u16 = 0x0720;
pub const I2C_RDWR_IOCTL_MAX_MSGS: usize = 42;
pub mod ioctls {
use libc::{ioctl, c_int, c_ulong};
use std::os::unix::io::RawFd;
use std::io;
#[inline]
fn ioctl_result(res: c_int) -> io::Result<c_int> {
if res == -1 {
Err(io::Error::last_os_error())
} else {
Ok(res)
}
}
pub unsafe fn i2c_retries(fd: RawFd, value: c_int) -> io::Result<c_int> {
ioctl_result(ioctl(fd, super::I2C_RETRIES as _, value))
}
pub unsafe fn i2c_timeout(fd: RawFd, value: c_int) -> io::Result<c_int> {
ioctl_result(ioctl(fd, super::I2C_TIMEOUT as _, value))
}
pub unsafe fn i2c_slave(fd: RawFd, value: c_int) -> io::Result<c_int> {
ioctl_result(ioctl(fd, super::I2C_SLAVE as _, value))
}
pub unsafe fn i2c_slave_force(fd: RawFd, value: c_int) -> io::Result<c_int> {
ioctl_result(ioctl(fd, super::I2C_SLAVE_FORCE as _, value))
}
pub unsafe fn i2c_tenbit(fd: RawFd, value: c_int) -> io::Result<c_int> {
ioctl_result(ioctl(fd, super::I2C_TENBIT as _, value))
}
pub unsafe fn i2c_funcs(fd: RawFd, value: *mut c_ulong) -> io::Result<c_int> {
ioctl_result(ioctl(fd, super::I2C_FUNCS as _, value))
}
pub unsafe fn i2c_rdwr(fd: RawFd, value: *mut super::i2c_rdwr_ioctl_data) -> io::Result<c_int> {
ioctl_result(ioctl(fd, super::I2C_RDWR as _, value))
}
pub unsafe fn i2c_pec(fd: RawFd, value: c_int) -> io::Result<c_int> {
ioctl_result(ioctl(fd, super::I2C_PEC as _, value))
}
pub unsafe fn i2c_smbus(fd: RawFd, value: *mut super::i2c_smbus_ioctl_data) -> io::Result<c_int> {
ioctl_result(ioctl(fd, super::I2C_SMBUS as _, value))
}
}
#[inline]
pub fn i2c_set_retries(fd: RawFd, value: usize) -> io::Result<()> {
unsafe {
ioctls::i2c_retries(fd, value as _).map(drop)
}
}
#[inline]
pub fn i2c_set_timeout_ms(fd: RawFd, value: usize) -> io::Result<()> {
unsafe {
ioctls::i2c_timeout(fd, (value / 10) as _).map(drop)
}
}
#[inline]
pub fn i2c_set_slave_address(fd: RawFd, address: u16, force: bool) -> io::Result<()> {
unsafe {
if force {
ioctls::i2c_slave(fd, address as _)
} else {
ioctls::i2c_slave_force(fd, address as _)
}.map(drop)
}
}
#[inline]
pub fn i2c_set_slave_address_10bit(fd: RawFd, tenbit: bool) -> io::Result<()> {
unsafe {
ioctls::i2c_tenbit(fd, if tenbit { 1 } else { 0 }).map(drop)
}
}
#[inline]
pub fn i2c_get_functionality(fd: RawFd) -> io::Result<Functionality> {
unsafe {
let mut res = 0;
ioctls::i2c_funcs(fd, &mut res)
.map(|_| Functionality::from_bits_truncate(res as _))
}
}
#[inline]
pub fn i2c_pec(fd: RawFd, pec: bool) -> io::Result<()> {
unsafe {
ioctls::i2c_pec(fd, if pec { 1 } else { 0 }).map(drop)
}
}
#[inline]
pub unsafe fn i2c_rdwr(fd: RawFd, msgs: &mut [i2c_msg]) -> io::Result<()> {
let mut data = i2c_rdwr_ioctl_data {
msgs: msgs.as_mut_ptr(),
nmsgs: msgs.len() as _,
};
ioctls::i2c_rdwr(fd, &mut data).map(drop)
}
#[inline]
pub unsafe fn i2c_smbus(fd: RawFd, data: &mut i2c_smbus_ioctl_data) -> io::Result<()> {
ioctls::i2c_smbus(fd, data).map(drop)
}
pub fn i2c_smbus_write_quick(fd: RawFd, value: SmbusReadWrite) -> io::Result<()> {
unsafe {
i2c_smbus(fd, &mut i2c_smbus_ioctl_data {
read_write: value,
command: 0,
size: SmbusTransaction::Quick,
data: ptr::null_mut(),
})
}
}
pub fn i2c_smbus_read_byte(fd: RawFd) -> io::Result<u8> {
unsafe {
let mut data = i2c_smbus_data::default();
let mut ioctl = i2c_smbus_ioctl_data {
read_write: SmbusReadWrite::Read,
command: 0,
size: SmbusTransaction::Byte,
data: &mut data,
};
i2c_smbus(fd, &mut ioctl)
.map(|_| data.byte())
}
}
pub fn i2c_smbus_write_byte(fd: RawFd, value: u8) -> io::Result<()> {
unsafe {
let mut ioctl = i2c_smbus_ioctl_data {
read_write: SmbusReadWrite::Write,
command: value,
size: SmbusTransaction::Byte,
data: ptr::null_mut(),
};
i2c_smbus(fd, &mut ioctl)
}
}
pub fn i2c_smbus_read_byte_data(fd: RawFd, command: u8) -> io::Result<u8> {
unsafe {
let mut data = i2c_smbus_data::default();
let mut ioctl = i2c_smbus_ioctl_data {
read_write: SmbusReadWrite::Read,
command: command,
size: SmbusTransaction::ByteData,
data: &mut data,
};
i2c_smbus(fd, &mut ioctl)
.map(|_| data.byte())
}
}
pub fn i2c_smbus_write_byte_data(fd: RawFd, command: u8, value: u8) -> io::Result<()> {
unsafe {
let mut data = i2c_smbus_data::from_byte(value);
let mut ioctl = i2c_smbus_ioctl_data {
read_write: SmbusReadWrite::Write,
command: command,
size: SmbusTransaction::ByteData,
data: &mut data,
};
i2c_smbus(fd, &mut ioctl)
}
}
pub fn i2c_smbus_read_word_data(fd: RawFd, command: u8) -> io::Result<u16> {
unsafe {
let mut data = i2c_smbus_data::default();
let mut ioctl = i2c_smbus_ioctl_data {
read_write: SmbusReadWrite::Read,
command: command,
size: SmbusTransaction::WordData,
data: &mut data,
};
i2c_smbus(fd, &mut ioctl)
.map(|_| data.word())
}
}
pub fn i2c_smbus_write_word_data(fd: RawFd, command: u8, value: u16) -> io::Result<()> {
unsafe {
let mut data = i2c_smbus_data::from_word(value);
let mut ioctl = i2c_smbus_ioctl_data {
read_write: SmbusReadWrite::Write,
command: command,
size: SmbusTransaction::WordData,
data: &mut data,
};
i2c_smbus(fd, &mut ioctl)
}
}
pub fn i2c_smbus_process_call(fd: RawFd, command: u8, value: u16) -> io::Result<u16> {
unsafe {
let mut data = i2c_smbus_data::from_word(value);
let mut ioctl = i2c_smbus_ioctl_data {
read_write: SmbusReadWrite::Write,
command: command,
size: SmbusTransaction::ProcCall,
data: &mut data,
};
i2c_smbus(fd, &mut ioctl)
.map(|_| data.word())
}
}
pub fn i2c_smbus_read_block_data(fd: RawFd, command: u8, value: &mut [u8]) -> io::Result<usize> {
unsafe {
let mut data = i2c_smbus_data::default();
let mut ioctl = i2c_smbus_ioctl_data {
read_write: SmbusReadWrite::Read,
command: command,
size: SmbusTransaction::BlockData,
data: &mut data,
};
i2c_smbus(fd, &mut ioctl)
.map(|_| {
let block = data.block().expect("kernel provided an invalid block length");
let len = cmp::min(block.len(), value.len());
value[..len].copy_from_slice(&block[..len]);
block.len()
})
}
}
pub fn i2c_smbus_write_block_data(fd: RawFd, command: u8, value: &[u8]) -> io::Result<()> {
unsafe {
let mut data = i2c_smbus_data::from_block(value);
let mut ioctl = i2c_smbus_ioctl_data {
read_write: SmbusReadWrite::Write,
command: command,
size: SmbusTransaction::BlockData,
data: &mut data,
};
i2c_smbus(fd, &mut ioctl)
}
}
pub fn i2c_smbus_read_i2c_block_data(fd: RawFd, command: u8, value: &mut [u8]) -> io::Result<usize> {
assert!(value.len() <= I2C_SMBUS_BLOCK_MAX);
unsafe {
let mut data = i2c_smbus_data::from_byte(value.len() as u8);
let mut ioctl = i2c_smbus_ioctl_data {
read_write: SmbusReadWrite::Read,
command: command,
size: if value.len() == I2C_SMBUS_BLOCK_MAX { SmbusTransaction::I2cBlockBroken } else { SmbusTransaction::I2cBlockData },
data: &mut data,
};
i2c_smbus(fd, &mut ioctl)
.map(|_| {
let block = data.block().expect("kernel provided an invalid block length");
let len = cmp::min(block.len(), value.len());
value[..len].copy_from_slice(&block[..len]);
block.len()
})
}
}
pub fn i2c_smbus_write_i2c_block_data(fd: RawFd, command: u8, value: &[u8]) -> io::Result<()> {
unsafe {
let mut data = i2c_smbus_data::from_block(value);
let mut ioctl = i2c_smbus_ioctl_data {
read_write: SmbusReadWrite::Write,
command: command,
size: SmbusTransaction::I2cBlockBroken,
data: &mut data,
};
i2c_smbus(fd, &mut ioctl)
}
}
pub fn i2c_smbus_block_process_call(fd: RawFd, command: u8, write: &[u8], read: &mut [u8]) -> io::Result<usize> {
assert!(read.len() <= I2C_SMBUS_BLOCK_MAX - 1);
unsafe {
let mut data = i2c_smbus_data::from_block(write);
let mut ioctl = i2c_smbus_ioctl_data {
read_write: SmbusReadWrite::Write,
command: command,
size: SmbusTransaction::BlockProcCall,
data: &mut data,
};
i2c_smbus(fd, &mut ioctl)
.map(|_| {
let block = data.block().expect("kernel provided an invalid block length");
let len = cmp::min(block.len(), read.len());
read[..len].copy_from_slice(&block[..len]);
block.len()
})
}
}