libftd2xx/
lib.rs

1//! Rust safe wrapper for the [FTDI D2XX drivers].
2//!
3//! This takes the [libftd2xx-ffi] C bindings crate and extends it with rust
4//! safe wrappers.
5//!
6//! # Usage
7//! Simply add this crate as a dependency in your `Cargo.toml`.
8//!
9//! ```toml
10//! [dependencies.libftd2xx]
11//! version = "0.33.1"
12//! # statically link the vendor library, defaults to dynamic if not set
13//! # this will make things "just work" on Linux and Windows
14//! features = ["static"]
15//! ```
16//!
17//! This is a basic example to get your started.
18//! Check the source code or documentation for more examples.
19//! ```no_run
20//! use libftd2xx::{Ftdi, FtdiCommon};
21//!
22//! let mut ft = Ftdi::new()?;
23//! let info = ft.device_info()?;
24//! println!("Device information: {:?}", info);
25//! # Ok::<(), libftd2xx::FtStatus>(())
26//! ```
27//!
28//! This crate is just a wrapper around the FTD2XX driver; I2C, SPI, and GPIO
29//! examples using the [`embedded-hal`] traits can be found in
30//! [`ftdi-embedded-hal`].
31//!
32//! ## udev rules
33//! To access the FTDI USB device as a regular user on Linux you need to update
34//! the [udev] rules.
35//!
36//! Create a file called `/etc/udev/rules.d/99-ftdi.rules` with:
37//! ```text
38//! SUBSYSTEM=="usb", ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6001", MODE="0666"
39//! SUBSYSTEM=="usb", ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6010", MODE="0666"
40//! SUBSYSTEM=="usb", ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6011", MODE="0666"
41//! SUBSYSTEM=="usb", ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6014", MODE="0666"
42//! SUBSYSTEM=="usb", ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6015", MODE="0666"
43//! ```
44//!
45//! Then, reload the rules:
46//! ```bash
47//! sudo udevadm control --reload-rules
48//! sudo udevadm trigger
49//! ```
50//!
51//! ## Linking
52//!
53//! By default this crate with use dynamic linking for the vendor library.
54//! Use the `static` feature flag to enable static linking.
55//!
56//! ### Dynamic Linking on Linux
57//!
58//! The shared object `libftd2xx.so` must exist on your system.
59//! See [FTDI Drivers Installation Guide for Linux] for instructions.
60//!
61//! ### Dynamic Linking on Windows
62//!
63//! The FTD2XX DLL must exist on your system PATH.
64//! The easiest way to install this is with the vendor provided [setup executable].
65//!
66//! ### Static Linking on Linux or Windows
67//!
68//! No special considerations are needed, the static library is distributed with
69//! permission from FTDI in the [libftd2xx-ffi] crate.
70//!
71//! # References
72//!
73//! * [D2XX Programmers Guide V1.4]
74//! * [D2XX Drivers Download Page]
75//!
76//! # Troubleshooting
77//! ## Unknown Device on Linux
78//! Remove the VCP FTDI driver.
79//! ```bash
80//! sudo rmmod ftdi_sio
81//! sudo rmmod usbserial
82//! ```
83//! See [FTDI Drivers Installation Guide for Linux] for more details.
84//!
85//! [D2XX Drivers Download Page]: https://www.ftdichip.com/Drivers/D2XX.htm
86//! [D2xx Programmers Guide V1.4]: https://ftdichip.com/document/programming-guides/
87//! [FTDI D2XX drivers]: https://www.ftdichip.com/Drivers/D2XX.htm
88//! [FTDI Drivers Installation Guide for Linux]: http://www.ftdichip.cn/Support/Documents/AppNotes/AN_220_FTDI_Drivers_Installation_Guide_for_Linux.pdf
89//! [libftd2xx-ffi]: https://github.com/ftdi-rs/libftd2xx-ffi
90//! [setup executable]: https://www.ftdichip.com/Drivers/CDM/CDM21228_Setup.zip
91//! [udev]: https://en.wikipedia.org/wiki/Udev
92//! [`ftdi-embedded-hal`]: https://github.com/ftdi-rs/ftdi-embedded-hal
93//! [`embedded-hal`]: https://crates.io/crates/embedded-hal
94#![cfg_attr(docsrs, feature(doc_cfg))]
95#![deny(missing_docs)]
96
97mod errors;
98pub use errors::{DeviceTypeError, EepromStringsError, EepromValueError, FtStatus, TimeoutError};
99
100mod mpsse;
101pub use mpsse::{FtdiMpsse, Ftx232hMpsse};
102
103mod types;
104use types::{vid_pid_from_id, STRING_LEN};
105pub use types::{
106    BitMode, BitsPerWord, ByteOrder, Cbus232h, Cbus232r, CbusX, ClockPolarity, DeviceInfo,
107    DeviceStatus, DeviceType, DriveCurrent, DriverType, Eeprom2232h, Eeprom232h, Eeprom232r,
108    Eeprom4232h, EepromHeader, EepromStrings, EepromXSeries, ModemStatus, Parity, Speed, StopBits,
109    Version,
110};
111
112mod util;
113use util::slice_into_string;
114
115pub use ftdi_mpsse::{
116    ClockBits, ClockBitsIn, ClockBitsOut, ClockData, ClockDataIn, ClockDataOut, MpsseCmd,
117    MpsseCmdBuilder, MpsseCmdExecutor, MpsseSettings,
118};
119
120use libftd2xx_ffi::{
121    FT_Close, FT_ClrDtr, FT_ClrRts, FT_CreateDeviceInfoList, FT_EEPROM_Program, FT_EEPROM_Read,
122    FT_EE_UARead, FT_EE_UASize, FT_EE_UAWrite, FT_EraseEE, FT_GetBitMode, FT_GetDeviceInfo,
123    FT_GetDeviceInfoList, FT_GetDriverVersion, FT_GetLatencyTimer, FT_GetLibraryVersion,
124    FT_GetModemStatus, FT_GetQueueStatus, FT_GetStatus, FT_ListDevices, FT_Open, FT_OpenEx,
125    FT_Purge, FT_Read, FT_ReadEE, FT_ResetDevice, FT_SetBaudRate, FT_SetBitMode, FT_SetBreakOff,
126    FT_SetBreakOn, FT_SetChars, FT_SetDataCharacteristics, FT_SetDeadmanTimeout, FT_SetDtr,
127    FT_SetFlowControl, FT_SetLatencyTimer, FT_SetRts, FT_SetTimeouts, FT_SetUSBParameters,
128    FT_Write, FT_WriteEE, FT_DEVICE_LIST_INFO_NODE, FT_EEPROM_2232H, FT_EEPROM_232H,
129    FT_EEPROM_232R, FT_EEPROM_4232H, FT_EEPROM_X_SERIES, FT_FLOW_DTR_DSR, FT_FLOW_NONE,
130    FT_FLOW_RTS_CTS, FT_FLOW_XON_XOFF, FT_HANDLE, FT_LIST_NUMBER_ONLY, FT_OPEN_BY_DESCRIPTION,
131    FT_OPEN_BY_SERIAL_NUMBER, FT_PURGE_RX, FT_PURGE_TX, FT_STATUS,
132};
133
134#[cfg(target_os = "windows")]
135use libftd2xx_ffi::{FT_CyclePort, FT_GetComPortNumber, FT_Rescan, FT_ResetPort};
136
137#[cfg(any(target_os = "linux", target_os = "macos"))]
138use libftd2xx_ffi::{FT_GetVIDPID, FT_SetVIDPID};
139
140use log::trace;
141use std::ffi::c_void;
142use std::fs;
143use std::io;
144use std::mem;
145use std::os::raw::c_char;
146use std::path::Path;
147use std::time::Duration;
148use std::vec::Vec;
149
150/// FTDI USB vendor id.
151pub const FTDI_VID: u16 = 0x0403;
152
153fn ft_result<T>(value: T, status: FT_STATUS) -> Result<T, FtStatus> {
154    if status != 0 {
155        Err(status.into())
156    } else {
157        Ok(value)
158    }
159}
160
161/// Returns the number of FTDI devices connected to the system.
162///
163/// # Example
164///
165/// ```no_run
166/// use libftd2xx::num_devices;
167///
168/// let num_devices = num_devices()?;
169/// println!("Number of devices: {}", num_devices);
170/// # Ok::<(), libftd2xx::FtStatus>(())
171/// ```
172pub fn num_devices() -> Result<u32, FtStatus> {
173    let mut num_devs: u32 = 0;
174    let dummy = std::ptr::null_mut();
175    trace!(
176        "FT_ListDevices({}, NULL, {})",
177        num_devs,
178        FT_LIST_NUMBER_ONLY
179    );
180    let status: FT_STATUS = unsafe {
181        FT_ListDevices(
182            &mut num_devs as *mut u32 as *mut c_void,
183            dummy,
184            FT_LIST_NUMBER_ONLY,
185        )
186    };
187
188    ft_result(num_devs, status)
189}
190
191/// A command to include a custom VID and PID combination within the internal
192/// device list table.
193///
194/// This function is available on Linux or mac only.
195///
196/// This will allow the driver to load for the specified VID and PID
197/// combination.
198///
199/// # Example
200///
201/// ```no_run
202/// use libftd2xx::{set_vid_pid, vid_pid};
203///
204/// set_vid_pid(0x1234, 0x1234)?;
205/// let (vid, pid) = vid_pid()?;
206/// assert_eq!(vid, 0x1234);
207/// assert_eq!(pid, 0x1234);
208/// # Ok::<(), libftd2xx::FtStatus>(())
209/// ```
210#[cfg(all(any(unix, doc), not(all(windows, doctest))))]
211#[cfg_attr(docsrs, doc(cfg(unix)))]
212pub fn set_vid_pid(vid: u16, pid: u16) -> Result<(), FtStatus> {
213    trace!("FT_SetVIDPID({}, {})", vid, pid);
214    let status: FT_STATUS = unsafe { FT_SetVIDPID(vid.into(), pid.into()) };
215    ft_result((), status)
216}
217
218/// A command to retrieve the current VID and PID combination from within the
219/// internal device list table.
220///
221/// This function is available on Linux or mac only.
222///
223/// This `vid` and `pid` are set by [`set_vid_pid`].
224///
225/// **Note** this returns a tuple of `(u32, u32)`, these should be `u16` but
226/// the underlying type in the FTD2XX driver is a `u32`, and the choice to
227/// convert is left up to the user.
228///
229/// # Example
230///
231/// ```no_run
232/// use libftd2xx::vid_pid;
233///
234/// let (vid, pid) = vid_pid()?;
235/// println!("VID: 0x{:04X}", vid);
236/// println!("PID: 0x{:04X}", vid);
237/// # Ok::<(), libftd2xx::FtStatus>(())
238/// ```
239#[cfg(all(any(unix, doc), not(all(windows, doctest))))]
240#[cfg_attr(docsrs, doc(cfg(unix)))]
241pub fn vid_pid() -> Result<(u32, u32), FtStatus> {
242    let mut vid: u32 = 0;
243    let mut pid: u32 = 0;
244    trace!("FT_GetVIDPID(_, _)");
245    let status: FT_STATUS = unsafe { FT_GetVIDPID(&mut vid, &mut pid) };
246    ft_result((vid, pid), status)
247}
248
249/// Returns the version of the underlying C library.
250///
251/// **Note**: The documentation says this function is only supported on Windows
252/// but it seems to work correctly on Linux.
253///
254/// # Example
255///
256/// ```no_run
257/// use libftd2xx::library_version;
258///
259/// let version = library_version()?;
260/// println!("libftd2xx C library version: {}", version);
261/// # Ok::<(), libftd2xx::FtStatus>(())
262/// ```
263pub fn library_version() -> Result<Version, FtStatus> {
264    let mut version: u32 = 0;
265    trace!("FT_GetLibraryVersion(_)");
266    let status: FT_STATUS = unsafe { FT_GetLibraryVersion(&mut version) };
267
268    ft_result(Version::with_raw(version), status)
269}
270
271fn create_device_info_list() -> Result<u32, FtStatus> {
272    let mut num_devices: u32 = 0;
273    trace!("FT_CreateDeviceInfoList(_)");
274    let status: FT_STATUS = unsafe { FT_CreateDeviceInfoList(&mut num_devices) };
275    ft_result(num_devices, status)
276}
277
278/// This function returns a device information vector with information about
279/// the D2XX devices connected to the system.
280///
281/// # Example
282///
283/// ```no_run
284/// use libftd2xx::list_devices;
285///
286/// let mut devices = list_devices()?;
287///
288/// while let Some(device) = devices.pop() {
289///     println!("device: {:?}", device);
290/// }
291/// # Ok::<(), libftd2xx::FtStatus>(())
292/// ```
293pub fn list_devices() -> Result<Vec<DeviceInfo>, FtStatus> {
294    let mut devices: Vec<DeviceInfo> = Vec::new();
295    let mut num_devices: u32 = create_device_info_list()?;
296    let num_devices_usize: usize = usize::try_from(num_devices).unwrap();
297    if num_devices == 0 {
298        return Ok(devices);
299    }
300
301    let mut list_info_vec: Vec<FT_DEVICE_LIST_INFO_NODE> = vec![
302        FT_DEVICE_LIST_INFO_NODE {
303            Flags: 0,
304            Type: 0,
305            ID: 0,
306            LocId: 0,
307            SerialNumber: [0; 16],
308            Description: [0; 64],
309            ftHandle: std::ptr::null_mut(),
310        };
311        num_devices_usize
312    ];
313
314    trace!("FT_GetDeviceInfoList(_, _)");
315    let status: FT_STATUS = unsafe {
316        FT_GetDeviceInfoList(
317            list_info_vec.as_mut_ptr() as *mut FT_DEVICE_LIST_INFO_NODE,
318            &mut num_devices,
319        )
320    };
321
322    if status != 0 {
323        Err(status.into())
324    } else {
325        while let Some(info_node) = list_info_vec.pop() {
326            let (vid, pid) = vid_pid_from_id(info_node.ID);
327            devices.push(DeviceInfo {
328                port_open: info_node.Flags & 0x1 == 0x1,
329                speed: Some((info_node.Flags & 0x2).into()),
330                device_type: info_node.Type.into(),
331                product_id: pid,
332                vendor_id: vid,
333                serial_number: slice_into_string(info_node.SerialNumber.as_ref()),
334                description: slice_into_string(info_node.Description.as_ref()),
335            });
336        }
337        devices.sort_unstable();
338        Ok(devices)
339    }
340}
341
342/// Lists FTDI devices using the Linux file system.
343///
344/// There is a bug in the vendor driver where the `serial_number` and
345/// `description` fields may be blank on the FT4232H(A) FT2232H when only
346/// some of the ports are unbound from the `ftdi_sio` linux kernel module.
347///
348/// This will not work if you have a custom VID/PID programmed onto your FTDI
349/// device.
350///
351/// # Limitations
352///
353/// * `port_open` will always be `false`.
354/// * `speed` will currently be `None`.
355/// * This will return an empty vector if `/sys/bus/usb/devices` does not exist.
356///
357/// # Example
358///
359/// ```no_run
360/// use libftd2xx::list_devices_fs;
361///
362/// let mut devices = list_devices_fs()?;
363///
364/// while let Some(device) = devices.pop() {
365///     println!("device: {:?}", device);
366/// }
367/// # Ok::<(), std::boxed::Box<dyn std::error::Error>>(())
368/// ```
369#[allow(clippy::redundant_field_names)]
370pub fn list_devices_fs() -> io::Result<Vec<DeviceInfo>> {
371    let sys_bus_usb_devices = Path::new("/sys/bus/usb/devices");
372    let mut devices: Vec<DeviceInfo> = Vec::new();
373    if sys_bus_usb_devices.is_dir() {
374        for entry in fs::read_dir(sys_bus_usb_devices)? {
375            let entry = entry?;
376            let path = entry.path();
377            let mut vendor_path = path.clone();
378            vendor_path.push("idVendor");
379            if vendor_path.is_file() {
380                let vid: String = fs::read_to_string(vendor_path)?;
381                let vid: u16 = u16::from_str_radix(vid.trim(), 16)
382                    .expect("idVendor file contains non-hex digits");
383                if vid != FTDI_VID {
384                    continue;
385                }
386            } else {
387                continue;
388            }
389
390            let mut id_product_path = path.clone();
391            id_product_path.push("idProduct");
392            if !id_product_path.exists() {
393                continue;
394            }
395            let pid: String = fs::read_to_string(id_product_path)?;
396            let pid: u16 = u16::from_str_radix(pid.trim(), 16)
397                .expect("idProduct file contains non-hex digits");
398
399            let device_type: DeviceType = match DeviceType::with_pid(pid) {
400                Some(device_type) => device_type,
401                None => continue,
402            };
403
404            let serial: String = {
405                let mut serial_path = path.clone();
406                serial_path.push("serial");
407                if !serial_path.exists() {
408                    continue;
409                }
410                let mut data: String = fs::read_to_string(serial_path)?;
411                let ch = data.pop(); // remove newline
412                debug_assert_eq!(ch, Some('\n'));
413                data
414            };
415
416            let description: String = {
417                let mut product_path = path.clone();
418                product_path.push("product");
419                if !product_path.exists() {
420                    continue;
421                }
422                let mut data: String = fs::read_to_string(product_path)?;
423                let ch = data.pop(); // remove newline
424                debug_assert_eq!(ch, Some('\n'));
425                data
426            };
427
428            let port_letters: Option<&'static [char]> = match device_type {
429                DeviceType::FT2232H => Some(&['A', 'B']),
430                DeviceType::FT4232H | DeviceType::FT4232HA => Some(&['A', 'B', 'C', 'D']),
431                _ => None,
432            };
433
434            if let Some(port_letters) = port_letters {
435                for letter in port_letters {
436                    let mut port_serial = serial.clone();
437                    port_serial.push(*letter);
438                    let mut port_description = description.clone();
439                    port_description.push(' ');
440                    port_description.push(*letter);
441                    devices.push(DeviceInfo {
442                        port_open: false,
443                        speed: None,
444                        device_type: device_type,
445                        product_id: pid,
446                        vendor_id: FTDI_VID,
447                        serial_number: port_serial,
448                        description: port_description,
449                    })
450                }
451            } else {
452                devices.push(DeviceInfo {
453                    port_open: false,
454                    speed: None,
455                    device_type: device_type,
456                    product_id: pid,
457                    vendor_id: FTDI_VID,
458                    serial_number: serial,
459                    description: description,
460                })
461            }
462        }
463
464        devices.sort_unstable();
465        Ok(devices)
466    } else {
467        // windows
468        Ok(devices)
469    }
470}
471
472/// Rescan devices on the system.
473///
474/// This function is available on Windows only.
475///
476/// This can be of used when trying to recover devices programatically.
477///
478/// Calling `rescan` is equivalent to clicking the "Scan for hardware changes"
479/// button in the Device Manager.
480/// Only USB hardware is checked for new devices.
481/// All USB devices are scanned, not just FTDI devices.
482///
483/// # Example
484///
485/// ```no_run
486/// libftd2xx::rescan()?;
487/// # Ok::<(), libftd2xx::FtStatus>(())
488/// ```
489#[cfg(all(any(windows, doc), not(doctest)))]
490#[cfg_attr(docsrs, doc(cfg(windows)))]
491pub fn rescan() -> Result<(), FtStatus> {
492    trace!("FT_Rescan()");
493    let status: FT_STATUS = unsafe { FT_Rescan() };
494    ft_result((), status)
495}
496
497/// Generic FTDI device.
498///
499/// This structure can be used for all FTDI devices.
500/// A device-specific structure is only necessary to access the EEPROM traits
501/// for that device.
502#[derive(Debug)]
503pub struct Ftdi {
504    handle: FT_HANDLE,
505}
506
507/// FT232H device.
508///
509/// # Example
510///
511/// Converting from an unknown FTDI device.
512///
513/// ```no_run
514/// use libftd2xx::{Ft232h, Ftdi};
515///
516/// let ft232h: Ft232h = Ftdi::new()?.try_into()?;
517/// # Ok::<(), libftd2xx::DeviceTypeError>(())
518/// ```
519#[derive(Debug)]
520pub struct Ft232h {
521    ftdi: Ftdi,
522}
523
524/// FT232R device.
525///
526/// # Example
527///
528/// Converting from an unknown FTDI device.
529///
530/// ```no_run
531/// use libftd2xx::{Ft232r, Ftdi};
532///
533/// let ft232r: Ft232r = Ftdi::new()?.try_into()?;
534/// # Ok::<(), libftd2xx::DeviceTypeError>(())
535/// ```
536#[derive(Debug)]
537pub struct Ft232r {
538    ftdi: Ftdi,
539}
540
541/// FT2232H device.
542///
543/// # Example
544///
545/// Converting from an unknown FTDI device.
546///
547/// ```no_run
548/// use libftd2xx::{Ft2232h, Ftdi};
549///
550/// let ft2232h: Ft2232h = Ftdi::new()?.try_into()?;
551/// # Ok::<(), libftd2xx::DeviceTypeError>(())
552/// ```
553#[derive(Debug)]
554pub struct Ft2232h {
555    ftdi: Ftdi,
556}
557
558/// FT4232H device.
559///
560/// # Example
561///
562/// Converting from an unknown FTDI device.
563///
564/// ```no_run
565/// use libftd2xx::{Ft4232h, Ftdi};
566///
567/// let ft4232h: Ft4232h = Ftdi::new()?.try_into()?;
568/// # Ok::<(), libftd2xx::DeviceTypeError>(())
569/// ```
570#[derive(Debug)]
571pub struct Ft4232h {
572    ftdi: Ftdi,
573}
574
575/// FT4232HA device.
576///
577/// # Example
578///
579/// Converting from an unknown FTDI device.
580///
581/// ```no_run
582/// use libftd2xx::{Ft4232ha, Ftdi};
583///
584/// let ft4232ha: Ft4232ha = Ftdi::new()?.try_into()?;
585/// # Ok::<(), libftd2xx::DeviceTypeError>(())
586/// ```
587#[derive(Debug)]
588pub struct Ft4232ha {
589    ftdi: Ftdi,
590}
591
592/// FT X Series device.
593///
594/// # Example
595///
596/// Converting from an unknown FTDI device.
597///
598/// ```no_run
599/// use libftd2xx::{FtXSeries, Ftdi};
600///
601/// let ft_x_series: FtXSeries = Ftdi::new()?.try_into()?;
602/// # Ok::<(), libftd2xx::DeviceTypeError>(())
603/// ```
604#[derive(Debug)]
605pub struct FtXSeries {
606    ftdi: Ftdi,
607}
608
609/// FTD2XX functions common to all devices.
610pub trait FtdiCommon {
611    /// FTDI device type.
612    const DEVICE_TYPE: DeviceType;
613
614    /// Get the FTDI device handle.
615    fn handle(&mut self) -> FT_HANDLE;
616
617    /// Identify device type.
618    ///
619    /// This will attempt to identify the device using the the [`device_info`]
620    /// method, if that method fails it will then try to determine the device type
621    /// from the value stored in the EEPROM.
622    /// If the EEPROM value does not match a known device this function returns
623    /// [`FtStatus::OTHER_ERROR`], though there may be other conditions in the
624    /// vendor driver that also return this code.
625    ///
626    /// This is not a native function in `libftd2xx`, this works around a bug in
627    /// `libftd2xx`, see <https://github.com/ftdi-rs/libftd2xx/pull/37> for more
628    /// information.
629    ///
630    /// # Example
631    ///
632    /// ```no_run
633    /// use libftd2xx::{Ftdi, FtdiCommon};
634    ///
635    /// let mut ft = Ftdi::new()?;
636    /// let dev_type = ft.device_type()?;
637    /// println!("Device type: {:?}", dev_type);
638    /// # Ok::<(), libftd2xx::FtStatus>(())
639    /// ```
640    ///
641    /// [`device_info`]: crate::FtdiCommon::device_info
642    fn device_type(&mut self) -> Result<DeviceType, FtStatus> {
643        if let Ok(info) = self.device_info() {
644            Ok(info.device_type)
645        } else {
646            match self.eeprom_word_read(0x3)? {
647                0x0200 => Ok(DeviceType::FTAM),
648                0x0400 => Ok(DeviceType::FTBM),
649                // ??? => Ok(DeviceType::FT100AX),
650                0x0500 => Ok(DeviceType::FT2232C),
651                0x0600 => Ok(DeviceType::FT232R),
652                0x0700 => Ok(DeviceType::FT2232H),
653                0x0800 => Ok(DeviceType::FT4232H),
654                0x0900 => Ok(DeviceType::FT232H),
655                0x1000 => Ok(DeviceType::FT_X_SERIES),
656                0x1700 => Ok(DeviceType::FT4222H_3),
657                0x1800 => Ok(DeviceType::FT4222H_0),
658                0x1900 => Ok(DeviceType::FT4222H_1_2),
659                0x2100 => Ok(DeviceType::FT4222_PROG),
660                0x3600 => Ok(DeviceType::FT4232HA),
661                _ => Err(FtStatus::OTHER_ERROR),
662            }
663        }
664    }
665
666    /// Get device information for an open device.
667    ///
668    /// # Example
669    ///
670    /// ```no_run
671    /// use libftd2xx::{Ftdi, FtdiCommon};
672    ///
673    /// let mut ft = Ftdi::new()?;
674    /// let info = ft.device_info()?;
675    /// println!("Device information: {:?}", info);
676    /// # Ok::<(), libftd2xx::FtStatus>(())
677    /// ```
678    fn device_info(&mut self) -> Result<DeviceInfo, FtStatus> {
679        let mut device_type: u32 = 0;
680        let mut device_id: u32 = 0;
681        let mut serial_number: [i8; STRING_LEN] = [0; STRING_LEN];
682        let mut description: [i8; STRING_LEN] = [0; STRING_LEN];
683        trace!("FT_GetDeviceInfo({:?}, _, _, _, _, NULL)", self.handle());
684        let status: FT_STATUS = unsafe {
685            FT_GetDeviceInfo(
686                self.handle(),
687                &mut device_type,
688                &mut device_id,
689                serial_number.as_mut_ptr() as *mut c_char,
690                description.as_mut_ptr() as *mut c_char,
691                std::ptr::null_mut(),
692            )
693        };
694        let (vid, pid) = vid_pid_from_id(device_id);
695        ft_result(
696            DeviceInfo {
697                port_open: true,
698                speed: None,
699                device_type: device_type.into(),
700                product_id: pid,
701                vendor_id: vid,
702                serial_number: slice_into_string(serial_number.as_ref()),
703                description: slice_into_string(description.as_ref()),
704            },
705            status,
706        )
707    }
708
709    /// Returns the D2XX driver version number.
710    ///
711    /// # Example
712    ///
713    /// ```no_run
714    /// use libftd2xx::{Ftdi, FtdiCommon};
715    ///
716    /// let mut ft = Ftdi::new()?;
717    /// let version = ft.driver_version()?;
718    /// println!("Driver Version: {}", version);
719    /// # Ok::<(), libftd2xx::FtStatus>(())
720    /// ```
721    fn driver_version(&mut self) -> Result<Version, FtStatus> {
722        let mut version: u32 = 0;
723        trace!("FT_GetDriverVersion({:?}, _)", self.handle());
724        let status: FT_STATUS = unsafe { FT_GetDriverVersion(self.handle(), &mut version) };
725
726        ft_result(Version::with_raw(version), status)
727    }
728
729    /// This function sends a reset command to the device.
730    ///
731    /// # Example
732    ///
733    /// ```no_run
734    /// use libftd2xx::{Ftdi, FtdiCommon};
735    ///
736    /// let mut ft = Ftdi::new()?;
737    /// ft.reset()?;
738    /// # Ok::<(), libftd2xx::FtStatus>(())
739    /// ```
740    fn reset(&mut self) -> Result<(), FtStatus> {
741        trace!("FT_ResetDevice({:?})", self.handle());
742        let status: FT_STATUS = unsafe { FT_ResetDevice(self.handle()) };
743        ft_result((), status)
744    }
745
746    /// Set the USB request transfer size.
747    ///
748    /// This function can be used to change the transfer sizes from the default
749    /// transfer size of 4096 bytes to better suit the application requirements.
750    ///
751    /// Transfer sizes must be set to a multiple of 64 bytes between 64 bytes
752    /// and 64k bytes.  Other values will result in panic.
753    ///
754    /// When [`set_usb_parameters`] is called, the change comes into effect
755    /// immediately and any data that was held in the driver at the time of the
756    /// change is lost.
757    ///
758    /// # Example
759    ///
760    /// ```no_run
761    /// use libftd2xx::{Ftdi, FtdiCommon};
762    ///
763    /// let mut ft = Ftdi::new()?;
764    /// ft.set_usb_parameters(16384)?;
765    /// # Ok::<(), libftd2xx::FtStatus>(())
766    /// ```
767    ///
768    /// [`set_usb_parameters`]: FtdiCommon::set_usb_parameters
769    fn set_usb_parameters(&mut self, in_transfer_size: u32) -> Result<(), FtStatus> {
770        const MAX: u32 = 64 * 1024;
771        const MIN: u32 = 64;
772        assert!(
773            in_transfer_size <= MAX,
774            "in_transfer_size of {in_transfer_size} exceeds maximum of {MAX}"
775        );
776        assert!(
777            in_transfer_size >= MIN,
778            "in_transfer_size of {in_transfer_size} exceeds minimum of {MIN}"
779        );
780        assert!(
781            in_transfer_size % MIN == 0,
782            "in_transfer_size of {in_transfer_size} not a multiple of {MIN}"
783        );
784        trace!(
785            "FT_SetUSBParameters({:?}, {}, {})",
786            self.handle(),
787            in_transfer_size,
788            in_transfer_size
789        );
790        let status: FT_STATUS =
791            unsafe { FT_SetUSBParameters(self.handle(), in_transfer_size, in_transfer_size) };
792        ft_result((), status)
793    }
794
795    /// This function sets the special characters for the device.
796    ///
797    /// This function allows for inserting specified characters in the data
798    /// stream to represent events firing or errors occurring.
799    ///
800    /// # Example
801    ///
802    /// ```no_run
803    /// use libftd2xx::{Ftdi, FtdiCommon};
804    ///
805    /// let mut ft = Ftdi::new()?;
806    ///
807    /// // disable all special characters
808    /// ft.set_chars(0, false, 0, false)?;
809    /// # Ok::<(), libftd2xx::FtStatus>(())
810    /// ```
811    fn set_chars(
812        &mut self,
813        event_char: u8,
814        event_enable: bool,
815        error_char: u8,
816        error_enable: bool,
817    ) -> Result<(), FtStatus> {
818        trace!(
819            "FT_SetChars({:?}, {}, {}, {}, {})",
820            self.handle(),
821            event_char,
822            u8::from(event_enable),
823            error_char,
824            u8::from(error_enable)
825        );
826        let status: FT_STATUS = unsafe {
827            FT_SetChars(
828                self.handle(),
829                event_char,
830                u8::from(event_enable),
831                error_char,
832                u8::from(error_enable),
833            )
834        };
835        ft_result((), status)
836    }
837
838    /// This function sets the read and write timeouts for the device.
839    ///
840    /// The timeout values are limited to 4,294,967,295 (`std::u32::MAX`)
841    /// milliseconds.
842    ///
843    /// The timeout values have a 1 millisecond resolution.
844    ///
845    /// # Example
846    ///
847    /// ```no_run
848    /// use libftd2xx::{Ftdi, FtdiCommon};
849    /// use std::time::Duration;
850    ///
851    /// let mut ft = Ftdi::new()?;
852    ///
853    /// // Set read timeout of 5sec, write timeout of 1sec
854    /// ft.set_timeouts(Duration::from_millis(5000), Duration::from_millis(1000))?;
855    /// # Ok::<(), libftd2xx::FtStatus>(())
856    /// ```
857    fn set_timeouts(
858        &mut self,
859        read_timeout: Duration,
860        write_timeout: Duration,
861    ) -> Result<(), FtStatus> {
862        let read_timeout_ms =
863            u32::try_from(read_timeout.as_millis()).expect("read_timeout integer overflow");
864        let write_timeout_ms =
865            u32::try_from(write_timeout.as_millis()).expect("write_timeout integer overflow");
866        trace!(
867            "FT_SetTimeouts({:?}, {}, {})",
868            self.handle(),
869            read_timeout_ms,
870            write_timeout_ms,
871        );
872        let status: FT_STATUS =
873            unsafe { FT_SetTimeouts(self.handle(), read_timeout_ms, write_timeout_ms) };
874        ft_result((), status)
875    }
876
877    /// This method allows the maximum time in milliseconds that a USB request
878    /// can remain outstandingto be set.
879    ///
880    /// The deadman timeout is referred to in FTDI application note
881    /// [AN232B-10 Advanced Driver Options] as the USB timeout.
882    /// It is unlikely that this method will be required by most users.
883    ///
884    /// The default duration is 5 seconds.
885    ///
886    /// The timeout value is limited to 4,294,967,295 (`std::u32::MAX`)
887    /// milliseconds.
888    ///
889    /// The timeout value has a 1 millisecond resolution.
890    ///
891    /// ```no_run
892    /// use libftd2xx::{Ftdi, FtdiCommon};
893    /// use std::time::Duration;
894    ///
895    /// let mut ft = Ftdi::new()?;
896    ///
897    /// // set deadman timeout to 5 seconds
898    /// ft.set_deadman_timeout(Duration::from_secs(5))?;
899    /// # Ok::<(), libftd2xx::FtStatus>(())
900    /// ```
901    ///
902    /// [AN232B-10 Advanced Driver Options]: https://www.ftdichip.com/Support/Documents/AppNotes/AN_107_AdvancedDriverOptions_AN_000073.pdf
903    fn set_deadman_timeout(&mut self, timeout: Duration) -> Result<(), FtStatus> {
904        let timeout_ms = u32::try_from(timeout.as_millis()).expect("timeout integer overflow");
905        trace!("FT_SetDeadmanTimeout({:?}, {})", self.handle(), timeout_ms);
906        let status: FT_STATUS = unsafe { FT_SetDeadmanTimeout(self.handle(), timeout_ms) };
907        ft_result((), status)
908    }
909
910    /// Set the latency timer value.
911    ///
912    /// In the FT8U232AM and FT8U245AM devices, the receive buffer timeout that
913    /// is used to flush remaining data from the receive buffer was fixed at
914    /// 16 ms.
915    /// In all other FTDI devices, this timeout is programmable and can be set
916    /// at 1 ms intervals between 0ms and 255 ms.  This allows the device to be
917    /// better optimized for protocols requiring faster response times from
918    /// short data packets.
919    ///
920    /// The valid range for the latency timer is 0 to 255 milliseconds.
921    ///
922    /// The resolution for the latency timer is 1 millisecond.
923    ///
924    /// **Note** the python FTDI library, [pyftdi] reports that values lower
925    /// than 16 ms may result in data loss.
926    ///
927    /// # Example
928    ///
929    /// ```no_run
930    /// use libftd2xx::{Ftdi, FtdiCommon};
931    /// use std::time::Duration;
932    ///
933    /// let mut ft = Ftdi::new()?;
934    ///
935    /// // Set latency timer to 16 milliseconds
936    /// ft.set_latency_timer(Duration::from_millis(16))?;
937    /// # Ok::<(), libftd2xx::FtStatus>(())
938    /// ```
939    ///
940    /// [pyftdi]: https://github.com/eblot/pyftdi/tree/master
941    fn set_latency_timer(&mut self, timer: Duration) -> Result<(), FtStatus> {
942        let millis = timer.as_millis();
943        debug_assert!(millis <= 255, "duration must be <= 255ms, got {timer:?}");
944        let millis = u8::try_from(millis).unwrap();
945        trace!("FT_SetLatencyTimer({:?}, {})", self.handle(), millis);
946        let status: FT_STATUS = unsafe { FT_SetLatencyTimer(self.handle(), millis) };
947        ft_result((), status)
948    }
949
950    /// Get the current value of the latency timer.
951    ///
952    /// # Example
953    ///
954    /// ```no_run
955    /// use libftd2xx::{Ftdi, FtdiCommon};
956    /// use std::time::Duration;
957    ///
958    /// let mut ft = Ftdi::new()?;
959    /// let timer = Duration::from_millis(32);
960    /// ft.set_latency_timer(timer)?;
961    /// assert_eq!(ft.latency_timer()?, timer);
962    /// # Ok::<(), libftd2xx::FtStatus>(())
963    /// ```
964    fn latency_timer(&mut self) -> Result<Duration, FtStatus> {
965        let mut timer: u8 = 0;
966        trace!("FT_GetLatencyTimer({:?}, _)", self.handle());
967        let status: FT_STATUS = unsafe { FT_GetLatencyTimer(self.handle(), &mut timer as *mut u8) };
968        ft_result(Duration::from_millis(timer as u64), status)
969    }
970
971    /// This function disables flow control for the device.
972    ///
973    /// # Example
974    ///
975    /// ```no_run
976    /// use libftd2xx::{Ftdi, FtdiCommon};
977    ///
978    /// let mut ft = Ftdi::new()?;
979    /// ft.set_flow_control_none()?;
980    /// # Ok::<(), libftd2xx::FtStatus>(())
981    /// ```
982    fn set_flow_control_none(&mut self) -> Result<(), FtStatus> {
983        trace!(
984            "FT_SetFlowControl({:?}, {}, 0, 0)",
985            self.handle(),
986            FT_FLOW_NONE
987        );
988        let status: FT_STATUS =
989            unsafe { FT_SetFlowControl(self.handle(), FT_FLOW_NONE as u16, 0, 0) };
990
991        ft_result((), status)
992    }
993
994    /// This function sets RTS/CTS flow control for the device.
995    ///
996    /// # Example
997    ///
998    /// ```no_run
999    /// use libftd2xx::{Ftdi, FtdiCommon};
1000    ///
1001    /// let mut ft = Ftdi::new()?;
1002    /// ft.set_flow_control_rts_cts()?;
1003    /// # Ok::<(), libftd2xx::FtStatus>(())
1004    /// ```
1005    fn set_flow_control_rts_cts(&mut self) -> Result<(), FtStatus> {
1006        trace!(
1007            "FT_SetFlowControl({:?}, {}, 0, 0)",
1008            self.handle(),
1009            FT_FLOW_RTS_CTS
1010        );
1011        let status: FT_STATUS =
1012            unsafe { FT_SetFlowControl(self.handle(), FT_FLOW_RTS_CTS as u16, 0, 0) };
1013
1014        ft_result((), status)
1015    }
1016
1017    /// This function sets DTS/DSR flow control for the device.
1018    ///
1019    /// # Example
1020    ///
1021    /// ```no_run
1022    /// use libftd2xx::{Ftdi, FtdiCommon};
1023    ///
1024    /// let mut ft = Ftdi::new()?;
1025    /// ft.set_flow_control_dtr_dsr()?;
1026    /// # Ok::<(), libftd2xx::FtStatus>(())
1027    /// ```
1028    fn set_flow_control_dtr_dsr(&mut self) -> Result<(), FtStatus> {
1029        trace!(
1030            "FT_SetFlowControl({:?}, {}, 0, 0)",
1031            self.handle(),
1032            FT_FLOW_DTR_DSR
1033        );
1034        let status: FT_STATUS =
1035            unsafe { FT_SetFlowControl(self.handle(), FT_FLOW_DTR_DSR as u16, 0, 0) };
1036
1037        ft_result((), status)
1038    }
1039
1040    /// This function sets XON/XOFF flow control for the device.
1041    ///
1042    /// # Arguments
1043    ///
1044    /// * `xon` - Character used to signal Xon.
1045    /// * `xoff` - Character used to signal Xoff.
1046    ///
1047    /// # Example
1048    ///
1049    /// ```no_run
1050    /// use libftd2xx::{Ftdi, FtdiCommon};
1051    ///
1052    /// let mut ft = Ftdi::new()?;
1053    /// ft.set_flow_control_xon_xoff(0x11, 0x13)?;
1054    /// # Ok::<(), libftd2xx::FtStatus>(())
1055    /// ```
1056    fn set_flow_control_xon_xoff(&mut self, xon: u8, xoff: u8) -> Result<(), FtStatus> {
1057        trace!(
1058            "FT_SetFlowControl({:?}, {}, {}, {})",
1059            self.handle(),
1060            FT_FLOW_XON_XOFF,
1061            xon,
1062            xoff
1063        );
1064        let status: FT_STATUS =
1065            unsafe { FT_SetFlowControl(self.handle(), FT_FLOW_XON_XOFF as u16, xon, xoff) };
1066
1067        ft_result((), status)
1068    }
1069
1070    /// Set the baud rate for the device.
1071    ///
1072    /// # Example
1073    ///
1074    /// ```no_run
1075    /// use libftd2xx::{Ftdi, FtdiCommon};
1076    ///
1077    /// let mut ft = Ftdi::new()?;
1078    /// ft.set_baud_rate(115200)?;
1079    /// # Ok::<(), libftd2xx::FtStatus>(())
1080    /// ```
1081    fn set_baud_rate(&mut self, baud_rate: u32) -> Result<(), FtStatus> {
1082        trace!("FT_SetBaudRate({:?}, {})", self.handle(), baud_rate);
1083        let status: FT_STATUS = unsafe { FT_SetBaudRate(self.handle(), baud_rate) };
1084        ft_result((), status)
1085    }
1086
1087    /// Set the data characteristics for the device.
1088    ///
1089    /// # Example
1090    ///
1091    /// ```no_run
1092    /// use libftd2xx::{BitsPerWord, Ftdi, FtdiCommon, Parity, StopBits};
1093    ///
1094    /// let mut ft = Ftdi::new()?;
1095    /// ft.set_data_characteristics(BitsPerWord::Bits8, StopBits::Bits1, Parity::No)?;
1096    /// # Ok::<(), libftd2xx::FtStatus>(())
1097    /// ```
1098    fn set_data_characteristics(
1099        &mut self,
1100        bits_per_word: BitsPerWord,
1101        stop_bits: StopBits,
1102        parity: Parity,
1103    ) -> Result<(), FtStatus> {
1104        trace!(
1105            "FT_SetDataCharacteristics({:?}, {}, {}, {})",
1106            self.handle(),
1107            u8::from(bits_per_word),
1108            u8::from(stop_bits),
1109            u8::from(parity),
1110        );
1111        let status: FT_STATUS = unsafe {
1112            FT_SetDataCharacteristics(
1113                self.handle(),
1114                bits_per_word.into(),
1115                stop_bits.into(),
1116                parity.into(),
1117            )
1118        };
1119        ft_result((), status)
1120    }
1121
1122    /// Set the Data Terminal Ready (DTR) control signal.
1123    ///
1124    /// # Example
1125    ///
1126    /// ```no_run
1127    /// use libftd2xx::{Ftdi, FtdiCommon};
1128    ///
1129    /// let mut ft = Ftdi::new()?;
1130    /// ft.set_dtr()?;
1131    /// # Ok::<(), libftd2xx::FtStatus>(())
1132    /// ```
1133    fn set_dtr(&mut self) -> Result<(), FtStatus> {
1134        trace!("FT_SetDtr({:?})", self.handle());
1135        let status: FT_STATUS = unsafe { FT_SetDtr(self.handle()) };
1136        ft_result((), status)
1137    }
1138
1139    /// Clear the Data Terminal Ready (DTR) control signal.
1140    ///
1141    /// # Example
1142    ///
1143    /// ```no_run
1144    /// use libftd2xx::{Ftdi, FtdiCommon};
1145    ///
1146    /// let mut ft = Ftdi::new()?;
1147    /// ft.clear_dtr()?;
1148    /// # Ok::<(), libftd2xx::FtStatus>(())
1149    /// ```
1150    fn clear_dtr(&mut self) -> Result<(), FtStatus> {
1151        trace!("FT_ClrtDtr({:?})", self.handle());
1152        let status: FT_STATUS = unsafe { FT_ClrDtr(self.handle()) };
1153        ft_result((), status)
1154    }
1155
1156    /// Set the Request to Send (RTS) control signal.
1157    ///
1158    /// # Example
1159    ///
1160    /// ```no_run
1161    /// use libftd2xx::{Ftdi, FtdiCommon};
1162    ///
1163    /// let mut ft = Ftdi::new()?;
1164    /// ft.set_rts()?;
1165    /// # Ok::<(), libftd2xx::FtStatus>(())
1166    /// ```
1167    fn set_rts(&mut self) -> Result<(), FtStatus> {
1168        trace!("FT_SetRts({:?})", self.handle());
1169        let status: FT_STATUS = unsafe { FT_SetRts(self.handle()) };
1170        ft_result((), status)
1171    }
1172
1173    /// Clear the Request to Send (RTS) control signal.
1174    ///
1175    /// # Example
1176    ///
1177    /// ```no_run
1178    /// use libftd2xx::{Ftdi, FtdiCommon};
1179    ///
1180    /// let mut ft = Ftdi::new()?;
1181    /// ft.clear_rts()?;
1182    /// # Ok::<(), libftd2xx::FtStatus>(())
1183    /// ```
1184    fn clear_rts(&mut self) -> Result<(), FtStatus> {
1185        trace!("FT_ClrtRts({:?})", self.handle());
1186        let status: FT_STATUS = unsafe { FT_ClrRts(self.handle()) };
1187        ft_result((), status)
1188    }
1189
1190    /// Enables different chip modes.
1191    ///
1192    /// # Arguments
1193    ///
1194    /// * `mask` - This bit mask sets up which bits are inputs and outputs.
1195    ///   A bit value of 0 sets the corresponding pin to an input,
1196    ///   a bit value of 1 sets the corresponding pin to an output.
1197    ///   In the case of CBUS Bit Bang, the upper nibble of this value controls
1198    ///   which pins are inputs and outputs, while the lower nibble controls
1199    ///   which of the outputs are high and low.
1200    /// * `mode` - Bitmode, see the [`BitMode`] struct for more details.
1201    ///
1202    /// For a description of available bit modes for the FT232R,
1203    /// see the application note [Bit Bang Modes for the FT232R and FT245R].
1204    ///
1205    /// For a description of available bit modes for the FT2232,
1206    /// see the application note [Bit Mode Functions for the FT2232].
1207    ///
1208    /// For a description of Bit Bang Mode for the FT232B and FT245B,
1209    /// see the application note [FT232B/FT245B Bit Bang Mode].
1210    ///
1211    /// Application notes are available for download from the [FTDI website].
1212    ///
1213    /// Note that to use CBUS Bit Bang for the FT232R,
1214    /// the CBUS must be configured for CBUS Bit Bang in the EEPROM.
1215    ///
1216    /// Note that to use Single Channel Synchronous 245 FIFO mode for the
1217    /// FT2232H, channel A must be configured for FT245 FIFO mode in the EEPROM.
1218    ///
1219    /// [Bit Bang Modes for the FT232R and FT245R]: https://www.ftdichip.com/Support/Documents/AppNotes/AN_232R-01_Bit_Bang_Mode_Available_For_FT232R_and_Ft245R.pdf
1220    /// [Bit Mode Functions for the FT2232]: https://www.ftdichip.com/Support/Documents/AppNotes/AN2232C-02_FT2232CBitMode.pdf
1221    /// [FT232B/FT245B Bit Bang Mode]: https://www.ftdichip.com/Support/Documents/AppNotes/AN232B-01_BitBang.pdf
1222    /// [FTDI website]: https://www.ftdichip.com/Support/Documents/AppNotes.htm
1223    ///
1224    /// # Example
1225    ///
1226    /// ```no_run
1227    /// use libftd2xx::{BitMode, Ftdi, FtdiCommon};
1228    ///
1229    /// let mut ft = Ftdi::new()?;
1230    /// ft.set_bit_mode(0xFF, BitMode::AsyncBitbang)?;
1231    /// # Ok::<(), libftd2xx::FtStatus>(())
1232    /// ```
1233    fn set_bit_mode(&mut self, mask: u8, mode: BitMode) -> Result<(), FtStatus> {
1234        trace!(
1235            "FT_SetBitMode({:?}, 0x{:02X}, 0x{:02X})",
1236            self.handle(),
1237            mask,
1238            mode as u8
1239        );
1240        let status: FT_STATUS = unsafe { FT_SetBitMode(self.handle(), mask, mode as u8) };
1241
1242        ft_result((), status)
1243    }
1244
1245    /// Get the instantaneous value of the data bus.
1246    ///
1247    /// **Note:** This is not the [`BitMode`] as set in [`set_bit_mode`],
1248    /// this is the value (logic high or logic low) of the pins on the bus.
1249    ///
1250    /// # Example
1251    ///
1252    /// ```no_run
1253    /// use libftd2xx::{Ftdi, FtdiCommon};
1254    ///
1255    /// let mut ft = Ftdi::new()?;
1256    /// let bitmode = ft.bit_mode()?;
1257    /// println!("Data bus state: {}", bitmode);
1258    /// # Ok::<(), libftd2xx::FtStatus>(())
1259    /// ```
1260    ///
1261    /// [`set_bit_mode`]: FtdiCommon::set_bit_mode
1262    fn bit_mode(&mut self) -> Result<u8, FtStatus> {
1263        let mut mode: u8 = 0;
1264        trace!("FT_GetBitMode({:?}, _)", self.handle());
1265        let status: FT_STATUS = unsafe { FT_GetBitMode(self.handle(), &mut mode) };
1266        ft_result(mode, status)
1267    }
1268
1269    /// Sets the BREAK condition for the device.
1270    ///
1271    /// # Example
1272    ///
1273    /// ```no_run
1274    /// use libftd2xx::{Ftdi, FtdiCommon};
1275    ///
1276    /// let mut ft = Ftdi::new()?;
1277    /// ft.set_break_off()?;
1278    /// # Ok::<(), libftd2xx::FtStatus>(())
1279    /// ```
1280    fn set_break_on(&mut self) -> Result<(), FtStatus> {
1281        trace!("FT_SetBreakOn({:?})", self.handle());
1282        let status: FT_STATUS = unsafe { FT_SetBreakOn(self.handle()) };
1283        ft_result((), status)
1284    }
1285
1286    /// Resets the BREAK condition for the device.
1287    ///
1288    /// # Example
1289    ///
1290    /// ```no_run
1291    /// use libftd2xx::{Ftdi, FtdiCommon};
1292    ///
1293    /// let mut ft = Ftdi::new()?;
1294    /// ft.set_break_off()?;
1295    /// # Ok::<(), libftd2xx::FtStatus>(())
1296    /// ```
1297    fn set_break_off(&mut self) -> Result<(), FtStatus> {
1298        trace!("FT_SetBreakOff({:?})", self.handle());
1299        let status: FT_STATUS = unsafe { FT_SetBreakOff(self.handle()) };
1300        ft_result((), status)
1301    }
1302
1303    /// Gets the number of bytes in the receive queue.
1304    ///
1305    /// # Example
1306    ///
1307    /// ```no_run
1308    /// use libftd2xx::{Ftdi, FtdiCommon};
1309    ///
1310    /// let mut buf: [u8; 4096] = [0; 4096];
1311    /// let mut ft = Ftdi::new()?;
1312    /// let rx_bytes = ft.queue_status()?;
1313    ///
1314    /// if (rx_bytes > 0) {
1315    ///     ft.read(&mut buf[0..rx_bytes])?;
1316    /// }
1317    /// # Ok::<(), libftd2xx::TimeoutError>(())
1318    /// ```
1319    fn queue_status(&mut self) -> Result<usize, FtStatus> {
1320        let mut queue_status: u32 = 0;
1321        trace!("FT_GetQueueStatus({:?}, _)", self.handle());
1322        let status: FT_STATUS = unsafe { FT_GetQueueStatus(self.handle(), &mut queue_status) };
1323
1324        ft_result(usize::try_from(queue_status).unwrap(), status)
1325    }
1326
1327    /// Gets the device status including number of characters in the receive
1328    /// queue, number of characters in the transmit queue, and the current event
1329    /// status.
1330    ///
1331    /// # Example
1332    ///
1333    /// ```no_run
1334    /// use libftd2xx::{Ftdi, FtdiCommon};
1335    ///
1336    /// let mut ft = Ftdi::new()?;
1337    /// let status = ft.status()?;
1338    /// println!("status={:?}", status);
1339    /// # Ok::<(), libftd2xx::TimeoutError>(())
1340    /// ```
1341    fn status(&mut self) -> Result<DeviceStatus, FtStatus> {
1342        let mut ammount_in_rx_queue: u32 = 0;
1343        let mut ammount_in_tx_queue: u32 = 0;
1344        let mut event_status: u32 = 0;
1345
1346        trace!("FT_GetStatus({:?}, _, _, _)", self.handle());
1347        let status: FT_STATUS = unsafe {
1348            FT_GetStatus(
1349                self.handle(),
1350                &mut ammount_in_rx_queue,
1351                &mut ammount_in_tx_queue,
1352                &mut event_status,
1353            )
1354        };
1355
1356        ft_result(
1357            DeviceStatus {
1358                ammount_in_rx_queue,
1359                ammount_in_tx_queue,
1360                event_status,
1361            },
1362            status,
1363        )
1364    }
1365
1366    /// Read data from the device, returning the number of bytes read.
1367    ///
1368    /// See [`read_all`] for more information about reading from the device.
1369    ///
1370    /// # Example
1371    ///
1372    /// ```no_run
1373    /// use libftd2xx::{Ftdi, FtdiCommon};
1374    ///
1375    /// const BUF_SIZE: usize = 256;
1376    /// let mut buf: [u8; BUF_SIZE] = [0; BUF_SIZE];
1377    /// let mut ft = Ftdi::new()?;
1378    /// let bytes_read: usize = ft.read(&mut buf)?;
1379    /// assert_eq!(bytes_read, BUF_SIZE);
1380    /// # Ok::<(), libftd2xx::FtStatus>(())
1381    /// ```
1382    ///
1383    /// [`read_all`]: FtdiCommon::read_all
1384    fn read(&mut self, buf: &mut [u8]) -> Result<usize, FtStatus> {
1385        let mut bytes_returned: u32 = 0;
1386        let len: u32 = u32::try_from(buf.len()).unwrap();
1387        trace!("FT_Read({:?}, _, {}, _)", self.handle(), len);
1388        let status: FT_STATUS = unsafe {
1389            FT_Read(
1390                self.handle(),
1391                buf.as_mut_ptr() as *mut c_void,
1392                len,
1393                &mut bytes_returned,
1394            )
1395        };
1396        ft_result(usize::try_from(bytes_returned).unwrap(), status)
1397    }
1398
1399    /// Read data from the device.
1400    ///
1401    /// This method does not return until the buffer has been filled, if no
1402    /// timeout has been set.
1403    /// The number of bytes in the receive queue can be determined by calling
1404    /// [`queue_status`], and then an buffer equal to the length of that
1405    /// value can be passed to [`read_all`] so that the function reads the
1406    /// device and returns immediately.
1407    ///
1408    /// When a read timeout value has been specified in a previous call to
1409    /// [`set_timeouts`], [`read_all`] returns when the timer expires or when
1410    /// the buffer has been filled, whichever occurs first.
1411    /// If the timeout occurred, [`read_all`] reads available data into the
1412    /// buffer and returns the [`TimeoutError`] error.
1413    ///
1414    /// # Examples
1415    ///
1416    /// ## Read all available data
1417    ///
1418    /// ```no_run
1419    /// use libftd2xx::{Ftdi, FtdiCommon};
1420    ///
1421    /// let mut buf: [u8; 4096] = [0; 4096];
1422    /// let mut ft = Ftdi::new()?;
1423    /// let rx_bytes = ft.queue_status()?;
1424    ///
1425    /// if rx_bytes > 0 {
1426    ///     ft.read_all(&mut buf[0..rx_bytes])?;
1427    /// }
1428    /// # Ok::<(), libftd2xx::TimeoutError>(())
1429    /// ```
1430    ///
1431    /// ## Read with a timeout of 5 seconds
1432    ///
1433    /// ```no_run
1434    /// use libftd2xx::{Ftdi, FtdiCommon, TimeoutError};
1435    /// use std::time::Duration;
1436    ///
1437    /// const BUF_LEN: usize = 4096;
1438    /// let mut buf: [u8; BUF_LEN] = [0; BUF_LEN];
1439    /// let mut ft = Ftdi::new()?;
1440    ///
1441    /// ft.set_timeouts(Duration::from_millis(5000), Duration::from_millis(0))?;
1442    ///
1443    /// let valid_data = match ft.read_all(&mut buf) {
1444    ///     Err(e) => match e {
1445    ///         TimeoutError::Timeout {
1446    ///             actual: actual,
1447    ///             expected: expected,
1448    ///         } => {
1449    ///             eprintln!("Read timeout occured after 5s! {:?}", e);
1450    ///             &buf[0..actual]
1451    ///         }
1452    ///         TimeoutError::FtStatus(status) => {
1453    ///             panic!("FTDI Status Error: {:?}", status);
1454    ///         }
1455    ///     },
1456    ///     _ => &buf[0..buf.len()],
1457    /// };
1458    /// # Ok::<(), libftd2xx::TimeoutError>(())
1459    /// ```
1460    ///
1461    /// [`read_all`]: FtdiCommon::read_all
1462    /// [`queue_status`]: FtdiCommon::queue_status
1463    /// [`set_timeouts`]: FtdiCommon::set_timeouts
1464    /// [`TimeoutError`]: TimeoutError
1465    fn read_all(&mut self, buf: &mut [u8]) -> Result<(), TimeoutError> {
1466        let num_read = self.read(buf)?;
1467        if num_read != buf.len() {
1468            Err(TimeoutError::Timeout {
1469                expected: buf.len(),
1470                actual: num_read,
1471            })
1472        } else {
1473            Ok(())
1474        }
1475    }
1476
1477    /// Write data to the device.
1478    ///
1479    /// # Example
1480    ///
1481    /// ```no_run
1482    /// use libftd2xx::{Ftdi, FtdiCommon};
1483    ///
1484    /// const BUF_SIZE: usize = 256;
1485    /// let buf: [u8; BUF_SIZE] = [0; BUF_SIZE];
1486    /// let mut ft = Ftdi::new()?;
1487    /// ft.write_all(&buf)?;
1488    /// # Ok::<(), libftd2xx::TimeoutError>(())
1489    /// ```
1490    fn write_all(&mut self, buf: &[u8]) -> Result<(), TimeoutError> {
1491        let num_written = self.write(buf)?;
1492        if num_written != buf.len() {
1493            Err(TimeoutError::Timeout {
1494                expected: buf.len(),
1495                actual: num_written,
1496            })
1497        } else {
1498            Ok(())
1499        }
1500    }
1501
1502    /// Write data to the device, returning how many bytes were written.
1503    ///
1504    /// # Example
1505    ///
1506    /// ```no_run
1507    /// use libftd2xx::{Ftdi, FtdiCommon};
1508    ///
1509    /// const BUF_SIZE: usize = 256;
1510    /// let buf: [u8; BUF_SIZE] = [0; BUF_SIZE];
1511    /// let mut ft = Ftdi::new()?;
1512    /// let bytes_written: usize = ft.write(&buf)?;
1513    /// assert_eq!(bytes_written, BUF_SIZE);
1514    /// # Ok::<(), libftd2xx::FtStatus>(())
1515    /// ```
1516    fn write(&mut self, buf: &[u8]) -> Result<usize, FtStatus> {
1517        let mut bytes_written: u32 = 0;
1518        let len: u32 = u32::try_from(buf.len()).unwrap();
1519        trace!("FT_Write({:?}, _, {}, _)", self.handle(), len);
1520        let status: FT_STATUS = unsafe {
1521            FT_Write(
1522                self.handle(),
1523                buf.as_ptr() as *mut c_void,
1524                len,
1525                &mut bytes_written,
1526            )
1527        };
1528        ft_result(usize::try_from(bytes_written).unwrap(), status)
1529    }
1530
1531    /// This function purges the transmit buffers in the device.
1532    ///
1533    /// # Example
1534    ///
1535    /// ```no_run
1536    /// use libftd2xx::{Ftdi, FtdiCommon};
1537    ///
1538    /// let mut ft = Ftdi::new()?;
1539    /// ft.purge_tx()?;
1540    /// # Ok::<(), libftd2xx::FtStatus>(())
1541    /// ```
1542    fn purge_tx(&mut self) -> Result<(), FtStatus> {
1543        trace!("FT_Purge({:?}, {})", self.handle(), FT_PURGE_TX);
1544        let status: FT_STATUS = unsafe { FT_Purge(self.handle(), FT_PURGE_TX) };
1545        ft_result((), status)
1546    }
1547
1548    /// This function purges the receive buffers in the device.
1549    ///
1550    /// # Example
1551    ///
1552    /// ```no_run
1553    /// use libftd2xx::{Ftdi, FtdiCommon};
1554    ///
1555    /// let mut ft = Ftdi::new()?;
1556    /// ft.purge_rx()?;
1557    /// # Ok::<(), libftd2xx::FtStatus>(())
1558    /// ```
1559    fn purge_rx(&mut self) -> Result<(), FtStatus> {
1560        trace!("FT_Purge({:?}, {})", self.handle(), FT_PURGE_RX);
1561        let status: FT_STATUS = unsafe { FT_Purge(self.handle(), FT_PURGE_RX) };
1562        ft_result((), status)
1563    }
1564
1565    /// This function purges the transmit and receive buffers in the device.
1566    ///
1567    /// # Example
1568    ///
1569    /// ```no_run
1570    /// use libftd2xx::{Ftdi, FtdiCommon};
1571    ///
1572    /// let mut ft = Ftdi::new()?;
1573    /// ft.purge_all()?;
1574    /// # Ok::<(), libftd2xx::FtStatus>(())
1575    /// ```
1576    fn purge_all(&mut self) -> Result<(), FtStatus> {
1577        trace!(
1578            "FT_Purge({:?}, {})",
1579            self.handle(),
1580            FT_PURGE_TX | FT_PURGE_RX
1581        );
1582        let status: FT_STATUS = unsafe { FT_Purge(self.handle(), FT_PURGE_TX | FT_PURGE_RX) };
1583        ft_result((), status)
1584    }
1585
1586    /// Close an open device.
1587    ///
1588    /// # Example
1589    ///
1590    /// ```no_run
1591    /// use libftd2xx::{Ftdi, FtdiCommon};
1592    ///
1593    /// let mut ft = Ftdi::new()?;
1594    /// ft.close()?;
1595    /// # Ok::<(), libftd2xx::FtStatus>(())
1596    /// ```
1597    fn close(&mut self) -> Result<(), FtStatus> {
1598        trace!("FT_Close({:?})", self.handle());
1599        let status: FT_STATUS = unsafe { FT_Close(self.handle()) };
1600        ft_result((), status)
1601    }
1602
1603    /// Get the COM port associated with a device.
1604    ///
1605    /// This method is only available when using the Windows CDM driver as both
1606    /// the D2XX and VCP drivers can be installed at the same time.
1607    ///
1608    /// If no COM port is associated with the device `None` is returned.
1609    ///
1610    /// # Example
1611    ///
1612    /// ```no_run
1613    /// use libftd2xx::{Ftdi, FtdiCommon};
1614    ///
1615    /// let mut ft = Ftdi::new()?;
1616    /// match ft.com_port_number()? {
1617    ///     Some(num) => println!("COM{}", num),
1618    ///     None => println!("no COM port"),
1619    /// }
1620    /// # Ok::<(), libftd2xx::FtStatus>(())
1621    /// ```
1622    #[cfg(all(any(windows, doc), not(doctest)))]
1623    #[cfg_attr(docsrs, doc(cfg(windows)))]
1624    fn com_port_number(&mut self) -> Result<Option<u32>, FtStatus> {
1625        let mut num: i32 = -1;
1626        trace!("FT_GetComPortNumber({:?}, _)", self.handle());
1627        let status: FT_STATUS = unsafe { FT_GetComPortNumber(self.handle(), &mut num as *mut i32) };
1628        ft_result(
1629            if num == -1 {
1630                None
1631            } else {
1632                Some(u32::try_from(num).unwrap())
1633            },
1634            status,
1635        )
1636    }
1637
1638    /// Send a reset command to the port.
1639    ///
1640    /// This method is available on Windows only.
1641    ///
1642    /// This function is used to attempt to recover the device upon failure.
1643    /// For the equivalent of a unplug-replug event use
1644    /// [`FtdiCommon::cycle_port`].
1645    ///
1646    /// # Example
1647    ///
1648    /// ```no_run
1649    /// use libftd2xx::{Ftdi, FtdiCommon};
1650    ///
1651    /// let mut ft = Ftdi::new()?;
1652    /// ft.reset_port()?;
1653    /// # Ok::<(), libftd2xx::FtStatus>(())
1654    /// ```
1655    #[cfg(all(any(windows, doc), not(doctest)))]
1656    #[cfg_attr(docsrs, doc(cfg(windows)))]
1657    fn reset_port(&mut self) -> Result<(), FtStatus> {
1658        trace!("FT_ResetPort({:?})", self.handle());
1659        let status: FT_STATUS = unsafe { FT_ResetPort(self.handle()) };
1660        ft_result((), status)
1661    }
1662
1663    /// Send a cycle command to the USB port.
1664    ///
1665    /// This method is available on Windows only.
1666    ///
1667    /// The effect of this method is the same as disconnecting then
1668    /// reconnecting the device from the USB port.
1669    /// Possible use of this method is situations where a fatal error has
1670    /// occurred and it is difficult, or not possible, to recover without
1671    /// unplugging and replugging the USB cable.
1672    /// This method can also be used after re-programming the EEPROM to force
1673    /// the FTDI device to read the new EEPROM contents which would otherwise
1674    /// require a physical disconnect-reconnect.
1675    ///
1676    /// As the current session is not restored when the driver is reloaded,
1677    /// the application must be able to recover after calling this method.
1678    /// It is the responsibility of the application to close the handle after
1679    /// successfully calling this method.
1680    ///
1681    /// For FT4232H(A), FT2232H and FT2232 devices, `cycle_port` will only work
1682    /// under Windows XP and later.
1683    ///
1684    /// # Example
1685    ///
1686    /// ```no_run
1687    /// use libftd2xx::{Ftdi, FtdiCommon};
1688    ///
1689    /// let mut ft = Ftdi::new()?;
1690    /// ft.cycle_port()?;
1691    /// # Ok::<(), libftd2xx::FtStatus>(())
1692    /// ```
1693    #[cfg(all(any(windows, doc), not(doctest)))]
1694    #[cfg_attr(docsrs, doc(cfg(windows)))]
1695    fn cycle_port(&mut self) -> Result<(), FtStatus> {
1696        trace!("FT_CyclePort({:?})", self.handle());
1697        let status: FT_STATUS = unsafe { FT_CyclePort(self.handle()) };
1698        ft_result((), status)
1699    }
1700
1701    /// Gets the modem status and line status from the device.
1702    ///
1703    /// # Example
1704    ///
1705    /// ```no_run
1706    /// use libftd2xx::{Ftdi, FtdiCommon};
1707    ///
1708    /// let mut ft = Ftdi::new()?;
1709    /// let modem_status = ft.modem_status()?;
1710    /// println!("CTS={}", modem_status.clear_to_send());
1711    /// # Ok::<(), libftd2xx::FtStatus>(())
1712    /// ```
1713    fn modem_status(&mut self) -> Result<ModemStatus, FtStatus> {
1714        let mut modem_status: u32 = 0;
1715        trace!("FT_GetModemStatus({:?}, _)", self.handle());
1716        let status: FT_STATUS = unsafe { FT_GetModemStatus(self.handle(), &mut modem_status) };
1717        ft_result(ModemStatus::new(modem_status), status)
1718    }
1719
1720    /// Read a value from an EEPROM location.
1721    ///
1722    /// # Arguments
1723    ///
1724    /// * `offset` - EEPROM location to read from.
1725    ///
1726    /// # Example
1727    ///
1728    /// ```no_run
1729    /// use libftd2xx::{Ftdi, FtdiCommon};
1730    ///
1731    /// const LOCATION: u32 = 0x0;
1732    /// let mut ft = Ftdi::new()?;
1733    /// let value = ft.eeprom_word_read(LOCATION)?;
1734    /// println!(
1735    ///     "The value at EEPROM address 0x{:X} is 0x{:04X}",
1736    ///     LOCATION, value
1737    /// );
1738    /// # Ok::<(), libftd2xx::FtStatus>(())
1739    /// ```
1740    fn eeprom_word_read(&mut self, offset: u32) -> Result<u16, FtStatus> {
1741        let mut value: u16 = 0;
1742        trace!("FT_ReadEE({:?}, {}, _)", self.handle(), offset);
1743        let status: FT_STATUS = unsafe { FT_ReadEE(self.handle(), offset, &mut value) };
1744        ft_result(value, status)
1745    }
1746
1747    /// Writes a value to an EEPROM location.
1748    ///
1749    /// # Arguments
1750    ///
1751    /// * `offset` - EEPROM location to write to.
1752    /// * `value` - Value to write.
1753    ///
1754    /// # Warning
1755    ///
1756    /// Writing bad values to the EEPROM can brick your device.
1757    /// Please take a moment to read the license for this crate before using
1758    /// this function.
1759    ///
1760    /// # Example
1761    ///
1762    /// ```no_run
1763    /// use libftd2xx::{Ftdi, FtdiCommon};
1764    ///
1765    /// let mut ft = Ftdi::new()?;
1766    /// ft.eeprom_word_write(0x0, 0x1234)?;
1767    /// # Ok::<(), libftd2xx::FtStatus>(())
1768    /// ```
1769    fn eeprom_word_write(&mut self, offset: u32, value: u16) -> Result<(), FtStatus> {
1770        trace!(
1771            "FT_WriteEE({:?}, 0x{:X}, 0x{:04X})",
1772            self.handle(),
1773            offset,
1774            value
1775        );
1776        let status: FT_STATUS = unsafe { FT_WriteEE(self.handle(), offset, value) };
1777        ft_result((), status)
1778    }
1779
1780    /// Erases the entire contents of the EEPROM, including the user area.
1781    ///
1782    /// **Note:** The FT232R and FT245R have an internal EEPROM that cannot be
1783    /// erased.
1784    ///
1785    /// # Example
1786    ///
1787    /// ```no_run
1788    /// use libftd2xx::{Ftdi, FtdiCommon};
1789    ///
1790    /// let mut ft = Ftdi::new()?;
1791    /// ft.eeprom_erase()?;
1792    /// # Ok::<(), libftd2xx::FtStatus>(())
1793    /// ```
1794    fn eeprom_erase(&mut self) -> Result<(), FtStatus> {
1795        trace!("FT_EraseEE({:?})", self.handle());
1796        let status: FT_STATUS = unsafe { FT_EraseEE(self.handle()) };
1797        ft_result((), status)
1798    }
1799
1800    /// Get the available size of the EEPROM user area in bytes.
1801    ///
1802    /// The user area of an FTDI device EEPROM is the total area of the EEPROM
1803    /// that is unused by device configuration information and descriptors.
1804    /// This area is available to the user to store information specific to
1805    /// their application.
1806    /// The size of the user area depends on the length of the Manufacturer,
1807    /// ManufacturerId, Description and SerialNumber strings programmed into the
1808    /// EEPROM.
1809    ///
1810    /// # Example
1811    ///
1812    /// ```no_run
1813    /// use libftd2xx::{Ftdi, FtdiCommon};
1814    ///
1815    /// let mut ft = Ftdi::new()?;
1816    /// let ua_size = ft.eeprom_user_size()?;
1817    /// println!("EEPROM user area size: {} Bytes", ua_size);
1818    /// # Ok::<(), libftd2xx::FtStatus>(())
1819    /// ```
1820    fn eeprom_user_size(&mut self) -> Result<usize, FtStatus> {
1821        let mut value: u32 = 0;
1822        trace!("FT_EE_UASize({:?}, _)", self.handle());
1823        let status: FT_STATUS = unsafe { FT_EE_UASize(self.handle(), &mut value) };
1824        ft_result(usize::try_from(value).unwrap(), status)
1825    }
1826
1827    /// Read the contents of the EEPROM user area.
1828    ///
1829    /// The size of the user area can be determined with [`eeprom_user_size`].
1830    ///
1831    /// The function returns the actual number of bytes read into the buffer.
1832    /// If the buffer is larger than the user size the return value will be less
1833    /// than the length of the buffer.
1834    /// The return value should be checked to ensure the expected number of
1835    /// bytes was read.
1836    ///
1837    /// # Example
1838    ///
1839    /// ```no_run
1840    /// use libftd2xx::{Ftdi, FtdiCommon};
1841    ///
1842    /// let mut ft = Ftdi::new()?;
1843    /// let mut buf: [u8; 9] = [0; 9];
1844    /// let num_read = ft.eeprom_user_read(&mut buf)?;
1845    /// assert_eq!(buf.len(), num_read);
1846    /// # Ok::<(), libftd2xx::FtStatus>(())
1847    /// ```
1848    ///
1849    /// [`eeprom_user_size`]: FtdiCommon::eeprom_user_size
1850    fn eeprom_user_read(&mut self, buf: &mut [u8]) -> Result<usize, FtStatus> {
1851        let mut num_read: u32 = 0;
1852        let len: u32 = u32::try_from(buf.len()).unwrap();
1853        trace!("FT_EE_UARead({:?}, _, {}, _)", self.handle(), len);
1854        let status: FT_STATUS =
1855            unsafe { FT_EE_UARead(self.handle(), buf.as_mut_ptr(), len, &mut num_read) };
1856        ft_result(usize::try_from(num_read).unwrap(), status)
1857    }
1858
1859    /// Write to the EEPROM user area.
1860    ///
1861    /// An error will be returned when the buffer size is larger than the user
1862    /// area size.
1863    /// The size of the user area can be determined with [`eeprom_user_size`].
1864    ///
1865    /// # Example
1866    ///
1867    /// ```no_run
1868    /// use libftd2xx::{Ftdi, FtdiCommon};
1869    ///
1870    /// let mut ft = Ftdi::new()?;
1871    /// let data = "Hello, World";
1872    /// ft.eeprom_user_write(&data.as_bytes())?;
1873    /// # Ok::<(), libftd2xx::FtStatus>(())
1874    /// ```
1875    ///
1876    /// [`eeprom_user_size`]: FtdiCommon::eeprom_user_size
1877    fn eeprom_user_write(&mut self, buf: &[u8]) -> Result<(), FtStatus> {
1878        let len: u32 = u32::try_from(buf.len()).unwrap();
1879        trace!("FT_EE_UAWrite({:?}, _, {})", self.handle(), len);
1880        let status: FT_STATUS =
1881            unsafe { FT_EE_UAWrite(self.handle(), buf.as_ptr() as *mut u8, len) };
1882        ft_result((), status)
1883    }
1884}
1885
1886/// FTDI device-specific EEPROM trait.
1887pub trait FtdiEeprom<
1888    T: std::fmt::Debug + std::convert::From<Eeprom>,
1889    Eeprom: Default + std::fmt::Debug + std::convert::From<T>,
1890>: FtdiCommon
1891{
1892    /// Read from the FTD2XX device EEPROM.
1893    ///
1894    /// # Example
1895    ///
1896    /// This example uses the FT4232H.
1897    ///
1898    /// ```no_run
1899    /// use libftd2xx::{Ft4232h, Ftdi, FtdiEeprom};
1900    ///
1901    /// let mut ft: Ft4232h = Ftdi::new()?.try_into()?;
1902    /// let (eeprom, eeprom_strings) = ft.eeprom_read()?;
1903    /// println!("FT4232H EEPROM contents: {:?}", eeprom);
1904    /// println!("FT4232H EEPROM strings: {:?}", eeprom_strings);
1905    /// # Ok::<(), libftd2xx::DeviceTypeError>(())
1906    /// ```
1907    fn eeprom_read(&mut self) -> Result<(Eeprom, EepromStrings), FtStatus> {
1908        let mut manufacturer: [i8; STRING_LEN] = [0; STRING_LEN];
1909        let mut manufacturer_id: [i8; STRING_LEN] = [0; STRING_LEN];
1910        let mut description: [i8; STRING_LEN] = [0; STRING_LEN];
1911        let mut serial_number: [i8; STRING_LEN] = [0; STRING_LEN];
1912
1913        let mut eeprom_data: T = Eeprom::default().into();
1914        let eeprom_data_size = u32::try_from(mem::size_of::<T>()).unwrap();
1915
1916        trace!(
1917            "FT_EEPROM_Read({:?}, _, {}, _, _, _, _)",
1918            self.handle(),
1919            eeprom_data_size
1920        );
1921        let status: FT_STATUS = unsafe {
1922            FT_EEPROM_Read(
1923                self.handle(),
1924                &mut eeprom_data as *mut T as *mut c_void,
1925                eeprom_data_size,
1926                manufacturer.as_mut_ptr() as *mut c_char,
1927                manufacturer_id.as_mut_ptr() as *mut c_char,
1928                description.as_mut_ptr() as *mut c_char,
1929                serial_number.as_mut_ptr() as *mut c_char,
1930            )
1931        };
1932
1933        ft_result(
1934            (
1935                eeprom_data.into(),
1936                EepromStrings::with_slices(
1937                    &manufacturer,
1938                    &manufacturer_id,
1939                    &description,
1940                    &serial_number,
1941                )
1942                // safe to unwrap since driver cannot return invalid strings
1943                // in this case
1944                .unwrap(),
1945            ),
1946            status,
1947        )
1948    }
1949
1950    /// Program the FTD2XX EEPROM.
1951    ///
1952    /// # Warning
1953    ///
1954    /// Writing bad values to the EEPROM can brick your device.
1955    /// Please take a moment to read the license for this crate before using
1956    /// this function.
1957    ///
1958    /// # Example
1959    ///
1960    /// This example uses the FT232H.
1961    ///
1962    /// ```no_run
1963    /// use libftd2xx::{DriverType, Eeprom232h, EepromStrings, Ft232h, Ftdi, FtdiEeprom};
1964    ///
1965    /// let mut ft = Ft232h::with_serial_number("FT4PWSEOA")?;
1966    /// let strings = EepromStrings::with_strs("FTDI", "FT", "Hello World", "FT1234567")?;
1967    /// let mut eeprom = Eeprom232h::default();
1968    /// eeprom.set_driver_type(DriverType::D2XX);
1969    /// ft.eeprom_program(eeprom, strings)?;
1970    /// # Ok::<(), std::boxed::Box<dyn std::error::Error>>(())
1971    /// ```
1972    fn eeprom_program(&mut self, eeprom: Eeprom, strings: EepromStrings) -> Result<(), FtStatus> {
1973        let manufacturer = std::ffi::CString::new(strings.manufacturer()).unwrap();
1974        let manufacturer_id = std::ffi::CString::new(strings.manufacturer_id()).unwrap();
1975        let description = std::ffi::CString::new(strings.description()).unwrap();
1976        let serial_number = std::ffi::CString::new(strings.serial_number()).unwrap();
1977        let mut eeprom_data: T = eeprom.into();
1978        let eeprom_data_size = u32::try_from(mem::size_of::<T>()).unwrap();
1979
1980        trace!(
1981            r#"FT_EEPROM_Program({:?}, {:?}, {}, "{}", "{}", "{}", "{}")"#,
1982            self.handle(),
1983            eeprom_data,
1984            eeprom_data_size,
1985            strings.manufacturer(),
1986            strings.manufacturer_id(),
1987            strings.description(),
1988            strings.serial_number(),
1989        );
1990        let status: FT_STATUS = unsafe {
1991            FT_EEPROM_Program(
1992                self.handle(),
1993                &mut eeprom_data as *mut T as *mut c_void,
1994                eeprom_data_size,
1995                manufacturer.as_ptr() as *mut c_char,
1996                manufacturer_id.as_ptr() as *mut c_char,
1997                description.as_ptr() as *mut c_char,
1998                serial_number.as_ptr() as *mut c_char,
1999            )
2000        };
2001
2002        ft_result((), status)
2003    }
2004}
2005
2006fn str_to_cstring(s: &str) -> std::ffi::CString {
2007    std::ffi::CString::new(s).unwrap_or(std::ffi::CString::from(c""))
2008}
2009
2010fn ft_open_ex(arg: &str, flag: u32) -> Result<FT_HANDLE, FtStatus> {
2011    let mut handle: FT_HANDLE = std::ptr::null_mut();
2012    let cstr_arg = str_to_cstring(arg);
2013    trace!(r#"FT_OpenEx("{}", {}, _)"#, arg, flag);
2014    let status: FT_STATUS =
2015        unsafe { FT_OpenEx(cstr_arg.as_ptr() as *mut c_void, flag, &mut handle) };
2016    ft_result(handle, status)
2017}
2018
2019impl Ftdi {
2020    /// Open the first device on the system.
2021    ///
2022    /// This is equivalent to calling [`with_index`] with an index of `0`.
2023    ///
2024    /// This function cannot be used to open a specific device.
2025    /// Ordering of devices on a system is not guaranteed to remain constant.
2026    /// Calling this function multiple times may result in a different device
2027    /// each time when there is more than one device connected to the system.
2028    /// Use [`with_serial_number`] to open a specific device.
2029    ///
2030    /// # Example
2031    ///
2032    /// ```no_run
2033    /// use libftd2xx::Ftdi;
2034    ///
2035    /// Ftdi::new()?;
2036    /// # Ok::<(), libftd2xx::FtStatus>(())
2037    /// ```
2038    ///
2039    /// [`with_index`]: Ftdi::with_index
2040    /// [`with_serial_number`]: Ftdi::with_serial_number
2041    pub fn new() -> Result<Ftdi, FtStatus> {
2042        Ftdi::with_index(0)
2043    }
2044
2045    /// Open the device by an arbitrary index and initialize the handle.
2046    ///
2047    /// This function can open multiple devices, but it cannot be used to open
2048    /// a specific device.
2049    /// Ordering of devices on a system is not guaranteed to remain constant.
2050    /// Calling this function multiple times with the same index may result in a
2051    /// different device each time when there is more than one device connected
2052    /// to the system.
2053    /// Use [`with_serial_number`] to open a specific device.
2054    ///
2055    /// # Example
2056    ///
2057    /// ```no_run
2058    /// use libftd2xx::Ftdi;
2059    ///
2060    /// Ftdi::with_index(0)?;
2061    /// # Ok::<(), libftd2xx::FtStatus>(())
2062    /// ```
2063    ///
2064    /// [`with_serial_number`]: Ftdi::with_serial_number
2065    pub fn with_index(index: i32) -> Result<Ftdi, FtStatus> {
2066        let mut handle: FT_HANDLE = std::ptr::null_mut();
2067        trace!("FT_Open({}, _)", index);
2068        let status: FT_STATUS = unsafe { FT_Open(index, &mut handle) };
2069        ft_result(Ftdi { handle }, status)
2070    }
2071
2072    /// Open the device by its serial number and initialize the handle.
2073    ///
2074    /// # Example
2075    ///
2076    /// ```no_run
2077    /// use libftd2xx::Ftdi;
2078    ///
2079    /// Ftdi::with_serial_number("FT59UO4C")?;
2080    /// # Ok::<(), libftd2xx::FtStatus>(())
2081    /// ```
2082    pub fn with_serial_number(serial_number: &str) -> Result<Ftdi, FtStatus> {
2083        let handle = ft_open_ex(serial_number, FT_OPEN_BY_SERIAL_NUMBER)?;
2084        Ok(Ftdi { handle })
2085    }
2086
2087    /// Open the device by its device description.
2088    ///
2089    /// # Example
2090    ///
2091    /// ```no_run
2092    /// use libftd2xx::Ftdi;
2093    ///
2094    /// Ftdi::with_description("Hello")?;
2095    /// # Ok::<(), libftd2xx::FtStatus>(())
2096    /// ```
2097    pub fn with_description(description: &str) -> Result<Ftdi, FtStatus> {
2098        let handle = ft_open_ex(description, FT_OPEN_BY_DESCRIPTION)?;
2099        Ok(Ftdi { handle })
2100    }
2101}
2102
2103impl Ft232h {
2104    /// Open a `Ft232h` device and initialize the handle.
2105    ///
2106    /// # Safety
2107    ///
2108    /// This is **unchecked** meaning a device type check will not be performed.
2109    /// Methods that require this specific device type may fail in unexpected
2110    /// ways if the wrong device is used.
2111    ///
2112    /// # Example
2113    ///
2114    /// ```no_run
2115    /// use libftd2xx::Ft232h;
2116    ///
2117    /// let mut ft = unsafe { Ft232h::with_serial_number_unchecked("FT5AVX6B")? };
2118    /// # Ok::<(), libftd2xx::FtStatus>(())
2119    /// ```
2120    pub unsafe fn with_serial_number_unchecked(serial_number: &str) -> Result<Ft232h, FtStatus> {
2121        let handle = ft_open_ex(serial_number, FT_OPEN_BY_SERIAL_NUMBER)?;
2122        Ok(Ft232h {
2123            ftdi: Ftdi { handle },
2124        })
2125    }
2126
2127    /// Open a `Ft232h` device and initialize the handle.
2128    ///
2129    /// # Example
2130    ///
2131    /// ```no_run
2132    /// use libftd2xx::Ft232h;
2133    ///
2134    /// Ft232h::with_serial_number("FT59UO4C")?;
2135    /// # Ok::<(), libftd2xx::DeviceTypeError>(())
2136    /// ```
2137    pub fn with_serial_number(serial_number: &str) -> Result<Ft232h, DeviceTypeError> {
2138        Ftdi::with_serial_number(serial_number)?.try_into()
2139    }
2140
2141    /// Open a `Ft232h` device by its device description.
2142    ///
2143    /// # Example
2144    ///
2145    /// ```no_run
2146    /// use libftd2xx::Ft232h;
2147    ///
2148    /// Ft232h::with_description("Hello")?;
2149    /// # Ok::<(), libftd2xx::DeviceTypeError>(())
2150    /// ```
2151    pub fn with_description(description: &str) -> Result<Ft232h, DeviceTypeError> {
2152        Ftdi::with_description(description)?.try_into()
2153    }
2154}
2155
2156impl Ft232r {
2157    /// Open a `Ft232r` device and initialize the handle.
2158    ///
2159    /// # Safety
2160    ///
2161    /// This is **unchecked** meaning a device type check will not be performed.
2162    /// Methods that require this specific device type may fail in unexpected
2163    /// ways if the wrong device is used.
2164    ///
2165    /// # Example
2166    ///
2167    /// ```no_run
2168    /// use libftd2xx::Ft232r;
2169    ///
2170    /// let mut ft = unsafe { Ft232r::with_serial_number_unchecked("AH06S0OE")? };
2171    /// # Ok::<(), libftd2xx::FtStatus>(())
2172    /// ```
2173    pub unsafe fn with_serial_number_unchecked(serial_number: &str) -> Result<Ft232r, FtStatus> {
2174        let handle = ft_open_ex(serial_number, FT_OPEN_BY_SERIAL_NUMBER)?;
2175        Ok(Ft232r {
2176            ftdi: Ftdi { handle },
2177        })
2178    }
2179
2180    /// Open a `Ft232r` device and initialize the handle.
2181    ///
2182    /// # Example
2183    ///
2184    /// ```no_run
2185    /// use libftd2xx::Ft232r;
2186    ///
2187    /// Ft232r::with_serial_number("AH06S0OE")?;
2188    /// # Ok::<(), libftd2xx::DeviceTypeError>(())
2189    /// ```
2190    pub fn with_serial_number(serial_number: &str) -> Result<Ft232r, DeviceTypeError> {
2191        Ftdi::with_serial_number(serial_number)?.try_into()
2192    }
2193
2194    /// Open a `Ft232r` device by its device description.
2195    ///
2196    /// # Example
2197    ///
2198    /// ```no_run
2199    /// use libftd2xx::Ft232r;
2200    ///
2201    /// Ft232r::with_description("Hello")?;
2202    /// # Ok::<(), libftd2xx::DeviceTypeError>(())
2203    /// ```
2204    pub fn with_description(description: &str) -> Result<Ft232r, DeviceTypeError> {
2205        Ftdi::with_description(description)?.try_into()
2206    }
2207}
2208
2209impl Ft2232h {
2210    /// Open a `Ft2232h` device and initialize the handle.
2211    ///
2212    /// # Safety
2213    ///
2214    /// This is **unchecked** meaning a device type check will not be performed.
2215    /// Methods that require this specific device type may fail in unexpected
2216    /// ways if the wrong device is used.
2217    ///
2218    /// # Example
2219    ///
2220    /// ```no_run
2221    /// use libftd2xx::Ft2232h;
2222    ///
2223    /// let mut ft = unsafe { Ft2232h::with_serial_number_unchecked("FT4PWSEOA")? };
2224    /// # Ok::<(), libftd2xx::FtStatus>(())
2225    /// ```
2226    pub unsafe fn with_serial_number_unchecked(serial_number: &str) -> Result<Ft2232h, FtStatus> {
2227        let handle = ft_open_ex(serial_number, FT_OPEN_BY_SERIAL_NUMBER)?;
2228        Ok(Ft2232h {
2229            ftdi: Ftdi { handle },
2230        })
2231    }
2232
2233    /// Open a `Ft2232h` device and initialize the handle.
2234    ///
2235    /// # Example
2236    ///
2237    /// ```no_run
2238    /// use libftd2xx::Ft2232h;
2239    ///
2240    /// Ft2232h::with_serial_number("FT4PWSEOA")?;
2241    /// # Ok::<(), libftd2xx::DeviceTypeError>(())
2242    /// ```
2243    pub fn with_serial_number(serial_number: &str) -> Result<Ft2232h, DeviceTypeError> {
2244        Ftdi::with_serial_number(serial_number)?.try_into()
2245    }
2246
2247    /// Open a `Ft2232h` device by its device description.
2248    ///
2249    /// # Example
2250    ///
2251    /// ```no_run
2252    /// use libftd2xx::Ft2232h;
2253    ///
2254    /// Ft2232h::with_description("FT2232H-56Q MiniModule A")?;
2255    /// # Ok::<(), libftd2xx::DeviceTypeError>(())
2256    /// ```
2257    pub fn with_description(description: &str) -> Result<Ft2232h, DeviceTypeError> {
2258        Ftdi::with_description(description)?.try_into()
2259    }
2260}
2261
2262impl Ft4232h {
2263    /// Open a `Ft4232h` device and initialize the handle.
2264    ///
2265    /// # Safety
2266    ///
2267    /// This is **unchecked** meaning a device type check will not be performed.
2268    /// Methods that require this specific device type may fail in unexpected
2269    /// ways if the wrong device is used.
2270    ///
2271    /// # Example
2272    ///
2273    /// ```no_run
2274    /// use libftd2xx::Ft4232h;
2275    ///
2276    /// let mut ft = unsafe { Ft4232h::with_serial_number_unchecked("FT4PWSEOA")? };
2277    /// # Ok::<(), libftd2xx::FtStatus>(())
2278    /// ```
2279    pub unsafe fn with_serial_number_unchecked(serial_number: &str) -> Result<Ft4232h, FtStatus> {
2280        let handle = ft_open_ex(serial_number, FT_OPEN_BY_SERIAL_NUMBER)?;
2281        Ok(Ft4232h {
2282            ftdi: Ftdi { handle },
2283        })
2284    }
2285
2286    /// Open a `Ft4232h` device and initialize the handle.
2287    ///
2288    /// # Example
2289    ///
2290    /// ```no_run
2291    /// use libftd2xx::Ft4232h;
2292    ///
2293    /// Ft4232h::with_serial_number("FT4PWSEOA")?;
2294    /// # Ok::<(), libftd2xx::DeviceTypeError>(())
2295    /// ```
2296    pub fn with_serial_number(serial_number: &str) -> Result<Ft4232h, DeviceTypeError> {
2297        Ftdi::with_serial_number(serial_number)?.try_into()
2298    }
2299
2300    /// Open a `Ft4232h` device by its device description.
2301    ///
2302    /// # Example
2303    ///
2304    /// ```no_run
2305    /// use libftd2xx::Ft4232h;
2306    ///
2307    /// Ft4232h::with_description("FT4232H-56Q MiniModule A")?;
2308    /// # Ok::<(), libftd2xx::DeviceTypeError>(())
2309    /// ```
2310    pub fn with_description(description: &str) -> Result<Ft4232h, DeviceTypeError> {
2311        Ftdi::with_description(description)?.try_into()
2312    }
2313}
2314
2315impl Ft4232ha {
2316    /// Open a `Ft4232ha` device and initialize the handle.
2317    ///
2318    /// # Safety
2319    ///
2320    /// This is **unchecked** meaning a device type check will not be performed.
2321    /// Methods that require this specific device type may fail in unexpected
2322    /// ways if the wrong device is used.
2323    ///
2324    /// # Example
2325    ///
2326    /// ```no_run
2327    /// use libftd2xx::Ft4232ha;
2328    ///
2329    /// let mut ft = unsafe { Ft4232ha::with_serial_number_unchecked("FT4PWSEOA")? };
2330    /// # Ok::<(), libftd2xx::FtStatus>(())
2331    /// ```
2332    pub unsafe fn with_serial_number_unchecked(serial_number: &str) -> Result<Ft4232ha, FtStatus> {
2333        let handle = ft_open_ex(serial_number, FT_OPEN_BY_SERIAL_NUMBER)?;
2334        Ok(Ft4232ha {
2335            ftdi: Ftdi { handle },
2336        })
2337    }
2338
2339    /// Open a `Ft4232ha` device and initialize the handle.
2340    ///
2341    /// # Example
2342    ///
2343    /// ```no_run
2344    /// use libftd2xx::Ft4232ha;
2345    ///
2346    /// Ft4232ha::with_serial_number("FT4PWSEOA")?;
2347    /// # Ok::<(), libftd2xx::DeviceTypeError>(())
2348    /// ```
2349    pub fn with_serial_number(serial_number: &str) -> Result<Ft4232ha, DeviceTypeError> {
2350        Ftdi::with_serial_number(serial_number)?.try_into()
2351    }
2352
2353    /// Open a `Ft4232ha` device by its device description.
2354    ///
2355    /// # Example
2356    ///
2357    /// ```no_run
2358    /// use libftd2xx::Ft4232ha;
2359    ///
2360    /// Ft4232ha::with_description("Quad RS232-HS A")?;
2361    /// # Ok::<(), libftd2xx::DeviceTypeError>(())
2362    /// ```
2363    pub fn with_description(description: &str) -> Result<Ft4232ha, DeviceTypeError> {
2364        Ftdi::with_description(description)?.try_into()
2365    }
2366}
2367
2368impl FtdiCommon for Ftdi {
2369    const DEVICE_TYPE: DeviceType = DeviceType::Unknown;
2370
2371    fn handle(&mut self) -> FT_HANDLE {
2372        self.handle
2373    }
2374}
2375
2376impl Drop for Ftdi {
2377    fn drop(&mut self) {
2378        self.close().ok();
2379    }
2380}
2381
2382// Safety: Unfortunatly there is no clear statement about the thread safety of
2383// the D2XX library in the official programming guide. But there are mentions
2384// about thread-safety fixes in the release notes of the driver, which suggests
2385// that the library is indeed thread synchronized. It would also be quite
2386// strange if the D2XX driver could not be used across threads, but the VCP
2387// driver can.
2388unsafe impl Send for Ftdi {}
2389unsafe impl Sync for Ftdi {}
2390
2391macro_rules! impl_ftdi_common_for {
2392    ($DEVICE:ident, $TYPE:expr) => {
2393        impl FtdiCommon for $DEVICE {
2394            const DEVICE_TYPE: DeviceType = $TYPE;
2395
2396            fn handle(&mut self) -> FT_HANDLE {
2397                self.ftdi.handle
2398            }
2399
2400            fn device_type(&mut self) -> Result<DeviceType, FtStatus> {
2401                Ok(Self::DEVICE_TYPE)
2402            }
2403        }
2404    };
2405}
2406
2407macro_rules! impl_mpsse_cmd_executor_for {
2408    ($DEVICE:ident, $TYPE:expr) => {
2409        impl MpsseCmdExecutor for $DEVICE {
2410            type Error = TimeoutError;
2411
2412            fn init(&mut self, settings: &MpsseSettings) -> Result<(), Self::Error> {
2413                self.initialize_mpsse(settings)
2414            }
2415
2416            fn send(&mut self, data: &[u8]) -> Result<(), Self::Error> {
2417                self.write_all(data)
2418            }
2419
2420            fn recv(&mut self, data: &mut [u8]) -> Result<(), Self::Error> {
2421                self.read_all(data)
2422            }
2423        }
2424    };
2425}
2426
2427macro_rules! impl_try_from_for {
2428    ($DEVICE:ident) => {
2429        impl TryFrom<Ftdi> for $DEVICE {
2430            type Error = DeviceTypeError;
2431
2432            fn try_from(mut ft: Ftdi) -> Result<Self, Self::Error> {
2433                let device_type: DeviceType = ft.device_type()?;
2434                if device_type != Self::DEVICE_TYPE {
2435                    Err(DeviceTypeError::DeviceType {
2436                        expected: $DEVICE::DEVICE_TYPE,
2437                        detected: device_type,
2438                    })
2439                } else {
2440                    Ok($DEVICE { ftdi: ft })
2441                }
2442            }
2443        }
2444    };
2445}
2446
2447impl_ftdi_common_for!(Ft232h, DeviceType::FT232H);
2448impl_ftdi_common_for!(Ft232r, DeviceType::FT232R);
2449impl_ftdi_common_for!(Ft2232h, DeviceType::FT2232H);
2450impl_ftdi_common_for!(Ft4232h, DeviceType::FT4232H);
2451impl_ftdi_common_for!(Ft4232ha, DeviceType::FT4232HA);
2452impl_ftdi_common_for!(FtXSeries, DeviceType::FT_X_SERIES);
2453
2454impl_mpsse_cmd_executor_for!(Ft232h, DeviceType::FT232H);
2455impl_mpsse_cmd_executor_for!(Ft2232h, DeviceType::FT2232H);
2456impl_mpsse_cmd_executor_for!(Ft4232h, DeviceType::FT4232H);
2457impl_mpsse_cmd_executor_for!(Ft4232ha, DeviceType::FT4232HA);
2458
2459impl_try_from_for!(Ft232h);
2460impl_try_from_for!(Ft232r);
2461impl_try_from_for!(Ft2232h);
2462impl_try_from_for!(Ft4232h);
2463impl_try_from_for!(Ft4232ha);
2464impl_try_from_for!(FtXSeries);
2465
2466impl FtdiEeprom<FT_EEPROM_232H, Eeprom232h> for Ft232h {}
2467impl FtdiEeprom<FT_EEPROM_232R, Eeprom232r> for Ft232r {}
2468impl FtdiEeprom<FT_EEPROM_2232H, Eeprom2232h> for Ft2232h {}
2469impl FtdiEeprom<FT_EEPROM_4232H, Eeprom4232h> for Ft4232h {}
2470impl FtdiEeprom<FT_EEPROM_X_SERIES, EepromXSeries> for FtXSeries {}
2471
2472impl FtdiMpsse for Ft232h {}
2473impl FtdiMpsse for Ft2232h {}
2474impl FtdiMpsse for Ft4232h {}
2475impl FtdiMpsse for Ft4232ha {}
2476impl Ftx232hMpsse for Ft232h {}
2477impl Ftx232hMpsse for Ft2232h {}
2478impl Ftx232hMpsse for Ft4232h {}
2479impl Ftx232hMpsse for Ft4232ha {}
2480
2481#[cfg(test)]
2482mod tests {
2483    use super::str_to_cstring;
2484
2485    #[test]
2486    fn str_to_cstr_basic() {
2487        assert_eq!(
2488            str_to_cstring("Hello, World!"),
2489            std::ffi::CString::from(c"Hello, World!")
2490        );
2491    }
2492
2493    // https://github.com/ftdi-rs/libftd2xx/issues/83
2494    #[test]
2495    fn str_to_cstr_interior_null() {
2496        str_to_cstring("\0\u{e}.r");
2497    }
2498}