#![deny(missing_docs, unsafe_code)]
use super::{BitMode, DeviceType, FtStatus, FtdiCommon, TimeoutError};
use std::convert::From;
use std::time::Duration;
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
#[repr(u8)]
enum MpsseCmd {
SetDataBitsLowbyte = 0x80,
GetDataBitsLowbyte = 0x81,
SetDataBitsHighbyte = 0x82,
GetDataBitsHighbyte = 0x83,
EnableLoopback = 0x84,
DisableLoopback = 0x85,
SetClockFrequency = 0x86,
SendImmediate = 0x87,
DisableClockDivide = 0x8A,
EnableClockDivide = 0x8B,
Enable3PhaseClocking = 0x8C,
Disable3PhaseClocking = 0x8D,
}
#[repr(u8)]
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum ClockDataOut {
MsbPos = 0x10,
MsbNeg = 0x11,
LsbPos = 0x18,
LsbNeg = 0x19,
}
impl From<ClockDataOut> for u8 {
fn from(value: ClockDataOut) -> u8 {
value as u8
}
}
#[repr(u8)]
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum ClockBitsOut {
MsbPos = 0x12,
MsbNeg = 0x13,
LsbPos = 0x1A,
LsbNeg = 0x1B,
}
impl From<ClockBitsOut> for u8 {
fn from(value: ClockBitsOut) -> u8 {
value as u8
}
}
#[repr(u8)]
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum ClockDataIn {
MsbPos = 0x20,
MsbNeg = 0x24,
LsbPos = 0x28,
LsbNeg = 0x2C,
}
impl From<ClockDataIn> for u8 {
fn from(value: ClockDataIn) -> u8 {
value as u8
}
}
#[repr(u8)]
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum ClockBitsIn {
MsbPos = 0x22,
MsbNeg = 0x26,
LsbPos = 0x2A,
LsbNeg = 0x2E,
}
impl From<ClockBitsIn> for u8 {
fn from(value: ClockBitsIn) -> u8 {
value as u8
}
}
#[repr(u8)]
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum ClockData {
MsbPosIn = 0x31,
MsbNegIn = 0x34,
LsbPosIn = 0x39,
LsbNegIn = 0x3C,
}
impl From<ClockData> for u8 {
fn from(value: ClockData) -> u8 {
value as u8
}
}
#[repr(u8)]
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum ClockBits {
MsbPosIn = 0x33,
MsbNegIn = 0x36,
LsbPosIn = 0x3B,
LsbNegIn = 0x3E,
}
impl From<ClockBits> for u8 {
fn from(value: ClockBits) -> u8 {
value as u8
}
}
const ECHO_CMD_2: u8 = 0xAB;
impl From<MpsseCmd> for u8 {
fn from(value: MpsseCmd) -> Self {
value as u8
}
}
fn check_limits(device: DeviceType, frequency: u32, max: u32) {
const MIN: u32 = 92;
assert!(
frequency >= MIN,
"frequency of {} exceeds minimum of {} for {:?}",
frequency,
MIN,
device
);
assert!(
frequency <= max,
"frequency of {} exceeds maximum of {} for {:?}",
frequency,
max,
device
);
}
fn clock_divisor(device: DeviceType, frequency: u32) -> (u32, Option<MpsseCmd>) {
match device {
DeviceType::FT2232C => {
check_limits(device, frequency, 6_000_000);
(6_000_000 / frequency - 1, None)
}
DeviceType::FT2232H | DeviceType::FT4232H | DeviceType::FT232H => {
check_limits(device, frequency, 30_000_000);
if frequency <= 6_000_000 {
(6_000_000 / frequency - 1, Some(MpsseCmd::EnableClockDivide))
} else {
(
30_000_000 / frequency - 1,
Some(MpsseCmd::DisableClockDivide),
)
}
}
_ => panic!("Unknown device type: {:?}", device),
}
}
#[cfg(test)]
mod clock_divisor {
use super::*;
macro_rules! pos {
($NAME:ident, $DEVICE:expr, $FREQ:expr, $OUT:expr) => {
#[test]
fn $NAME() {
assert_eq!(clock_divisor($DEVICE, $FREQ), $OUT);
}
};
}
macro_rules! neg {
($NAME:ident, $DEVICE:expr, $FREQ:expr) => {
#[test]
#[should_panic]
fn $NAME() {
clock_divisor($DEVICE, $FREQ);
}
};
}
pos!(ft232c_min, DeviceType::FT2232C, 92, (65216, None));
pos!(ft232c_max, DeviceType::FT2232C, 6_000_000, (0, None));
pos!(
min,
DeviceType::FT2232H,
92,
(65216, Some(MpsseCmd::EnableClockDivide))
);
pos!(
max_with_div,
DeviceType::FT2232H,
6_000_000,
(0, Some(MpsseCmd::EnableClockDivide))
);
pos!(
min_without_div,
DeviceType::FT2232H,
6_000_001,
(3, Some(MpsseCmd::DisableClockDivide))
);
pos!(
max,
DeviceType::FT4232H,
30_000_000,
(0, Some(MpsseCmd::DisableClockDivide))
);
neg!(panic_unknown, DeviceType::Unknown, 1_000);
neg!(panic_ft232c_min, DeviceType::FT2232C, 91);
neg!(panic_ft232c_max, DeviceType::FT2232C, 6_000_001);
neg!(panic_min, DeviceType::FT232H, 91);
neg!(panic_max, DeviceType::FT232H, 30_000_001);
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct MpsseSettings {
pub reset: bool,
pub in_transfer_size: u32,
pub read_timeout: Duration,
pub write_timeout: Duration,
pub latency_timer: Duration,
pub mask: u8,
pub clock_frequency: Option<u32>,
}
impl std::default::Default for MpsseSettings {
fn default() -> Self {
MpsseSettings {
reset: true,
in_transfer_size: 4096,
read_timeout: Duration::from_secs(1),
write_timeout: Duration::from_secs(1),
latency_timer: Duration::from_millis(16),
mask: 0x00,
clock_frequency: None,
}
}
}
pub trait FtdiMpsse: FtdiCommon {
fn set_clock(&mut self, frequency: u32) -> Result<(), TimeoutError> {
let (value, divisor) = clock_divisor(Self::DEVICE_TYPE, frequency);
debug_assert!(value <= 0xFFFF);
let mut buf: Vec<u8> = Vec::new();
if let Some(div) = divisor {
buf.push(div.into());
};
buf.push(MpsseCmd::SetClockFrequency.into());
buf.push((value & 0xFF) as u8);
buf.push(((value >> 8) & 0xFF) as u8);
self.write_all(&buf.as_slice())
}
fn initialize_mpsse(&mut self, settings: &MpsseSettings) -> Result<(), TimeoutError> {
if settings.reset {
self.reset()?;
}
self.purge_rx()?;
debug_assert_eq!(self.queue_status()?, 0);
self.set_usb_parameters(settings.in_transfer_size)?;
self.set_chars(0, false, 0, false)?;
self.set_timeouts(settings.read_timeout, settings.write_timeout)?;
self.set_latency_timer(settings.latency_timer)?;
self.set_flow_control_rts_cts()?;
self.set_bit_mode(0x0, BitMode::Reset)?;
self.set_bit_mode(settings.mask, BitMode::Mpsse)?;
self.enable_loopback()?;
self.synchronize_mpsse()?;
let mut mpsse_cmd = MpsseCmdBuilder::new().disable_loopback();
if let Some(frequency) = settings.clock_frequency {
mpsse_cmd = mpsse_cmd.set_clock(frequency, Self::DEVICE_TYPE);
}
self.write_all(mpsse_cmd.as_slice())?;
Ok(())
}
fn initialize_mpsse_default(&mut self) -> Result<(), TimeoutError> {
self.initialize_mpsse(&MpsseSettings::default())
}
fn synchronize_mpsse(&mut self) -> Result<(), TimeoutError> {
self.purge_rx()?;
debug_assert_eq!(self.queue_status()?, 0);
self.write_all(&[ECHO_CMD_2])?;
let mut buf: [u8; 2] = [0; 2];
self.read_all(&mut buf)?;
if buf[0] == 0xFA && buf[1] == ECHO_CMD_2 {
Ok(())
} else {
Err(TimeoutError::from(FtStatus::OTHER_ERROR))
}
}
fn enable_loopback(&mut self) -> Result<(), TimeoutError> {
self.write_all(&[MpsseCmd::EnableLoopback.into()])
}
fn disable_loopback(&mut self) -> Result<(), TimeoutError> {
self.write_all(&[MpsseCmd::DisableLoopback.into()])
}
fn set_gpio_lower(&mut self, state: u8, direction: u8) -> Result<(), TimeoutError> {
self.write_all(&[MpsseCmd::SetDataBitsLowbyte.into(), state, direction])
}
fn gpio_lower(&mut self) -> Result<u8, TimeoutError> {
self.write_all(&[
MpsseCmd::GetDataBitsLowbyte.into(),
MpsseCmd::SendImmediate.into(),
])?;
let mut buf: [u8; 1] = [0];
self.read_all(&mut buf)?;
Ok(buf[0])
}
fn set_gpio_upper(&mut self, state: u8, direction: u8) -> Result<(), TimeoutError> {
self.write_all(&[MpsseCmd::SetDataBitsHighbyte.into(), state, direction])
}
fn gpio_upper(&mut self) -> Result<u8, TimeoutError> {
self.write_all(&[
MpsseCmd::GetDataBitsHighbyte.into(),
MpsseCmd::SendImmediate.into(),
])?;
let mut buf: [u8; 1] = [0];
self.read_all(&mut buf)?;
Ok(buf[0])
}
fn clock_data_out(&mut self, mode: ClockDataOut, data: &[u8]) -> Result<(), TimeoutError> {
let mut len = data.len();
if len == 0 {
return Ok(());
}
len -= 1;
assert!(len <= 65536);
let mut payload = vec![mode.into(), (len & 0xFF) as u8, ((len >> 8) & 0xFF) as u8];
payload.extend_from_slice(&data);
self.write_all(&payload.as_slice())
}
fn clock_data_in(&mut self, mode: ClockDataIn, data: &mut [u8]) -> Result<(), TimeoutError> {
let mut len = data.len();
if len == 0 {
return Ok(());
}
len -= 1;
assert!(len <= 65536);
self.write_all(&[mode.into(), (len & 0xFF) as u8, ((len >> 8) & 0xFF) as u8])?;
self.read_all(data)
}
fn clock_data(&mut self, mode: ClockData, data: &mut [u8]) -> Result<(), TimeoutError> {
let mut len = data.len();
if len == 0 {
return Ok(());
}
len -= 1;
assert!(len <= 65536);
let mut payload = vec![mode.into(), (len & 0xFF) as u8, ((len >> 8) & 0xFF) as u8];
payload.extend_from_slice(&data);
self.write_all(&payload.as_slice())?;
self.read_all(data)
}
}
pub trait Ftx232hMpsse: FtdiMpsse {
fn enable_3phase_data_clocking(&mut self) -> Result<(), TimeoutError> {
self.write_all(&[MpsseCmd::Enable3PhaseClocking.into()])
}
fn disable_3phase_data_clocking(&mut self) -> Result<(), TimeoutError> {
self.write_all(&[MpsseCmd::Disable3PhaseClocking.into()])
}
}
pub struct MpsseCmdBuilder(pub Vec<u8>);
impl MpsseCmdBuilder {
pub const fn new() -> MpsseCmdBuilder {
MpsseCmdBuilder(Vec::new())
}
pub const fn with_vec(vec: Vec<u8>) -> MpsseCmdBuilder {
MpsseCmdBuilder(vec)
}
pub fn as_slice(&self) -> &[u8] {
self.0.as_slice()
}
pub fn set_clock(mut self, frequency: u32, device_type: DeviceType) -> Self {
let (value, divisor) = clock_divisor(device_type, frequency);
debug_assert!(value <= 0xFFFF);
if let Some(div) = divisor {
self.0.push(div.into());
};
self.0.push(MpsseCmd::SetClockFrequency.into());
self.0.push((value & 0xFF) as u8);
self.0.push(((value >> 8) & 0xFF) as u8);
self
}
pub fn enable_loopback(mut self) -> Self {
self.0.push(MpsseCmd::EnableLoopback.into());
self
}
pub fn disable_loopback(mut self) -> Self {
self.0.push(MpsseCmd::DisableLoopback.into());
self
}
pub fn disable_3phase_data_clocking(mut self) -> Self {
self.0.push(MpsseCmd::Disable3PhaseClocking.into());
self
}
pub fn enable_3phase_data_clocking(mut self) -> Self {
self.0.push(MpsseCmd::Enable3PhaseClocking.into());
self
}
pub fn set_gpio_lower(mut self, state: u8, direction: u8) -> Self {
self.0
.extend_from_slice(&[MpsseCmd::SetDataBitsLowbyte.into(), state, direction]);
self
}
pub fn set_gpio_upper(mut self, state: u8, direction: u8) -> Self {
self.0
.extend_from_slice(&[MpsseCmd::SetDataBitsHighbyte.into(), state, direction]);
self
}
pub fn gpio_lower(mut self) -> Self {
self.0.push(MpsseCmd::GetDataBitsLowbyte.into());
self
}
pub fn gpio_upper(mut self) -> Self {
self.0.push(MpsseCmd::GetDataBitsHighbyte.into());
self
}
pub fn send_immediate(mut self) -> Self {
self.0.push(MpsseCmd::SendImmediate.into());
self
}
pub fn clock_data_out(mut self, mode: ClockDataOut, data: &[u8]) -> Self {
let mut len = data.len();
assert!(len <= 65536, "data length cannot exceed u16::MAX + 1");
if len == 0 {
return self;
}
len -= 1;
self.0
.extend_from_slice(&[mode.into(), (len & 0xFF) as u8, ((len >> 8) & 0xFF) as u8]);
self.0.extend_from_slice(&data);
self
}
pub fn clock_data_in(mut self, mode: ClockDataIn, mut len: usize) -> Self {
assert!(len <= 65536, "data length cannot exceed u16::MAX + 1");
if len == 0 {
return self;
}
len -= 1;
self.0
.extend_from_slice(&[mode.into(), (len & 0xFF) as u8, ((len >> 8) & 0xFF) as u8]);
self
}
pub fn clock_data(mut self, mode: ClockData, data: &[u8]) -> Self {
let mut len = data.len();
assert!(len <= 65536, "data length cannot exceed u16::MAX + 1");
if len == 0 {
return self;
}
len -= 1;
self.0
.extend_from_slice(&[mode.into(), (len & 0xFF) as u8, ((len >> 8) & 0xFF) as u8]);
self.0.extend_from_slice(&data);
self
}
pub fn clock_bits_out(mut self, mode: ClockBitsOut, data: u8, mut len: u8) -> Self {
assert!(len <= 8, "data length cannot exceed 8");
if len == 0 {
return self;
}
len -= 1;
self.0.extend_from_slice(&[mode.into(), len, data]);
self
}
pub fn clock_bits_in(mut self, mode: ClockBitsIn, mut len: u8) -> Self {
assert!(len <= 8, "data length cannot exceed 8");
if len == 0 {
return self;
}
len -= 1;
self.0.extend_from_slice(&[mode.into(), len]);
self
}
pub fn clock_bits(mut self, mode: ClockBits, data: u8, mut len: u8) -> Self {
assert!(len <= 8, "data length cannot exceed 8");
if len == 0 {
return self;
}
len -= 1;
self.0.extend_from_slice(&[mode.into(), len, data]);
self
}
}