#![deny(missing_docs, unsafe_code)]
use super::{BitMode, DeviceType, FtStatus, FtdiCommon, TimeoutError};
use ftdi_mpsse::mpsse;
use ftdi_mpsse::{ClockData, ClockDataIn, ClockDataOut};
use ftdi_mpsse::{MpsseCmdBuilder, MpsseSettings};
use std::convert::From;
const ECHO_CMD_2: u8 = 0xAB;
fn check_limits(device: DeviceType, frequency: u32, max: u32) {
const MIN: u32 = 92;
assert!(
frequency >= MIN,
"frequency of {frequency} exceeds minimum of {MIN} for {device:?}"
);
assert!(
frequency <= max,
"frequency of {frequency} exceeds maximum of {max} for {device:?}"
);
}
fn clock_divisor(device: DeviceType, frequency: u32) -> (u32, Option<bool>) {
match device {
DeviceType::FT2232C => {
check_limits(device, frequency, 6_000_000);
(6_000_000 / frequency - 1, None)
}
DeviceType::FT2232H | DeviceType::FT4232H | DeviceType::FT4232HA | DeviceType::FT232H => {
check_limits(device, frequency, 30_000_000);
if frequency <= 6_000_000 {
(6_000_000 / frequency - 1, Some(true))
} else {
(30_000_000 / frequency - 1, Some(false))
}
}
_ => 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(true)));
pos!(
max_with_div,
DeviceType::FT2232H,
6_000_000,
(0, Some(true))
);
pos!(
min_without_div,
DeviceType::FT2232H,
6_000_001,
(3, Some(false))
);
pos!(
ft4232h_max,
DeviceType::FT4232H,
30_000_000,
(0, Some(false))
);
pos!(
ft4232ha_max,
DeviceType::FT4232HA,
30_000_000,
(0, Some(false))
);
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);
}
pub trait FtdiMpsse: FtdiCommon {
fn set_clock(&mut self, frequency: u32) -> Result<(), TimeoutError> {
let (divisor, clkdiv) = clock_divisor(Self::DEVICE_TYPE, frequency);
debug_assert!(divisor <= 0xFFFF);
let cmd = MpsseCmdBuilder::new().set_clock(divisor, clkdiv);
self.write_all(cmd.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()?;
self.disable_loopback()?;
if let Some(frequency) = settings.clock_frequency {
self.set_clock(frequency)?;
}
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> {
mpsse! {
let cmd = { enable_loopback(); };
}
self.write_all(&cmd)
}
fn disable_loopback(&mut self) -> Result<(), TimeoutError> {
mpsse! {
let cmd = { disable_loopback(); };
}
self.write_all(&cmd)
}
fn set_gpio_lower(&mut self, state: u8, direction: u8) -> Result<(), TimeoutError> {
let cmd = MpsseCmdBuilder::new().set_gpio_lower(state, direction);
self.write_all(cmd.as_slice())
}
fn gpio_lower(&mut self) -> Result<u8, TimeoutError> {
let cmd = MpsseCmdBuilder::new().gpio_lower().send_immediate();
let mut buf: [u8; 1] = [0];
self.write_all(cmd.as_slice())?;
self.read_all(&mut buf)?;
Ok(buf[0])
}
fn set_gpio_upper(&mut self, state: u8, direction: u8) -> Result<(), TimeoutError> {
let cmd = MpsseCmdBuilder::new().set_gpio_upper(state, direction);
self.write_all(cmd.as_slice())
}
fn gpio_upper(&mut self) -> Result<u8, TimeoutError> {
let cmd = MpsseCmdBuilder::new().gpio_upper().send_immediate();
let mut buf: [u8; 1] = [0];
self.write_all(cmd.as_slice())?;
self.read_all(&mut buf)?;
Ok(buf[0])
}
fn clock_data_out(&mut self, mode: ClockDataOut, data: &[u8]) -> Result<(), TimeoutError> {
if data.is_empty() {
return Ok(());
}
let cmd = MpsseCmdBuilder::new().clock_data_out(mode, data);
self.write_all(cmd.as_slice())
}
fn clock_data_in(&mut self, mode: ClockDataIn, data: &mut [u8]) -> Result<(), TimeoutError> {
if data.is_empty() {
return Ok(());
}
let cmd = MpsseCmdBuilder::new().clock_data_in(mode, data.len());
self.write_all(cmd.as_slice())?;
self.read_all(data)
}
fn clock_data(&mut self, mode: ClockData, data: &mut [u8]) -> Result<(), TimeoutError> {
if data.is_empty() {
return Ok(());
}
let cmd = MpsseCmdBuilder::new().clock_data(mode, data);
self.write_all(cmd.as_slice())?;
self.read_all(data)
}
}
pub trait Ftx232hMpsse: FtdiMpsse {
fn enable_3phase_data_clocking(&mut self) -> Result<(), TimeoutError> {
mpsse! {
let cmd = { enable_3phase_data_clocking(); };
}
self.write_all(&cmd)
}
fn disable_3phase_data_clocking(&mut self) -> Result<(), TimeoutError> {
mpsse! {
let cmd = { disable_3phase_data_clocking(); };
}
self.write_all(&cmd)
}
}