Skip to content

Commit

Permalink
Add FT240X EEPROM. Remove FtdiMpsse impl for FT232R because it is uns…
Browse files Browse the repository at this point in the history
…upported (#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
  • Loading branch information
Farmadupe authored Sep 2, 2024
1 parent 8a3f502 commit 1e169f3
Show file tree
Hide file tree
Showing 2 changed files with 184 additions and 12 deletions.
53 changes: 41 additions & 12 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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,
};

Expand Down Expand Up @@ -206,7 +207,7 @@ pub fn num_devices() -> Result<u32, FtStatus> {
/// 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);
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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;
Expand All @@ -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;

Expand Down Expand Up @@ -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<FT_EEPROM_232H, Eeprom232h> for Ft232h {}
impl FtdiEeprom<FT_EEPROM_232R, Eeprom232r> for Ft232r {}
impl FtdiEeprom<FT_EEPROM_2232H, Eeprom2232h> for Ft2232h {}
impl FtdiEeprom<FT_EEPROM_4232H, Eeprom4232h> for Ft4232h {}
impl FtdiEeprom<FT_EEPROM_X_SERIES, EepromXSeries> for FtXSeries {}

impl FtdiMpsse for Ft232h {}
impl FtdiMpsse for Ft232r {}
impl FtdiMpsse for Ft2232h {}
impl FtdiMpsse for Ft4232h {}
impl FtdiMpsse for Ft4232ha {}
Expand Down
143 changes: 143 additions & 0 deletions src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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<EepromXSeries> for FT_EEPROM_X_SERIES {
fn from(val: EepromXSeries) -> FT_EEPROM_X_SERIES {
val.0
}
}

impl From<FT_EEPROM_X_SERIES> 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);
Expand Down Expand Up @@ -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 [<invert_ $FIELD:lower>](&self) -> bool {
return (self.0).[<Invert $FIELD:upper>] == 1;
}

#[doc = "Set whether " $FIELD " is inverted."]
pub fn [<set_invert_ $FIELD:lower>](&mut self, val: bool) {
(self.0).[<Invert $FIELD:upper>] = val as u8;
}
}
)*
}
};
}

macro_rules! impl_cbus {
(
$NAME:ident,
Expand Down Expand Up @@ -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;
Expand Down

0 comments on commit 1e169f3

Please sign in to comment.