From 3146cc8c990372626ba8d87e2e302f3fb7fa0817 Mon Sep 17 00:00:00 2001 From: Mateusz Faderewski Date: Tue, 3 Sep 2024 12:48:36 +0200 Subject: [PATCH] [SC64][SW] Fix USB reset procedure (I/O buffer purge) --- sw/deployer/src/sc64/ftdi.rs | 59 +++++++++++++++------------------- sw/deployer/src/sc64/link.rs | 21 +++--------- sw/deployer/src/sc64/serial.rs | 31 +++++++++--------- 3 files changed, 46 insertions(+), 65 deletions(-) diff --git a/sw/deployer/src/sc64/ftdi.rs b/sw/deployer/src/sc64/ftdi.rs index 782dfc1..4a7db96 100644 --- a/sw/deployer/src/sc64/ftdi.rs +++ b/sw/deployer/src/sc64/ftdi.rs @@ -28,21 +28,17 @@ struct Wrapper { context: *mut libftdi1_sys::ftdi_context, unclog_buffer: std::collections::VecDeque, write_buffer: Vec, - read_timeout: std::time::Duration, - write_timeout: std::time::Duration, + io_timeout: std::time::Duration, read_chunksize: usize, write_chunksize: usize, } impl Wrapper { const DEFAULT_POLL_TIMEOUT: std::time::Duration = std::time::Duration::from_millis(16); - const DEFAULT_RW_TIMEOUT: std::time::Duration = std::time::Duration::from_secs(5); + const DEFAULT_IO_TIMEOUT: std::time::Duration = std::time::Duration::from_secs(5); const WRITE_CHUNK_TIMEOUT: std::time::Duration = std::time::Duration::from_millis(100); - fn new( - read_timeout: Option, - write_timeout: Option, - ) -> std::io::Result { + fn new(io_timeout: Option) -> std::io::Result { let context = unsafe { libftdi1_sys::ftdi_new() }; if context.is_null() { return Err(std::io::ErrorKind::OutOfMemory.into()); @@ -51,19 +47,18 @@ impl Wrapper { context, unclog_buffer: std::collections::VecDeque::new(), write_buffer: vec![], - read_timeout: Self::DEFAULT_RW_TIMEOUT, - write_timeout: Self::DEFAULT_RW_TIMEOUT, + io_timeout: Self::DEFAULT_IO_TIMEOUT, read_chunksize: 4096, write_chunksize: 4096, }; - wrapper.set_timeouts(read_timeout, write_timeout)?; + wrapper.set_io_timeout(io_timeout)?; wrapper.read_data_set_chunksize(wrapper.read_chunksize)?; wrapper.write_data_set_chunksize(wrapper.write_chunksize)?; Ok(wrapper) } fn list_devices(vendor: u16, product: u16) -> std::io::Result> { - let wrapper = Self::new(None, None)?; + let wrapper = Self::new(None)?; let mut device_list: *mut libftdi1_sys::ftdi_device_list = std::ptr::null_mut(); let devices = unsafe { @@ -162,21 +157,15 @@ impl Wrapper { .into() } - fn set_timeouts( - &mut self, - read_timeout: Option, - write_timeout: Option, - ) -> std::io::Result<()> { - let read_timeout = read_timeout.unwrap_or(Self::DEFAULT_RW_TIMEOUT); - let write_timeout = write_timeout.unwrap_or(Self::DEFAULT_RW_TIMEOUT); + fn set_io_timeout(&mut self, io_timeout: Option) -> std::io::Result<()> { + let io_timeout = io_timeout.unwrap_or(Self::DEFAULT_IO_TIMEOUT); unsafe { - (*self.context).usb_read_timeout = i32::try_from(read_timeout.as_millis()) + (*self.context).usb_read_timeout = i32::try_from(io_timeout.as_millis()) .map_err(|_| std::io::ErrorKind::InvalidInput)?; - (*self.context).usb_write_timeout = i32::try_from(write_timeout.as_millis()) + (*self.context).usb_write_timeout = i32::try_from(io_timeout.as_millis()) .map_err(|_| std::io::ErrorKind::InvalidInput)?; } - self.read_timeout = read_timeout; - self.write_timeout = write_timeout; + self.io_timeout = io_timeout; Ok(()) } @@ -330,6 +319,14 @@ impl Wrapper { } fn tciflush(&mut self) -> std::io::Result<()> { + match unsafe { libftdi1_sys::ftdi_tciflush(self.context) } { + 0 => Ok(()), + -1 => Err(std::io::ErrorKind::BrokenPipe.into()), + -2 => Err(std::io::ErrorKind::NotConnected.into()), + result => Err(std::io::Error::other(format!( + "Unexpected response from ftdi_tciflush: {result}" + ))), + }?; let timeout = std::time::Instant::now(); loop { match self.read(&mut vec![0u8; self.read_chunksize]) { @@ -337,14 +334,12 @@ impl Wrapper { Err(error) => match error.kind() { std::io::ErrorKind::Interrupted | std::io::ErrorKind::TimedOut - | std::io::ErrorKind::WouldBlock => { - return Ok(()); - } + | std::io::ErrorKind::WouldBlock => {} _ => return Err(error), }, }; - if timeout.elapsed() > self.read_timeout { - return Err(std::io::ErrorKind::TimedOut.into()); + if timeout.elapsed() > std::time::Duration::from_millis(1) { + return Ok(()); } } } @@ -354,8 +349,7 @@ impl Wrapper { match unsafe { libftdi1_sys::ftdi_tcoflush(self.context) } { 0 => Ok(()), -1 => Err(std::io::ErrorKind::BrokenPipe.into()), - -2 => Err(std::io::ErrorKind::BrokenPipe.into()), - -3 => Err(std::io::ErrorKind::NotConnected.into()), + -2 => Err(std::io::ErrorKind::NotConnected.into()), result => Err(std::io::Error::other(format!( "Unexpected response from ftdi_tcoflush: {result}" ))), @@ -421,7 +415,7 @@ impl Wrapper { _ => return Err(error), } } - if timeout.elapsed() > self.write_timeout { + if timeout.elapsed() > self.io_timeout { return Err(std::io::ErrorKind::TimedOut.into()); } } @@ -478,10 +472,9 @@ impl FtdiDevice { pub fn open( description: &str, poll_timeout: Option, - read_timeout: Option, - write_timeout: Option, + io_timeout: Option, ) -> std::io::Result { - let mut wrapper = Wrapper::new(read_timeout, write_timeout)?; + let mut wrapper = Wrapper::new(io_timeout)?; wrapper.set_module_detach_mode(ModuleDetachMode::AutoDetachReattach); wrapper.set_interface(InterfaceIndex::A)?; diff --git a/sw/deployer/src/sc64/link.rs b/sw/deployer/src/sc64/link.rs index 45f0f33..4785525 100644 --- a/sw/deployer/src/sc64/link.rs +++ b/sw/deployer/src/sc64/link.rs @@ -59,8 +59,7 @@ const FTDI_PREFIX: &str = "ftdi://"; const RESET_TIMEOUT: Duration = Duration::from_secs(1); const POLL_TIMEOUT: Duration = Duration::from_millis(5); -const READ_TIMEOUT: Duration = Duration::from_secs(10); -const WRITE_TIMEOUT: Duration = Duration::from_secs(10); +const IO_TIMEOUT: Duration = Duration::from_secs(10); pub trait Backend { fn read(&mut self, buffer: &mut [u8]) -> std::io::Result; @@ -142,7 +141,7 @@ pub trait Backend { _ => return Err(error.into()), }, } - if timeout.elapsed() > READ_TIMEOUT { + if timeout.elapsed() > IO_TIMEOUT { return Err(std::io::ErrorKind::TimedOut.into()); } } @@ -249,12 +248,7 @@ impl Backend for SerialBackend { fn new_serial_backend(port: &str) -> std::io::Result { Ok(SerialBackend { - device: SerialDevice::new( - port, - Some(POLL_TIMEOUT), - Some(READ_TIMEOUT), - Some(WRITE_TIMEOUT), - )?, + device: SerialDevice::new(port, Some(POLL_TIMEOUT), Some(IO_TIMEOUT))?, }) } @@ -294,12 +288,7 @@ impl Backend for FtdiBackend { fn new_ftdi_backend(port: &str) -> std::io::Result { Ok(FtdiBackend { - device: FtdiDevice::open( - port, - Some(POLL_TIMEOUT), - Some(READ_TIMEOUT), - Some(WRITE_TIMEOUT), - )?, + device: FtdiDevice::open(port, Some(POLL_TIMEOUT), Some(IO_TIMEOUT))?, }) } @@ -403,7 +392,7 @@ fn new_tcp_backend(address: &str) -> Result { Error::new(format!("Couldn't connect to [{address}]: {error}").as_str()) })?; stream.set_read_timeout(Some(POLL_TIMEOUT))?; - stream.set_write_timeout(Some(WRITE_TIMEOUT))?; + stream.set_write_timeout(Some(IO_TIMEOUT))?; let reader = BufReader::new(stream.try_clone()?); let writer = BufWriter::new(stream.try_clone()?); Ok(TcpBackend { diff --git a/sw/deployer/src/sc64/serial.rs b/sw/deployer/src/sc64/serial.rs index 0c040ac..0bcc1c6 100644 --- a/sw/deployer/src/sc64/serial.rs +++ b/sw/deployer/src/sc64/serial.rs @@ -6,10 +6,10 @@ pub struct DeviceInfo { pub struct SerialDevice { serial: serial2::SerialPort, + writer: std::io::BufWriter, unclog_buffer: std::collections::VecDeque, poll_timeout: std::time::Duration, - read_timeout: std::time::Duration, - write_timeout: std::time::Duration, + io_timeout: std::time::Duration, } impl SerialDevice { @@ -21,15 +21,16 @@ impl SerialDevice { pub fn new( port: &str, poll_timeout: Option, - read_timeout: Option, - write_timeout: Option, + io_timeout: Option, ) -> std::io::Result { + let serial = serial2::SerialPort::open(port, 115_200)?; + let writer = std::io::BufWriter::with_capacity(Self::BUFFER_SIZE, serial.try_clone()?); let mut device = Self { - serial: serial2::SerialPort::open(port, 115_200)?, + serial, + writer, unclog_buffer: std::collections::VecDeque::new(), poll_timeout: poll_timeout.unwrap_or(Self::DEFAULT_POLL_TIMEOUT), - read_timeout: read_timeout.unwrap_or(Self::DEFAULT_RW_TIMEOUT), - write_timeout: write_timeout.unwrap_or(Self::DEFAULT_RW_TIMEOUT), + io_timeout: io_timeout.unwrap_or(Self::DEFAULT_RW_TIMEOUT), }; device.serial.set_read_timeout(device.poll_timeout)?; device.serial.set_write_timeout(Self::WRITE_CHUNK_TIMEOUT)?; @@ -73,14 +74,12 @@ impl SerialDevice { Err(error) => match error.kind() { std::io::ErrorKind::Interrupted | std::io::ErrorKind::TimedOut - | std::io::ErrorKind::WouldBlock => { - return Ok(()); - } + | std::io::ErrorKind::WouldBlock => {} _ => return Err(error), }, }; - if timeout.elapsed() > self.read_timeout { - return Err(std::io::ErrorKind::TimedOut.into()); + if timeout.elapsed() > std::time::Duration::from_millis(1) { + return Ok(()); } } } @@ -128,14 +127,14 @@ impl std::io::Write for SerialDevice { fn write(&mut self, buffer: &[u8]) -> std::io::Result { let timeout = std::time::Instant::now(); loop { - match self.serial.write(buffer) { + match self.writer.write(buffer) { Ok(bytes) => return Ok(bytes), Err(error) => match error.kind() { std::io::ErrorKind::TimedOut => self.unclog_pipe()?, _ => return Err(error), }, }; - if timeout.elapsed() > self.write_timeout { + if timeout.elapsed() > self.io_timeout { return Err(std::io::ErrorKind::TimedOut.into()); } } @@ -144,14 +143,14 @@ impl std::io::Write for SerialDevice { fn flush(&mut self) -> std::io::Result<()> { let timeout = std::time::Instant::now(); loop { - match self.serial.flush() { + match self.writer.flush() { Ok(()) => return Ok(()), Err(error) => match error.kind() { std::io::ErrorKind::TimedOut => self.unclog_pipe()?, _ => return Err(error), }, }; - if timeout.elapsed() > self.write_timeout { + if timeout.elapsed() > self.io_timeout { return Err(std::io::ErrorKind::TimedOut.into()); } }