use core::{I2CDevice, I2CTransfer};
use ffi;
use nix;
use std::error::Error;
use std::fmt;
use std::fs::File;
use std::fs::OpenOptions;
use std::io;
use std::io::prelude::*;
use std::marker::PhantomData;
use std::os::unix::prelude::*;
use std::path::Path;
pub use core::I2CMessage;
pub struct LinuxI2CDevice {
devfile: File,
slave_address: u16,
pec: bool,
}
pub struct LinuxI2CBus {
devfile: File,
}
#[derive(Debug)]
pub enum LinuxI2CError {
Errno(i32),
Io(io::Error),
}
impl From<nix::Error> for LinuxI2CError {
fn from(e: nix::Error) -> Self {
LinuxI2CError::Errno(e as i32)
}
}
impl From<io::Error> for LinuxI2CError {
fn from(e: io::Error) -> Self {
LinuxI2CError::Io(e)
}
}
impl From<LinuxI2CError> for io::Error {
fn from(e: LinuxI2CError) -> io::Error {
match e {
LinuxI2CError::Io(e) => e,
LinuxI2CError::Errno(e) => io::Error::from_raw_os_error(e),
}
}
}
impl fmt::Display for LinuxI2CError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
LinuxI2CError::Errno(e) => {
let error = nix::Error::from_i32(e);
fmt::Display::fmt(&error, f)
}
LinuxI2CError::Io(ref e) => fmt::Display::fmt(e, f),
}
}
}
impl Error for LinuxI2CError {
fn cause(&self) -> Option<&dyn Error> {
match *self {
LinuxI2CError::Io(ref e) => Some(e),
LinuxI2CError::Errno(_) => None,
}
}
}
impl AsRawFd for LinuxI2CDevice {
fn as_raw_fd(&self) -> RawFd {
self.devfile.as_raw_fd()
}
}
impl AsRawFd for LinuxI2CBus {
fn as_raw_fd(&self) -> RawFd {
self.devfile.as_raw_fd()
}
}
impl LinuxI2CDevice {
pub fn new<P: AsRef<Path>>(
path: P,
slave_address: u16,
) -> Result<LinuxI2CDevice, LinuxI2CError> {
let file = OpenOptions::new().read(true).write(true).open(path)?;
let mut device = LinuxI2CDevice {
devfile: file,
slave_address: 0, pec: false,
};
device.set_slave_address(slave_address)?;
device.set_smbus_pec(false)?;
Ok(device)
}
pub unsafe fn force_new<P: AsRef<Path>>(
path: P,
slave_address: u16,
) -> Result<LinuxI2CDevice, LinuxI2CError> {
let file = OpenOptions::new().read(true).write(true).open(path)?;
let mut device = LinuxI2CDevice {
devfile: file,
slave_address: 0, pec: false,
};
device.force_set_slave_address(slave_address)?;
device.set_smbus_pec(false)?;
Ok(device)
}
pub fn set_slave_address(&mut self, slave_address: u16) -> Result<(), LinuxI2CError> {
ffi::i2c_set_slave_address(self.as_raw_fd(), slave_address)?;
self.slave_address = slave_address;
Ok(())
}
unsafe fn force_set_slave_address(&mut self, slave_address: u16) -> Result<(), LinuxI2CError> {
ffi::i2c_set_slave_address_force(self.as_raw_fd(), slave_address)?;
self.slave_address = slave_address;
Ok(())
}
pub fn set_smbus_pec(&mut self, enable: bool) -> Result<(), LinuxI2CError> {
ffi::i2c_set_smbus_pec(self.as_raw_fd(), enable)?;
self.pec = enable;
Ok(())
}
}
impl I2CDevice for LinuxI2CDevice {
type Error = LinuxI2CError;
fn read(&mut self, data: &mut [u8]) -> Result<(), LinuxI2CError> {
self.devfile.read_exact(data).map_err(From::from).map(drop)
}
fn write(&mut self, data: &[u8]) -> Result<(), LinuxI2CError> {
self.devfile.write(data).map_err(From::from).map(drop)
}
fn smbus_write_quick(&mut self, bit: bool) -> Result<(), LinuxI2CError> {
ffi::i2c_smbus_write_quick(self.as_raw_fd(), bit).map_err(From::from)
}
fn smbus_read_byte(&mut self) -> Result<u8, LinuxI2CError> {
ffi::i2c_smbus_read_byte(self.as_raw_fd()).map_err(From::from)
}
fn smbus_write_byte(&mut self, value: u8) -> Result<(), LinuxI2CError> {
ffi::i2c_smbus_write_byte(self.as_raw_fd(), value).map_err(From::from)
}
fn smbus_read_byte_data(&mut self, register: u8) -> Result<u8, LinuxI2CError> {
ffi::i2c_smbus_read_byte_data(self.as_raw_fd(), register).map_err(From::from)
}
fn smbus_write_byte_data(&mut self, register: u8, value: u8) -> Result<(), LinuxI2CError> {
ffi::i2c_smbus_write_byte_data(self.as_raw_fd(), register, value).map_err(From::from)
}
fn smbus_read_word_data(&mut self, register: u8) -> Result<u16, LinuxI2CError> {
ffi::i2c_smbus_read_word_data(self.as_raw_fd(), register).map_err(From::from)
}
fn smbus_write_word_data(&mut self, register: u8, value: u16) -> Result<(), LinuxI2CError> {
ffi::i2c_smbus_write_word_data(self.as_raw_fd(), register, value).map_err(From::from)
}
fn smbus_process_word(&mut self, register: u8, value: u16) -> Result<u16, LinuxI2CError> {
ffi::i2c_smbus_process_call(self.as_raw_fd(), register, value).map_err(From::from)
}
fn smbus_read_block_data(&mut self, register: u8) -> Result<Vec<u8>, LinuxI2CError> {
ffi::i2c_smbus_read_block_data(self.as_raw_fd(), register).map_err(From::from)
}
fn smbus_read_i2c_block_data(
&mut self,
register: u8,
len: u8,
) -> Result<Vec<u8>, LinuxI2CError> {
ffi::i2c_smbus_read_i2c_block_data(self.as_raw_fd(), register, len).map_err(From::from)
}
fn smbus_write_block_data(&mut self, register: u8, values: &[u8]) -> Result<(), LinuxI2CError> {
ffi::i2c_smbus_write_block_data(self.as_raw_fd(), register, values).map_err(From::from)
}
fn smbus_write_i2c_block_data(
&mut self,
register: u8,
values: &[u8],
) -> Result<(), LinuxI2CError> {
ffi::i2c_smbus_write_i2c_block_data(self.as_raw_fd(), register, values).map_err(From::from)
}
fn smbus_process_block(
&mut self,
register: u8,
values: &[u8],
) -> Result<Vec<u8>, LinuxI2CError> {
ffi::i2c_smbus_process_call_block(self.as_raw_fd(), register, values).map_err(From::from)
}
}
impl<'a> I2CTransfer<'a> for LinuxI2CDevice {
type Error = LinuxI2CError;
type Message = LinuxI2CMessage<'a>;
fn transfer(&mut self, messages: &'a mut [Self::Message]) -> Result<u32, LinuxI2CError> {
let msg_type = |flag: u16| flag & I2CMessageFlags::READ.bits();
let mut prev_msg_type = None;
for msg in messages.iter_mut() {
msg.addr = self.slave_address;
let cur_msg_type = msg_type(msg.flags);
if prev_msg_type
.map(|prev| prev == cur_msg_type)
.unwrap_or_default()
{
msg.flags |= I2CMessageFlags::NO_START.bits();
} else {
prev_msg_type = Some(cur_msg_type);
}
}
ffi::i2c_rdwr(self.as_raw_fd(), messages).map_err(From::from)
}
}
impl LinuxI2CBus {
pub fn new<P: AsRef<Path>>(path: P) -> Result<LinuxI2CBus, LinuxI2CError> {
let file = OpenOptions::new().read(true).write(true).open(path)?;
let bus = LinuxI2CBus { devfile: file };
Ok(bus)
}
}
pub use ffi::i2c_msg as LinuxI2CMessage;
impl<'a> I2CTransfer<'a> for LinuxI2CBus {
type Error = LinuxI2CError;
type Message = LinuxI2CMessage<'a>;
fn transfer(&mut self, msgs: &'a mut [Self::Message]) -> Result<u32, LinuxI2CError> {
ffi::i2c_rdwr(self.as_raw_fd(), msgs).map_err(From::from)
}
}
bitflags! {
pub struct I2CMessageFlags: u16 {
const TEN_BIT_ADDRESS = 0x0010;
const READ = 0x0001;
const STOP = 0x8000;
const NO_START = 0x4000;
const INVERT_COMMAND = 0x2000;
const IGNORE_NACK = 0x1000;
const IGNORE_ACK = 0x0800;
const USE_RECEIVE_LENGTH = 0x0400;
}
}
impl<'a> I2CMessage<'a> for LinuxI2CMessage<'a> {
fn read(data: &'a mut [u8]) -> LinuxI2CMessage<'a> {
Self {
addr: 0, flags: I2CMessageFlags::READ.bits(),
len: data.len() as u16,
buf: data.as_ptr(),
_p: PhantomData,
}
}
fn write(data: &'a [u8]) -> LinuxI2CMessage<'a> {
Self {
addr: 0, flags: I2CMessageFlags::empty().bits(),
len: data.len() as u16,
buf: data.as_ptr(),
_p: PhantomData,
}
}
}
impl<'a> LinuxI2CMessage<'a> {
pub fn with_address(self, slave_address: u16) -> Self {
Self {
addr: slave_address,
flags: self.flags,
len: self.len,
buf: self.buf,
_p: PhantomData,
}
}
pub fn with_flags(self, flags: I2CMessageFlags) -> Self {
Self {
addr: self.addr,
flags: flags.bits(),
len: self.len,
buf: self.buf,
_p: PhantomData,
}
}
}