use std::io::{Read, Write};
use std::time::{Duration, Instant};
use anyhow::{bail, Result};
use bitflags::bitflags;
use log;
use thiserror::Error;
use crate::io::gpio::GpioError;
use crate::io::spi::SpiError;
pub const MPSSE_WRCLK_FALLING: u8 = 0x01;
pub const MPSSE_RDCLK_FALLING: u8 = 0x04;
pub const MPSSE_DIR_LSB_FIRST: u8 = 0x08;
pub const MPSSE_WRITE_DATA: u8 = 0x10;
pub const MPSSE_READ_DATA: u8 = 0x20;
pub const MPSSE_SET_LOW_GPIO: u8 = 0x80;
pub const MPSSE_SET_HIGH_GPIO: u8 = 0x82;
pub const MPSSE_GET_LOW_GPIO: u8 = 0x81;
pub const MPSSE_GET_HIGH_GPIO: u8 = 0x83;
pub const MPSSE_SET_CLKDIV: u8 = 0x86;
pub const MPSSE_DISABLE_DIVBY5: u8 = 0x8A;
pub const MPSSE_INVALID_COMMAND: u8 = 0xAA;
pub const MPSSE_CLOCK_FREQUENCY: u32 = 12_000_000;
bitflags! {
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct GpioDirection: u8 {
const OUT_0 = 0x01;
const OUT_1 = 0x02;
const OUT_2 = 0x04;
const OUT_3 = 0x08;
const OUT_4 = 0x10;
const OUT_5 = 0x20;
const OUT_6 = 0x40;
const OUT_7 = 0x80;
}
}
#[derive(Debug, Clone, Copy)]
pub enum ClockEdge {
Rising,
Falling,
}
#[derive(Debug, Clone, Copy)]
pub enum BitDirection {
LsbFirst,
MsbFirst,
}
#[derive(Debug)]
pub struct DataShiftOptions {
pub read_clock_edge: ClockEdge,
pub write_clock_edge: ClockEdge,
pub bit_direction: BitDirection,
pub write_data: bool,
pub read_data: bool,
}
impl DataShiftOptions {
pub fn as_opcode(&self) -> u8 {
let mut opcode = 0u8;
opcode |= match self.read_clock_edge {
ClockEdge::Rising => 0,
ClockEdge::Falling => MPSSE_RDCLK_FALLING,
};
opcode |= match self.write_clock_edge {
ClockEdge::Rising => 0,
ClockEdge::Falling => MPSSE_WRCLK_FALLING,
};
opcode |= match self.bit_direction {
BitDirection::LsbFirst => MPSSE_DIR_LSB_FIRST,
BitDirection::MsbFirst => 0,
};
opcode |= match self.write_data {
false => 0,
true => MPSSE_WRITE_DATA,
};
opcode |= match self.read_data {
false => 0,
true => MPSSE_READ_DATA,
};
opcode
}
}
impl Default for DataShiftOptions {
fn default() -> Self {
DataShiftOptions {
read_clock_edge: ClockEdge::Rising,
write_clock_edge: ClockEdge::Rising,
bit_direction: BitDirection::MsbFirst,
write_data: false,
read_data: false,
}
}
}
pub enum Command<'rd, 'wr> {
ReadData(DataShiftOptions, &'rd mut [u8]),
WriteData(DataShiftOptions, &'wr [u8]),
TransactData(DataShiftOptions, &'wr [u8], DataShiftOptions, &'rd mut [u8]),
SetLowGpio(GpioDirection, u8),
GetLowGpio(&'rd mut u8),
SetClockDivisor(u16),
DisableDivBy5,
InvalidCommand,
}
impl Command<'_, '_> {
const MAX_LENGTH: usize = 65536;
pub fn response_length(&self) -> usize {
match self {
Command::ReadData(_, buf) => buf.len(),
Command::TransactData(_, _, _, buf) => buf.len(),
Command::GetLowGpio(_) => 1,
Command::WriteData(_, _)
| Command::SetLowGpio(_, _)
| Command::SetClockDivisor(_)
| Command::DisableDivBy5
| Command::InvalidCommand => 0,
}
}
pub fn extend(&self, buf: &mut Vec<u8>) -> Result<()> {
match self {
Command::ReadData(options, data) => {
if data.len() > Command::MAX_LENGTH {
bail!(SpiError::InvalidDataLength(data.len()));
}
buf.push(options.as_opcode());
buf.extend_from_slice(&((data.len() - 1) as u16).to_le_bytes());
}
Command::WriteData(options, data) => {
if data.len() > Command::MAX_LENGTH {
bail!(SpiError::InvalidDataLength(data.len()));
}
buf.push(options.as_opcode());
buf.extend_from_slice(&((data.len() - 1) as u16).to_le_bytes());
buf.extend(data.iter());
}
Command::TransactData(woptions, wdata, roptions, rdata) => {
if wdata.len() > Command::MAX_LENGTH {
bail!(SpiError::InvalidDataLength(wdata.len()));
}
if wdata.len() != rdata.len() {
bail!(SpiError::MismatchedDataLength(wdata.len(), rdata.len()));
}
buf.push(woptions.as_opcode() | roptions.as_opcode());
buf.extend_from_slice(&((wdata.len() - 1) as u16).to_le_bytes());
buf.extend(wdata.iter());
}
Command::SetLowGpio(direction, value) => {
buf.push(MPSSE_SET_LOW_GPIO);
buf.push(*value);
buf.push(direction.bits());
}
Command::GetLowGpio(_) => {
buf.push(MPSSE_GET_LOW_GPIO);
}
Command::SetClockDivisor(divisor) => {
buf.push(MPSSE_SET_CLKDIV);
buf.extend_from_slice(&divisor.to_le_bytes());
}
Command::DisableDivBy5 => {
buf.push(MPSSE_DISABLE_DIVBY5);
}
Command::InvalidCommand => {
buf.push(MPSSE_INVALID_COMMAND);
}
}
Ok(())
}
}
#[derive(Error, Debug)]
pub enum Error {
#[error("unknown MPSSE error: {0:02x} {1:02x}")]
MpsseUnknown(u8, u8),
}
pub struct Context {
pub device: ftdi::Device,
pub max_clock_frequency: u32,
pub clock_frequency: u32,
pub gpio_direction: GpioDirection,
pub gpio_value: u8,
pub receive_timeout: Duration,
}
impl Context {
pub fn new(mut device: ftdi::Device) -> Result<Self> {
device.usb_set_event_char(None)?;
device.usb_set_error_char(None)?;
device.set_latency_timer(1)?;
device.set_bitmode(0, ftdi::BitMode::Reset)?;
device.set_bitmode(0, ftdi::BitMode::Mpsse)?;
std::thread::sleep(Duration::from_millis(50));
let mut context = Context {
device,
max_clock_frequency: MPSSE_CLOCK_FREQUENCY / 2,
clock_frequency: MPSSE_CLOCK_FREQUENCY / 2,
gpio_direction: GpioDirection::empty(),
gpio_value: 0,
receive_timeout: Duration::from_millis(100),
};
context.set_clock_frequency(context.clock_frequency)?;
Ok(context)
}
fn read_timeout(&mut self, rxbuf: &mut [u8], timeout: Duration) -> Result<usize> {
let deadline = Instant::now() + timeout;
let mut rxlen = 0;
while rxlen < rxbuf.len() {
if Instant::now() > deadline {
return Ok(0);
}
let n = self.device.read(&mut rxbuf[rxlen..])?;
rxlen += n;
}
Ok(rxlen)
}
fn read_status(&mut self) -> Result<()> {
let mut buf = [0u8; 2];
let n = self.device.read(&mut buf)?;
if n > 0 {
Err(Error::MpsseUnknown(buf[0], buf[1]).into())
} else {
Ok(())
}
}
pub fn execute(&mut self, commands: &mut [Command]) -> Result<()> {
let mut txbuf = Vec::new();
let mut rxlen = 0;
for command in commands.iter() {
command.extend(&mut txbuf)?;
rxlen += command.response_length();
}
let mut rxbuf = vec![0u8; rxlen];
let mut txlen = 0;
rxlen = 0;
while txlen < txbuf.len() || rxlen < rxbuf.len() {
if txlen < txbuf.len() {
let n = self.device.write(&txbuf[txlen..])?;
txlen += n;
}
if rxlen < rxbuf.len() {
let n = if txlen < txbuf.len() {
self.device.read(&mut rxbuf[rxlen..])?
} else {
self.read_timeout(&mut rxbuf[rxlen..], self.receive_timeout)?
};
rxlen += n;
}
}
let mut pos = 0usize;
for command in commands.iter_mut() {
let len = command.response_length();
match command {
Command::ReadData(_, buf) => {
buf.copy_from_slice(&rxbuf[pos..(pos + len)]);
}
Command::TransactData(_, _, _, buf) => {
buf.copy_from_slice(&rxbuf[pos..(pos + len)]);
}
Command::GetLowGpio(value) => {
**value = rxbuf[pos];
}
_ => {}
}
pos += len;
}
self.read_status()
}
pub fn gpio_get(&mut self) -> Result<u8> {
let mut value = 0u8;
let command = Command::GetLowGpio(&mut value);
self.execute(&mut [command])?;
log::debug!("gpio_get {:x}", value);
self.gpio_value = value;
Ok(value)
}
pub fn gpio_set(&mut self, pin: u8, high: bool) -> Result<()> {
let dir = GpioDirection::from_bits(1 << pin).ok_or(GpioError::InvalidPinNumber(pin))?;
if (dir & self.gpio_direction).bits() == 0 {
return Err(GpioError::InvalidPinMode(pin).into());
}
if high {
self.gpio_value |= 1 << pin;
} else {
self.gpio_value &= !(1 << pin);
}
log::debug!(
"gpio_set dir={:x} value={:x}",
self.gpio_direction.bits(),
self.gpio_value
);
let command = Command::SetLowGpio(self.gpio_direction, self.gpio_value);
self.execute(&mut [command])
}
pub fn gpio_set_direction(&mut self, pin: u8, output: bool) -> Result<()> {
let direction =
GpioDirection::from_bits(1 << pin).ok_or(GpioError::InvalidPinNumber(pin))?;
if output {
self.gpio_direction |= direction;
} else {
self.gpio_direction &= direction;
}
let _ = self.gpio_get()?;
Ok(())
}
pub fn set_clock_frequency(&mut self, frequency: u32) -> Result<()> {
let base = self.max_clock_frequency;
let divisor = base / frequency - 1;
let actual = base / (divisor + 1);
log::debug!(
"mpsse: requested clock frequency {}. actual={}",
frequency,
actual
);
self.execute(&mut [Command::SetClockDivisor(divisor as u16)])?;
self.clock_frequency = actual;
Ok(())
}
pub fn invalid_command(&mut self) -> Result<()> {
self.execute(&mut [Command::InvalidCommand])
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_data_shift_options() {
let opt = DataShiftOptions {
read_clock_edge: ClockEdge::Rising,
read_data: true,
..Default::default()
};
assert_eq!(opt.as_opcode(), 0x20);
let opt = DataShiftOptions {
read_clock_edge: ClockEdge::Falling,
read_data: true,
..Default::default()
};
assert_eq!(opt.as_opcode(), 0x24);
let opt = DataShiftOptions {
read_clock_edge: ClockEdge::Falling,
read_data: true,
bit_direction: BitDirection::LsbFirst,
..Default::default()
};
assert_eq!(opt.as_opcode(), 0x2c);
let opt = DataShiftOptions {
write_clock_edge: ClockEdge::Rising,
write_data: true,
..Default::default()
};
assert_eq!(opt.as_opcode(), 0x10);
let opt = DataShiftOptions {
write_clock_edge: ClockEdge::Falling,
write_data: true,
..Default::default()
};
assert_eq!(opt.as_opcode(), 0x11);
let opt = DataShiftOptions {
read_clock_edge: ClockEdge::Rising,
write_clock_edge: ClockEdge::Falling,
read_data: true,
write_data: true,
..Default::default()
};
assert_eq!(opt.as_opcode(), 0x31);
}
#[test]
fn test_command_read_data() -> Result<()> {
let mut read_buf = [0u8; 8];
let opt = DataShiftOptions {
read_clock_edge: ClockEdge::Rising,
read_data: true,
..Default::default()
};
let command = Command::ReadData(opt, &mut read_buf);
assert_eq!(command.response_length(), 8);
let mut low_level_command = Vec::new();
command.extend(&mut low_level_command)?;
assert_eq!(&low_level_command, &[0x20, 7, 0]);
Ok(())
}
#[test]
fn test_command_write_data() -> Result<()> {
let write_buf = [1u8, 2, 3, 4, 5];
let opt = DataShiftOptions {
write_clock_edge: ClockEdge::Falling,
write_data: true,
..Default::default()
};
let command = Command::WriteData(opt, &write_buf);
assert_eq!(command.response_length(), 0);
let mut low_level_command = Vec::new();
command.extend(&mut low_level_command)?;
assert_eq!(&low_level_command, &[0x11, 4, 0, 1, 2, 3, 4, 5]);
Ok(())
}
#[test]
fn test_command_transact_data() -> Result<()> {
let write_buf = [1u8, 2, 3, 4, 5];
let write_opt = DataShiftOptions {
write_clock_edge: ClockEdge::Falling,
write_data: true,
..Default::default()
};
let mut read_buf = [0u8; 5];
let read_opt = DataShiftOptions {
read_clock_edge: ClockEdge::Rising,
read_data: true,
..Default::default()
};
let command = Command::TransactData(write_opt, &write_buf, read_opt, &mut read_buf);
assert_eq!(command.response_length(), 5);
let mut low_level_command = Vec::new();
command.extend(&mut low_level_command)?;
assert_eq!(&low_level_command, &[0x31, 4, 0, 1, 2, 3, 4, 5]);
Ok(())
}
#[test]
fn test_set_gpio() -> Result<()> {
let direction = GpioDirection::OUT_0 | GpioDirection::OUT_1;
let command = Command::SetLowGpio(direction, 0x5a);
assert_eq!(command.response_length(), 0);
let mut low_level_command = Vec::new();
command.extend(&mut low_level_command)?;
assert_eq!(&low_level_command, &[0x80, 0x5a, 0x3]);
Ok(())
}
#[test]
fn test_get_gpio() -> Result<()> {
let mut value = 0u8;
let command = Command::GetLowGpio(&mut value);
assert_eq!(command.response_length(), 1);
let mut low_level_command = Vec::new();
command.extend(&mut low_level_command)?;
assert_eq!(&low_level_command, &[0x81]);
Ok(())
}
#[test]
fn test_set_clock_divisor() -> Result<()> {
let command = Command::SetClockDivisor(0x1234);
assert_eq!(command.response_length(), 0);
let mut low_level_command = Vec::new();
command.extend(&mut low_level_command)?;
assert_eq!(&low_level_command, &[0x86, 0x34, 0x12]);
Ok(())
}
#[test]
fn test_disable_div_by_five() -> Result<()> {
let command = Command::DisableDivBy5;
assert_eq!(command.response_length(), 0);
let mut low_level_command = Vec::new();
command.extend(&mut low_level_command)?;
assert_eq!(&low_level_command, &[0x8a]);
Ok(())
}
#[test]
fn test_invalid_command() -> Result<()> {
let command = Command::InvalidCommand;
assert_eq!(command.response_length(), 0);
let mut low_level_command = Vec::new();
command.extend(&mut low_level_command)?;
assert_eq!(&low_level_command, &[0xaa]);
Ok(())
}
}