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}