ftdi-prog/ftdi-vcp-rs/src/lib.rs

395 lines
12 KiB
Rust

use ftdi_vcp_sys::{
FT_Close, FT_GetBitMode, FT_GetComPortNumber, FT_GetLatencyTimer, FT_OpenEx, FT_Purge, FT_Read,
FT_ResetDevice, FT_SetBitMode, FT_SetLatencyTimer, FT_Write, DWORD, FT_HANDLE,
FT_OPEN_BY_DESCRIPTION, FT_STATUS, LONG, LPDWORD, LPVOID, PVOID, UCHAR,
};
use std::convert::TryInto;
use std::ffi::CString;
use std::io::{Read, Write};
use std::mem::MaybeUninit;
pub mod mpsse;
#[derive(Debug)]
pub enum BitMode {
Reset,
AsyncBitbang,
MPSSE,
SyncBitbang,
MCUHost,
FastSerial,
CBUSBitbang,
SyncFIFO,
Unknown(u8),
}
impl From<UCHAR> for BitMode {
fn from(src: UCHAR) -> Self {
use BitMode::*;
match src {
0x00 => Reset,
0x01 => AsyncBitbang,
0x02 => MPSSE,
0x04 => SyncBitbang,
0x08 => MCUHost,
0x10 => FastSerial,
0x20 => CBUSBitbang,
0x40 => SyncFIFO,
x => Unknown(x),
}
}
}
impl BitMode {
pub fn to_u8(&self) -> UCHAR {
use BitMode::*;
match *self {
Reset => 0x00,
AsyncBitbang => 0x01,
MPSSE => 0x02,
SyncBitbang => 0x04,
MCUHost => 0x08,
FastSerial => 0x10,
CBUSBitbang => 0x20,
SyncFIFO => 0x40,
Unknown(x) => x,
}
}
}
#[derive(PartialEq, Debug)]
pub enum Error {
NoError,
InvalidHandle,
DeviceNotFound,
DeviceNotOpened,
IoError,
InsufficientResources,
InvalidParameter,
InvalidBaudRate,
DeviceNotOpenedForErase,
DeviceNotOpenedForWrite,
FailedToWriteDevice,
EepromReadFailed,
EepromWriteFailed,
EepromEraseFailed,
EepromNotPresent,
EepromNotProgrammed,
InvalidArgs,
NotSupported,
OtherError,
DeviceListNotReady,
NoComPortAssigned,
/// The provided string contained at least one NULL byte
StringContainsNullByte,
// UnknownBitMode(u8),
UnknownError(FT_STATUS),
}
impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "VCPError: {:?}", self)
}
}
impl std::error::Error for Error {}
impl From<FT_STATUS> for Error {
fn from(src: FT_STATUS) -> Self {
match src {
0 => Error::NoError,
1 => Error::InvalidHandle,
2 => Error::DeviceNotFound,
3 => Error::DeviceNotOpened,
4 => Error::IoError,
5 => Error::InsufficientResources,
6 => Error::InvalidParameter,
7 => Error::InvalidBaudRate,
8 => Error::DeviceNotOpenedForErase,
9 => Error::DeviceNotOpenedForWrite,
10 => Error::FailedToWriteDevice,
11 => Error::EepromReadFailed,
12 => Error::EepromWriteFailed,
13 => Error::EepromEraseFailed,
14 => Error::EepromNotPresent,
15 => Error::EepromNotProgrammed,
16 => Error::InvalidArgs,
17 => Error::NotSupported,
18 => Error::OtherError,
19 => Error::DeviceListNotReady,
x => Error::UnknownError(x),
}
}
}
#[derive(Debug)]
pub struct VCP {
handle: FT_HANDLE,
bit_mode: BitMode,
}
impl VCP {
pub fn new_from_name(name: &str) -> Result<VCP, Error> {
let c_str = CString::new(name).or(Err(Error::StringContainsNullByte))?;
let mut handle = MaybeUninit::<FT_HANDLE>::uninit();
let result = Error::from(unsafe {
FT_OpenEx(
c_str.as_ptr() as PVOID,
FT_OPEN_BY_DESCRIPTION,
handle.as_mut_ptr(),
)
});
if result != Error::NoError {
return Err(result);
}
let handle = unsafe { handle.assume_init() };
let mut bit_mode = MaybeUninit::<UCHAR>::uninit();
let result = Error::from(unsafe { FT_GetBitMode(handle, bit_mode.as_mut_ptr()) });
if result != Error::NoError {
unsafe { FT_Close(handle) };
return Err(result);
}
let bit_mode = BitMode::from(unsafe { bit_mode.assume_init() });
Ok(VCP { handle, bit_mode })
}
pub fn com_port(&self) -> Result<usize, Error> {
let mut com_port_number = MaybeUninit::<LONG>::uninit();
let result =
Error::from(unsafe { FT_GetComPortNumber(self.handle, com_port_number.as_mut_ptr()) });
if result != Error::NoError {
Err(result)
} else {
let com_port_number = unsafe { com_port_number.assume_init() };
if let Ok(e) = com_port_number.try_into() {
Ok(e)
} else {
Err(Error::NoComPortAssigned)
}
}
}
/// Set the given signals to "OUTPUT". All other signals will be "INPUT".
pub fn set_bitmode(&mut self, outputs: u8, bitmode: BitMode) -> Result<(), Error> {
let result = Error::from(unsafe { FT_SetBitMode(self.handle, outputs, bitmode.to_u8()) });
if result != Error::NoError {
Err(result)
} else {
self.bit_mode = bitmode;
Ok(())
}
}
pub fn reset(&mut self) -> Result<(), Error> {
let result = Error::from(unsafe { FT_ResetDevice(self.handle) });
if result != Error::NoError {
Err(result)
} else {
Ok(())
}
}
pub fn purge(&mut self) -> Result<(), Error> {
let result = Error::from(unsafe {
FT_Purge(
self.handle,
ftdi_vcp_sys::FT_PURGE_RX | ftdi_vcp_sys::FT_PURGE_TX,
)
});
if result != Error::NoError {
Err(result)
} else {
Ok(())
}
}
pub fn latency_timer(&mut self) -> Result<u8, Error> {
let mut latency = MaybeUninit::<UCHAR>::uninit();
let result = Error::from(unsafe { FT_GetLatencyTimer(self.handle, latency.as_mut_ptr()) });
if result != Error::NoError {
Err(result)
} else {
Ok(unsafe { latency.assume_init() })
}
}
pub fn set_latency_timer(&mut self, latency: u8) -> Result<(), Error> {
let result = Error::from(unsafe { FT_SetLatencyTimer(self.handle, latency) });
if result != Error::NoError {
Err(result)
} else {
Ok(())
}
}
// pub fn write(&mut self, out_buffer: &[u8]) -> Result<usize, Error> {
// let mut bytes_written = MaybeUninit::<DWORD>::uninit();
// let result = Error::from(unsafe {
// FT_Write(
// self.handle,
// out_buffer.as_ptr() as LPVOID,
// out_buffer
// .len()
// .try_into()
// .expect("couldn't convert buffer length to DWORD"),
// bytes_written.as_mut_ptr() as LPDWORD,
// )
// });
// if result != Error::NoError {
// Err(result)
// } else {
// Ok(unsafe { bytes_written.assume_init() }
// .try_into()
// .expect("invalid number of bytes written"))
// }
// }
pub fn set_gpio(&mut self, value: u8, direction: u8) -> Result<(), Error> {
match self.bit_mode {
BitMode::MPSSE => self
.write_all(&[mpsse::Command::MC_SETB_LOW.to_u8(), value, direction])
.or_else(|_| Err(Error::IoError)),
_ => unimplemented!(),
}
}
pub fn readb_low(&mut self) -> Result<u8, Error> {
self.write_all(&[mpsse::Command::MC_READB_LOW.to_u8()])
.or_else(|_| Err(Error::IoError))?;
let mut result = [0; 1];
self.read(&mut result).or_else(|_| Err(Error::IoError))?;
Ok(result[0])
}
pub fn readb_high(&mut self) -> Result<u8, Error> {
self.write_all(&[mpsse::Command::MC_READB_HIGH.to_u8()])
.or_else(|_| Err(Error::IoError))?;
let mut result = [0; 1];
self.read(&mut result).or_else(|_| Err(Error::IoError))?;
Ok(result[0])
}
/// The purpose of this function is unclear. It appears to send some number
/// of bits out the line.
pub fn xfer_spi_bits(&mut self, data: u8, bits: usize) -> Result<u8, Error> {
if bits < 1 {
return Ok(0);
}
let buffer = &[
/* Input and output, update data on negative edge read on positive, bits. */
0x20 /*MC_DATA_IN*/ | 0x10 /*MC_DATA_OUT*/ | 0x01 /*MC_DATA_OCN*/ | 0x02, /*MC_DATA_BITS*/
bits as u8 - 1,
data,
];
self.write_all(buffer).or_else(|_| Err(Error::IoError))?;
let mut return_val = [0; 1];
self.read_exact(&mut return_val)
.or_else(|_| Err(Error::IoError))?;
Ok(return_val[0])
}
pub fn xfer_spi(&mut self, data: &mut [u8]) -> Result<(), Error> {
if data.is_empty() {
return Ok(());
}
/* Input and output, update data on negative edge read on positive. */
let buffer = &[
0x20 /*MC_DATA_IN*/ | 0x10 /*MC_DATA_OUT*/ | 0x01, /*MC_DATA_OCN*/
(data.len() - 1) as u8,
((data.len() - 1) / 256) as u8,
];
self.write_all(buffer).or_else(|_| Err(Error::IoError))?;
self.write_all(data).or_else(|_| Err(Error::IoError))?;
self.read_exact(data).or_else(|_| Err(Error::IoError))?;
Ok(())
}
pub fn send_spi(&mut self, data: &[u8]) -> Result<(), Error> {
if data.is_empty() {
return Ok(());
}
/* Input and output, update data on negative edge read on positive. */
let buffer = &[
0x10 /*MC_DATA_OUT*/ | 0x01, /*MC_DATA_OCN*/
(data.len() - 1) as u8,
((data.len() - 1) / 256) as u8,
];
self.write_all(buffer).or_else(|_| Err(Error::IoError))?;
self.write_all(data).or_else(|_| Err(Error::IoError))?;
Ok(())
}
}
impl Write for VCP {
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
let mut bytes_written = MaybeUninit::<DWORD>::uninit();
let result = Error::from(unsafe {
FT_Write(
self.handle,
buf.as_ptr() as LPVOID,
buf.len()
.try_into()
.expect("couldn't convert buffer length to DWORD"),
bytes_written.as_mut_ptr() as LPDWORD,
)
});
if result != Error::NoError {
Err(std::io::Error::new(std::io::ErrorKind::Other, result))
} else {
let bytes_written = unsafe { bytes_written.assume_init() };
// println!(
// "Wrote {} bytes (wanted to write {})",
// bytes_written,
// buf.len()
// );
Ok(bytes_written
.try_into()
.expect("invalid number of bytes written"))
}
}
fn flush(&mut self) -> std::io::Result<()> {
Ok(())
}
}
impl Read for VCP {
fn read(&mut self, buf: &mut [u8]) -> Result<usize, std::io::Error> {
let mut bytes_read = MaybeUninit::<DWORD>::uninit();
let result = Error::from(unsafe {
FT_Read(
self.handle,
buf.as_mut_ptr() as LPVOID,
buf.len()
.try_into()
.expect("couldn't convert buffer length to DWORD"),
bytes_read.as_mut_ptr() as LPDWORD,
)
});
if result != Error::NoError {
Err(std::io::Error::new(std::io::ErrorKind::Other, result))
} else {
let bytes_read = unsafe { bytes_read.assume_init() };
// println!("Read {} bytes (wanted to read {})", bytes_read, buf.len());
Ok(bytes_read
.try_into()
.expect("invalid number of bytes written"))
}
}
}
impl Drop for VCP {
fn drop(&mut self) {
let result = Error::from(unsafe { FT_Close(self.handle) });
if result != Error::NoError {
panic!("unable to close device: {:?}", result);
}
}
}