serial refactor

This commit is contained in:
Mateusz Faderewski 2024-06-30 21:44:32 +02:00
parent 6d460e83bd
commit db9e9fa515
7 changed files with 285 additions and 128 deletions

View File

@ -45,12 +45,26 @@ module memory_dma (
logic dma_start; logic dma_start;
logic dma_stop; logic dma_stop;
logic dma_stop_requested;
always_comb begin always_comb begin
dma_start = dma_scb.start && !dma_scb.stop && !dma_scb.busy; dma_start = dma_scb.start && !dma_scb.stop && !dma_scb.busy;
dma_stop = dma_scb.stop; dma_stop = dma_scb.stop;
end end
always_ff @(posedge clk) begin
if (reset) begin
dma_stop_requested <= 1'b0;
end else begin
if (dma_stop) begin
dma_stop_requested <= 1'b1;
end
if (dma_start) begin
dma_stop_requested <= 1'b0;
end
end
end
// Remaining counter and FIFO enable // Remaining counter and FIFO enable
@ -197,7 +211,7 @@ module memory_dma (
remaining <= dma_scb.transfer_length; remaining <= dma_scb.transfer_length;
end end
if ((mem_bus.write && rx_rdata_pop) || (!mem_bus.write && tx_wdata_push)) begin if (!dma_stop_requested && ((mem_bus.write && rx_rdata_pop) || (!mem_bus.write && tx_wdata_push))) begin
remaining <= remaining - 1'd1; remaining <= remaining - 1'd1;
end end
end end

36
sw/deployer/Cargo.lock generated
View File

@ -218,9 +218,9 @@ checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495"
[[package]] [[package]]
name = "cc" name = "cc"
version = "1.0.101" version = "1.0.103"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac367972e516d45567c7eafc73d24e1c193dcf200a8d94e9db7b3d38b349572d" checksum = "2755ff20a1d93490d26ba33a6f092a38a508398a5320df5d4b3014fcccce9410"
dependencies = [ dependencies = [
"jobserver", "jobserver",
"libc", "libc",
@ -265,9 +265,9 @@ dependencies = [
[[package]] [[package]]
name = "clap" name = "clap"
version = "4.5.7" version = "4.5.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5db83dced34638ad474f39f250d7fea9598bdd239eaced1bdf45d597da0f433f" checksum = "84b3edb18336f4df585bc9aa31dd99c036dfa5dc5e9a2939a722a188f3a8970d"
dependencies = [ dependencies = [
"clap_builder", "clap_builder",
"clap_derive", "clap_derive",
@ -284,9 +284,9 @@ dependencies = [
[[package]] [[package]]
name = "clap_builder" name = "clap_builder"
version = "4.5.7" version = "4.5.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7e204572485eb3fbf28f871612191521df159bc3e15a9f5064c66dba3a8c05f" checksum = "c1c09dd5ada6c6c78075d6fd0da3f90d8080651e2d6cc8eb2f1aaa4034ced708"
dependencies = [ dependencies = [
"anstream", "anstream",
"anstyle", "anstyle",
@ -296,9 +296,9 @@ dependencies = [
[[package]] [[package]]
name = "clap_derive" name = "clap_derive"
version = "4.5.5" version = "4.5.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c780290ccf4fb26629baa7a1081e68ced113f1d3ec302fa5948f1c381ebf06c6" checksum = "2bac35c6dafb060fd4d275d9a4ffae97917c13a6327903a8be2153cd964f7085"
dependencies = [ dependencies = [
"heck", "heck",
"proc-macro2", "proc-macro2",
@ -799,9 +799,9 @@ dependencies = [
[[package]] [[package]]
name = "log" name = "log"
version = "0.4.21" version = "0.4.22"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
[[package]] [[package]]
name = "loop9" name = "loop9"
@ -906,9 +906,9 @@ checksum = "0676bb32a98c1a483ce53e500a81ad9c3d5b3f7c920c28c24e9cb0980d0b5bc8"
[[package]] [[package]]
name = "num-bigint" name = "num-bigint"
version = "0.4.5" version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c165a9ab64cf766f73521c0dd2cfdff64f488b8f0b3e621face3462d3db536d7" checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9"
dependencies = [ dependencies = [
"num-integer", "num-integer",
"num-traits", "num-traits",
@ -1196,9 +1196,9 @@ checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b"
[[package]] [[package]]
name = "rgb" name = "rgb"
version = "0.8.37" version = "0.8.40"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05aaa8004b64fd573fc9d002f4e632d51ad4f026c2b5ba95fcb6c2f32c2c47d8" checksum = "a7439be6844e40133eda024efd85bf07f59d0dd2f59b10c00dd6cfb92cc5c741"
dependencies = [ dependencies = [
"bytemuck", "bytemuck",
] ]
@ -1291,9 +1291,9 @@ dependencies = [
[[package]] [[package]]
name = "serialport" name = "serialport"
version = "4.3.0" version = "4.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f5a15d0be940df84846264b09b51b10b931fb2f275becb80934e3568a016828" checksum = "de7c4f0cce25b9b3518eea99618112f9ee4549f974480c8f43d3c06f03c131a0"
dependencies = [ dependencies = [
"bitflags 2.6.0", "bitflags 2.6.0",
"cfg-if", "cfg-if",
@ -1452,9 +1452,9 @@ dependencies = [
[[package]] [[package]]
name = "unescaper" name = "unescaper"
version = "0.1.4" version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0adf6ad32eb5b3cadff915f7b770faaac8f7ff0476633aa29eb0d9584d889d34" checksum = "c878a167baa8afd137494101a688ef8c67125089ff2249284bd2b5f9bfedb815"
dependencies = [ dependencies = [
"thiserror", "thiserror",
] ]

View File

@ -8,7 +8,7 @@ documentation = "https://github.com/Polprzewodnikowy/SummerCart64"
[dependencies] [dependencies]
chrono = "0.4.38" chrono = "0.4.38"
clap = { version = "4.5.7", features = ["derive"] } clap = { version = "4.5.8", features = ["derive"] }
clap-num = "1.1.1" clap-num = "1.1.1"
colored = "2.1.0" colored = "2.1.0"
crc32fast = "1.4.2" crc32fast = "1.4.2"
@ -24,7 +24,7 @@ panic-message = "0.3.0"
rand = "0.8.5" rand = "0.8.5"
rust-ini = "0.18.0" rust-ini = "0.18.0"
serial2 = "0.2.26" serial2 = "0.2.26"
serialport = "4.3.0" serialport = "4.4.0"
[profile.release] [profile.release]
lto = true lto = true

View File

@ -36,24 +36,24 @@ struct Wrapper {
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_RW_TIMEOUT: std::time::Duration = std::time::Duration::from_secs(5);
const DEFAULT_CHUNKSIZE: usize = 4096; const WRITE_CHUNK_TIMEOUT: std::time::Duration = std::time::Duration::from_millis(100);
fn new( fn new(
read_timeout: Option<std::time::Duration>, read_timeout: Option<std::time::Duration>,
write_timeout: Option<std::time::Duration>, write_timeout: Option<std::time::Duration>,
) -> std::io::Result<Wrapper> { ) -> 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());
} }
let mut wrapper = Wrapper { let mut wrapper = Self {
context, context,
unclog_buffer: std::collections::VecDeque::new(), unclog_buffer: std::collections::VecDeque::new(),
write_buffer: vec![], write_buffer: vec![],
read_timeout: Wrapper::DEFAULT_RW_TIMEOUT, read_timeout: Self::DEFAULT_RW_TIMEOUT,
write_timeout: Wrapper::DEFAULT_RW_TIMEOUT, write_timeout: Self::DEFAULT_RW_TIMEOUT,
read_chunksize: Wrapper::DEFAULT_CHUNKSIZE, read_chunksize: 4096,
write_chunksize: Wrapper::DEFAULT_CHUNKSIZE, write_chunksize: 4096,
}; };
wrapper.set_timeouts(read_timeout, write_timeout)?; wrapper.set_timeouts(read_timeout, write_timeout)?;
wrapper.read_data_set_chunksize(wrapper.read_chunksize)?; wrapper.read_data_set_chunksize(wrapper.read_chunksize)?;
@ -62,7 +62,7 @@ impl 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 = Wrapper::new(None, None)?; let wrapper = Self::new(None, 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 {
@ -152,8 +152,8 @@ impl Wrapper {
read_timeout: Option<std::time::Duration>, read_timeout: Option<std::time::Duration>,
write_timeout: Option<std::time::Duration>, write_timeout: Option<std::time::Duration>,
) -> std::io::Result<()> { ) -> std::io::Result<()> {
let read_timeout = read_timeout.unwrap_or(Wrapper::DEFAULT_RW_TIMEOUT); let read_timeout = read_timeout.unwrap_or(Self::DEFAULT_RW_TIMEOUT);
let write_timeout = write_timeout.unwrap_or(Wrapper::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(read_timeout.as_millis())
.map_err(|_| std::io::ErrorKind::InvalidInput)?; .map_err(|_| std::io::ErrorKind::InvalidInput)?;
@ -236,7 +236,7 @@ impl Wrapper {
} }
fn set_latency_timer(&mut self, latency: Option<std::time::Duration>) -> std::io::Result<()> { fn set_latency_timer(&mut self, latency: Option<std::time::Duration>) -> std::io::Result<()> {
let latency = u8::try_from(latency.unwrap_or(Wrapper::DEFAULT_POLL_TIMEOUT).as_millis()) let latency = u8::try_from(latency.unwrap_or(Self::DEFAULT_POLL_TIMEOUT).as_millis())
.map_err(|_| std::io::ErrorKind::InvalidInput)?; .map_err(|_| std::io::ErrorKind::InvalidInput)?;
match unsafe { libftdi1_sys::ftdi_set_latency_timer(self.context, latency) } { match unsafe { libftdi1_sys::ftdi_set_latency_timer(self.context, latency) } {
0 => Ok(()), 0 => Ok(()),
@ -320,7 +320,9 @@ impl Wrapper {
match self.read(&mut vec![0u8; self.read_chunksize]) { match self.read(&mut vec![0u8; self.read_chunksize]) {
Ok(_) => {} Ok(_) => {}
Err(error) => match error.kind() { Err(error) => match error.kind() {
std::io::ErrorKind::Interrupted | std::io::ErrorKind::WouldBlock => { std::io::ErrorKind::Interrupted
| std::io::ErrorKind::TimedOut
| std::io::ErrorKind::WouldBlock => {
return Ok(()); return Ok(());
} }
_ => return Err(error), _ => return Err(error),
@ -369,7 +371,7 @@ impl Wrapper {
Vec::from(buffer).as_mut_ptr(), Vec::from(buffer).as_mut_ptr(),
buffer.len() as i32, buffer.len() as i32,
&mut transferred, &mut transferred,
100, Self::WRITE_CHUNK_TIMEOUT.as_millis() as u32,
) )
}; };
*written = transferred as usize; *written = transferred as usize;
@ -469,7 +471,7 @@ impl FtdiDevice {
wrapper.set_module_detach_mode(ModuleDetachMode::AutoDetachReattach); wrapper.set_module_detach_mode(ModuleDetachMode::AutoDetachReattach);
wrapper.set_interface(InterfaceIndex::A)?; wrapper.set_interface(InterfaceIndex::A)?;
const CHUNK_SIZE: usize = 64 * 1024; const CHUNK_SIZE: usize = 16 * 1024;
wrapper.read_data_set_chunksize(CHUNK_SIZE)?; wrapper.read_data_set_chunksize(CHUNK_SIZE)?;
wrapper.write_data_set_chunksize(CHUNK_SIZE)?; wrapper.write_data_set_chunksize(CHUNK_SIZE)?;

View File

@ -1,4 +1,4 @@
use super::{error::Error, ftdi::FtdiDevice}; use super::{error::Error, ftdi::FtdiDevice, serial::SerialDevice};
use std::{ use std::{
collections::VecDeque, collections::VecDeque,
fmt::Display, fmt::Display,
@ -75,34 +75,58 @@ pub trait Backend {
fn flush(&mut self) -> std::io::Result<()>; fn flush(&mut self) -> std::io::Result<()>;
fn reset(&mut self) -> std::io::Result<()> { fn discard_input(&mut self) -> std::io::Result<()> {
Ok(()) Ok(())
} }
fn discard_output(&mut self) -> std::io::Result<()> {
Ok(())
}
fn set_dtr(&mut self, _value: bool) -> std::io::Result<()> {
Ok(())
}
fn read_dsr(&mut self) -> std::io::Result<bool> {
Ok(false)
}
fn close(&mut self) {} fn close(&mut self) {}
fn purge_incoming_data(&mut self) -> std::io::Result<()> { fn reset(&mut self) -> std::io::Result<()> {
self.discard_output()?;
let timeout = Instant::now(); let timeout = Instant::now();
self.set_dtr(true)?;
loop { loop {
match self.read(&mut vec![0; 1]) { if self.read_dsr()? {
Ok(length) => match length { break;
0 => return Ok(()),
_ => {}
},
Err(error) => match error.kind() {
std::io::ErrorKind::Interrupted
| std::io::ErrorKind::TimedOut
| std::io::ErrorKind::WouldBlock => return Ok(()),
_ => return Err(error),
},
} }
if timeout.elapsed() >= RESET_TIMEOUT { if timeout.elapsed() > RESET_TIMEOUT {
return Err(std::io::Error::new( return Err(std::io::Error::new(
std::io::ErrorKind::TimedOut, std::io::ErrorKind::TimedOut,
"SC64 read buffer flush took too long", "Couldn't reset SC64 device (on)",
)); ));
} }
} }
self.discard_input()?;
let timeout = Instant::now();
self.set_dtr(false)?;
loop {
if !self.read_dsr()? {
break;
}
if timeout.elapsed() > RESET_TIMEOUT {
return Err(std::io::Error::new(
std::io::ErrorKind::TimedOut,
"Couldn't reset SC64 device (off)",
));
}
}
Ok(())
} }
fn try_read_exact(&mut self, buffer: &mut [u8], block: bool) -> std::io::Result<Option<()>> { fn try_read_exact(&mut self, buffer: &mut [u8], block: bool) -> std::io::Result<Option<()>> {
@ -196,7 +220,7 @@ pub trait Backend {
} }
pub struct SerialBackend { pub struct SerialBackend {
device: serial2::SerialPort, device: SerialDevice,
} }
impl Backend for SerialBackend { impl Backend for SerialBackend {
@ -212,47 +236,32 @@ impl Backend for SerialBackend {
self.device.flush() self.device.flush()
} }
fn reset(&mut self) -> std::io::Result<()> { fn discard_input(&mut self) -> std::io::Result<()> {
self.device.set_dtr(true)?; self.device.discard_input()
let timeout = Instant::now(); }
loop {
self.device.discard_buffers()?;
if self.device.read_dsr()? {
break;
}
if timeout.elapsed() > RESET_TIMEOUT {
return Err(std::io::Error::new(
std::io::ErrorKind::TimedOut,
"Couldn't reset SC64 device (on)",
));
}
}
self.purge_incoming_data()?; fn discard_output(&mut self) -> std::io::Result<()> {
self.device.discard_output()
}
self.device.set_dtr(false)?; fn set_dtr(&mut self, value: bool) -> std::io::Result<()> {
let timeout = Instant::now(); self.device.set_dtr(value)
loop { }
if !self.device.read_dsr()? {
break;
}
if timeout.elapsed() > RESET_TIMEOUT {
return Err(std::io::Error::new(
std::io::ErrorKind::TimedOut,
"Couldn't reset SC64 device (off)",
));
}
}
Ok(()) fn read_dsr(&mut self) -> std::io::Result<bool> {
self.device.read_dsr()
} }
} }
fn new_serial_backend(port: &str) -> std::io::Result<SerialBackend> { fn new_serial_backend(port: &str) -> std::io::Result<SerialBackend> {
let mut serial = serial2::SerialPort::open(port, 115_200)?; Ok(SerialBackend {
serial.set_read_timeout(POLL_TIMEOUT)?; device: SerialDevice::new(
serial.set_write_timeout(WRITE_TIMEOUT)?; port,
Ok(SerialBackend { device: serial }) Some(POLL_TIMEOUT),
Some(READ_TIMEOUT),
Some(WRITE_TIMEOUT),
)?,
})
} }
struct FtdiBackend { struct FtdiBackend {
@ -272,40 +281,20 @@ impl Backend for FtdiBackend {
self.device.flush() self.device.flush()
} }
fn reset(&mut self) -> std::io::Result<()> { fn discard_input(&mut self) -> std::io::Result<()> {
self.device.discard_output()?; self.device.discard_input()
}
let timeout = Instant::now(); fn discard_output(&mut self) -> std::io::Result<()> {
self.device.set_dtr(true)?; self.device.discard_output()
loop { }
if self.device.read_dsr()? {
break;
}
if timeout.elapsed() > RESET_TIMEOUT {
return Err(std::io::Error::new(
std::io::ErrorKind::TimedOut,
"Couldn't reset SC64 device (on)",
));
}
}
self.device.discard_input()?; fn set_dtr(&mut self, value: bool) -> std::io::Result<()> {
self.device.set_dtr(value)
}
let timeout = Instant::now(); fn read_dsr(&mut self) -> std::io::Result<bool> {
self.device.set_dtr(false)?; self.device.read_dsr()
loop {
if !self.device.read_dsr()? {
break;
}
if timeout.elapsed() > RESET_TIMEOUT {
return Err(std::io::Error::new(
std::io::ErrorKind::TimedOut,
"Couldn't reset SC64 device (off)",
));
}
}
Ok(())
} }
} }
@ -580,18 +569,14 @@ pub fn list_local_devices() -> Result<Vec<DeviceInfo>, Error> {
} }
} }
if let Ok(list) = serialport::available_ports() { if let Ok(list) = SerialDevice::list(SC64_VID, SC64_PID) {
for device in list.into_iter() { for device in list.into_iter() {
if let serialport::SerialPortType::UsbPort(i) = device.port_type { if device.serial.starts_with(SC64_SID) {
if let Some(serial) = i.serial_number { devices.push(DeviceInfo {
if i.vid == SC64_VID && i.pid == SC64_PID && serial.starts_with(SC64_SID) { backend: BackendType::Serial,
devices.push(DeviceInfo { port: format!("{SERIAL_PREFIX}{}", device.port),
backend: BackendType::Serial, serial: device.serial,
port: format!("{SERIAL_PREFIX}{}", device.port_name), });
serial,
});
}
}
} }
} }
} }

View File

@ -3,6 +3,7 @@ mod error;
pub mod firmware; pub mod firmware;
mod ftdi; mod ftdi;
mod link; mod link;
mod serial;
pub mod server; pub mod server;
mod time; mod time;
mod types; mod types;

View File

@ -0,0 +1,155 @@
pub struct DeviceInfo {
pub port: String,
pub serial: String,
}
pub struct SerialDevice {
serial: serial2::SerialPort,
unclog_buffer: std::collections::VecDeque<u8>,
poll_timeout: std::time::Duration,
read_timeout: std::time::Duration,
write_timeout: std::time::Duration,
}
impl SerialDevice {
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 WRITE_CHUNK_TIMEOUT: std::time::Duration = std::time::Duration::from_millis(100);
const BUFFER_SIZE: usize = 16 * 1024;
pub fn new(
port: &str,
poll_timeout: Option<std::time::Duration>,
read_timeout: Option<std::time::Duration>,
write_timeout: Option<std::time::Duration>,
) -> std::io::Result<Self> {
let mut device = Self {
serial: serial2::SerialPort::open(port, 115_200)?,
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),
};
device.serial.set_read_timeout(device.poll_timeout)?;
device.serial.set_write_timeout(Self::WRITE_CHUNK_TIMEOUT)?;
Ok(device)
}
pub fn list(vendor: u16, product: u16) -> std::io::Result<Vec<DeviceInfo>> {
let mut devices = vec![];
for port in serialport::available_ports()? {
if let serialport::SerialPortType::UsbPort(info) = port.port_type {
if info.vid == vendor && info.pid == product && info.serial_number.is_some() {
devices.push(DeviceInfo {
port: port.port_name,
serial: info.serial_number.unwrap(),
})
}
}
}
Ok(devices)
}
pub fn set_dtr(&mut self, value: bool) -> std::io::Result<()> {
self.serial.set_dtr(value)
}
pub fn read_dsr(&mut self) -> std::io::Result<bool> {
self.serial.read_dsr()
}
pub fn discard_input(&mut self) -> std::io::Result<()> {
let timeout = std::time::Instant::now();
self.serial.discard_input_buffer()?;
loop {
match self.serial.read(&mut vec![0u8; Self::BUFFER_SIZE]) {
Ok(_) => {}
Err(error) => match error.kind() {
std::io::ErrorKind::Interrupted
| std::io::ErrorKind::TimedOut
| std::io::ErrorKind::WouldBlock => {
return Ok(());
}
_ => return Err(error),
},
};
if timeout.elapsed() > self.read_timeout {
return Err(std::io::ErrorKind::TimedOut.into());
}
}
}
pub fn discard_output(&mut self) -> std::io::Result<()> {
self.serial.discard_output_buffer()
}
fn unclog_pipe(&mut self) -> std::io::Result<()> {
let mut buffer = vec![0u8; Self::BUFFER_SIZE];
let read = match self.serial.read(&mut buffer) {
Ok(read) => read,
Err(error) => match error.kind() {
std::io::ErrorKind::Interrupted
| std::io::ErrorKind::TimedOut
| std::io::ErrorKind::WouldBlock => 0,
_ => return Err(error),
},
};
self.unclog_buffer.extend(buffer[0..read].iter());
Ok(())
}
}
impl std::io::Read for SerialDevice {
fn read(&mut self, buffer: &mut [u8]) -> std::io::Result<usize> {
if buffer.is_empty() {
Err(std::io::ErrorKind::InvalidInput.into())
} else if self.unclog_buffer.is_empty() {
self.serial.read(buffer)
} else {
for (index, item) in buffer.iter_mut().enumerate() {
if let Some(byte) = self.unclog_buffer.pop_front() {
*item = byte;
} else {
return Ok(index);
}
}
Ok(buffer.len())
}
}
}
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) {
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 {
return Err(std::io::ErrorKind::TimedOut.into());
}
}
}
fn flush(&mut self) -> std::io::Result<()> {
let timeout = std::time::Instant::now();
loop {
match self.serial.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 {
return Err(std::io::ErrorKind::TimedOut.into());
}
}
}
}