From 92803401161c5fb8132285004cbaa5c784464982 Mon Sep 17 00:00:00 2001 From: Charles Strahan Date: Sun, 30 Jul 2023 00:34:53 -0500 Subject: [PATCH 1/5] replace hid crate with hidapi crate --- Cargo.toml | 2 +- examples/simple.rs | 14 ++++++++------ src/error.rs | 4 ++-- src/lib.rs | 30 ++++++++++++++---------------- 4 files changed, 25 insertions(+), 25 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index d2927f6..a5ceff2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,7 +12,7 @@ categories = ["hardware-support"] include = ["src/**/*", "Cargo.toml", "README.md", "LICENSE"] [dependencies] -hid = "^0.4.1" +hidapi = "^2.4.1" error-chain = "^0.11.0" [badges] diff --git a/examples/simple.rs b/examples/simple.rs index 71d3297..e1f24b9 100644 --- a/examples/simple.rs +++ b/examples/simple.rs @@ -1,13 +1,15 @@ -extern crate hid; extern crate cp211x_uart; +extern crate hidapi; +use cp211x_uart::{DataBits, FlowControl, HidUart, Parity, StopBits, UartConfig}; use std::time::Duration; -use cp211x_uart::{HidUart, UartConfig, DataBits, StopBits, Parity, FlowControl}; fn run() -> Result<(), cp211x_uart::Error> { - let manager = hid::init()?; - for device in manager.find(Some(0x10C4), Some(0xEA80)) { - let handle = device.open()?; + let manager = hidapi::HidApi::new()?; + for device_info in manager.device_list().filter(|device_info| { + device_info.vendor_id() == 0x10C4 && device_info.product_id() == 0xEA80 + }) { + let handle = device_info.open_device(&manager)?; let mut uart = HidUart::new(handle)?; let config = UartConfig { @@ -37,4 +39,4 @@ fn main() { } _ => {} } -} \ No newline at end of file +} diff --git a/src/error.rs b/src/error.rs index a15f6fa..a937204 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,6 +1,6 @@ error_chain! { foreign_links { - HidError(::hid::Error); + HidError(::hidapi::HidError); } errors { @@ -9,4 +9,4 @@ error_chain! { display("write operation is time out") } } -} \ No newline at end of file +} diff --git a/src/lib.rs b/src/lib.rs index 97e2f5e..4057dbb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,7 +3,7 @@ //! See more information [here][1]. //! [1]: https://www.silabs.com/products/interface/usb-bridges/classic-usb-bridges/device.cp2110-f01-gm -extern crate hid; +extern crate hidapi; #[macro_use] extern crate error_chain; use std::cmp::min; @@ -116,14 +116,14 @@ impl Default for UartConfig { } } -/// Wrapper around `hid::Handle` to provide UART control. +/// Wrapper around `hidapi::HidDevice` to provide UART control. pub struct HidUart { - handle: hid::Handle, + handle: hidapi::HidDevice, read_timeout: Duration, write_timeout: Duration, } -fn set_uart_enable(handle: &mut hid::Handle, enable: bool) -> Result<()> { +fn set_uart_enable(handle: &mut hidapi::HidDevice, enable: bool) -> Result<()> { let mut buf: [u8; FEATURE_REPORT_LENGTH] = [0; FEATURE_REPORT_LENGTH]; buf[0] = GETSET_UART_ENABLE; @@ -132,15 +132,15 @@ fn set_uart_enable(handle: &mut hid::Handle, enable: bool) -> Result<()> { } else { buf[1] = 0x00; } - handle.feature().send(&buf[..])?; + handle.send_feature_report(&buf[..])?; Ok(()) } impl HidUart { - /// Returns a new instance of `HidUart` from `hid::Handle`. + /// Returns a new instance of `HidUart` from `hidapi::HidDevice`. /// /// The instance enables UART automatically. - pub fn new(handle: hid::Handle) -> Result { + pub fn new(handle: hidapi::HidDevice) -> Result { let mut instance = HidUart { handle, read_timeout: Duration::from_millis(1000), @@ -185,7 +185,7 @@ impl HidUart { let mut buf: [u8; FEATURE_REPORT_LENGTH] = [0; FEATURE_REPORT_LENGTH]; buf[0] = GETSET_UART_ENABLE; - self.handle.feature().get(&mut buf[..])?; + self.handle.get_feature_report(&mut buf[..])?; if buf[1] == 0x00 { Ok(false) } else { @@ -228,7 +228,7 @@ impl HidUart { StopBits::Long => 0x01, }; - self.handle.feature().send(&buf[..])?; + self.handle.send_feature_report(&buf[..])?; Ok(()) } @@ -237,7 +237,7 @@ impl HidUart { let mut buf: [u8; FEATURE_REPORT_LENGTH] = [0; FEATURE_REPORT_LENGTH]; buf[0] = GETSET_UART_CONFIG; - self.handle.feature().get(&mut buf[..])?; + self.handle.get_feature_report(&mut buf[..])?; let baud_rate: u32 = u32::from(buf[1]) << 24 | u32::from(buf[2]) << 16 | u32::from(buf[3]) << 8 | u32::from(buf[4]); let parity = @@ -295,7 +295,7 @@ impl HidUart { if tx { buf[1] |= PURGE_TRANSMIT_MASK; } - self.handle.feature().send(&buf[..])?; + self.handle.send_feature_report(&buf[..])?; Ok(()) } @@ -304,8 +304,6 @@ impl HidUart { pub fn write(&mut self, data: &[u8]) -> Result<()> { let mut buf: [u8; INTERRUPT_REPORT_LENGTH]; - let mut data_handle = self.handle.data(); - let start_time = Instant::now(); for chunk in data.chunks(INTERRUPT_REPORT_LENGTH - 1) { if start_time.elapsed() > self.write_timeout { @@ -314,7 +312,7 @@ impl HidUart { buf = [0; INTERRUPT_REPORT_LENGTH]; buf[0] = chunk.len() as u8; buf[1..chunk.len()+1].copy_from_slice(chunk); - data_handle.write(&buf[..])?; + self.handle.write(&buf[..])?; } Ok(()) @@ -325,13 +323,13 @@ impl HidUart { let mut buf: [u8; INTERRUPT_REPORT_LENGTH]; let start_time = Instant::now(); let mut num_bytes_read = 0; - let mut data_handle = self.handle.data(); while start_time.elapsed() < self.read_timeout { let data_free = data.len() - num_bytes_read; if data_free > 0 { buf = [0; INTERRUPT_REPORT_LENGTH]; let buf_size = min(data_free, INTERRUPT_REPORT_LENGTH - 1) + 1; - if let Some(_) = data_handle.read(&mut buf[0..buf_size], Duration::from_millis(1))? { + let total_read = self.handle.read_timeout(&mut buf[0..buf_size], 1)?; + if total_read != 0 { let report_len: usize = buf[0] as usize; data[num_bytes_read..(num_bytes_read + report_len)].copy_from_slice(&buf[1..(report_len + 1)]); num_bytes_read += report_len; From 41c31995790f889d706494fa5d3c0268d8e0fac5 Mon Sep 17 00:00:00 2001 From: Charles Strahan Date: Sun, 30 Jul 2023 00:42:10 -0500 Subject: [PATCH 2/5] check timeout at end of loop --- src/lib.rs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 4057dbb..f0978bb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -306,13 +306,13 @@ impl HidUart { let start_time = Instant::now(); for chunk in data.chunks(INTERRUPT_REPORT_LENGTH - 1) { - if start_time.elapsed() > self.write_timeout { - return Err(ErrorKind::WriteTimeout.into()); - } buf = [0; INTERRUPT_REPORT_LENGTH]; buf[0] = chunk.len() as u8; buf[1..chunk.len()+1].copy_from_slice(chunk); self.handle.write(&buf[..])?; + if start_time.elapsed() > self.write_timeout { + return Err(ErrorKind::WriteTimeout.into()); + } } Ok(()) @@ -323,7 +323,7 @@ impl HidUart { let mut buf: [u8; INTERRUPT_REPORT_LENGTH]; let start_time = Instant::now(); let mut num_bytes_read = 0; - while start_time.elapsed() < self.read_timeout { + loop { let data_free = data.len() - num_bytes_read; if data_free > 0 { buf = [0; INTERRUPT_REPORT_LENGTH]; @@ -339,7 +339,11 @@ impl HidUart { } else { break; } + if start_time.elapsed() < self.read_timeout { + break; + } } + Ok(num_bytes_read) } } From 93ffa91f860d2448f9bc8680dfcd59364a16eb6d Mon Sep 17 00:00:00 2001 From: Charles Strahan Date: Sun, 30 Jul 2023 04:40:00 -0500 Subject: [PATCH 3/5] buffer RX input reports When we call read_timeout to fill the report buffer, if we specify a size smaller than the max input report length, the report can become truncated. The report will indicate that it has length M, where the read size is N; when M > N, the data would be lost. So we need to buffer up to the max input report length to avoid losing data. This also fixes a bug that resulted in a panic: the existing code might try to fill the last N bytes of the data output buffer, while in actuality attempting to write M bytes to the data slice, resulting in an out-of-range panic. --- src/lib.rs | 50 +++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 43 insertions(+), 7 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index f0978bb..b60587d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -121,6 +121,9 @@ pub struct HidUart { handle: hidapi::HidDevice, read_timeout: Duration, write_timeout: Duration, + read_buffer_start: u8, + read_buffer_left: u8, + read_buffer: [u8; INTERRUPT_REPORT_LENGTH], } fn set_uart_enable(handle: &mut hidapi::HidDevice, enable: bool) -> Result<()> { @@ -145,6 +148,9 @@ impl HidUart { handle, read_timeout: Duration::from_millis(1000), write_timeout: Duration::from_millis(1000), + read_buffer: [0; INTERRUPT_REPORT_LENGTH], + read_buffer_start: 0, + read_buffer_left: 0, }; instance.enable()?; Ok(instance) @@ -291,6 +297,10 @@ impl HidUart { buf[0] = PURGE_FIFOS; if rx { buf[1] |= PURGE_RECEIVE_MASK; + + // also dump any buffered data + self.read_buffer_start = 0; + self.read_buffer_left = 0; } if tx { buf[1] |= PURGE_TRANSMIT_MASK; @@ -320,21 +330,47 @@ impl HidUart { /// Receive `data` and returns a number of read bytes. pub fn read(&mut self, data: &mut [u8]) -> Result { + let mut num_bytes_read = 0; + + // drain any buffered data + if self.read_buffer_left > 0 { + num_bytes_read = min(data.len(), self.read_buffer_left as usize); + let start = self.read_buffer_start as usize; + let end = start + num_bytes_read; + let source_buf = &self.read_buffer[start..end]; + data[0..num_bytes_read].copy_from_slice(&source_buf); + self.read_buffer_left -= num_bytes_read as u8; + if self.read_buffer_left == 0 { + self.read_buffer_start = 0; + } else { + self.read_buffer_start += num_bytes_read as u8; + } + } + + // read from usb let mut buf: [u8; INTERRUPT_REPORT_LENGTH]; let start_time = Instant::now(); - let mut num_bytes_read = 0; loop { let data_free = data.len() - num_bytes_read; if data_free > 0 { buf = [0; INTERRUPT_REPORT_LENGTH]; - let buf_size = min(data_free, INTERRUPT_REPORT_LENGTH - 1) + 1; - let total_read = self.handle.read_timeout(&mut buf[0..buf_size], 1)?; + let total_read = self.handle.read_timeout(&mut buf, 1)?; if total_read != 0 { let report_len: usize = buf[0] as usize; - data[num_bytes_read..(num_bytes_read + report_len)].copy_from_slice(&buf[1..(report_len + 1)]); - num_bytes_read += report_len; - } else { - break; + let copy_len = min(report_len, data_free); + data[num_bytes_read..(num_bytes_read + copy_len)].copy_from_slice(&buf[1..(copy_len + 1)]); + num_bytes_read += copy_len; + + // buffer the left overs + if copy_len < report_len { + let left = report_len - copy_len; + let start = 1 + copy_len; + let end = start + left; + self.read_buffer[0..left].copy_from_slice(&buf[start..end]); + self.read_buffer_start = 0; + self.read_buffer_left = left as u8; + return Ok(num_bytes_read) + } } } else { break; From 7fa0390f1a154b05df11b3343afb2933913d52ee Mon Sep 17 00:00:00 2001 From: Charles Strahan Date: Sun, 30 Jul 2023 07:23:31 -0500 Subject: [PATCH 4/5] fix read timeout --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index b60587d..5d2b61a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -375,7 +375,7 @@ impl HidUart { } else { break; } - if start_time.elapsed() < self.read_timeout { + if start_time.elapsed() > self.read_timeout { break; } } From 2a2f3d87dce63704825058437da62c8e59074bd1 Mon Sep 17 00:00:00 2001 From: Charles Strahan Date: Wed, 30 Aug 2023 00:57:31 -0500 Subject: [PATCH 5/5] wip: rxbuffer --- src/lib.rs | 205 ++++++++++++++++++++++++++++++----------------------- 1 file changed, 117 insertions(+), 88 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 5d2b61a..fa0a2f8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,25 +4,26 @@ //! [1]: https://www.silabs.com/products/interface/usb-bridges/classic-usb-bridges/device.cp2110-f01-gm extern crate hidapi; -#[macro_use] extern crate error_chain; +#[macro_use] +extern crate error_chain; use std::cmp::min; use std::default::Default; use std::time::{Duration, Instant}; mod error; -use error::*; pub use error::Error; +use error::*; const FEATURE_REPORT_LENGTH: usize = 64; const INTERRUPT_REPORT_LENGTH: usize = 64; const GETSET_UART_ENABLE: u8 = 0x41; // Get Set Receive Status -const PURGE_FIFOS: u8 = 0x43; // Purge FIFOs +const PURGE_FIFOS: u8 = 0x43; // Purge FIFOs const GETSET_UART_CONFIG: u8 = 0x50; // Get Set UART Config const PURGE_TRANSMIT_MASK: u8 = 0x01; -const PURGE_RECEIVE_MASK: u8 = 0x02; +const PURGE_RECEIVE_MASK: u8 = 0x02; /// The number of data bits in [UART configuration](struct.UartConfig.html). #[derive(Clone, Copy, PartialEq, Eq, Debug)] @@ -121,9 +122,7 @@ pub struct HidUart { handle: hidapi::HidDevice, read_timeout: Duration, write_timeout: Duration, - read_buffer_start: u8, - read_buffer_left: u8, - read_buffer: [u8; INTERRUPT_REPORT_LENGTH], + rx_buffer: RxBuffer, } fn set_uart_enable(handle: &mut hidapi::HidDevice, enable: bool) -> Result<()> { @@ -148,9 +147,7 @@ impl HidUart { handle, read_timeout: Duration::from_millis(1000), write_timeout: Duration::from_millis(1000), - read_buffer: [0; INTERRUPT_REPORT_LENGTH], - read_buffer_start: 0, - read_buffer_left: 0, + rx_buffer: RxBuffer::new(), }; instance.enable()?; Ok(instance) @@ -208,31 +205,27 @@ impl HidUart { buf[2] = ((config.baud_rate >> 16) & 0xFF) as u8; buf[3] = ((config.baud_rate >> 8) & 0xFF) as u8; buf[4] = (config.baud_rate & 0xFF) as u8; - buf[5] = - match config.parity { - Parity::None => 0x00, - Parity::Odd => 0x01, - Parity::Even => 0x02, - Parity::Mark => 0x03, - Parity::Space => 0x04, - }; - buf[6] = - match config.flow_control { - FlowControl::None => 0x00, - FlowControl::RtsCts => 0x01, - }; - buf[7] = - match config.data_bits { - DataBits::Bits5 => 0x00, - DataBits::Bits6 => 0x01, - DataBits::Bits7 => 0x02, - DataBits::Bits8 => 0x03, - }; - buf[8] = - match config.stop_bits { - StopBits::Short => 0x00, - StopBits::Long => 0x01, - }; + buf[5] = match config.parity { + Parity::None => 0x00, + Parity::Odd => 0x01, + Parity::Even => 0x02, + Parity::Mark => 0x03, + Parity::Space => 0x04, + }; + buf[6] = match config.flow_control { + FlowControl::None => 0x00, + FlowControl::RtsCts => 0x01, + }; + buf[7] = match config.data_bits { + DataBits::Bits5 => 0x00, + DataBits::Bits6 => 0x01, + DataBits::Bits7 => 0x02, + DataBits::Bits8 => 0x03, + }; + buf[8] = match config.stop_bits { + StopBits::Short => 0x00, + StopBits::Long => 0x01, + }; self.handle.send_feature_report(&buf[..])?; Ok(()) @@ -245,36 +238,35 @@ impl HidUart { buf[0] = GETSET_UART_CONFIG; self.handle.get_feature_report(&mut buf[..])?; - let baud_rate: u32 = u32::from(buf[1]) << 24 | u32::from(buf[2]) << 16 | u32::from(buf[3]) << 8 | u32::from(buf[4]); - let parity = - match buf[5] { - 0x00 => Ok(Parity::None), - 0x01 => Ok(Parity::Odd), - 0x02 => Ok(Parity::Even), - 0x03 => Ok(Parity::Mark), - 0x04 => Ok(Parity::Space), - _ => Err("Unknown parity mode"), - }?; - let flow_control = - match buf[6] { - 0x00 => Ok(FlowControl::None), - 0x01 => Ok(FlowControl::RtsCts), - _ => Err("Unknown flow control mode"), - }?; - let data_bits = - match buf[7] { - 0x00 => Ok(DataBits::Bits5), - 0x01 => Ok(DataBits::Bits6), - 0x02 => Ok(DataBits::Bits7), - 0x03 => Ok(DataBits::Bits8), - _ => Err("Unknown data bits mode"), - }?; - let stop_bits = - match buf[8] { - 0x00 => Ok(StopBits::Short), - 0x01 => Ok(StopBits::Long), - _ => Err("Unknown stop bits mode"), - }?; + let baud_rate: u32 = u32::from(buf[1]) << 24 + | u32::from(buf[2]) << 16 + | u32::from(buf[3]) << 8 + | u32::from(buf[4]); + let parity = match buf[5] { + 0x00 => Ok(Parity::None), + 0x01 => Ok(Parity::Odd), + 0x02 => Ok(Parity::Even), + 0x03 => Ok(Parity::Mark), + 0x04 => Ok(Parity::Space), + _ => Err("Unknown parity mode"), + }?; + let flow_control = match buf[6] { + 0x00 => Ok(FlowControl::None), + 0x01 => Ok(FlowControl::RtsCts), + _ => Err("Unknown flow control mode"), + }?; + let data_bits = match buf[7] { + 0x00 => Ok(DataBits::Bits5), + 0x01 => Ok(DataBits::Bits6), + 0x02 => Ok(DataBits::Bits7), + 0x03 => Ok(DataBits::Bits8), + _ => Err("Unknown data bits mode"), + }?; + let stop_bits = match buf[8] { + 0x00 => Ok(StopBits::Short), + 0x01 => Ok(StopBits::Long), + _ => Err("Unknown stop bits mode"), + }?; let config = UartConfig { baud_rate, parity, @@ -299,8 +291,7 @@ impl HidUart { buf[1] |= PURGE_RECEIVE_MASK; // also dump any buffered data - self.read_buffer_start = 0; - self.read_buffer_left = 0; + self.rx_buffer.clear(); } if tx { buf[1] |= PURGE_TRANSMIT_MASK; @@ -318,7 +309,7 @@ impl HidUart { for chunk in data.chunks(INTERRUPT_REPORT_LENGTH - 1) { buf = [0; INTERRUPT_REPORT_LENGTH]; buf[0] = chunk.len() as u8; - buf[1..chunk.len()+1].copy_from_slice(chunk); + buf[1..chunk.len() + 1].copy_from_slice(chunk); self.handle.write(&buf[..])?; if start_time.elapsed() > self.write_timeout { return Err(ErrorKind::WriteTimeout.into()); @@ -330,22 +321,8 @@ impl HidUart { /// Receive `data` and returns a number of read bytes. pub fn read(&mut self, data: &mut [u8]) -> Result { - let mut num_bytes_read = 0; - // drain any buffered data - if self.read_buffer_left > 0 { - num_bytes_read = min(data.len(), self.read_buffer_left as usize); - let start = self.read_buffer_start as usize; - let end = start + num_bytes_read; - let source_buf = &self.read_buffer[start..end]; - data[0..num_bytes_read].copy_from_slice(&source_buf); - self.read_buffer_left -= num_bytes_read as u8; - if self.read_buffer_left == 0 { - self.read_buffer_start = 0; - } else { - self.read_buffer_start += num_bytes_read as u8; - } - } + let mut num_bytes_read = self.rx_buffer.read(data); // read from usb let mut buf: [u8; INTERRUPT_REPORT_LENGTH]; @@ -358,7 +335,8 @@ impl HidUart { if total_read != 0 { let report_len: usize = buf[0] as usize; let copy_len = min(report_len, data_free); - data[num_bytes_read..(num_bytes_read + copy_len)].copy_from_slice(&buf[1..(copy_len + 1)]); + data[num_bytes_read..(num_bytes_read + copy_len)] + .copy_from_slice(&buf[1..(copy_len + 1)]); num_bytes_read += copy_len; // buffer the left overs @@ -366,10 +344,10 @@ impl HidUart { let left = report_len - copy_len; let start = 1 + copy_len; let end = start + left; - self.read_buffer[0..left].copy_from_slice(&buf[start..end]); - self.read_buffer_start = 0; - self.read_buffer_left = left as u8; - return Ok(num_bytes_read) + + self.rx_buffer.write(&buf[start..end]); + + return Ok(num_bytes_read); } } } else { @@ -383,3 +361,54 @@ impl HidUart { Ok(num_bytes_read) } } + +struct RxBuffer { + start: u8, + len: u8, + data: [u8; INTERRUPT_REPORT_LENGTH], +} + +impl RxBuffer { + fn new() -> Self { + Self { + start: 0, + len: 0, + data: [0; INTERRUPT_REPORT_LENGTH], + } + } + + fn read(&mut self, dest: &mut [u8]) -> usize { + if self.len == 0 { + return 0; + } + + let num_bytes_read = min(dest.len(), self.len as usize); + let start = self.start as usize; + let end = start + num_bytes_read; + let source_buf = &self.data[start..end]; + dest[0..num_bytes_read].copy_from_slice(&source_buf); + self.len -= num_bytes_read as u8; + if self.len == 0 { + self.start = 0; + } else { + self.start += num_bytes_read as u8; + } + + return num_bytes_read; + } + + fn write(&mut self, source: &[u8]) { + if source.len() == 0 { + return; + } + + self.data[0..source.len()].copy_from_slice(&source); + self.start = 0; + self.len = source.len() as u8; + } + + fn clear(&mut self) { + self.start = 0; + self.len = 0; + } +}