From e3bacc1e320c327da6a3d962ba3bf31fda27d836 Mon Sep 17 00:00:00 2001 From: Sean Cross Date: Mon, 30 Mar 2020 19:00:10 +0800 Subject: [PATCH] bitbang: first example of it actually working Signed-off-by: Sean Cross --- ftdi-vcp-rs/src/lib.rs | 175 +++++++++++++++++++++++++++++++++++----- ftdi-vcp-sys/src/lib.rs | 18 ++++- src/main.rs | 12 +++ 3 files changed, 184 insertions(+), 21 deletions(-) diff --git a/ftdi-vcp-rs/src/lib.rs b/ftdi-vcp-rs/src/lib.rs index 83c0367..d3128d4 100644 --- a/ftdi-vcp-rs/src/lib.rs +++ b/ftdi-vcp-rs/src/lib.rs @@ -1,10 +1,58 @@ use ftdi_vcp_sys::{ - FT_Close, FT_GetComPortNumber, FT_OpenEx, FT_HANDLE, FT_OPEN_BY_DESCRIPTION, FT_STATUS, LONG, - PVOID, + FT_Close, FT_GetBitMode, FT_GetComPortNumber, FT_OpenEx, FT_SetBitMode, 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::mem::MaybeUninit; -use std::convert::TryInto; +use std::io::{Read, Write}; + +#[derive(Debug)] +pub enum BitMode { + Reset, + AsyncBitbang, + MPSSE, + SyncBitbang, + MCUHost, + FastSerial, + CBUSBitbang, + SyncFIFO, + Unknown(u8), +} + +impl From 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 { @@ -29,9 +77,17 @@ pub enum Error { OtherError, DeviceListNotReady, NoComPortAssigned, + // 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 for Error { fn from(src: FT_STATUS) -> Self { match src { @@ -60,8 +116,10 @@ impl From for Error { } } +#[derive(Debug)] pub struct VCP { handle: FT_HANDLE, + bit_mode: BitMode, } impl VCP { @@ -76,19 +134,24 @@ impl VCP { ) }); if result != Error::NoError { - Err(result) - } else { - Ok(VCP { - handle: unsafe { handle.assume_init() }, - }) + return Err(result); } + let handle = unsafe { handle.assume_init() }; + + let mut bit_mode = MaybeUninit::::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 { let mut com_port_number = MaybeUninit::::uninit(); - let result = Error::from(unsafe { - FT_GetComPortNumber(self.handle, com_port_number.as_mut_ptr()) - }); + let result = + Error::from(unsafe { FT_GetComPortNumber(self.handle, com_port_number.as_mut_ptr()) }); if result != Error::NoError { Err(result) } else { @@ -100,6 +163,88 @@ impl VCP { } } } + + /// Set the given signals to "OUTPUT". All other signals will be "INPUT". + pub fn set_bit_mode(&mut self, outputs: u8) -> Result<(), Error> { + let result = Error::from(unsafe { + FT_SetBitMode(self.handle, outputs, BitMode::SyncBitbang.to_u8()) + }); + if result != Error::NoError { + Err(result) + } else { + Ok(()) + } + } + + pub fn write(&mut self, out_buffer: &[u8]) -> Result { + let mut bytes_written = MaybeUninit::::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")) + } + } +} + +impl Write for VCP { + fn write(&mut self, buf: &[u8]) -> std::io::Result { + let mut bytes_written = MaybeUninit::::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 { + Ok(unsafe { bytes_written.assume_init() } + .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 { + let mut bytes_written = MaybeUninit::::uninit(); + let result = Error::from(unsafe { + FT_Write( + self.handle, + buf.as_ptr() as LPVOID, + 1, + bytes_written.as_mut_ptr() as LPDWORD, + ) + }); + if result != Error::NoError { + Err(std::io::Error::new(std::io::ErrorKind::Other, result)) + } else { + Ok(unsafe { bytes_written.assume_init() } + .try_into() + .expect("invalid number of bytes written")) + } + } } impl Drop for VCP { @@ -110,11 +255,3 @@ impl Drop for VCP { } } } - -#[cfg(test)] -mod tests { - #[test] - fn it_works() { - assert_eq!(2 + 2, 4); - } -} diff --git a/ftdi-vcp-sys/src/lib.rs b/ftdi-vcp-sys/src/lib.rs index 936e5f6..94b36c6 100644 --- a/ftdi-vcp-sys/src/lib.rs +++ b/ftdi-vcp-sys/src/lib.rs @@ -2,8 +2,8 @@ extern crate winapi; // use winapi::um::winnt::{PVOID, ULONG, DWORD}; -pub use winapi::shared::minwindef::{DWORD, LPDWORD, LPLONG, LPVOID}; -pub use winapi::shared::ntdef::{PVOID, ULONG, LONG}; +pub use winapi::shared::minwindef::{DWORD, LPDWORD, LPLONG, LPVOID, UCHAR, PUCHAR}; +pub use winapi::shared::ntdef::{LONG, PVOID, ULONG}; #[allow(non_camel_case_types)] pub type FT_HANDLE = PVOID; @@ -88,6 +88,20 @@ extern "stdcall" { pub fn FT_Open(deviceNumber: u32, pHandle: *mut FT_HANDLE) -> FT_STATUS; pub fn FT_OpenEx(pArg1: PVOID, Flags: DWORD, pHandle: *mut FT_HANDLE) -> FT_STATUS; pub fn FT_Close(ftHandle: FT_HANDLE) -> FT_STATUS; + pub fn FT_Write( + ftHandle: FT_HANDLE, + lpBuffer: LPVOID, + dwBytesToWrite: DWORD, + lpBytesWritten: LPDWORD, + ) -> FT_STATUS; + pub fn FT_Read( + ftHandle: FT_HANDLE, + lpBuffer: LPVOID, + dwBytesToRead: DWORD, + lpBytesReturned: LPDWORD, + ) -> FT_STATUS; + pub fn FT_SetBitMode(ftHandle: FT_HANDLE, ucMask: UCHAR, ucEnable: UCHAR) -> FT_STATUS; + pub fn FT_GetBitMode(ftHandle: FT_HANDLE, pucMode: PUCHAR) -> FT_STATUS; } pub fn create_device_info_list() -> Result { diff --git a/src/main.rs b/src/main.rs index 788553e..ddf07b7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,8 +7,20 @@ #define FT_LIST_MASK (FT_LIST_NUMBER_ONLY|FT_LIST_BY_INDEX|FT_LIST_ALL) */ use ftdi_vcp_rs::VCP; +use std::thread::sleep; +use std::time::Duration; fn main() { let mut vcp = VCP::new_from_name("iCEBreaker V1.0e A").expect("couldn't open vcp"); + println!("Opened VCP: {:?}", vcp); + vcp.set_bit_mode(0x80).expect("couldn't set bit mode"); + for i in 0..10 { + if i & 1 != 0 { + vcp.write(&[0x80]).expect("couldn't set all 1"); + } else { + vcp.write(&[0x00]).expect("couldn't set all 1"); + } + sleep(Duration::from_millis(500)); + } println!("VCP COM{}:", vcp.com_port().expect("couldn't get com port")); }