#[macro_use]
extern crate nix;
use std::{
fs::OpenOptions,
io::Result,
os::unix::io::{IntoRawFd, RawFd},
};
use nix::{libc::ioctl, sys::ioctl::ioctl_num_type};
use num_derive::ToPrimitive;
use num_traits::ToPrimitive;
pub type I2CAddress = u8;
pub const SG_INTERFACE_ID_ORIG: u8 = 'S' as u8;
pub const SG_IO: u32 = 0x2285;
#[derive(ToPrimitive)]
pub enum SgDxfer {
ToDev = -2,
FromDev = -3,
}
pub const USB2642_SCSI_OPCODE: u8 = 0xcf;
pub const USB2642_I2C_WRITE_STREAM: u8 = 0x23;
pub const USB2642_I2C_WRITE_READ_STREAM: u8 = 0x22;
pub trait USB2642I2CCommand {}
#[derive(Debug, Default)]
#[repr(C)]
pub struct USB2642I2CWriteReadCommand {
scsi_vendor_command: u8,
scsi_vendor_action_write_read_i2c: u8,
i2c_write_slave_address: u8,
i2c_read_slave_address: u8,
i2c_read_data_phase_length_high: u8,
i2c_read_data_phase_length_low: u8,
i2c_write_phase_length: u8,
i2c_write_phase_payload: [u8; 9],
}
impl USB2642I2CWriteReadCommand {
pub fn new(i2c_addr: u8, write_data: &[u8], read_len: usize) -> Self {
assert!(read_len < 512);
assert!(write_data.len() < 9);
let i2c_write_addr = i2c_addr << 1;
let i2c_read_addr = i2c_write_addr + 1;
let mut write_data_buffer = [0u8; 9];
for (i, b) in write_data.iter().enumerate() {
write_data_buffer[i] = *b;
}
Self {
scsi_vendor_command: USB2642_SCSI_OPCODE,
scsi_vendor_action_write_read_i2c: USB2642_I2C_WRITE_READ_STREAM,
i2c_write_slave_address: i2c_write_addr,
i2c_read_slave_address: i2c_read_addr,
i2c_read_data_phase_length_high: ((read_len >> 8) & 0xff) as u8,
i2c_read_data_phase_length_low: (read_len & 0xff) as u8,
i2c_write_phase_length: write_data.len() as u8,
i2c_write_phase_payload: write_data_buffer,
}
}
}
impl USB2642I2CCommand for USB2642I2CWriteReadCommand {}
#[derive(Debug, Default)]
#[repr(C)]
pub struct USB2642I2CWriteCommand {
scsi_vendor_command: u8,
scsi_vendor_action_write_i2c: u8,
i2c_slave_address: u8,
i2c_unused: u8,
i2c_data_phase_length_high: u8,
i2c_data_phase_length_low: u8,
i2c_command_phase_length: u8,
i2c_command_phase_payload: [u8; 9],
}
impl USB2642I2CWriteCommand {
pub fn new(i2c_addr: u8, data_len: usize) -> Self {
assert!(data_len < 512);
let i2c_write_addr = i2c_addr << 1;
Self {
scsi_vendor_command: USB2642_SCSI_OPCODE,
scsi_vendor_action_write_i2c: USB2642_I2C_WRITE_STREAM,
i2c_slave_address: i2c_write_addr,
i2c_unused: 0,
i2c_data_phase_length_high: ((data_len >> 8) & 0xff) as u8,
i2c_data_phase_length_low: (data_len & 0xff) as u8,
i2c_command_phase_length: 0,
i2c_command_phase_payload: Default::default(),
}
}
}
impl USB2642I2CCommand for USB2642I2CWriteCommand {}
#[derive(Debug)]
#[repr(C)]
pub struct SgIoHdr<CMD: USB2642I2CCommand> {
interface_id: i32,
dxfer_direction: i32,
cmd_len: u8,
mx_sb_len: u8,
iovec_count: u16,
dxfer_len: u32,
dxferp: *mut u8,
cmdp: *mut CMD,
sbp: *mut u8,
timeout: u32,
flags: u32,
pack_id: i32,
usr_ptr: *const u8,
status: u8,
masked_status: u8,
msg_status: u8,
sb_len_wr: u8,
host_status: u16,
driver_status: u16,
resid: i32,
duration: u32,
info: u32,
}
impl<CMD: USB2642I2CCommand> SgIoHdr<CMD> {
pub fn new(mut command: CMD, sg_dxfer: SgDxfer, data_len: usize, data_buffer: *mut u8) -> Self {
let mut sense = [0u8; 64];
Self {
interface_id: 'S' as i32,
dxfer_direction: sg_dxfer.to_i32().unwrap(),
cmd_len: std::mem::size_of::<CMD>() as u8,
mx_sb_len: sense.len() as u8,
iovec_count: 0,
dxfer_len: data_len as u32,
dxferp: data_buffer,
cmdp: &mut command,
sbp: sense.as_mut_ptr(),
timeout: 3000,
flags: 0,
pack_id: 0,
usr_ptr: std::ptr::null(),
status: 0,
masked_status: 0,
msg_status: 0,
sb_len_wr: 0,
host_status: 0,
driver_status: 0,
resid: 0,
duration: 0,
info: 0,
}
}
}
pub fn sg_ioctl<CMD: USB2642I2CCommand>(sg_raw_fd: RawFd, sg_io_hdr: &SgIoHdr<CMD>) -> Result<()> {
if let Err(e) =
unsafe { convert_ioctl_res!(ioctl(sg_raw_fd, SG_IO as ioctl_num_type, sg_io_hdr)) }
{
return Err(std::io::Error::new(std::io::ErrorKind::Other, e));
}
Ok(())
}
pub struct USB2642I2C {
sg_fd: RawFd,
}
impl USB2642I2C {
pub fn open<S: Into<String>>(sg_dev: S) -> Result<Self> {
let sg_fd = OpenOptions::new()
.read(true)
.write(true)
.open(sg_dev.into())?;
Ok(Self {
sg_fd: sg_fd.into_raw_fd(),
})
}
pub fn write(&mut self, i2c_addr: I2CAddress, data: &mut [u8]) -> Result<()> {
let command = USB2642I2CWriteCommand::new(i2c_addr, data.len());
println!("Command:\n{:?}", command);
let sgio = SgIoHdr::new(command, SgDxfer::ToDev, data.len(), data.as_mut_ptr());
println!("SgIoHdr:\n{:?}", sgio);
sg_ioctl(self.sg_fd, &sgio)
}
pub fn write_read(
&mut self,
i2c_addr: I2CAddress,
write_data: &mut [u8],
read_len: usize,
) -> Result<()> {
let read_data = [0u8; 512];
let command = USB2642I2CWriteReadCommand::new(i2c_addr, write_data, read_len);
let sgio = SgIoHdr::new(
command,
SgDxfer::FromDev,
write_data.len(),
write_data.as_mut_ptr(),
);
sg_ioctl(self.sg_fd, &sgio)
}
}