mirror of
https://github.com/Polprzewodnikowy/SummerCart64.git
synced 2024-11-21 21:49:15 +01:00
serial refactor
This commit is contained in:
parent
6d460e83bd
commit
db9e9fa515
@ -45,12 +45,26 @@ module memory_dma (
|
||||
|
||||
logic dma_start;
|
||||
logic dma_stop;
|
||||
logic dma_stop_requested;
|
||||
|
||||
always_comb begin
|
||||
dma_start = dma_scb.start && !dma_scb.stop && !dma_scb.busy;
|
||||
dma_stop = dma_scb.stop;
|
||||
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
|
||||
|
||||
@ -197,7 +211,7 @@ module memory_dma (
|
||||
remaining <= dma_scb.transfer_length;
|
||||
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;
|
||||
end
|
||||
end
|
||||
|
36
sw/deployer/Cargo.lock
generated
36
sw/deployer/Cargo.lock
generated
@ -218,9 +218,9 @@ checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.101"
|
||||
version = "1.0.103"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac367972e516d45567c7eafc73d24e1c193dcf200a8d94e9db7b3d38b349572d"
|
||||
checksum = "2755ff20a1d93490d26ba33a6f092a38a508398a5320df5d4b3014fcccce9410"
|
||||
dependencies = [
|
||||
"jobserver",
|
||||
"libc",
|
||||
@ -265,9 +265,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.5.7"
|
||||
version = "4.5.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5db83dced34638ad474f39f250d7fea9598bdd239eaced1bdf45d597da0f433f"
|
||||
checksum = "84b3edb18336f4df585bc9aa31dd99c036dfa5dc5e9a2939a722a188f3a8970d"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
"clap_derive",
|
||||
@ -284,9 +284,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.5.7"
|
||||
version = "4.5.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f7e204572485eb3fbf28f871612191521df159bc3e15a9f5064c66dba3a8c05f"
|
||||
checksum = "c1c09dd5ada6c6c78075d6fd0da3f90d8080651e2d6cc8eb2f1aaa4034ced708"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
@ -296,9 +296,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap_derive"
|
||||
version = "4.5.5"
|
||||
version = "4.5.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c780290ccf4fb26629baa7a1081e68ced113f1d3ec302fa5948f1c381ebf06c6"
|
||||
checksum = "2bac35c6dafb060fd4d275d9a4ffae97917c13a6327903a8be2153cd964f7085"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
@ -799,9 +799,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.21"
|
||||
version = "0.4.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c"
|
||||
checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
|
||||
|
||||
[[package]]
|
||||
name = "loop9"
|
||||
@ -906,9 +906,9 @@ checksum = "0676bb32a98c1a483ce53e500a81ad9c3d5b3f7c920c28c24e9cb0980d0b5bc8"
|
||||
|
||||
[[package]]
|
||||
name = "num-bigint"
|
||||
version = "0.4.5"
|
||||
version = "0.4.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c165a9ab64cf766f73521c0dd2cfdff64f488b8f0b3e621face3462d3db536d7"
|
||||
checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9"
|
||||
dependencies = [
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
@ -1196,9 +1196,9 @@ checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b"
|
||||
|
||||
[[package]]
|
||||
name = "rgb"
|
||||
version = "0.8.37"
|
||||
version = "0.8.40"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "05aaa8004b64fd573fc9d002f4e632d51ad4f026c2b5ba95fcb6c2f32c2c47d8"
|
||||
checksum = "a7439be6844e40133eda024efd85bf07f59d0dd2f59b10c00dd6cfb92cc5c741"
|
||||
dependencies = [
|
||||
"bytemuck",
|
||||
]
|
||||
@ -1291,9 +1291,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serialport"
|
||||
version = "4.3.0"
|
||||
version = "4.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f5a15d0be940df84846264b09b51b10b931fb2f275becb80934e3568a016828"
|
||||
checksum = "de7c4f0cce25b9b3518eea99618112f9ee4549f974480c8f43d3c06f03c131a0"
|
||||
dependencies = [
|
||||
"bitflags 2.6.0",
|
||||
"cfg-if",
|
||||
@ -1452,9 +1452,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "unescaper"
|
||||
version = "0.1.4"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0adf6ad32eb5b3cadff915f7b770faaac8f7ff0476633aa29eb0d9584d889d34"
|
||||
checksum = "c878a167baa8afd137494101a688ef8c67125089ff2249284bd2b5f9bfedb815"
|
||||
dependencies = [
|
||||
"thiserror",
|
||||
]
|
||||
|
@ -8,7 +8,7 @@ documentation = "https://github.com/Polprzewodnikowy/SummerCart64"
|
||||
|
||||
[dependencies]
|
||||
chrono = "0.4.38"
|
||||
clap = { version = "4.5.7", features = ["derive"] }
|
||||
clap = { version = "4.5.8", features = ["derive"] }
|
||||
clap-num = "1.1.1"
|
||||
colored = "2.1.0"
|
||||
crc32fast = "1.4.2"
|
||||
@ -24,7 +24,7 @@ panic-message = "0.3.0"
|
||||
rand = "0.8.5"
|
||||
rust-ini = "0.18.0"
|
||||
serial2 = "0.2.26"
|
||||
serialport = "4.3.0"
|
||||
serialport = "4.4.0"
|
||||
|
||||
[profile.release]
|
||||
lto = true
|
||||
|
@ -36,24 +36,24 @@ struct Wrapper {
|
||||
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_CHUNKSIZE: usize = 4096;
|
||||
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<Wrapper> {
|
||||
) -> std::io::Result<Self> {
|
||||
let context = unsafe { libftdi1_sys::ftdi_new() };
|
||||
if context.is_null() {
|
||||
return Err(std::io::ErrorKind::OutOfMemory.into());
|
||||
}
|
||||
let mut wrapper = Wrapper {
|
||||
let mut wrapper = Self {
|
||||
context,
|
||||
unclog_buffer: std::collections::VecDeque::new(),
|
||||
write_buffer: vec![],
|
||||
read_timeout: Wrapper::DEFAULT_RW_TIMEOUT,
|
||||
write_timeout: Wrapper::DEFAULT_RW_TIMEOUT,
|
||||
read_chunksize: Wrapper::DEFAULT_CHUNKSIZE,
|
||||
write_chunksize: Wrapper::DEFAULT_CHUNKSIZE,
|
||||
read_timeout: Self::DEFAULT_RW_TIMEOUT,
|
||||
write_timeout: Self::DEFAULT_RW_TIMEOUT,
|
||||
read_chunksize: 4096,
|
||||
write_chunksize: 4096,
|
||||
};
|
||||
wrapper.set_timeouts(read_timeout, write_timeout)?;
|
||||
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>> {
|
||||
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 devices = unsafe {
|
||||
@ -152,8 +152,8 @@ impl Wrapper {
|
||||
read_timeout: Option<std::time::Duration>,
|
||||
write_timeout: Option<std::time::Duration>,
|
||||
) -> std::io::Result<()> {
|
||||
let read_timeout = read_timeout.unwrap_or(Wrapper::DEFAULT_RW_TIMEOUT);
|
||||
let write_timeout = write_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(Self::DEFAULT_RW_TIMEOUT);
|
||||
unsafe {
|
||||
(*self.context).usb_read_timeout = i32::try_from(read_timeout.as_millis())
|
||||
.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<()> {
|
||||
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)?;
|
||||
match unsafe { libftdi1_sys::ftdi_set_latency_timer(self.context, latency) } {
|
||||
0 => Ok(()),
|
||||
@ -320,7 +320,9 @@ impl Wrapper {
|
||||
match self.read(&mut vec![0u8; self.read_chunksize]) {
|
||||
Ok(_) => {}
|
||||
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 Err(error),
|
||||
@ -369,7 +371,7 @@ impl Wrapper {
|
||||
Vec::from(buffer).as_mut_ptr(),
|
||||
buffer.len() as i32,
|
||||
&mut transferred,
|
||||
100,
|
||||
Self::WRITE_CHUNK_TIMEOUT.as_millis() as u32,
|
||||
)
|
||||
};
|
||||
*written = transferred as usize;
|
||||
@ -469,7 +471,7 @@ impl FtdiDevice {
|
||||
wrapper.set_module_detach_mode(ModuleDetachMode::AutoDetachReattach);
|
||||
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.write_data_set_chunksize(CHUNK_SIZE)?;
|
||||
|
@ -1,4 +1,4 @@
|
||||
use super::{error::Error, ftdi::FtdiDevice};
|
||||
use super::{error::Error, ftdi::FtdiDevice, serial::SerialDevice};
|
||||
use std::{
|
||||
collections::VecDeque,
|
||||
fmt::Display,
|
||||
@ -75,34 +75,58 @@ pub trait Backend {
|
||||
|
||||
fn flush(&mut self) -> std::io::Result<()>;
|
||||
|
||||
fn reset(&mut self) -> std::io::Result<()> {
|
||||
fn discard_input(&mut self) -> std::io::Result<()> {
|
||||
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 purge_incoming_data(&mut self) -> std::io::Result<()> {
|
||||
fn reset(&mut self) -> std::io::Result<()> {
|
||||
self.discard_output()?;
|
||||
|
||||
let timeout = Instant::now();
|
||||
self.set_dtr(true)?;
|
||||
loop {
|
||||
match self.read(&mut vec![0; 1]) {
|
||||
Ok(length) => match length {
|
||||
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 self.read_dsr()? {
|
||||
break;
|
||||
}
|
||||
if timeout.elapsed() >= RESET_TIMEOUT {
|
||||
if timeout.elapsed() > RESET_TIMEOUT {
|
||||
return Err(std::io::Error::new(
|
||||
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<()>> {
|
||||
@ -196,7 +220,7 @@ pub trait Backend {
|
||||
}
|
||||
|
||||
pub struct SerialBackend {
|
||||
device: serial2::SerialPort,
|
||||
device: SerialDevice,
|
||||
}
|
||||
|
||||
impl Backend for SerialBackend {
|
||||
@ -212,47 +236,32 @@ impl Backend for SerialBackend {
|
||||
self.device.flush()
|
||||
}
|
||||
|
||||
fn reset(&mut self) -> std::io::Result<()> {
|
||||
self.device.set_dtr(true)?;
|
||||
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)",
|
||||
));
|
||||
}
|
||||
}
|
||||
fn discard_input(&mut self) -> std::io::Result<()> {
|
||||
self.device.discard_input()
|
||||
}
|
||||
|
||||
self.purge_incoming_data()?;
|
||||
fn discard_output(&mut self) -> std::io::Result<()> {
|
||||
self.device.discard_output()
|
||||
}
|
||||
|
||||
self.device.set_dtr(false)?;
|
||||
let timeout = Instant::now();
|
||||
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)",
|
||||
));
|
||||
}
|
||||
}
|
||||
fn set_dtr(&mut self, value: bool) -> std::io::Result<()> {
|
||||
self.device.set_dtr(value)
|
||||
}
|
||||
|
||||
Ok(())
|
||||
fn read_dsr(&mut self) -> std::io::Result<bool> {
|
||||
self.device.read_dsr()
|
||||
}
|
||||
}
|
||||
|
||||
fn new_serial_backend(port: &str) -> std::io::Result<SerialBackend> {
|
||||
let mut serial = serial2::SerialPort::open(port, 115_200)?;
|
||||
serial.set_read_timeout(POLL_TIMEOUT)?;
|
||||
serial.set_write_timeout(WRITE_TIMEOUT)?;
|
||||
Ok(SerialBackend { device: serial })
|
||||
Ok(SerialBackend {
|
||||
device: SerialDevice::new(
|
||||
port,
|
||||
Some(POLL_TIMEOUT),
|
||||
Some(READ_TIMEOUT),
|
||||
Some(WRITE_TIMEOUT),
|
||||
)?,
|
||||
})
|
||||
}
|
||||
|
||||
struct FtdiBackend {
|
||||
@ -272,40 +281,20 @@ impl Backend for FtdiBackend {
|
||||
self.device.flush()
|
||||
}
|
||||
|
||||
fn reset(&mut self) -> std::io::Result<()> {
|
||||
self.device.discard_output()?;
|
||||
fn discard_input(&mut self) -> std::io::Result<()> {
|
||||
self.device.discard_input()
|
||||
}
|
||||
|
||||
let timeout = Instant::now();
|
||||
self.device.set_dtr(true)?;
|
||||
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)",
|
||||
));
|
||||
}
|
||||
}
|
||||
fn discard_output(&mut self) -> std::io::Result<()> {
|
||||
self.device.discard_output()
|
||||
}
|
||||
|
||||
self.device.discard_input()?;
|
||||
fn set_dtr(&mut self, value: bool) -> std::io::Result<()> {
|
||||
self.device.set_dtr(value)
|
||||
}
|
||||
|
||||
let timeout = Instant::now();
|
||||
self.device.set_dtr(false)?;
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
@ -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() {
|
||||
if let serialport::SerialPortType::UsbPort(i) = device.port_type {
|
||||
if let Some(serial) = i.serial_number {
|
||||
if i.vid == SC64_VID && i.pid == SC64_PID && serial.starts_with(SC64_SID) {
|
||||
devices.push(DeviceInfo {
|
||||
backend: BackendType::Serial,
|
||||
port: format!("{SERIAL_PREFIX}{}", device.port_name),
|
||||
serial,
|
||||
});
|
||||
}
|
||||
}
|
||||
if device.serial.starts_with(SC64_SID) {
|
||||
devices.push(DeviceInfo {
|
||||
backend: BackendType::Serial,
|
||||
port: format!("{SERIAL_PREFIX}{}", device.port),
|
||||
serial: device.serial,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ mod error;
|
||||
pub mod firmware;
|
||||
mod ftdi;
|
||||
mod link;
|
||||
mod serial;
|
||||
pub mod server;
|
||||
mod time;
|
||||
mod types;
|
||||
|
155
sw/deployer/src/sc64/serial.rs
Normal file
155
sw/deployer/src/sc64/serial.rs
Normal 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());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user