use std::str::FromStr;
use std::time::{Duration, SystemTime};
use bitflags::bitflags;
use byteorder::{ByteOrder, BE, LE};
use log::{debug, error, trace};
use rusb::{
Context as UsbContext, 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 {
_device: UsbDevice<UsbContext>,
handle: DeviceHandle<UsbContext>,
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,
}
#[derive(Debug, PartialEq, Clone)]
#[cfg_attr(feature = "clap", derive(clap::Parser))]
pub struct UsbOptions {
#[cfg_attr(feature = "clap", clap(long))]
pub detach_kernel_driver: bool,
#[cfg_attr(feature = "clap", clap(long))]
pub claim_interface: bool,
}
impl Default for UsbOptions {
fn default() -> Self {
Self {
#[cfg(target_os = "linux")]
detach_kernel_driver: true,
#[cfg(target_os = "windows")]
detach_kernel_driver: false,
#[cfg(target_os = "macos")]
detach_kernel_driver: true,
#[cfg(target_os = "linux")]
claim_interface: false,
#[cfg(target_os = "windows")]
claim_interface: true,
#[cfg(target_os = "macos")]
claim_interface: true,
}
}
}
impl Inner {
pub fn new(
device: UsbDevice<UsbContext>,
descriptor: DeviceDescriptor,
opts: UsbOptions,
) -> 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 opts.detach_kernel_driver {
debug!("Checking for active kernel driver");
match handle.kernel_driver_active(control.iface)? {
true => {
debug!("Detaching kernel driver");
handle.detach_kernel_driver(control.iface)?;
}
false => {
debug!("Kernel driver inactive");
}
}
} else {
debug!("Skipping kernel driver attach check");
}
if opts.claim_interface {
debug!("Claiming device interface");
handle.claim_interface(control.iface)?;
} else {
debug!("Skipping claim device interface");
}
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: 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,
}
pub const SPI_OP_DELAY_US: u64 = 100;
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 + SPI_OP_DELAY_US)
}
}
impl std::convert::TryFrom<usize> for SpiClock {
type Error = Error;
fn try_from(v: usize) -> Result<Self, Self::Error> {
match v {
12_000_000 => Ok(SpiClock::Clock12Mhz),
6_000_000 => Ok(SpiClock::Clock6MHz),
3_000_000 => Ok(SpiClock::Clock3MHz),
1_500_000 => Ok(SpiClock::Clock1_5MHz),
750_000 => Ok(SpiClock::Clock750KHz),
375_000 => Ok(SpiClock::Clock375MHz),
_ => Err(Error::InvalidBaud),
}
}
}
#[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 Inner {
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);
trace!("SPI write (cmd: {:?} time: {} us)", cmd, t.as_micros());
self.handle.write_bulk(
self.endpoints.write.address,
&cmd,
Duration::from_millis(200),
)?;
self.delay(t);
trace!("SPI write done");
Ok(())
}
fn delay(&mut self, d: Duration) {
let n = SystemTime::now();
while n.elapsed().unwrap() < d {}
}
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);
trace!(
"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;
self.delay(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)
}
}