[SC64][SW] Fix USB reset procedure (I/O buffer purge)

This commit is contained in:
Mateusz Faderewski 2024-09-03 12:48:36 +02:00
parent 4a50e33acd
commit 3146cc8c99
3 changed files with 46 additions and 65 deletions

View File

@ -28,21 +28,17 @@ struct Wrapper {
context: *mut libftdi1_sys::ftdi_context, context: *mut libftdi1_sys::ftdi_context,
unclog_buffer: std::collections::VecDeque<u8>, unclog_buffer: std::collections::VecDeque<u8>,
write_buffer: Vec<u8>, write_buffer: Vec<u8>,
read_timeout: std::time::Duration, io_timeout: std::time::Duration,
write_timeout: std::time::Duration,
read_chunksize: usize, read_chunksize: usize,
write_chunksize: usize, write_chunksize: usize,
} }
impl Wrapper { impl Wrapper {
const DEFAULT_POLL_TIMEOUT: std::time::Duration = std::time::Duration::from_millis(16); 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); const WRITE_CHUNK_TIMEOUT: std::time::Duration = std::time::Duration::from_millis(100);
fn new( fn new(io_timeout: Option<std::time::Duration>) -> std::io::Result<Self> {
read_timeout: Option<std::time::Duration>,
write_timeout: Option<std::time::Duration>,
) -> std::io::Result<Self> {
let context = unsafe { libftdi1_sys::ftdi_new() }; let context = unsafe { libftdi1_sys::ftdi_new() };
if context.is_null() { if context.is_null() {
return Err(std::io::ErrorKind::OutOfMemory.into()); return Err(std::io::ErrorKind::OutOfMemory.into());
@ -51,19 +47,18 @@ impl Wrapper {
context, context,
unclog_buffer: std::collections::VecDeque::new(), unclog_buffer: std::collections::VecDeque::new(),
write_buffer: vec![], write_buffer: vec![],
read_timeout: Self::DEFAULT_RW_TIMEOUT, io_timeout: Self::DEFAULT_IO_TIMEOUT,
write_timeout: Self::DEFAULT_RW_TIMEOUT,
read_chunksize: 4096, read_chunksize: 4096,
write_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.read_data_set_chunksize(wrapper.read_chunksize)?;
wrapper.write_data_set_chunksize(wrapper.write_chunksize)?; wrapper.write_data_set_chunksize(wrapper.write_chunksize)?;
Ok(wrapper) Ok(wrapper)
} }
fn list_devices(vendor: u16, product: u16) -> std::io::Result<Vec<DeviceInfo>> { fn list_devices(vendor: u16, product: u16) -> std::io::Result<Vec<DeviceInfo>> {
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 mut device_list: *mut libftdi1_sys::ftdi_device_list = std::ptr::null_mut();
let devices = unsafe { let devices = unsafe {
@ -162,21 +157,15 @@ impl Wrapper {
.into() .into()
} }
fn set_timeouts( fn set_io_timeout(&mut self, io_timeout: Option<std::time::Duration>) -> std::io::Result<()> {
&mut self, let io_timeout = io_timeout.unwrap_or(Self::DEFAULT_IO_TIMEOUT);
read_timeout: Option<std::time::Duration>,
write_timeout: Option<std::time::Duration>,
) -> 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);
unsafe { 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)?; .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)?; .map_err(|_| std::io::ErrorKind::InvalidInput)?;
} }
self.read_timeout = read_timeout; self.io_timeout = io_timeout;
self.write_timeout = write_timeout;
Ok(()) Ok(())
} }
@ -330,6 +319,14 @@ impl Wrapper {
} }
fn tciflush(&mut self) -> std::io::Result<()> { 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(); let timeout = std::time::Instant::now();
loop { loop {
match self.read(&mut vec![0u8; self.read_chunksize]) { match self.read(&mut vec![0u8; self.read_chunksize]) {
@ -337,14 +334,12 @@ impl Wrapper {
Err(error) => match error.kind() { Err(error) => match error.kind() {
std::io::ErrorKind::Interrupted std::io::ErrorKind::Interrupted
| std::io::ErrorKind::TimedOut | std::io::ErrorKind::TimedOut
| std::io::ErrorKind::WouldBlock => { | std::io::ErrorKind::WouldBlock => {}
return Ok(());
}
_ => return Err(error), _ => return Err(error),
}, },
}; };
if timeout.elapsed() > self.read_timeout { if timeout.elapsed() > std::time::Duration::from_millis(1) {
return Err(std::io::ErrorKind::TimedOut.into()); return Ok(());
} }
} }
} }
@ -354,8 +349,7 @@ impl Wrapper {
match unsafe { libftdi1_sys::ftdi_tcoflush(self.context) } { match unsafe { libftdi1_sys::ftdi_tcoflush(self.context) } {
0 => Ok(()), 0 => Ok(()),
-1 => Err(std::io::ErrorKind::BrokenPipe.into()), -1 => Err(std::io::ErrorKind::BrokenPipe.into()),
-2 => Err(std::io::ErrorKind::BrokenPipe.into()), -2 => Err(std::io::ErrorKind::NotConnected.into()),
-3 => Err(std::io::ErrorKind::NotConnected.into()),
result => Err(std::io::Error::other(format!( result => Err(std::io::Error::other(format!(
"Unexpected response from ftdi_tcoflush: {result}" "Unexpected response from ftdi_tcoflush: {result}"
))), ))),
@ -421,7 +415,7 @@ impl Wrapper {
_ => return Err(error), _ => return Err(error),
} }
} }
if timeout.elapsed() > self.write_timeout { if timeout.elapsed() > self.io_timeout {
return Err(std::io::ErrorKind::TimedOut.into()); return Err(std::io::ErrorKind::TimedOut.into());
} }
} }
@ -478,10 +472,9 @@ impl FtdiDevice {
pub fn open( pub fn open(
description: &str, description: &str,
poll_timeout: Option<std::time::Duration>, poll_timeout: Option<std::time::Duration>,
read_timeout: Option<std::time::Duration>, io_timeout: Option<std::time::Duration>,
write_timeout: Option<std::time::Duration>,
) -> std::io::Result<FtdiDevice> { ) -> std::io::Result<FtdiDevice> {
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_module_detach_mode(ModuleDetachMode::AutoDetachReattach);
wrapper.set_interface(InterfaceIndex::A)?; wrapper.set_interface(InterfaceIndex::A)?;

View File

@ -59,8 +59,7 @@ const FTDI_PREFIX: &str = "ftdi://";
const RESET_TIMEOUT: Duration = Duration::from_secs(1); const RESET_TIMEOUT: Duration = Duration::from_secs(1);
const POLL_TIMEOUT: Duration = Duration::from_millis(5); const POLL_TIMEOUT: Duration = Duration::from_millis(5);
const READ_TIMEOUT: Duration = Duration::from_secs(10); const IO_TIMEOUT: Duration = Duration::from_secs(10);
const WRITE_TIMEOUT: Duration = Duration::from_secs(10);
pub trait Backend { pub trait Backend {
fn read(&mut self, buffer: &mut [u8]) -> std::io::Result<usize>; fn read(&mut self, buffer: &mut [u8]) -> std::io::Result<usize>;
@ -142,7 +141,7 @@ pub trait Backend {
_ => return Err(error.into()), _ => return Err(error.into()),
}, },
} }
if timeout.elapsed() > READ_TIMEOUT { if timeout.elapsed() > IO_TIMEOUT {
return Err(std::io::ErrorKind::TimedOut.into()); return Err(std::io::ErrorKind::TimedOut.into());
} }
} }
@ -249,12 +248,7 @@ impl Backend for SerialBackend {
fn new_serial_backend(port: &str) -> std::io::Result<SerialBackend> { fn new_serial_backend(port: &str) -> std::io::Result<SerialBackend> {
Ok(SerialBackend { Ok(SerialBackend {
device: SerialDevice::new( device: SerialDevice::new(port, Some(POLL_TIMEOUT), Some(IO_TIMEOUT))?,
port,
Some(POLL_TIMEOUT),
Some(READ_TIMEOUT),
Some(WRITE_TIMEOUT),
)?,
}) })
} }
@ -294,12 +288,7 @@ impl Backend for FtdiBackend {
fn new_ftdi_backend(port: &str) -> std::io::Result<FtdiBackend> { fn new_ftdi_backend(port: &str) -> std::io::Result<FtdiBackend> {
Ok(FtdiBackend { Ok(FtdiBackend {
device: FtdiDevice::open( device: FtdiDevice::open(port, Some(POLL_TIMEOUT), Some(IO_TIMEOUT))?,
port,
Some(POLL_TIMEOUT),
Some(READ_TIMEOUT),
Some(WRITE_TIMEOUT),
)?,
}) })
} }
@ -403,7 +392,7 @@ fn new_tcp_backend(address: &str) -> Result<TcpBackend, Error> {
Error::new(format!("Couldn't connect to [{address}]: {error}").as_str()) Error::new(format!("Couldn't connect to [{address}]: {error}").as_str())
})?; })?;
stream.set_read_timeout(Some(POLL_TIMEOUT))?; 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 reader = BufReader::new(stream.try_clone()?);
let writer = BufWriter::new(stream.try_clone()?); let writer = BufWriter::new(stream.try_clone()?);
Ok(TcpBackend { Ok(TcpBackend {

View File

@ -6,10 +6,10 @@ pub struct DeviceInfo {
pub struct SerialDevice { pub struct SerialDevice {
serial: serial2::SerialPort, serial: serial2::SerialPort,
writer: std::io::BufWriter<serial2::SerialPort>,
unclog_buffer: std::collections::VecDeque<u8>, unclog_buffer: std::collections::VecDeque<u8>,
poll_timeout: std::time::Duration, poll_timeout: std::time::Duration,
read_timeout: std::time::Duration, io_timeout: std::time::Duration,
write_timeout: std::time::Duration,
} }
impl SerialDevice { impl SerialDevice {
@ -21,15 +21,16 @@ impl SerialDevice {
pub fn new( pub fn new(
port: &str, port: &str,
poll_timeout: Option<std::time::Duration>, poll_timeout: Option<std::time::Duration>,
read_timeout: Option<std::time::Duration>, io_timeout: Option<std::time::Duration>,
write_timeout: Option<std::time::Duration>,
) -> std::io::Result<Self> { ) -> std::io::Result<Self> {
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 { let mut device = Self {
serial: serial2::SerialPort::open(port, 115_200)?, serial,
writer,
unclog_buffer: std::collections::VecDeque::new(), unclog_buffer: std::collections::VecDeque::new(),
poll_timeout: poll_timeout.unwrap_or(Self::DEFAULT_POLL_TIMEOUT), poll_timeout: poll_timeout.unwrap_or(Self::DEFAULT_POLL_TIMEOUT),
read_timeout: read_timeout.unwrap_or(Self::DEFAULT_RW_TIMEOUT), io_timeout: io_timeout.unwrap_or(Self::DEFAULT_RW_TIMEOUT),
write_timeout: write_timeout.unwrap_or(Self::DEFAULT_RW_TIMEOUT),
}; };
device.serial.set_read_timeout(device.poll_timeout)?; device.serial.set_read_timeout(device.poll_timeout)?;
device.serial.set_write_timeout(Self::WRITE_CHUNK_TIMEOUT)?; device.serial.set_write_timeout(Self::WRITE_CHUNK_TIMEOUT)?;
@ -73,14 +74,12 @@ impl SerialDevice {
Err(error) => match error.kind() { Err(error) => match error.kind() {
std::io::ErrorKind::Interrupted std::io::ErrorKind::Interrupted
| std::io::ErrorKind::TimedOut | std::io::ErrorKind::TimedOut
| std::io::ErrorKind::WouldBlock => { | std::io::ErrorKind::WouldBlock => {}
return Ok(());
}
_ => return Err(error), _ => return Err(error),
}, },
}; };
if timeout.elapsed() > self.read_timeout { if timeout.elapsed() > std::time::Duration::from_millis(1) {
return Err(std::io::ErrorKind::TimedOut.into()); return Ok(());
} }
} }
} }
@ -128,14 +127,14 @@ impl std::io::Write for SerialDevice {
fn write(&mut self, buffer: &[u8]) -> std::io::Result<usize> { fn write(&mut self, buffer: &[u8]) -> std::io::Result<usize> {
let timeout = std::time::Instant::now(); let timeout = std::time::Instant::now();
loop { loop {
match self.serial.write(buffer) { match self.writer.write(buffer) {
Ok(bytes) => return Ok(bytes), Ok(bytes) => return Ok(bytes),
Err(error) => match error.kind() { Err(error) => match error.kind() {
std::io::ErrorKind::TimedOut => self.unclog_pipe()?, std::io::ErrorKind::TimedOut => self.unclog_pipe()?,
_ => return Err(error), _ => return Err(error),
}, },
}; };
if timeout.elapsed() > self.write_timeout { if timeout.elapsed() > self.io_timeout {
return Err(std::io::ErrorKind::TimedOut.into()); return Err(std::io::ErrorKind::TimedOut.into());
} }
} }
@ -144,14 +143,14 @@ impl std::io::Write for SerialDevice {
fn flush(&mut self) -> std::io::Result<()> { fn flush(&mut self) -> std::io::Result<()> {
let timeout = std::time::Instant::now(); let timeout = std::time::Instant::now();
loop { loop {
match self.serial.flush() { match self.writer.flush() {
Ok(()) => return Ok(()), Ok(()) => return Ok(()),
Err(error) => match error.kind() { Err(error) => match error.kind() {
std::io::ErrorKind::TimedOut => self.unclog_pipe()?, std::io::ErrorKind::TimedOut => self.unclog_pipe()?,
_ => return Err(error), _ => return Err(error),
}, },
}; };
if timeout.elapsed() > self.write_timeout { if timeout.elapsed() > self.io_timeout {
return Err(std::io::ErrorKind::TimedOut.into()); return Err(std::io::ErrorKind::TimedOut.into());
} }
} }