[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,
unclog_buffer: std::collections::VecDeque<u8>,
write_buffer: Vec<u8>,
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<std::time::Duration>,
write_timeout: Option<std::time::Duration>,
) -> std::io::Result<Self> {
fn new(io_timeout: Option<std::time::Duration>) -> std::io::Result<Self> {
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<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 devices = unsafe {
@ -162,21 +157,15 @@ impl Wrapper {
.into()
}
fn set_timeouts(
&mut self,
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);
fn set_io_timeout(&mut self, io_timeout: Option<std::time::Duration>) -> 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<std::time::Duration>,
read_timeout: Option<std::time::Duration>,
write_timeout: Option<std::time::Duration>,
io_timeout: Option<std::time::Duration>,
) -> 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_interface(InterfaceIndex::A)?;

View File

@ -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<usize>;
@ -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<SerialBackend> {
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<FtdiBackend> {
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<TcpBackend, Error> {
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 {

View File

@ -6,10 +6,10 @@ pub struct DeviceInfo {
pub struct SerialDevice {
serial: serial2::SerialPort,
writer: std::io::BufWriter<serial2::SerialPort>,
unclog_buffer: std::collections::VecDeque<u8>,
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<std::time::Duration>,
read_timeout: Option<std::time::Duration>,
write_timeout: Option<std::time::Duration>,
io_timeout: Option<std::time::Duration>,
) -> 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 {
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<usize> {
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());
}
}