Skip to main content

ftdi2/
lib.rs

1#![allow(dead_code)]
2#![allow(unused_variables)]
3
4use libftdi1_sys as ffi;
5use libusb1_sys as usb_ffi;
6use std::ffi::{CStr, CString};
7use std::ptr::null_mut;
8
9use std::error::Error;
10use std::fmt;
11
12use std::io;
13use std::io::{Read, Write};
14
15#[derive(Debug)]
16pub struct FtdiError {
17    error_string: Option<&'static CStr>,
18}
19
20impl Error for FtdiError {}
21
22impl fmt::Display for FtdiError {
23    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
24        match self.error_string {
25            Some(err) => write!(f, "libftdi error: {:?}", err),
26            None => write!(f, "libftdi error occurred, but no error text was found"),
27        }
28    }
29}
30
31pub enum Interface {
32    A,
33    B,
34    C,
35    D,
36    Any,
37}
38
39impl Into<ffi::ftdi_interface> for Interface {
40    fn into(self) -> ffi::ftdi_interface {
41        match self {
42            Interface::A => ffi::ftdi_interface::INTERFACE_A,
43            Interface::B => ffi::ftdi_interface::INTERFACE_B,
44            Interface::C => ffi::ftdi_interface::INTERFACE_C,
45            Interface::D => ffi::ftdi_interface::INTERFACE_D,
46            Interface::Any => ffi::ftdi_interface::INTERFACE_ANY,
47        }
48    }
49}
50
51pub enum BitsType {
52    Bits7,
53    Bits8,
54}
55
56impl Into<ffi::ftdi_bits_type> for BitsType {
57    fn into(self) -> ffi::ftdi_bits_type {
58        match self {
59            BitsType::Bits7 => ffi::ftdi_bits_type::BITS_7,
60            BitsType::Bits8 => ffi::ftdi_bits_type::BITS_8,
61        }
62    }
63}
64
65pub enum StopBitsType {
66    StopBit1,
67    StopBit15,
68    StopBit2,
69}
70
71impl Into<ffi::ftdi_stopbits_type> for StopBitsType {
72    fn into(self) -> ffi::ftdi_stopbits_type {
73        match self {
74            StopBitsType::StopBit1 => ffi::ftdi_stopbits_type::STOP_BIT_1,
75            StopBitsType::StopBit15 => ffi::ftdi_stopbits_type::STOP_BIT_15,
76            StopBitsType::StopBit2 => ffi::ftdi_stopbits_type::STOP_BIT_2,
77        }
78    }
79}
80
81pub enum ParityType {
82    None,
83    Odd,
84    Even,
85    Mark,
86    Space,
87}
88
89impl Into<ffi::ftdi_parity_type> for ParityType {
90    fn into(self) -> ffi::ftdi_parity_type {
91        match self {
92            ParityType::None => ffi::ftdi_parity_type::NONE,
93            ParityType::Odd => ffi::ftdi_parity_type::ODD,
94            ParityType::Even => ffi::ftdi_parity_type::EVEN,
95            ParityType::Mark => ffi::ftdi_parity_type::MARK,
96            ParityType::Space => ffi::ftdi_parity_type::SPACE,
97        }
98    }
99}
100
101pub enum BreakType {
102    Off,
103    On,
104}
105
106impl Into<ffi::ftdi_break_type> for BreakType {
107    fn into(self) -> ffi::ftdi_break_type {
108        match self {
109            BreakType::Off => ffi::ftdi_break_type::BREAK_OFF,
110            BreakType::On => ffi::ftdi_break_type::BREAK_ON,
111        }
112    }
113}
114
115pub enum ChipType {
116    Am,
117    Bm,
118    _2232C,
119    R,
120    _2232H,
121    _4232H,
122    _232H,
123    _230X,
124}
125
126impl Into<ffi::ftdi_chip_type> for ChipType {
127    fn into(self) -> ffi::ftdi_chip_type {
128        match self {
129            ChipType::Am => ffi::ftdi_chip_type::TYPE_AM,
130            ChipType::Bm => ffi::ftdi_chip_type::TYPE_BM,
131            ChipType::_2232C => ffi::ftdi_chip_type::TYPE_2232C,
132            ChipType::R => ffi::ftdi_chip_type::TYPE_R,
133            ChipType::_2232H => ffi::ftdi_chip_type::TYPE_2232H,
134            ChipType::_4232H => ffi::ftdi_chip_type::TYPE_4232H,
135            ChipType::_232H => ffi::ftdi_chip_type::TYPE_232H,
136            ChipType::_230X => ffi::ftdi_chip_type::TYPE_230X,
137        }
138    }
139}
140
141#[derive(Debug, Default)]
142pub struct MpsseMode {
143    pub bitbang: bool,
144    pub mpsse: bool,
145    pub syncbb: bool,
146    pub mcu: bool,
147    pub opto: bool,
148    pub cbus: bool,
149    pub syncff: bool,
150    pub ft1284: bool,
151}
152
153impl Into<u8> for MpsseMode {
154    fn into(self) -> u8 {
155        let mut result = ffi::ftdi_mpsse_mode::BITMODE_RESET.0 as u8;
156        if self.bitbang {
157            result |= ffi::ftdi_mpsse_mode::BITMODE_BITBANG.0  as u8
158        }
159        if self.mpsse {
160            result |= ffi::ftdi_mpsse_mode::BITMODE_MPSSE.0  as u8
161        }
162        if self.syncbb {
163            result |= ffi::ftdi_mpsse_mode::BITMODE_SYNCBB.0  as u8
164        }
165        if self.mcu {
166            result |= ffi::ftdi_mpsse_mode::BITMODE_MCU.0  as u8
167        }
168        if self.opto {
169            result |= ffi::ftdi_mpsse_mode::BITMODE_OPTO.0  as u8
170        }
171        if self.cbus {
172            result |= ffi::ftdi_mpsse_mode::BITMODE_CBUS.0  as u8
173        }
174        if self.syncff {
175            result |= ffi::ftdi_mpsse_mode::BITMODE_SYNCFF.0  as u8
176        }
177        if self.ft1284 {
178            result |= ffi::ftdi_mpsse_mode::BITMODE_FT1284.0  as u8
179        }
180
181        result
182    }
183}
184
185pub enum ModuleDetachMode {
186    AutoDetachSioModule,
187    DontDetachSioModule,
188    // AutoDetachReattachSioModule,
189}
190
191impl Into<ffi::ftdi_module_detach_mode> for ModuleDetachMode {
192    fn into(self) -> ffi::ftdi_module_detach_mode {
193        match self {
194            ModuleDetachMode::AutoDetachSioModule => {
195                ffi::ftdi_module_detach_mode::AUTO_DETACH_SIO_MODULE
196            }
197            ModuleDetachMode::DontDetachSioModule => {
198                ffi::ftdi_module_detach_mode::DONT_DETACH_SIO_MODULE
199            } // ModuleDetachMode::AutoDetachReattachSioModule => ffi::ftdi_module_detach_mode::AUTO_DETACH_REATACH_SIO_MODULE,
200        }
201    }
202}
203
204pub enum EepromValue {
205    VendorId,
206    ProductId,
207    SelfPowered,
208    RemoteWakeup,
209    IsNotPnp,
210    SuspendDbus7,
211    InIsIsochronous,
212    OutIsIsochronous,
213    SuspendPullDowns,
214    UseSerial,
215    UsbVersion,
216    UseUsbVersion,
217    MaxPower,
218    ChannelAType,
219    ChannelBType,
220    ChannelADriver,
221    ChannelBDriver,
222    CbusFunction0,
223    CbusFunction1,
224    CbusFunction2,
225    CbusFunction3,
226    CbusFunction4,
227    CbusFunction5,
228    CbusFunction6,
229    CbusFunction7,
230    CbusFunction8,
231    CbusFunction9,
232    HighCurrent,
233    HighCurrentA,
234    HighCurrentB,
235    Invert,
236    Group0Drive,
237    Group0Schmitt,
238    Group0Slew,
239    Group1Drive,
240    Group1Schmitt,
241    Group1Slew,
242    Group2Drive,
243    Group2Schmitt,
244    Group2Slew,
245    Group3Drive,
246    Group3Schmitt,
247    Group3Slew,
248    ChipSize,
249    ChipType,
250    PowerSave,
251    ClockPolarity,
252    DataOrder,
253    FlowControl,
254    ChannelCDriver,
255    ChannelDDriver,
256    ChannelARs485,
257    ChannelBRs485,
258    ChannelCRs485,
259    ChannelDRs485,
260    ReleaseNumber,
261}
262
263impl Into<ffi::ftdi_eeprom_value> for EepromValue {
264    fn into(self) -> ffi::ftdi_eeprom_value {
265        match self {
266            EepromValue::VendorId => ffi::ftdi_eeprom_value::VENDOR_ID,
267            EepromValue::ProductId => ffi::ftdi_eeprom_value::PRODUCT_ID,
268            EepromValue::SelfPowered => ffi::ftdi_eeprom_value::SELF_POWERED,
269            EepromValue::RemoteWakeup => ffi::ftdi_eeprom_value::REMOTE_WAKEUP,
270            EepromValue::IsNotPnp => ffi::ftdi_eeprom_value::IS_NOT_PNP,
271            EepromValue::SuspendDbus7 => ffi::ftdi_eeprom_value::SUSPEND_DBUS7,
272            EepromValue::InIsIsochronous => ffi::ftdi_eeprom_value::IN_IS_ISOCHRONOUS,
273            EepromValue::OutIsIsochronous => ffi::ftdi_eeprom_value::OUT_IS_ISOCHRONOUS,
274            EepromValue::SuspendPullDowns => ffi::ftdi_eeprom_value::SUSPEND_PULL_DOWNS,
275            EepromValue::UseSerial => ffi::ftdi_eeprom_value::USE_SERIAL,
276            EepromValue::UsbVersion => ffi::ftdi_eeprom_value::USB_VERSION,
277            EepromValue::UseUsbVersion => ffi::ftdi_eeprom_value::USE_USB_VERSION,
278            EepromValue::MaxPower => ffi::ftdi_eeprom_value::MAX_POWER,
279            EepromValue::ChannelAType => ffi::ftdi_eeprom_value::CHANNEL_A_TYPE,
280            EepromValue::ChannelBType => ffi::ftdi_eeprom_value::CHANNEL_B_TYPE,
281            EepromValue::ChannelADriver => ffi::ftdi_eeprom_value::CHANNEL_A_DRIVER,
282            EepromValue::ChannelBDriver => ffi::ftdi_eeprom_value::CHANNEL_B_DRIVER,
283            EepromValue::CbusFunction0 => ffi::ftdi_eeprom_value::CBUS_FUNCTION_0,
284            EepromValue::CbusFunction1 => ffi::ftdi_eeprom_value::CBUS_FUNCTION_1,
285            EepromValue::CbusFunction2 => ffi::ftdi_eeprom_value::CBUS_FUNCTION_2,
286            EepromValue::CbusFunction3 => ffi::ftdi_eeprom_value::CBUS_FUNCTION_3,
287            EepromValue::CbusFunction4 => ffi::ftdi_eeprom_value::CBUS_FUNCTION_4,
288            EepromValue::CbusFunction5 => ffi::ftdi_eeprom_value::CBUS_FUNCTION_5,
289            EepromValue::CbusFunction6 => ffi::ftdi_eeprom_value::CBUS_FUNCTION_6,
290            EepromValue::CbusFunction7 => ffi::ftdi_eeprom_value::CBUS_FUNCTION_7,
291            EepromValue::CbusFunction8 => ffi::ftdi_eeprom_value::CBUS_FUNCTION_8,
292            EepromValue::CbusFunction9 => ffi::ftdi_eeprom_value::CBUS_FUNCTION_9,
293            EepromValue::HighCurrent => ffi::ftdi_eeprom_value::HIGH_CURRENT,
294            EepromValue::HighCurrentA => ffi::ftdi_eeprom_value::HIGH_CURRENT_A,
295            EepromValue::HighCurrentB => ffi::ftdi_eeprom_value::HIGH_CURRENT_B,
296            EepromValue::Invert => ffi::ftdi_eeprom_value::INVERT,
297            EepromValue::Group0Drive => ffi::ftdi_eeprom_value::GROUP0_DRIVE,
298            EepromValue::Group0Schmitt => ffi::ftdi_eeprom_value::GROUP0_SCHMITT,
299            EepromValue::Group0Slew => ffi::ftdi_eeprom_value::GROUP0_SLEW,
300            EepromValue::Group1Drive => ffi::ftdi_eeprom_value::GROUP1_DRIVE,
301            EepromValue::Group1Schmitt => ffi::ftdi_eeprom_value::GROUP1_SCHMITT,
302            EepromValue::Group1Slew => ffi::ftdi_eeprom_value::GROUP1_SLEW,
303            EepromValue::Group2Drive => ffi::ftdi_eeprom_value::GROUP2_DRIVE,
304            EepromValue::Group2Schmitt => ffi::ftdi_eeprom_value::GROUP2_SCHMITT,
305            EepromValue::Group2Slew => ffi::ftdi_eeprom_value::GROUP2_SLEW,
306            EepromValue::Group3Drive => ffi::ftdi_eeprom_value::GROUP3_DRIVE,
307            EepromValue::Group3Schmitt => ffi::ftdi_eeprom_value::GROUP3_SCHMITT,
308            EepromValue::Group3Slew => ffi::ftdi_eeprom_value::GROUP3_SLEW,
309            EepromValue::ChipSize => ffi::ftdi_eeprom_value::CHIP_SIZE,
310            EepromValue::ChipType => ffi::ftdi_eeprom_value::CHIP_TYPE,
311            EepromValue::PowerSave => ffi::ftdi_eeprom_value::POWER_SAVE,
312            EepromValue::ClockPolarity => ffi::ftdi_eeprom_value::CLOCK_POLARITY,
313            EepromValue::DataOrder => ffi::ftdi_eeprom_value::DATA_ORDER,
314            EepromValue::FlowControl => ffi::ftdi_eeprom_value::FLOW_CONTROL,
315            EepromValue::ChannelCDriver => ffi::ftdi_eeprom_value::CHANNEL_C_DRIVER,
316            EepromValue::ChannelDDriver => ffi::ftdi_eeprom_value::CHANNEL_D_DRIVER,
317            EepromValue::ChannelARs485 => ffi::ftdi_eeprom_value::CHANNEL_A_RS485,
318            EepromValue::ChannelBRs485 => ffi::ftdi_eeprom_value::CHANNEL_B_RS485,
319            EepromValue::ChannelCRs485 => ffi::ftdi_eeprom_value::CHANNEL_C_RS485,
320            EepromValue::ChannelDRs485 => ffi::ftdi_eeprom_value::CHANNEL_D_RS485,
321            EepromValue::ReleaseNumber => ffi::ftdi_eeprom_value::RELEASE_NUMBER,
322        }
323    }
324}
325
326pub enum CbusFunc {
327    TxdEn,
328    PwrEn,
329    RxLed,
330    TxLed,
331    TxRxLed,
332    Sleep,
333    Clk48,
334    Clk24,
335    Clk12,
336    Clk6,
337    IoMode,
338    BbWr,
339    BbRd,
340}
341
342impl Into<ffi::ftdi_cbus_func> for CbusFunc {
343    fn into(self) -> ffi::ftdi_cbus_func {
344        match self {
345            CbusFunc::TxdEn => ffi::ftdi_cbus_func::CBUS_TXDEN,
346            CbusFunc::PwrEn => ffi::ftdi_cbus_func::CBUS_PWREN,
347            CbusFunc::RxLed => ffi::ftdi_cbus_func::CBUS_RXLED,
348            CbusFunc::TxLed => ffi::ftdi_cbus_func::CBUS_TXLED,
349            CbusFunc::TxRxLed => ffi::ftdi_cbus_func::CBUS_TXRXLED,
350            CbusFunc::Sleep => ffi::ftdi_cbus_func::CBUS_SLEEP,
351            CbusFunc::Clk48 => ffi::ftdi_cbus_func::CBUS_CLK48,
352            CbusFunc::Clk24 => ffi::ftdi_cbus_func::CBUS_CLK24,
353            CbusFunc::Clk12 => ffi::ftdi_cbus_func::CBUS_CLK12,
354            CbusFunc::Clk6 => ffi::ftdi_cbus_func::CBUS_CLK6,
355            CbusFunc::IoMode => ffi::ftdi_cbus_func::CBUS_IOMODE,
356            CbusFunc::BbWr => ffi::ftdi_cbus_func::CBUS_BB_WR,
357            CbusFunc::BbRd => ffi::ftdi_cbus_func::CBUS_BB_RD,
358        }
359    }
360}
361
362pub enum CbusHFunc {
363    Tristate,
364    TxLed,
365    RxLed,
366    TxRxLed,
367    PwrEn,
368    Sleep,
369    Drive0,
370    Drive1,
371    IoMode,
372    Clk30,
373    Clk15,
374    Clk7p5,
375}
376
377impl Into<ffi::ftdi_cbush_func> for CbusHFunc {
378    fn into(self) -> ffi::ftdi_cbush_func {
379        match self {
380            CbusHFunc::Tristate => ffi::ftdi_cbush_func::CBUSH_TRISTATE,
381            CbusHFunc::PwrEn => ffi::ftdi_cbush_func::CBUSH_PWREN,
382            CbusHFunc::RxLed => ffi::ftdi_cbush_func::CBUSH_RXLED,
383            CbusHFunc::TxLed => ffi::ftdi_cbush_func::CBUSH_TXLED,
384            CbusHFunc::TxRxLed => ffi::ftdi_cbush_func::CBUSH_TXRXLED,
385            CbusHFunc::Sleep => ffi::ftdi_cbush_func::CBUSH_SLEEP,
386            CbusHFunc::Drive0 => ffi::ftdi_cbush_func::CBUSH_DRIVE_0,
387            CbusHFunc::Drive1 => ffi::ftdi_cbush_func::CBUSH_DRIVE1,
388            CbusHFunc::IoMode => ffi::ftdi_cbush_func::CBUSH_IOMODE,
389            CbusHFunc::Clk30 => ffi::ftdi_cbush_func::CBUSH_CLK30,
390            CbusHFunc::Clk15 => ffi::ftdi_cbush_func::CBUSH_CLK15,
391            CbusHFunc::Clk7p5 => ffi::ftdi_cbush_func::CBUSH_CLK7_5,
392        }
393    }
394}
395
396fn bytes_into_cstring(bytes: &[u8]) -> Option<CString> {
397    let first_nul = bytes.iter().position(|&b| b == 0);
398    match first_nul {
399        Some(i) => Some(
400            CStr::from_bytes_with_nul(&bytes[0..=i])
401                .expect("Data wasn't well formed")
402                .to_owned(),
403        ),
404        None => None,
405    }
406}
407
408#[derive(Debug)]
409pub struct Device {
410    context: Context,
411}
412
413impl Device {
414    /// Check for errors from the result of a method.
415    ///
416    /// If `result_code` is non-negative, the action was a success, and we will return `Ok(self)`.
417    ///
418    /// If `result_code` is negative, the action was a failure, and we will collect the error
419    /// string and return it as an `FtdiError`.
420    fn check_for_result(&self, result_code: i32) -> Result<&Self, FtdiError> {
421        if result_code >= 0 {
422            Ok(&self)
423        } else {
424            Err(self.context.get_error())
425        }
426    }
427
428    fn panic_on_error(&self, result_code: i32) -> &Self {
429        if result_code >= 0 {
430            &self
431        } else {
432            panic!(
433                "Expected action to always be successful, but got non-zero result code: {}",
434                result_code
435            )
436        }
437    }
438
439    pub fn set_interface(&mut self, interface: Interface) -> Result<&Self, FtdiError> {
440        let result_code = unsafe { ffi::ftdi_set_interface(self.context.0, interface.into()) };
441        if result_code == 0 {
442            Ok(self)
443        } else {
444            Err(self.context.get_error())
445        }
446    }
447
448    pub fn set_baudrate(&mut self, baudrate: u32) -> Result<&Self, FtdiError> {
449        let result_code = unsafe { ffi::ftdi_set_baudrate(self.context.0, baudrate as i32) };
450
451        self.check_for_result(result_code)
452    }
453
454    /// Set (RS232) line characteristics
455    pub fn set_line_property(
456        &mut self,
457        bits: BitsType,
458        stop_bits: StopBitsType,
459        parity: ParityType,
460        break_type: BreakType,
461    ) -> Result<&Self, FtdiError> {
462        let result_code = unsafe {
463            ffi::ftdi_set_line_property2(
464                self.context.0,
465                bits.into(),
466                stop_bits.into(),
467                parity.into(),
468                break_type.into(),
469            )
470        };
471        self.check_for_result(result_code)
472    }
473
474    pub fn reset(&mut self) -> Result<&Self, FtdiError> {
475        let result_code = unsafe { ffi::ftdi_usb_reset(self.context.0) };
476
477        self.check_for_result(result_code)
478    }
479
480    pub fn close(self) -> Result<Context, FtdiError> {
481        let result_code = unsafe { ffi::ftdi_usb_close(self.context.0) };
482
483        if result_code == 0 {
484            Ok(self.context)
485        } else {
486            Err(self.context.get_error())
487        }
488    }
489
490    fn input_flush(&mut self) -> Result<&Self, FtdiError> {
491        let result_code = unsafe { ffi::ftdi_usb_purge_rx_buffer(self.context.0) };
492
493        self.check_for_result(result_code)
494    }
495
496    fn output_flush(&mut self) -> Result<&Self, FtdiError> {
497        let result_code = unsafe { ffi::ftdi_usb_purge_tx_buffer(self.context.0) };
498
499        self.check_for_result(result_code)
500    }
501
502    fn io_flush(&mut self) -> Result<&Self, FtdiError> {
503        let result_code = unsafe { ffi::ftdi_usb_purge_buffers(self.context.0) };
504
505        self.check_for_result(result_code)
506    }
507
508    fn read_data(&mut self, buf: &mut [u8]) -> Result<usize, FtdiError> {
509        let result_code =
510            unsafe { ffi::ftdi_read_data(self.context.0, buf.as_mut_ptr(), buf.len() as i32) };
511
512        if result_code >= 0 {
513            Ok(result_code as usize)
514        } else {
515            Err(self.context.get_error())
516        }
517    }
518
519    pub fn set_read_chunksize(&mut self, chunksize: u32) -> &Self {
520        let result_code = unsafe { ffi::ftdi_read_data_set_chunksize(self.context.0, chunksize) };
521
522        self.panic_on_error(result_code)
523    }
524
525    pub fn get_read_chunksize(&self) -> u32 {
526        let mut chunksize: u32 = 0;
527        let result_code =
528            unsafe { ffi::ftdi_read_data_get_chunksize(self.context.0, &mut chunksize) };
529
530        self.panic_on_error(result_code);
531
532        result_code as u32
533    }
534
535    pub fn write_data(&mut self, data: &[u8]) -> Result<usize, FtdiError> {
536        let result_code =
537            unsafe { ffi::ftdi_write_data(self.context.0, data.as_ptr(), data.len() as i32) };
538
539        if result_code >= 0 {
540            Ok(result_code as usize)
541        } else {
542            Err(self.context.get_error())
543        }
544    }
545
546    pub fn set_write_chunksize(&mut self, chunksize: u32) -> &Self {
547        let result_code = unsafe { ffi::ftdi_write_data_set_chunksize(self.context.0, chunksize) };
548
549        self.panic_on_error(result_code)
550    }
551
552    pub fn get_write_chunksize(&self) -> u32 {
553        let mut chunksize: u32 = 0;
554        let result_code =
555            unsafe { ffi::ftdi_write_data_get_chunksize(self.context.0, &mut chunksize) };
556
557        self.panic_on_error(result_code);
558
559        result_code as u32
560    }
561
562    // TODO use struct for pin bitmask
563    pub fn set_bitmode(&mut self, bitmask: u8, mode: MpsseMode) -> Result<&Self, FtdiError> {
564        let result_code = unsafe { ffi::ftdi_set_bitmode(self.context.0, bitmask, mode.into()) };
565
566        self.check_for_result(result_code)
567    }
568
569    pub fn disable_bitbang(&mut self) -> Result<&Self, FtdiError> {
570        let result_code = unsafe { ffi::ftdi_disable_bitbang(self.context.0) };
571
572        self.check_for_result(result_code)
573    }
574
575    // TODO use stuct for pins
576    pub fn read_pins(&self) -> Result<u8, FtdiError> {
577        let mut pins: u8 = 0;
578        let result_code = unsafe { ffi::ftdi_read_pins(self.context.0, &mut pins) };
579
580        if result_code == 0 {
581            Ok(pins)
582        } else {
583            Err(self.context.get_error())
584        }
585    }
586
587    pub fn set_latency_timer(&mut self, latency: u8) -> Result<&Self, FtdiError> {
588        let result_code = unsafe { ffi::ftdi_set_latency_timer(self.context.0, latency) };
589
590        self.check_for_result(result_code)
591    }
592
593    pub fn get_latency_timer(&self) -> Result<u8, FtdiError> {
594        let mut latency: u8 = 0;
595        let result_code = unsafe { ffi::ftdi_get_latency_timer(self.context.0, &mut latency) };
596
597        if result_code == 0 {
598            Ok(latency)
599        } else {
600            Err(self.context.get_error())
601        }
602    }
603
604    pub fn set_event_char(&self, character: Option<u8>) -> Result<&Self, FtdiError> {
605        let result_code = match character {
606            Some(c) => unsafe { ffi::ftdi_set_event_char(self.context.0, c, 1u8) },
607            None => unsafe { ffi::ftdi_set_event_char(self.context.0, 0, 0u8) },
608        };
609
610        self.check_for_result(result_code)
611    }
612
613    pub fn set_error_char(&self, character: Option<u8>) -> Result<&Self, FtdiError> {
614        let result_code = match character {
615            Some(c) => unsafe { ffi::ftdi_set_error_char(self.context.0, c, 1u8) },
616            None => unsafe { ffi::ftdi_set_error_char(self.context.0, 0, 0u8) },
617        };
618
619        self.check_for_result(result_code)
620    }
621
622    pub fn eeprom_erase(&self) -> Result<&Self, FtdiError> {
623        let result_code = unsafe { ffi::ftdi_erase_eeprom(self.context.0) };
624
625        self.check_for_result(result_code)
626    }
627
628    // Methods that require a USB device
629    //
630    // poll_modem_status
631    // setflowctrl
632    // setflowctrl_xonxoff
633    // setdtr_rts
634    // setdtr
635    // setrts
636
637    // Async transfer methods
638    //
639    // write_data_submit
640    // read_data_submit
641    // transfer_data_done
642    // tranfer_data_cancel
643
644    // EEPROM methods
645    //
646    // eeprom_get_strings
647    // eeprom_get_strings
648    // write_eeprom
649    // erase_eeprom
650    // read_eeprom_location
651    // write_eeprom_location
652    // eeprom_initdefaults
653    // read_eeprom
654    // read_chipid
655}
656
657impl Read for Device {
658    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
659        let result = self.read_data(buf);
660
661        match result {
662            Ok(bytes_read) => Ok(bytes_read),
663            Err(ftdi_error) => Err(io::Error::new(io::ErrorKind::Other, ftdi_error)),
664        }
665    }
666}
667
668impl Write for Device {
669    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
670        let result = self.write_data(buf);
671
672        match result {
673            Ok(bytes_written) => Ok(bytes_written),
674            Err(ftdi_error) => Err(io::Error::new(io::ErrorKind::Other, ftdi_error)),
675        }
676    }
677
678    fn flush(&mut self) -> io::Result<()> {
679        // We don't have a flush mechanism. Only a purge one. So we'll always return Ok
680        Ok(())
681    }
682}
683
684#[derive(Debug)]
685pub struct DiscoveredDevice(*mut usb_ffi::libusb_device);
686
687impl DiscoveredDevice {}
688
689#[derive(Debug)]
690pub struct DeviceInfo {
691    manufacturer: Option<CString>,
692    description: Option<CString>,
693    serial: Option<CString>,
694}
695
696pub struct VersionInfo(pub ffi::ftdi_version_info);
697
698pub fn version_info() -> VersionInfo {
699    let result = unsafe { ffi::ftdi_get_library_version() };
700    VersionInfo(result)
701}
702
703#[derive(Debug)]
704pub struct Context(*mut ffi::ftdi_context);
705
706impl Context {
707    pub fn new() -> Result<Self, FtdiError> {
708        let result = unsafe { ffi::ftdi_new() };
709        if result.is_null() {
710            panic!("Could not allocate FTDI Context")
711        } else {
712            Ok(Context(result))
713        }
714    }
715
716    // pub fn set_usbdev(&mut self, usb: DeviceHandle) {
717    //     unsafe { ffi::ftdi_set_usbdev(self.0, usb.0) }
718    // }
719
720    pub fn usb_find_all(
721        &self,
722        vendor: i32,
723        product: i32,
724    ) -> Result<Vec<DiscoveredDevice>, FtdiError> {
725        let mut list = null_mut();
726        let result_code = unsafe { ffi::ftdi_usb_find_all(self.0, &mut list, vendor, product) };
727
728        if result_code < 0 {
729            return Err(self.get_error());
730        }
731
732        // Unpack the results from the linked-list type struct
733        let mut results = Vec::<DiscoveredDevice>::with_capacity(result_code as usize);
734        let mut cur = list;
735        while !cur.is_null() {
736            results.push(DiscoveredDevice(unsafe { (*cur).dev }));
737            cur = unsafe { (*cur).next };
738        }
739        unsafe { ffi::ftdi_list_free2(list) };
740
741        Ok(results)
742    }
743
744    pub fn read_data_set_chunksize(&mut self, size: u32) -> Result<Self, FtdiError> {
745        todo!()
746    }
747
748    pub fn read_data_get_chunksize(&self, size: u32) -> Result<Self, FtdiError> {
749        todo!()
750    }
751
752    pub fn write_data_set_chunksize(&mut self, size: u32) -> Result<Self, FtdiError> {
753        todo!()
754    }
755
756    pub fn write_data_get_chunksize(&self, size: u32) -> Result<Self, FtdiError> {
757        todo!()
758    }
759
760    fn get_error(&self) -> FtdiError {
761        let error_string = unsafe { ffi::ftdi_get_error_string(self.0) };
762        if error_string.is_null() {
763            FtdiError { error_string: None }
764        } else {
765            let safe_string = unsafe { CStr::from_ptr(error_string) };
766            FtdiError {
767                error_string: Some(safe_string),
768            }
769        }
770    }
771
772    /// Check for errors from the result of a method.
773    ///
774    /// If `result_code` is non-negative, the action was a success, and we will return `Ok(self)`.
775    ///
776    /// If `result_code` is negative, the action was a failure, and we will collect the error
777    /// string and return it as an `FtdiError`.
778    fn check_for_result(&self, result_code: i32) -> Result<&Self, FtdiError> {
779        if result_code >= 0 {
780            Ok(&self)
781        } else {
782            Err(self.get_error())
783        }
784    }
785
786    pub fn open(self, device: &DiscoveredDevice) -> Result<Device, FtdiError> {
787        let result = unsafe { ffi::ftdi_usb_open_dev(self.0, device.0) };
788
789        if result == 0 {
790            Ok(Device { context: self })
791        } else {
792            Err(self.get_error())
793        }
794    }
795
796    pub fn get_info(&self, device: &DiscoveredDevice) -> Result<DeviceInfo, FtdiError> {
797        let mut manufacturer_bytes = [0u8; 256];
798        let mut description_bytes = [0u8; 256];
799        let mut serial_bytes = [0u8; 256];
800
801        let result = unsafe {
802            ffi::ftdi_usb_get_strings(
803                self.0,
804                device.0,
805                manufacturer_bytes.as_mut_ptr() as *mut i8,
806                manufacturer_bytes.len() as i32,
807                description_bytes.as_mut_ptr() as *mut i8,
808                description_bytes.len() as i32,
809                serial_bytes.as_mut_ptr() as *mut i8,
810                serial_bytes.len() as i32,
811            )
812        };
813
814        let manufacturer: Option<CString>;
815        let description: Option<CString>;
816        let serial: Option<CString>;
817
818        if result == 0 {
819            manufacturer = bytes_into_cstring(&manufacturer_bytes);
820            description = bytes_into_cstring(&description_bytes);
821            serial = bytes_into_cstring(&serial_bytes);
822        } else if result == -9 {
823            manufacturer = bytes_into_cstring(&manufacturer_bytes);
824            description = bytes_into_cstring(&description_bytes);
825            serial = None;
826        } else if result == -8 {
827            manufacturer = bytes_into_cstring(&manufacturer_bytes);
828            description = None;
829            serial = None;
830        } else if result == -7 {
831            manufacturer = None;
832            description = None;
833            serial = None;
834        } else {
835            return Err(self.get_error());
836        }
837
838        Ok(DeviceInfo {
839            manufacturer,
840            description,
841            serial,
842        })
843    }
844
845    // Methods that bind a USB device
846    // set_usbdev
847    // usb_open
848    // usb_open_desc
849    // usb_open_desc_index
850    // usb_open_bus_addr
851    // usb_open_dev
852    // usb_open_string
853
854    // Methods that require an eeprom
855
856    // eeprom_build
857    // eeprom_decode
858    // get_eeprom_value
859    // set_eeprom_value
860    // get_eeprom_buf
861    // set_eeprom_buf
862    // set_eeprom_user_data
863
864    // Methods that close the USB
865}
866
867impl Drop for Context {
868    fn drop(&mut self) {
869        unsafe { ffi::ftdi_free(self.0) }
870    }
871}