#![allow(dead_code)]
#![allow(unused_variables)]
use libftdi1_sys as ffi;
use libusb1_sys as usb_ffi;
use std::ffi::{CStr, CString};
use std::ptr::null_mut;
use std::error::Error;
use std::fmt;
use std::io;
use std::io::{Read, Write};
#[derive(Debug)]
pub struct FtdiError {
error_string: Option<&'static CStr>,
}
impl Error for FtdiError {}
impl fmt::Display for FtdiError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.error_string {
Some(err) => write!(f, "libftdi error: {:?}", err),
None => write!(f, "libftdi error occurred, but no error text was found"),
}
}
}
pub enum Interface {
A,
B,
C,
D,
Any,
}
impl Into<ffi::ftdi_interface> for Interface {
fn into(self) -> ffi::ftdi_interface {
match self {
Interface::A => ffi::ftdi_interface::INTERFACE_A,
Interface::B => ffi::ftdi_interface::INTERFACE_B,
Interface::C => ffi::ftdi_interface::INTERFACE_C,
Interface::D => ffi::ftdi_interface::INTERFACE_D,
Interface::Any => ffi::ftdi_interface::INTERFACE_ANY,
}
}
}
pub enum BitsType {
Bits7,
Bits8,
}
impl Into<ffi::ftdi_bits_type> for BitsType {
fn into(self) -> ffi::ftdi_bits_type {
match self {
BitsType::Bits7 => ffi::ftdi_bits_type::BITS_7,
BitsType::Bits8 => ffi::ftdi_bits_type::BITS_8,
}
}
}
pub enum StopBitsType {
StopBit1,
StopBit15,
StopBit2,
}
impl Into<ffi::ftdi_stopbits_type> for StopBitsType {
fn into(self) -> ffi::ftdi_stopbits_type {
match self {
StopBitsType::StopBit1 => ffi::ftdi_stopbits_type::STOP_BIT_1,
StopBitsType::StopBit15 => ffi::ftdi_stopbits_type::STOP_BIT_15,
StopBitsType::StopBit2 => ffi::ftdi_stopbits_type::STOP_BIT_2,
}
}
}
pub enum ParityType {
None,
Odd,
Even,
Mark,
Space,
}
impl Into<ffi::ftdi_parity_type> for ParityType {
fn into(self) -> ffi::ftdi_parity_type {
match self {
ParityType::None => ffi::ftdi_parity_type::NONE,
ParityType::Odd => ffi::ftdi_parity_type::ODD,
ParityType::Even => ffi::ftdi_parity_type::EVEN,
ParityType::Mark => ffi::ftdi_parity_type::MARK,
ParityType::Space => ffi::ftdi_parity_type::SPACE,
}
}
}
pub enum BreakType {
Off,
On,
}
impl Into<ffi::ftdi_break_type> for BreakType {
fn into(self) -> ffi::ftdi_break_type {
match self {
BreakType::Off => ffi::ftdi_break_type::BREAK_OFF,
BreakType::On => ffi::ftdi_break_type::BREAK_ON,
}
}
}
pub enum ChipType {
Am,
Bm,
_2232C,
R,
_2232H,
_4232H,
_232H,
_230X,
}
impl Into<ffi::ftdi_chip_type> for ChipType {
fn into(self) -> ffi::ftdi_chip_type {
match self {
ChipType::Am => ffi::ftdi_chip_type::TYPE_AM,
ChipType::Bm => ffi::ftdi_chip_type::TYPE_BM,
ChipType::_2232C => ffi::ftdi_chip_type::TYPE_2232C,
ChipType::R => ffi::ftdi_chip_type::TYPE_R,
ChipType::_2232H => ffi::ftdi_chip_type::TYPE_2232H,
ChipType::_4232H => ffi::ftdi_chip_type::TYPE_4232H,
ChipType::_232H => ffi::ftdi_chip_type::TYPE_232H,
ChipType::_230X => ffi::ftdi_chip_type::TYPE_230X,
}
}
}
#[derive(Debug, Default)]
pub struct MpsseMode {
pub bitbang: bool,
pub mpsse: bool,
pub syncbb: bool,
pub mcu: bool,
pub opto: bool,
pub cbus: bool,
pub syncff: bool,
pub ft1284: bool,
}
impl Into<u8> for MpsseMode {
fn into(self) -> u8 {
let mut result = ffi::ftdi_mpsse_mode::BITMODE_RESET.0 as u8;
if self.bitbang {
result |= ffi::ftdi_mpsse_mode::BITMODE_BITBANG.0 as u8
}
if self.mpsse {
result |= ffi::ftdi_mpsse_mode::BITMODE_MPSSE.0 as u8
}
if self.syncbb {
result |= ffi::ftdi_mpsse_mode::BITMODE_SYNCBB.0 as u8
}
if self.mcu {
result |= ffi::ftdi_mpsse_mode::BITMODE_MCU.0 as u8
}
if self.opto {
result |= ffi::ftdi_mpsse_mode::BITMODE_OPTO.0 as u8
}
if self.cbus {
result |= ffi::ftdi_mpsse_mode::BITMODE_CBUS.0 as u8
}
if self.syncff {
result |= ffi::ftdi_mpsse_mode::BITMODE_SYNCFF.0 as u8
}
if self.ft1284 {
result |= ffi::ftdi_mpsse_mode::BITMODE_FT1284.0 as u8
}
result
}
}
pub enum ModuleDetachMode {
AutoDetachSioModule,
DontDetachSioModule,
}
impl Into<ffi::ftdi_module_detach_mode> for ModuleDetachMode {
fn into(self) -> ffi::ftdi_module_detach_mode {
match self {
ModuleDetachMode::AutoDetachSioModule => {
ffi::ftdi_module_detach_mode::AUTO_DETACH_SIO_MODULE
}
ModuleDetachMode::DontDetachSioModule => {
ffi::ftdi_module_detach_mode::DONT_DETACH_SIO_MODULE
}
}
}
}
pub enum EepromValue {
VendorId,
ProductId,
SelfPowered,
RemoteWakeup,
IsNotPnp,
SuspendDbus7,
InIsIsochronous,
OutIsIsochronous,
SuspendPullDowns,
UseSerial,
UsbVersion,
UseUsbVersion,
MaxPower,
ChannelAType,
ChannelBType,
ChannelADriver,
ChannelBDriver,
CbusFunction0,
CbusFunction1,
CbusFunction2,
CbusFunction3,
CbusFunction4,
CbusFunction5,
CbusFunction6,
CbusFunction7,
CbusFunction8,
CbusFunction9,
HighCurrent,
HighCurrentA,
HighCurrentB,
Invert,
Group0Drive,
Group0Schmitt,
Group0Slew,
Group1Drive,
Group1Schmitt,
Group1Slew,
Group2Drive,
Group2Schmitt,
Group2Slew,
Group3Drive,
Group3Schmitt,
Group3Slew,
ChipSize,
ChipType,
PowerSave,
ClockPolarity,
DataOrder,
FlowControl,
ChannelCDriver,
ChannelDDriver,
ChannelARs485,
ChannelBRs485,
ChannelCRs485,
ChannelDRs485,
ReleaseNumber,
}
impl Into<ffi::ftdi_eeprom_value> for EepromValue {
fn into(self) -> ffi::ftdi_eeprom_value {
match self {
EepromValue::VendorId => ffi::ftdi_eeprom_value::VENDOR_ID,
EepromValue::ProductId => ffi::ftdi_eeprom_value::PRODUCT_ID,
EepromValue::SelfPowered => ffi::ftdi_eeprom_value::SELF_POWERED,
EepromValue::RemoteWakeup => ffi::ftdi_eeprom_value::REMOTE_WAKEUP,
EepromValue::IsNotPnp => ffi::ftdi_eeprom_value::IS_NOT_PNP,
EepromValue::SuspendDbus7 => ffi::ftdi_eeprom_value::SUSPEND_DBUS7,
EepromValue::InIsIsochronous => ffi::ftdi_eeprom_value::IN_IS_ISOCHRONOUS,
EepromValue::OutIsIsochronous => ffi::ftdi_eeprom_value::OUT_IS_ISOCHRONOUS,
EepromValue::SuspendPullDowns => ffi::ftdi_eeprom_value::SUSPEND_PULL_DOWNS,
EepromValue::UseSerial => ffi::ftdi_eeprom_value::USE_SERIAL,
EepromValue::UsbVersion => ffi::ftdi_eeprom_value::USB_VERSION,
EepromValue::UseUsbVersion => ffi::ftdi_eeprom_value::USE_USB_VERSION,
EepromValue::MaxPower => ffi::ftdi_eeprom_value::MAX_POWER,
EepromValue::ChannelAType => ffi::ftdi_eeprom_value::CHANNEL_A_TYPE,
EepromValue::ChannelBType => ffi::ftdi_eeprom_value::CHANNEL_B_TYPE,
EepromValue::ChannelADriver => ffi::ftdi_eeprom_value::CHANNEL_A_DRIVER,
EepromValue::ChannelBDriver => ffi::ftdi_eeprom_value::CHANNEL_B_DRIVER,
EepromValue::CbusFunction0 => ffi::ftdi_eeprom_value::CBUS_FUNCTION_0,
EepromValue::CbusFunction1 => ffi::ftdi_eeprom_value::CBUS_FUNCTION_1,
EepromValue::CbusFunction2 => ffi::ftdi_eeprom_value::CBUS_FUNCTION_2,
EepromValue::CbusFunction3 => ffi::ftdi_eeprom_value::CBUS_FUNCTION_3,
EepromValue::CbusFunction4 => ffi::ftdi_eeprom_value::CBUS_FUNCTION_4,
EepromValue::CbusFunction5 => ffi::ftdi_eeprom_value::CBUS_FUNCTION_5,
EepromValue::CbusFunction6 => ffi::ftdi_eeprom_value::CBUS_FUNCTION_6,
EepromValue::CbusFunction7 => ffi::ftdi_eeprom_value::CBUS_FUNCTION_7,
EepromValue::CbusFunction8 => ffi::ftdi_eeprom_value::CBUS_FUNCTION_8,
EepromValue::CbusFunction9 => ffi::ftdi_eeprom_value::CBUS_FUNCTION_9,
EepromValue::HighCurrent => ffi::ftdi_eeprom_value::HIGH_CURRENT,
EepromValue::HighCurrentA => ffi::ftdi_eeprom_value::HIGH_CURRENT_A,
EepromValue::HighCurrentB => ffi::ftdi_eeprom_value::HIGH_CURRENT_B,
EepromValue::Invert => ffi::ftdi_eeprom_value::INVERT,
EepromValue::Group0Drive => ffi::ftdi_eeprom_value::GROUP0_DRIVE,
EepromValue::Group0Schmitt => ffi::ftdi_eeprom_value::GROUP0_SCHMITT,
EepromValue::Group0Slew => ffi::ftdi_eeprom_value::GROUP0_SLEW,
EepromValue::Group1Drive => ffi::ftdi_eeprom_value::GROUP1_DRIVE,
EepromValue::Group1Schmitt => ffi::ftdi_eeprom_value::GROUP1_SCHMITT,
EepromValue::Group1Slew => ffi::ftdi_eeprom_value::GROUP1_SLEW,
EepromValue::Group2Drive => ffi::ftdi_eeprom_value::GROUP2_DRIVE,
EepromValue::Group2Schmitt => ffi::ftdi_eeprom_value::GROUP2_SCHMITT,
EepromValue::Group2Slew => ffi::ftdi_eeprom_value::GROUP2_SLEW,
EepromValue::Group3Drive => ffi::ftdi_eeprom_value::GROUP3_DRIVE,
EepromValue::Group3Schmitt => ffi::ftdi_eeprom_value::GROUP3_SCHMITT,
EepromValue::Group3Slew => ffi::ftdi_eeprom_value::GROUP3_SLEW,
EepromValue::ChipSize => ffi::ftdi_eeprom_value::CHIP_SIZE,
EepromValue::ChipType => ffi::ftdi_eeprom_value::CHIP_TYPE,
EepromValue::PowerSave => ffi::ftdi_eeprom_value::POWER_SAVE,
EepromValue::ClockPolarity => ffi::ftdi_eeprom_value::CLOCK_POLARITY,
EepromValue::DataOrder => ffi::ftdi_eeprom_value::DATA_ORDER,
EepromValue::FlowControl => ffi::ftdi_eeprom_value::FLOW_CONTROL,
EepromValue::ChannelCDriver => ffi::ftdi_eeprom_value::CHANNEL_C_DRIVER,
EepromValue::ChannelDDriver => ffi::ftdi_eeprom_value::CHANNEL_D_DRIVER,
EepromValue::ChannelARs485 => ffi::ftdi_eeprom_value::CHANNEL_A_RS485,
EepromValue::ChannelBRs485 => ffi::ftdi_eeprom_value::CHANNEL_B_RS485,
EepromValue::ChannelCRs485 => ffi::ftdi_eeprom_value::CHANNEL_C_RS485,
EepromValue::ChannelDRs485 => ffi::ftdi_eeprom_value::CHANNEL_D_RS485,
EepromValue::ReleaseNumber => ffi::ftdi_eeprom_value::RELEASE_NUMBER,
}
}
}
pub enum CbusFunc {
TxdEn,
PwrEn,
RxLed,
TxLed,
TxRxLed,
Sleep,
Clk48,
Clk24,
Clk12,
Clk6,
IoMode,
BbWr,
BbRd,
}
impl Into<ffi::ftdi_cbus_func> for CbusFunc {
fn into(self) -> ffi::ftdi_cbus_func {
match self {
CbusFunc::TxdEn => ffi::ftdi_cbus_func::CBUS_TXDEN,
CbusFunc::PwrEn => ffi::ftdi_cbus_func::CBUS_PWREN,
CbusFunc::RxLed => ffi::ftdi_cbus_func::CBUS_RXLED,
CbusFunc::TxLed => ffi::ftdi_cbus_func::CBUS_TXLED,
CbusFunc::TxRxLed => ffi::ftdi_cbus_func::CBUS_TXRXLED,
CbusFunc::Sleep => ffi::ftdi_cbus_func::CBUS_SLEEP,
CbusFunc::Clk48 => ffi::ftdi_cbus_func::CBUS_CLK48,
CbusFunc::Clk24 => ffi::ftdi_cbus_func::CBUS_CLK24,
CbusFunc::Clk12 => ffi::ftdi_cbus_func::CBUS_CLK12,
CbusFunc::Clk6 => ffi::ftdi_cbus_func::CBUS_CLK6,
CbusFunc::IoMode => ffi::ftdi_cbus_func::CBUS_IOMODE,
CbusFunc::BbWr => ffi::ftdi_cbus_func::CBUS_BB_WR,
CbusFunc::BbRd => ffi::ftdi_cbus_func::CBUS_BB_RD,
}
}
}
pub enum CbusHFunc {
Tristate,
TxLed,
RxLed,
TxRxLed,
PwrEn,
Sleep,
Drive0,
Drive1,
IoMode,
Clk30,
Clk15,
Clk7p5,
}
impl Into<ffi::ftdi_cbush_func> for CbusHFunc {
fn into(self) -> ffi::ftdi_cbush_func {
match self {
CbusHFunc::Tristate => ffi::ftdi_cbush_func::CBUSH_TRISTATE,
CbusHFunc::PwrEn => ffi::ftdi_cbush_func::CBUSH_PWREN,
CbusHFunc::RxLed => ffi::ftdi_cbush_func::CBUSH_RXLED,
CbusHFunc::TxLed => ffi::ftdi_cbush_func::CBUSH_TXLED,
CbusHFunc::TxRxLed => ffi::ftdi_cbush_func::CBUSH_TXRXLED,
CbusHFunc::Sleep => ffi::ftdi_cbush_func::CBUSH_SLEEP,
CbusHFunc::Drive0 => ffi::ftdi_cbush_func::CBUSH_DRIVE_0,
CbusHFunc::Drive1 => ffi::ftdi_cbush_func::CBUSH_DRIVE1,
CbusHFunc::IoMode => ffi::ftdi_cbush_func::CBUSH_IOMODE,
CbusHFunc::Clk30 => ffi::ftdi_cbush_func::CBUSH_CLK30,
CbusHFunc::Clk15 => ffi::ftdi_cbush_func::CBUSH_CLK15,
CbusHFunc::Clk7p5 => ffi::ftdi_cbush_func::CBUSH_CLK7_5,
}
}
}
fn bytes_into_cstring(bytes: &[u8]) -> Option<CString> {
let first_nul = bytes.iter().position(|&b| b == 0);
match first_nul {
Some(i) => Some(
CStr::from_bytes_with_nul(&bytes[0..=i])
.expect("Data wasn't well formed")
.to_owned(),
),
None => None,
}
}
#[derive(Debug)]
pub struct Device {
context: Context,
}
impl Device {
fn check_for_result(&self, result_code: i32) -> Result<&Self, FtdiError> {
if result_code >= 0 {
Ok(&self)
} else {
Err(self.context.get_error())
}
}
fn panic_on_error(&self, result_code: i32) -> &Self {
if result_code >= 0 {
&self
} else {
panic!(
"Expected action to always be successful, but got non-zero result code: {}",
result_code
)
}
}
pub fn set_interface(&mut self, interface: Interface) -> Result<&Self, FtdiError> {
let result_code = unsafe { ffi::ftdi_set_interface(self.context.0, interface.into()) };
if result_code == 0 {
Ok(self)
} else {
Err(self.context.get_error())
}
}
pub fn set_baudrate(&mut self, baudrate: u32) -> Result<&Self, FtdiError> {
let result_code = unsafe { ffi::ftdi_set_baudrate(self.context.0, baudrate as i32) };
self.check_for_result(result_code)
}
pub fn set_line_property(
&mut self,
bits: BitsType,
stop_bits: StopBitsType,
parity: ParityType,
break_type: BreakType,
) -> Result<&Self, FtdiError> {
let result_code = unsafe {
ffi::ftdi_set_line_property2(
self.context.0,
bits.into(),
stop_bits.into(),
parity.into(),
break_type.into(),
)
};
self.check_for_result(result_code)
}
pub fn reset(&mut self) -> Result<&Self, FtdiError> {
let result_code = unsafe { ffi::ftdi_usb_reset(self.context.0) };
self.check_for_result(result_code)
}
pub fn close(self) -> Result<Context, FtdiError> {
let result_code = unsafe { ffi::ftdi_usb_close(self.context.0) };
if result_code == 0 {
Ok(self.context)
} else {
Err(self.context.get_error())
}
}
fn input_flush(&mut self) -> Result<&Self, FtdiError> {
let result_code = unsafe { ffi::ftdi_usb_purge_rx_buffer(self.context.0) };
self.check_for_result(result_code)
}
fn output_flush(&mut self) -> Result<&Self, FtdiError> {
let result_code = unsafe { ffi::ftdi_usb_purge_tx_buffer(self.context.0) };
self.check_for_result(result_code)
}
fn io_flush(&mut self) -> Result<&Self, FtdiError> {
let result_code = unsafe { ffi::ftdi_usb_purge_buffers(self.context.0) };
self.check_for_result(result_code)
}
fn read_data(&mut self, buf: &mut [u8]) -> Result<usize, FtdiError> {
let result_code =
unsafe { ffi::ftdi_read_data(self.context.0, buf.as_mut_ptr(), buf.len() as i32) };
if result_code >= 0 {
Ok(result_code as usize)
} else {
Err(self.context.get_error())
}
}
pub fn set_read_chunksize(&mut self, chunksize: u32) -> &Self {
let result_code = unsafe { ffi::ftdi_read_data_set_chunksize(self.context.0, chunksize) };
self.panic_on_error(result_code)
}
pub fn get_read_chunksize(&self) -> u32 {
let mut chunksize: u32 = 0;
let result_code =
unsafe { ffi::ftdi_read_data_get_chunksize(self.context.0, &mut chunksize) };
self.panic_on_error(result_code);
result_code as u32
}
pub fn write_data(&mut self, data: &[u8]) -> Result<usize, FtdiError> {
let result_code =
unsafe { ffi::ftdi_write_data(self.context.0, data.as_ptr(), data.len() as i32) };
if result_code >= 0 {
Ok(result_code as usize)
} else {
Err(self.context.get_error())
}
}
pub fn set_write_chunksize(&mut self, chunksize: u32) -> &Self {
let result_code = unsafe { ffi::ftdi_write_data_set_chunksize(self.context.0, chunksize) };
self.panic_on_error(result_code)
}
pub fn get_write_chunksize(&self) -> u32 {
let mut chunksize: u32 = 0;
let result_code =
unsafe { ffi::ftdi_write_data_get_chunksize(self.context.0, &mut chunksize) };
self.panic_on_error(result_code);
result_code as u32
}
pub fn set_bitmode(&mut self, bitmask: u8, mode: MpsseMode) -> Result<&Self, FtdiError> {
let result_code = unsafe { ffi::ftdi_set_bitmode(self.context.0, bitmask, mode.into()) };
self.check_for_result(result_code)
}
pub fn disable_bitbang(&mut self) -> Result<&Self, FtdiError> {
let result_code = unsafe { ffi::ftdi_disable_bitbang(self.context.0) };
self.check_for_result(result_code)
}
pub fn read_pins(&self) -> Result<u8, FtdiError> {
let mut pins: u8 = 0;
let result_code = unsafe { ffi::ftdi_read_pins(self.context.0, &mut pins) };
if result_code == 0 {
Ok(pins)
} else {
Err(self.context.get_error())
}
}
pub fn set_latency_timer(&mut self, latency: u8) -> Result<&Self, FtdiError> {
let result_code = unsafe { ffi::ftdi_set_latency_timer(self.context.0, latency) };
self.check_for_result(result_code)
}
pub fn get_latency_timer(&self) -> Result<u8, FtdiError> {
let mut latency: u8 = 0;
let result_code = unsafe { ffi::ftdi_get_latency_timer(self.context.0, &mut latency) };
if result_code == 0 {
Ok(latency)
} else {
Err(self.context.get_error())
}
}
pub fn set_event_char(&self, character: Option<u8>) -> Result<&Self, FtdiError> {
let result_code = match character {
Some(c) => unsafe { ffi::ftdi_set_event_char(self.context.0, c, 1u8) },
None => unsafe { ffi::ftdi_set_event_char(self.context.0, 0, 0u8) },
};
self.check_for_result(result_code)
}
pub fn set_error_char(&self, character: Option<u8>) -> Result<&Self, FtdiError> {
let result_code = match character {
Some(c) => unsafe { ffi::ftdi_set_error_char(self.context.0, c, 1u8) },
None => unsafe { ffi::ftdi_set_error_char(self.context.0, 0, 0u8) },
};
self.check_for_result(result_code)
}
pub fn eeprom_erase(&self) -> Result<&Self, FtdiError> {
let result_code = unsafe { ffi::ftdi_erase_eeprom(self.context.0) };
self.check_for_result(result_code)
}
}
impl Read for Device {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
let result = self.read_data(buf);
match result {
Ok(bytes_read) => Ok(bytes_read),
Err(ftdi_error) => Err(io::Error::new(io::ErrorKind::Other, ftdi_error)),
}
}
}
impl Write for Device {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
let result = self.write_data(buf);
match result {
Ok(bytes_written) => Ok(bytes_written),
Err(ftdi_error) => Err(io::Error::new(io::ErrorKind::Other, ftdi_error)),
}
}
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}
#[derive(Debug)]
pub struct DiscoveredDevice(*mut usb_ffi::libusb_device);
impl DiscoveredDevice {}
#[derive(Debug)]
pub struct DeviceInfo {
manufacturer: Option<CString>,
description: Option<CString>,
serial: Option<CString>,
}
pub struct VersionInfo(pub ffi::ftdi_version_info);
pub fn version_info() -> VersionInfo {
let result = unsafe { ffi::ftdi_get_library_version() };
VersionInfo(result)
}
#[derive(Debug)]
pub struct Context(*mut ffi::ftdi_context);
impl Context {
pub fn new() -> Result<Self, FtdiError> {
let result = unsafe { ffi::ftdi_new() };
if result.is_null() {
panic!("Could not allocate FTDI Context")
} else {
Ok(Context(result))
}
}
pub fn usb_find_all(
&self,
vendor: i32,
product: i32,
) -> Result<Vec<DiscoveredDevice>, FtdiError> {
let mut list = null_mut();
let result_code = unsafe { ffi::ftdi_usb_find_all(self.0, &mut list, vendor, product) };
if result_code < 0 {
return Err(self.get_error());
}
let mut results = Vec::<DiscoveredDevice>::with_capacity(result_code as usize);
let mut cur = list;
while !cur.is_null() {
results.push(DiscoveredDevice(unsafe { (*cur).dev }));
cur = unsafe { (*cur).next };
}
unsafe { ffi::ftdi_list_free2(list) };
Ok(results)
}
pub fn read_data_set_chunksize(&mut self, size: u32) -> Result<Self, FtdiError> {
todo!()
}
pub fn read_data_get_chunksize(&self, size: u32) -> Result<Self, FtdiError> {
todo!()
}
pub fn write_data_set_chunksize(&mut self, size: u32) -> Result<Self, FtdiError> {
todo!()
}
pub fn write_data_get_chunksize(&self, size: u32) -> Result<Self, FtdiError> {
todo!()
}
fn get_error(&self) -> FtdiError {
let error_string = unsafe { ffi::ftdi_get_error_string(self.0) };
if error_string.is_null() {
FtdiError { error_string: None }
} else {
let safe_string = unsafe { CStr::from_ptr(error_string) };
FtdiError {
error_string: Some(safe_string),
}
}
}
fn check_for_result(&self, result_code: i32) -> Result<&Self, FtdiError> {
if result_code >= 0 {
Ok(&self)
} else {
Err(self.get_error())
}
}
pub fn open(self, device: &DiscoveredDevice) -> Result<Device, FtdiError> {
let result = unsafe { ffi::ftdi_usb_open_dev(self.0, device.0) };
if result == 0 {
Ok(Device { context: self })
} else {
Err(self.get_error())
}
}
pub fn get_info(&self, device: &DiscoveredDevice) -> Result<DeviceInfo, FtdiError> {
let mut manufacturer_bytes = [0u8; 256];
let mut description_bytes = [0u8; 256];
let mut serial_bytes = [0u8; 256];
let result = unsafe {
ffi::ftdi_usb_get_strings(
self.0,
device.0,
manufacturer_bytes.as_mut_ptr() as *mut i8,
manufacturer_bytes.len() as i32,
description_bytes.as_mut_ptr() as *mut i8,
description_bytes.len() as i32,
serial_bytes.as_mut_ptr() as *mut i8,
serial_bytes.len() as i32,
)
};
let manufacturer: Option<CString>;
let description: Option<CString>;
let serial: Option<CString>;
if result == 0 {
manufacturer = bytes_into_cstring(&manufacturer_bytes);
description = bytes_into_cstring(&description_bytes);
serial = bytes_into_cstring(&serial_bytes);
} else if result == -9 {
manufacturer = bytes_into_cstring(&manufacturer_bytes);
description = bytes_into_cstring(&description_bytes);
serial = None;
} else if result == -8 {
manufacturer = bytes_into_cstring(&manufacturer_bytes);
description = None;
serial = None;
} else if result == -7 {
manufacturer = None;
description = None;
serial = None;
} else {
return Err(self.get_error());
}
Ok(DeviceInfo {
manufacturer,
description,
serial,
})
}
}
impl Drop for Context {
fn drop(&mut self) {
unsafe { ffi::ftdi_free(self.0) }
}
}