mirror of
https://github.com/Polprzewodnikowy/SummerCart64.git
synced 2024-11-28 00:14:14 +01:00
[SC64][SW] Fix USB reset procedure (I/O buffer purge)
This commit is contained in:
parent
4a50e33acd
commit
3146cc8c99
@ -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)?;
|
||||||
|
@ -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 {
|
||||||
|
@ -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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user