use std::time::Duration;
use std::str::FromStr;
use byteorder::{LE, BE, ByteOrder};
use libusb::{Device as UsbDevice, DeviceDescriptor, DeviceHandle, Direction, TransferType};
use embedded_hal::spi::{Mode as SpiMode, Phase, Polarity, MODE_0};
use crate::Error;
#[derive(Debug, Clone, PartialEq)]
pub struct Info {
manufacturer: String,
product: String,
serial: String,
}
#[derive(Debug, PartialEq, Clone, Copy)]
pub enum Commands {
GetClockDivider = 0x46,
GetEventCounter = 0x44,
GetFullThreshold = 0x34,
GetGpioChipSelect = 0x24,
GetGpioModeAndLevel = 0x22,
GetGpioValues = 0x20,
GetRtrState = 0x36,
GetSpiWord = 0x30,
GetSpiDelay = 0x32,
GetReadOnlyVersion = 0x11,
ResetDevice = 0x10,
SetClockDivider = 0x47,
SetEventCOunter = 0x45,
SetFullThreshold = 0x35,
SetGpioChipSelect = 0x25,
SetGpioModeAndLevel = 0x23,
SetGpioValues = 0x21,
SetRtrStop = 0x37,
SetSpiWord = 0x31,
SetSpiDelay = 0x33,
}
pub const VID: u16 = 0x10c4;
pub const PID: u16 = 0x87a0;
bitflags!(
pub struct RequestType: u8 {
const HOST_TO_DEVICE = 0b0000_0000;
const DEVICE_TO_HOST = 0b1000_0000;
const TYPE_STANDARD = 0b0000_0000;
const TYPE_CLASS = 0b0010_0000;
const TYPE_VENDOR = 0b0100_0000;
const RECIPIENT_DEVICE = 0b0000_0000;
const RECIPIENT_INTERFACE = 0b0000_0001;
const RECIPIENT_ENDPOINT = 0b0000_0010;
const RECIPIENT_OTHER = 0b0000_0011;
}
);
bitflags!(
pub struct GpioLevels: u16 {
const GPIO_10 = (1 << 14);
const GPIO_9 = (1 << 13);
const GPIO_8 = (1 << 12);
const GPIO_7 = (1 << 11);
const GPIO_6 = (1 << 10);
const GPIO_5 = (1 << 8);
const GPIO_4 = (1 << 7);
const GPIO_3 = (1 << 6);
const GPIO_2 = (1 << 5);
const GPIO_1 = (1 << 4);
const GPIO_0 = (1 << 3);
}
);
#[derive(Debug, PartialEq, Clone, Copy)]
pub enum GpioMode {
Input = 0x00,
OpenDrain = 0x01,
PushPull = 0x02,
}
impl FromStr for GpioMode {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"input" => Ok(Self::Input),
"open-drain" => Ok(Self::OpenDrain),
"push-pull" => Ok(Self::PushPull),
_ => Err(format!("Unrecognised GPIO mode, try 'input', 'open-drain', or 'push-pull'")),
}
}
}
#[derive(Debug, PartialEq, Clone, Copy)]
pub enum GpioLevel {
Low = 0x00,
High = 0x01,
}
impl FromStr for GpioLevel {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"1" | "true" | "high" => Ok(Self::High),
"0" | "false" | "low" => Ok(Self::Low),
_ => Err(format!("Unrecognised GPIO level, try 'high' or 'low'")),
}
}
}
#[derive(Debug, PartialEq, Clone)]
pub enum TransferCommand {
Read = 0x00,
Write = 0x01,
WriteRead = 0x02,
ReadWithRTR = 0x04,
}
pub(crate) struct Inner<'a> {
_device: UsbDevice<'a>,
handle: DeviceHandle<'a>,
endpoints: Endpoints,
pub(crate) gpio_allocated: [bool; 11],
spi_clock: SpiClock,
}
#[derive(Debug)]
struct Endpoints {
control: Endpoint,
read: Endpoint,
write: Endpoint,
}
#[derive(Debug, PartialEq, Clone)]
struct Endpoint {
config: u8,
iface: u8,
setting: u8,
address: u8
}
impl <'a> Inner<'a> {
pub fn new(device: UsbDevice<'a>, descriptor: DeviceDescriptor) -> Result<(Self, Info), Error> {
let timeout = Duration::from_millis(200);
let mut handle = match device.open() {
Ok(v) => v,
Err(e) => {
error!("Opening device: {}", e);
return Err(Error::Usb(e))
}
};
handle.reset()?;
let languages = handle.read_languages(timeout)?;
let active_config = handle.active_configuration()?;
trace!("Active configuration: {}", active_config);
trace!("Languages: {:?}", languages);
if languages.len() == 0 {
return Err(Error::NoLanguages)
}
let language = languages[0];
let manufacturer = handle.read_manufacturer_string(language, &descriptor, timeout)?;
let product = handle.read_product_string(language, &descriptor, timeout)?;
let serial = handle.read_serial_number_string(language, &descriptor, timeout)?;
let info = Info{manufacturer, product, serial};
if descriptor.num_configurations() != 1 {
error!("Unexpected number of configurations");
return Err(Error::Configurations)
}
let config_desc = device.config_descriptor(0)?;
let (mut write, mut read) = (None, None);
for interface in config_desc.interfaces() {
for interface_desc in interface.descriptors() {
for endpoint_desc in interface_desc.endpoint_descriptors() {
let e = Endpoint {
config: config_desc.number(),
iface: interface_desc.interface_number(),
setting: interface_desc.setting_number(),
address: endpoint_desc.address(),
};
trace!("Endpoint: {:?}", e);
match (endpoint_desc.transfer_type(), endpoint_desc.direction()) {
(TransferType::Bulk, Direction::In) => read = Some(e),
(TransferType::Bulk, Direction::Out) => write = Some(e),
(_, _) => continue,
}
}
}
}
let control = Endpoint {
config: 1,
iface: 0,
setting: 0,
address: 0,
};
if handle.kernel_driver_active(control.iface)? {
debug!("Detaching kernel driver");
handle.detach_kernel_driver(control.iface)?;
}
let write = match write {
Some(c) => c,
None => {
error!("No write endpoint found");
return Err(Error::Endpoint)
}
};
handle.set_active_configuration(write.config)?;
let read = match read {
Some(c) => c,
None => {
error!("No read endpoint found");
return Err(Error::Endpoint)
}
};
handle.set_active_configuration(read.config)?;
let endpoints = Endpoints{control, write, read};
Ok((Inner{_device: device, handle, endpoints, gpio_allocated: [false; 11], spi_clock: SpiClock::Clock12Mhz}, info))
}
}
#[derive(Debug, PartialEq, Copy, Clone)]
pub enum SpiClock {
Clock12Mhz,
Clock6MHz,
Clock3MHz,
Clock1_5MHz,
Clock750KHz,
Clock375MHz,
}
impl SpiClock {
pub fn freq(&self) -> u64 {
match self {
SpiClock::Clock12Mhz => 12_000_000,
SpiClock::Clock6MHz => 6_000_000,
SpiClock::Clock3MHz => 3_000_000,
SpiClock::Clock1_5MHz => 1_500_000,
SpiClock::Clock750KHz => 750_000,
SpiClock::Clock375MHz => 375_000,
}
}
pub fn transfer_time(&self, len_bytes: u64) -> std::time::Duration {
let micros = len_bytes * 8 * 1_000_000 / self.freq();
Duration::from_micros(micros)
}
}
#[derive(Debug, PartialEq, Clone)]
pub enum CsMode {
Disabled = 0x00,
Enabled = 0x01,
Exclusive = 0x02,
}
pub const CPOL_TRAILING: u8 = (0 << 5);
bitflags!(
pub struct DelayMask: u8 {
const CS_TOGGLE = 1 << 3;
const PRE_DEASSERT = 1 << 2;
const POST_ASSERT = 1 << 1;
const INTER_BYE = 1 << 0;
}
);
#[derive(Debug, PartialEq, Clone)]
pub struct SpiDelays {
mask: DelayMask,
pre_deassert: u8,
post_assert: u8,
inter_byte: u8,
}
#[derive(PartialEq, Clone)]
pub struct SpiConfig {
pub clock: SpiClock,
pub spi_mode: SpiMode,
pub cs_mode: CsMode,
pub cs_pin_mode: GpioMode,
pub delays: SpiDelays,
}
impl Default for SpiConfig {
fn default() -> Self {
Self {
clock: SpiClock::Clock3MHz,
spi_mode: MODE_0,
cs_mode: CsMode::Disabled,
cs_pin_mode: GpioMode::PushPull,
delays: SpiDelays {
mask: DelayMask::empty(),
pre_deassert: 0,
post_assert: 0,
inter_byte: 0,
}
}
}
}
impl <'a> Inner<'a> {
pub(crate) fn spi_configure(&mut self, channel: u8, config: SpiConfig) -> Result<(), Error> {
debug!("Setting SPI channel: {:?} clock: {:?} cs mode: {:?}", channel, config.clock, config.cs_mode);
self.set_spi_word(channel, config.clock, config.spi_mode, config.cs_pin_mode)?;
self.set_gpio_chip_select(channel, config.cs_mode)?;
self.set_spi_delay(channel, config.delays)?;
Ok(())
}
pub(crate) fn set_spi_word(&mut self, channel: u8, clock: SpiClock, spi_mode: SpiMode, cs_pin_mode: GpioMode) -> Result<(), Error> {
let mut flags = 0;
if let Phase::CaptureOnSecondTransition = spi_mode.phase {
flags |= 1 << 5;
}
if let Polarity::IdleHigh = spi_mode.polarity {
flags |= 1 << 4;
};
if let GpioMode::PushPull = cs_pin_mode {
flags |= 1 << 3
}
flags |= (clock as u8) & 0b0111;
debug!("Set SPI word: 0x{:02x?}", flags);
let cmd = [
channel,
flags
];
self.handle.write_control(
(RequestType::HOST_TO_DEVICE | RequestType::TYPE_VENDOR).bits(),
Commands::SetSpiWord as u8,
0, 0,
&cmd,
Duration::from_millis(200)
)?;
self.spi_clock = clock;
Ok(())
}
pub(crate) fn reset(&mut self) -> Result<(), Error> {
self.handle.write_control(
(RequestType::HOST_TO_DEVICE | RequestType::TYPE_VENDOR).bits(),
Commands::ResetDevice as u8,
0, 0,
&[],
Duration::from_millis(200)
)?;
Ok(())
}
pub(crate) fn set_spi_delay(&mut self, channel: u8, delays: SpiDelays) -> Result<(), Error> {
let cmd = [
channel,
delays.mask.bits(),
delays.inter_byte,
delays.post_assert,
delays.pre_deassert,
];
self.handle.write_control(
(RequestType::HOST_TO_DEVICE | RequestType::TYPE_VENDOR).bits(),
Commands::SetSpiDelay as u8,
0, 0,
&cmd,
Duration::from_millis(200)
)?;
Ok(())
}
pub(crate) fn set_gpio_chip_select(&mut self, channel: u8, cs_mode: CsMode) -> Result<(), Error> {
let cmd = [
channel,
cs_mode as u8,
];
self.handle.write_control(
(RequestType::HOST_TO_DEVICE | RequestType::TYPE_VENDOR).bits(),
Commands::SetGpioChipSelect as u8,
0, 0,
&cmd,
Duration::from_millis(200)
)?;
Ok(())
}
pub(crate) fn spi_read(&mut self, buff: &mut [u8]) -> Result<usize, Error> {
let mut cmd = [0u8; 8];
cmd[2] = TransferCommand::Read as u8;
LE::write_u32(&mut cmd[4..], buff.len() as u32);
trace!("SPI read (cmd: {:?})", cmd);
self.handle.write_bulk(
self.endpoints.write.address,
&cmd,
Duration::from_millis(200),
)?;
let mut index = 0;
while index < buff.len() {
let remainder = if buff.len() > index + 64 {
64
} else {
buff.len() - index
};
debug!("SPI read (i: {}, rem: {})", index, remainder);
let n = self.handle.read_bulk(
self.endpoints.read.address,
&mut buff[index..index+remainder],
Duration::from_millis(200),
)?;
index += n;
}
trace!("SPI read done");
Ok(index)
}
pub(crate) fn spi_write(&mut self, buff: &[u8]) -> Result<(), Error> {
let mut cmd = vec![0u8; buff.len() + 8];
cmd[2] = TransferCommand::Write as u8;
LE::write_u32(&mut cmd[4..], buff.len() as u32);
(&mut cmd[8..]).copy_from_slice(buff);
let t = self.spi_clock.transfer_time(buff.len() as u64);
debug!("SPI write (cmd: {:?} time: {} us)", cmd, t.as_micros());
self.handle.write_bulk(
self.endpoints.write.address,
&cmd,
Duration::from_millis(200),
)?;
std::thread::sleep(t);
trace!("SPI write done");
Ok(())
}
pub(crate) fn spi_write_read(&mut self, buff_out: &[u8], buff_in: &mut [u8]) -> Result<usize, Error> {
let mut cmd = vec![0u8; buff_out.len() + 8];
cmd[2] = TransferCommand::WriteRead as u8;
LE::write_u32(&mut cmd[4..], buff_out.len() as u32);
(&mut cmd[8..]).copy_from_slice(buff_out);
let total_time = self.spi_clock.transfer_time(buff_out.len() as u64);
debug!("SPI transfer (cmd: {:?} time: {} us)", cmd, total_time.as_micros());
self.handle.write_bulk(
self.endpoints.write.address,
&cmd,
Duration::from_millis(200),
)?;
trace!("SPI transfer await resp");
let mut index = 0;
while index < buff_in.len() {
let remainder = if buff_in.len() > index + 64 {
64
} else {
buff_in.len() - index
};
let t = self.spi_clock.transfer_time(buff_out.len() as u64);
trace!("SPI read (len: {}, index: {}, rem: {}, time: {} us)",
buff_in.len(), index, remainder, t.as_micros());
let n = self.handle.read_bulk(
self.endpoints.read.address,
&mut buff_in[index..index+remainder],
Duration::from_millis(200),
)?;
index += n;
std::thread::sleep(t);
}
trace!("SPI transfer done");
Ok(index)
}
pub(crate) fn version(&mut self) -> Result<u16, Error> {
let mut buff = [0u8; 2];
self.handle.read_control(
(RequestType::DEVICE_TO_HOST | RequestType::TYPE_VENDOR).bits(),
Commands::GetReadOnlyVersion as u8,
0, 0,
&mut buff,
Duration::from_millis(200)
)?;
let version = LE::read_u16(&buff);
Ok(version)
}
pub(crate) fn set_gpio_mode_level(&mut self, pin: u8, mode: GpioMode, level: GpioLevel) -> Result<(), Error> {
assert!(pin <= 10);
let cmd = [
pin,
mode as u8,
level as u8,
];
trace!("GPIO set pin: {} mode: {:?} level: {:?} (cmd: {:?})", pin, mode, level, cmd);
self.handle.write_control(
(RequestType::HOST_TO_DEVICE | RequestType::TYPE_VENDOR).bits(),
Commands::SetGpioModeAndLevel as u8,
0, 0,
&cmd,
Duration::from_millis(200)
)?;
Ok(())
}
pub(crate) fn get_gpio_values(&mut self) -> Result<GpioLevels, Error> {
let mut buff = [0u8; 2];
self.handle.read_control(
(RequestType::DEVICE_TO_HOST | RequestType::TYPE_VENDOR).bits(),
Commands::GetGpioValues as u8,
0, 0,
&mut buff,
Duration::from_millis(200)
)?;
let values = GpioLevels::from_bits_truncate(BE::read_u16(&buff));
trace!("GPIO get pins (values: {:?})", values);
Ok(values)
}
pub (crate) fn get_gpio_level(&mut self, pin: u8) -> Result<bool, Error> {
assert!(pin <= 10);
let levels = self.get_gpio_values()?;
let v = match pin {
0 => levels.contains(GpioLevels::GPIO_0),
1 => levels.contains(GpioLevels::GPIO_1),
2 => levels.contains(GpioLevels::GPIO_2),
3 => levels.contains(GpioLevels::GPIO_3),
4 => levels.contains(GpioLevels::GPIO_4),
5 => levels.contains(GpioLevels::GPIO_5),
6 => levels.contains(GpioLevels::GPIO_6),
7 => levels.contains(GpioLevels::GPIO_7),
8 => levels.contains(GpioLevels::GPIO_8),
9 => levels.contains(GpioLevels::GPIO_9),
10 => levels.contains(GpioLevels::GPIO_10),
_ => panic!("invalid pin {}", pin),
};
Ok(v)
}
}