#![allow(dead_code)]
use std::error;
use std::fmt;
use std::fs::{File, OpenOptions};
use std::io;
use std::io::{Read, Write};
use std::marker::PhantomData;
use std::os::unix::io::AsRawFd;
use std::result;
use libc::c_ulong;
use crate::system;
use crate::system::{DeviceInfo, Model};
#[cfg(feature = "hal")]
mod hal;
mod ioctl;
pub use self::ioctl::Capabilities;
#[derive(Debug)]
pub enum Error {
Io(io::Error),
InvalidSlaveAddress(u16),
FeatureNotSupported,
UnknownModel,
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
Error::Io(ref err) => write!(f, "I/O error: {}", err),
Error::InvalidSlaveAddress(address) => write!(f, "Invalid slave address: {}", address),
Error::FeatureNotSupported => write!(f, "I2C/SMBus feature not supported"),
Error::UnknownModel => write!(f, "Unknown Raspberry Pi model"),
}
}
}
impl error::Error for Error {}
impl From<io::Error> for Error {
fn from(err: io::Error) -> Error {
Error::Io(err)
}
}
impl From<system::Error> for Error {
fn from(_err: system::Error) -> Error {
Error::UnknownModel
}
}
pub type Result<T> = result::Result<T, Error>;
#[derive(Debug)]
pub struct I2c {
bus: u8,
funcs: Capabilities,
i2cdev: File,
addr_10bit: bool,
address: u16,
not_sync: PhantomData<*const ()>,
}
impl I2c {
pub fn new() -> Result<I2c> {
match DeviceInfo::new()?.model() {
Model::RaspberryPiBRev1 => I2c::with_bus(0),
_ => I2c::with_bus(1),
}
}
pub fn with_bus(bus: u8) -> Result<I2c> {
let i2cdev = OpenOptions::new()
.read(true)
.write(true)
.open(format!("/dev/i2c-{}", bus))?;
let capabilities = ioctl::funcs(i2cdev.as_raw_fd())?;
if capabilities.addr_10bit() {
ioctl::set_addr_10bit(i2cdev.as_raw_fd(), 0)?;
}
if capabilities.smbus_pec() {
ioctl::set_pec(i2cdev.as_raw_fd(), 0)?;
}
Ok(I2c {
bus,
funcs: capabilities,
i2cdev,
addr_10bit: false,
address: 0,
not_sync: PhantomData,
})
}
pub fn capabilities(&self) -> Capabilities {
self.funcs
}
pub fn bus(&self) -> u8 {
self.bus
}
pub fn clock_speed(&self) -> Result<u32> {
let mut buffer = [0u8; 4];
File::open(format!(
"/sys/class/i2c-adapter/i2c-{}/of_node/clock-frequency",
self.bus
))?
.read_exact(&mut buffer)?;
Ok(u32::from(buffer[3])
| (u32::from(buffer[2]) << 8)
| (u32::from(buffer[1]) << 16)
| (u32::from(buffer[0]) << 24))
}
pub fn set_slave_address(&mut self, slave_address: u16) -> Result<()> {
if (!self.addr_10bit
&& (slave_address < 8 || (slave_address >> 3) == 0b1111 || slave_address > 0x7F))
|| (self.addr_10bit && slave_address > 0x03FF)
{
return Err(Error::InvalidSlaveAddress(slave_address));
}
ioctl::set_slave_address(self.i2cdev.as_raw_fd(), c_ulong::from(slave_address))?;
self.address = slave_address;
Ok(())
}
pub fn set_timeout(&self, timeout: u32) -> Result<()> {
ioctl::set_timeout(self.i2cdev.as_raw_fd(), timeout as c_ulong)?;
Ok(())
}
fn set_retries(&self, retries: u32) -> Result<()> {
ioctl::set_retries(self.i2cdev.as_raw_fd(), retries as c_ulong)?;
Ok(())
}
pub fn set_addr_10bit(&mut self, addr_10bit: bool) -> Result<()> {
if !self.capabilities().addr_10bit() {
return Err(Error::FeatureNotSupported);
}
ioctl::set_addr_10bit(self.i2cdev.as_raw_fd(), addr_10bit as c_ulong)?;
self.addr_10bit = addr_10bit;
Ok(())
}
pub fn read(&mut self, buffer: &mut [u8]) -> Result<usize> {
Ok(self.i2cdev.read(buffer)?)
}
pub fn write(&mut self, buffer: &[u8]) -> Result<usize> {
Ok(self.i2cdev.write(buffer)?)
}
pub fn write_read(&self, write_buffer: &[u8], read_buffer: &mut [u8]) -> Result<()> {
ioctl::i2c_write_read(
self.i2cdev.as_raw_fd(),
self.address,
self.addr_10bit,
write_buffer,
read_buffer,
)?;
Ok(())
}
pub fn block_read(&self, command: u8, buffer: &mut [u8]) -> Result<()> {
ioctl::i2c_block_read(self.i2cdev.as_raw_fd(), command, buffer)?;
Ok(())
}
pub fn block_write(&self, command: u8, buffer: &[u8]) -> Result<()> {
ioctl::i2c_block_write(self.i2cdev.as_raw_fd(), command, buffer)?;
Ok(())
}
pub fn smbus_quick_command(&self, command: bool) -> Result<()> {
ioctl::smbus_quick_command(self.i2cdev.as_raw_fd(), command)?;
Ok(())
}
pub fn smbus_receive_byte(&self) -> Result<u8> {
Ok(ioctl::smbus_receive_byte(self.i2cdev.as_raw_fd())?)
}
pub fn smbus_send_byte(&self, value: u8) -> Result<()> {
ioctl::smbus_send_byte(self.i2cdev.as_raw_fd(), value)?;
Ok(())
}
pub fn smbus_read_byte(&self, command: u8) -> Result<u8> {
Ok(ioctl::smbus_read_byte(self.i2cdev.as_raw_fd(), command)?)
}
pub fn smbus_write_byte(&self, command: u8, value: u8) -> Result<()> {
ioctl::smbus_write_byte(self.i2cdev.as_raw_fd(), command, value)?;
Ok(())
}
pub fn smbus_read_word(&self, command: u8) -> Result<u16> {
Ok(ioctl::smbus_read_word(self.i2cdev.as_raw_fd(), command)?)
}
pub fn smbus_read_word_swapped(&self, command: u8) -> Result<u16> {
let value = ioctl::smbus_read_word(self.i2cdev.as_raw_fd(), command)?;
Ok(((value & 0xFF00) >> 8) | ((value & 0xFF) << 8))
}
pub fn smbus_write_word(&self, command: u8, value: u16) -> Result<()> {
ioctl::smbus_write_word(self.i2cdev.as_raw_fd(), command, value)?;
Ok(())
}
pub fn smbus_write_word_swapped(&self, command: u8, value: u16) -> Result<()> {
ioctl::smbus_write_word(
self.i2cdev.as_raw_fd(),
command,
((value & 0xFF00) >> 8) | ((value & 0xFF) << 8),
)?;
Ok(())
}
pub fn smbus_process_call(&self, command: u8, value: u16) -> Result<u16> {
Ok(ioctl::smbus_process_call(
self.i2cdev.as_raw_fd(),
command,
value,
)?)
}
pub fn smbus_process_call_swapped(&self, command: u8, value: u16) -> Result<u16> {
let response = ioctl::smbus_process_call(
self.i2cdev.as_raw_fd(),
command,
((value & 0xFF00) >> 8) | ((value & 0xFF) << 8),
)?;
Ok(((response & 0xFF00) >> 8) | ((response & 0xFF) << 8))
}
pub fn smbus_block_read(&self, command: u8, buffer: &mut [u8]) -> Result<usize> {
if !self.capabilities().smbus_block_read() {
return Err(Error::FeatureNotSupported);
}
Ok(ioctl::smbus_block_read(
self.i2cdev.as_raw_fd(),
command,
buffer,
)?)
}
pub fn smbus_block_write(&self, command: u8, buffer: &[u8]) -> Result<()> {
ioctl::smbus_block_write(self.i2cdev.as_raw_fd(), command, buffer)?;
Ok(())
}
pub fn set_smbus_pec(&self, pec: bool) -> Result<()> {
ioctl::set_pec(self.i2cdev.as_raw_fd(), pec as c_ulong)?;
Ok(())
}
}
unsafe impl Send for I2c {}