From 1e169f31ea99a932338c2eb007bd20d383c16101 Mon Sep 17 00:00:00 2001 From: Farmadupe Date: Mon, 2 Sep 2024 18:17:12 +0100 Subject: [PATCH] Add FT240X EEPROM. Remove FtdiMpsse impl for FT232R because it is unsupported (#79) * Add FT240X EEPROM. Remove FtdiMpsse impl for FT232R because it is unsupported. * Add FT240X EEPROM impls * Factorize out mpsse functions from impl_boilerplate_for! macro, as not all devices support mpsse (FT240X, FT232R) * Remove impl of FtdiMpsse for Ft232R, as the docs say it is not supported (breaking change) * Inhibit tests for set_vid_pid functions on windows, as those functions are not available. * X series eeprom can invert UART signals * Respond to comments --- src/lib.rs | 53 ++++++++++++++----- src/types.rs | 143 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 184 insertions(+), 12 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 4642a45..392308e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -105,7 +105,8 @@ use types::{vid_pid_from_id, STRING_LEN}; pub use types::{ BitMode, BitsPerWord, ByteOrder, Cbus232h, Cbus232r, CbusX, ClockPolarity, DeviceInfo, DeviceStatus, DeviceType, DriveCurrent, DriverType, Eeprom2232h, Eeprom232h, Eeprom232r, - Eeprom4232h, EepromHeader, EepromStrings, ModemStatus, Parity, Speed, StopBits, Version, + Eeprom4232h, EepromHeader, EepromStrings, EepromXSeries, ModemStatus, Parity, Speed, StopBits, + Version, }; mod util; @@ -125,8 +126,8 @@ use libftd2xx_ffi::{ FT_SetBreakOn, FT_SetChars, FT_SetDataCharacteristics, FT_SetDeadmanTimeout, FT_SetDtr, FT_SetFlowControl, FT_SetLatencyTimer, FT_SetRts, FT_SetTimeouts, FT_SetUSBParameters, FT_Write, FT_WriteEE, FT_DEVICE_LIST_INFO_NODE, FT_EEPROM_2232H, FT_EEPROM_232H, - FT_EEPROM_232R, FT_EEPROM_4232H, FT_FLOW_DTR_DSR, FT_FLOW_NONE, FT_FLOW_RTS_CTS, - FT_FLOW_XON_XOFF, FT_HANDLE, FT_LIST_NUMBER_ONLY, FT_OPEN_BY_DESCRIPTION, + FT_EEPROM_232R, FT_EEPROM_4232H, FT_EEPROM_X_SERIES, FT_FLOW_DTR_DSR, FT_FLOW_NONE, + FT_FLOW_RTS_CTS, FT_FLOW_XON_XOFF, FT_HANDLE, FT_LIST_NUMBER_ONLY, FT_OPEN_BY_DESCRIPTION, FT_OPEN_BY_SERIAL_NUMBER, FT_PURGE_RX, FT_PURGE_TX, FT_STATUS, }; @@ -206,7 +207,7 @@ pub fn num_devices() -> Result { /// assert_eq!(pid, 0x1234); /// # Ok::<(), libftd2xx::FtStatus>(()) /// ``` -#[cfg(any(unix, doc))] +#[cfg(all(any(unix, doc), not(all(windows, doctest))))] #[cfg_attr(docsrs, doc(cfg(unix)))] pub fn set_vid_pid(vid: u16, pid: u16) -> Result<(), FtStatus> { trace!("FT_SetVIDPID({}, {})", vid, pid); @@ -235,7 +236,7 @@ pub fn set_vid_pid(vid: u16, pid: u16) -> Result<(), FtStatus> { /// println!("PID: 0x{:04X}", vid); /// # Ok::<(), libftd2xx::FtStatus>(()) /// ``` -#[cfg(any(unix, doc))] +#[cfg(all(any(unix, doc), not(all(windows, doctest))))] #[cfg_attr(docsrs, doc(cfg(unix)))] pub fn vid_pid() -> Result<(u32, u32), FtStatus> { let mut vid: u32 = 0; @@ -588,6 +589,23 @@ pub struct Ft4232ha { ftdi: Ftdi, } +/// FT X Series device. +/// +/// # Example +/// +/// Converting from an unknown FTDI device. +/// +/// ```no_run +/// use libftd2xx::{FtXSeries, Ftdi}; +/// +/// let ft_x_series: FtXSeries = Ftdi::new()?.try_into()?; +/// # Ok::<(), libftd2xx::DeviceTypeError>(()) +/// ``` +#[derive(Debug)] +pub struct FtXSeries { + ftdi: Ftdi, +} + /// FTD2XX functions common to all devices. pub trait FtdiCommon { /// FTDI device type. @@ -2366,7 +2384,7 @@ impl Drop for Ftdi { unsafe impl Send for Ftdi {} unsafe impl Sync for Ftdi {} -macro_rules! impl_boilerplate_for { +macro_rules! impl_ftdi_common_for { ($DEVICE:ident, $TYPE:expr) => { impl FtdiCommon for $DEVICE { const DEVICE_TYPE: DeviceType = $TYPE; @@ -2379,7 +2397,11 @@ macro_rules! impl_boilerplate_for { Ok(Self::DEVICE_TYPE) } } + }; +} +macro_rules! impl_mpsse_cmd_executor_for { + ($DEVICE:ident, $TYPE:expr) => { impl MpsseCmdExecutor for $DEVICE { type Error = TimeoutError; @@ -2418,25 +2440,32 @@ macro_rules! impl_try_from_for { }; } -impl_boilerplate_for!(Ft232h, DeviceType::FT232H); -impl_boilerplate_for!(Ft232r, DeviceType::FT232R); -impl_boilerplate_for!(Ft2232h, DeviceType::FT2232H); -impl_boilerplate_for!(Ft4232h, DeviceType::FT4232H); -impl_boilerplate_for!(Ft4232ha, DeviceType::FT4232HA); +impl_ftdi_common_for!(Ft232h, DeviceType::FT232H); +impl_ftdi_common_for!(Ft232r, DeviceType::FT232R); +impl_ftdi_common_for!(Ft2232h, DeviceType::FT2232H); +impl_ftdi_common_for!(Ft4232h, DeviceType::FT4232H); +impl_ftdi_common_for!(Ft4232ha, DeviceType::FT4232HA); +impl_ftdi_common_for!(FtXSeries, DeviceType::FT_X_SERIES); + +impl_mpsse_cmd_executor_for!(Ft232h, DeviceType::FT232H); +impl_mpsse_cmd_executor_for!(Ft2232h, DeviceType::FT2232H); +impl_mpsse_cmd_executor_for!(Ft4232h, DeviceType::FT4232H); +impl_mpsse_cmd_executor_for!(Ft4232ha, DeviceType::FT4232HA); impl_try_from_for!(Ft232h); impl_try_from_for!(Ft232r); impl_try_from_for!(Ft2232h); impl_try_from_for!(Ft4232h); impl_try_from_for!(Ft4232ha); +impl_try_from_for!(FtXSeries); impl FtdiEeprom for Ft232h {} impl FtdiEeprom for Ft232r {} impl FtdiEeprom for Ft2232h {} impl FtdiEeprom for Ft4232h {} +impl FtdiEeprom for FtXSeries {} impl FtdiMpsse for Ft232h {} -impl FtdiMpsse for Ft232r {} impl FtdiMpsse for Ft2232h {} impl FtdiMpsse for Ft4232h {} impl FtdiMpsse for Ft4232ha {} diff --git a/src/types.rs b/src/types.rs index e3add84..0e08798 100644 --- a/src/types.rs +++ b/src/types.rs @@ -51,6 +51,7 @@ use libftd2xx_ffi::{FT_DRIVER_TYPE_D2XX, FT_DRIVER_TYPE_VCP}; // FT_EEPROM_ use libftd2xx_ffi::{ FT_EEPROM_2232H, FT_EEPROM_232H, FT_EEPROM_232R, FT_EEPROM_4232H, FT_EEPROM_HEADER, + FT_EEPROM_X_SERIES, }; use super::{EepromStringsError, EepromValueError}; @@ -1272,6 +1273,115 @@ impl Eeprom4232h { } } +/// EEPROM structure for the FT X series. +/// +/// This is used by the [`eeprom_read`] and [`eeprom_program`] methods. +/// +/// [`eeprom_read`]: crate::FtdiEeprom::eeprom_read +/// [`eeprom_program`]: crate::FtdiEeprom::eeprom_program +#[derive(Debug, Copy, Clone)] +pub struct EepromXSeries(FT_EEPROM_X_SERIES); + +impl From for FT_EEPROM_X_SERIES { + fn from(val: EepromXSeries) -> FT_EEPROM_X_SERIES { + val.0 + } +} + +impl From for EepromXSeries { + fn from(val: FT_EEPROM_X_SERIES) -> EepromXSeries { + EepromXSeries(val) + } +} + +impl Default for EepromXSeries { + fn default() -> Self { + let mut header = EepromHeader::default(); + header.set_device_type(DeviceType::FT_X_SERIES); + header.set_product_id(0x6015); + header.set_max_current(90); + Self(FT_EEPROM_X_SERIES { + common: header.0, + ACSlowSlew: 0, + ACSchmittInput: 0, + ACDriveCurrent: 4, + ADSlowSlew: 0, + ADSchmittInput: 0, + ADDriveCurrent: 4, + Cbus0: 0, + Cbus1: 0, + Cbus2: 0, + Cbus3: 0, + Cbus4: 0, + Cbus5: FT_X_SERIES_CBUS_VBUS_SENSE as u8, + Cbus6: FT_X_SERIES_CBUS_KEEP_AWAKE as u8, + InvertTXD: 0, + InvertRXD: 0, + InvertRTS: 0, + InvertCTS: 0, + InvertDTR: 0, + InvertDSR: 0, + InvertDCD: 0, + InvertRI: 0, + BCDEnable: 0, + BCDForceCbusPWREN: 0, + BCDDisableSleep: 0, + I2CSlaveAddress: 0, + I2CDeviceId: 0, + I2CDisableSchmitt: 0, + FT1248Cpol: 0, + FT1248Lsb: 0, + FT1248FlowControl: 0, + RS485EchoSuppress: 0, + PowerSaveEnable: 0, + DriverType: 0, + }) + } +} + +impl EepromXSeries { + /// Get the EEPROM header. + pub fn header(&self) -> EepromHeader { + EepromHeader((self.0).common) + } + + /// Set the EEPROM header. + pub fn set_header(&mut self, header: EepromHeader) { + (self.0).common = header.into() + } + + /// get battery charge enable + pub fn battery_charge_enable(&self) -> bool { + //assume nonzero + self.0.BCDEnable != 0 + } + + /// set battery charge enable + pub fn set_battery_charge_enable(&mut self, val: bool) { + self.0.BCDEnable = val.into(); + } + + /// get battery charge force power enable + pub fn battery_charge_force_power_enable(&self) -> bool { + self.0.BCDForceCbusPWREN != 0 + } + + /// set battery charge force power enable + pub fn set_battery_charge_force_power_enable(&mut self, val: bool) { + self.0.BCDForceCbusPWREN = val.into() + } + + /// get battery charge deactivate sleep + pub fn battery_charge_deactivate_sleep(&self) -> bool { + self.0.BCDDisableSleep != 0 + } + + /// set battery charge deactivate sleep + pub fn set_battery_charge_deactivate_sleep(&mut self, val: bool) { + self.0.BCDDisableSleep = val.into() + } +} + /// FTDI EEPROM header common to all FTDI devices. #[derive(Debug, Copy, Clone)] pub struct EepromHeader(FT_EEPROM_HEADER); @@ -1644,6 +1754,26 @@ macro_rules! impl_tx_data_enable { }; } +macro_rules! impl_invert_232_signals { + ($NAME:ident, $($FIELD:ident),+) => { + impl $NAME { + $( + paste::item! { + #[doc = "Get if " $FIELD " is inverted."] + pub fn [](&self) -> bool { + return (self.0).[] == 1; + } + + #[doc = "Set whether " $FIELD " is inverted."] + pub fn [](&mut self, val: bool) { + (self.0).[] = val as u8; + } + } + )* + } + }; +} + macro_rules! impl_cbus { ( $NAME:ident, @@ -1677,16 +1807,29 @@ macro_rules! impl_cbus { } // this is where most of the boilerplate is implemented + +//Ft232h impl_bus_pins!(Eeprom232h, AD, AC); impl_cbus!( Eeprom232h, Cbus232h, Cbus0, Cbus1, Cbus2, Cbus3, Cbus4, Cbus5, Cbus6, Cbus7, Cbus8, Cbus9, ); impl_driver_type!(Eeprom232h); +//Ft232r +impl_driver_type!(Eeprom232r); +impl_invert_232_signals!(Eeprom232r, TXD, RXD, RTS, CTS, DTR, DSR, DCD, RI); + +//Ft4232h impl_bus_pins!(Eeprom4232h, A, B, C, D); impl_tx_data_enable!(Eeprom4232h, A, B, C, D); impl_driver_type!(Eeprom4232h, A, B, C, D); +//FtXseries +impl_cbus!(EepromXSeries, CbusX, Cbus5, Cbus6,); +impl_driver_type!(EepromXSeries); +impl_bus_pins!(EepromXSeries, AC, AD); +impl_invert_232_signals!(EepromXSeries, TXD, RXD, RTS, CTS, DTR, DSR, DCD, RI); + // These get around an annoyance with bindgen generating different types for // preprocessor macros on Linux vs Windows. const NORM_232R_CBUS_TXDEN: u8 = FT_232R_CBUS_TXDEN as u8;