use std::io;
use std::path::{Path, PathBuf};
use std::time::Duration;
use crate::error::{Error, Result};
pub(crate) mod codec;
pub(crate) mod link;
pub(crate) mod os;
pub(crate) mod probe;
#[cfg(feature = "trace")]
mod trace;
#[cfg(test)]
pub(crate) mod fake;
#[cfg(unix)]
pub(crate) mod unix;
#[cfg(windows)]
mod windows;
pub(crate) fn monotonic_ns() -> u64 {
#[cfg(windows)]
let now = windows::monotonic_ns();
#[cfg(unix)]
let now = unix::monotonic_ns();
now
}
pub(crate) const READ_SLICE: Duration = Duration::from_millis(100);
#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) struct PortUsb {
pub(crate) vid: u16,
pub(crate) pid: u16,
pub(crate) serial: Option<String>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) struct Candidate {
pub(crate) path: PathBuf,
pub(crate) usb: Option<PortUsb>,
}
pub(crate) trait System: Send + Sync + 'static {
fn ports(&self) -> Vec<Candidate>;
fn open(&self, path: &Path, baud: u32) -> Result<Box<dyn Port>>;
}
pub(crate) trait Port: Send + Sync {
fn read(&self, buffer: &mut [u8]) -> io::Result<usize>;
fn write_all(&self, bytes: &[u8]) -> io::Result<()>;
fn wake(&self);
fn retire(&self);
}
pub(crate) fn is_ch343(usb: &PortUsb) -> bool {
(usb.vid, usb.pid) == (0x1A86, 0x55D3)
}
pub(crate) fn is_ysc(usb: &PortUsb) -> bool {
(usb.vid, usb.pid) == (0x1A86, 0xFE0C)
}
pub(crate) fn trustworthy_serial(serial: &str) -> bool {
serial.len() > 2 && !serial.contains('&')
}
pub(crate) fn open_error(path: &Path, error: io::Error) -> Error {
let port = path.display().to_string();
match classify_open(error.raw_os_error()) {
Some(OpenFailure::Busy) => Error::PortBusy { port },
Some(OpenFailure::Forbidden) => Error::PermissionDenied { port },
Some(OpenFailure::Missing) => Error::NotFound { tried: vec![port] },
None => Error::Io(error),
}
}
enum OpenFailure {
Busy,
#[cfg_attr(windows, allow(dead_code))]
Forbidden,
Missing,
}
#[cfg(windows)]
fn classify_open(code: Option<i32>) -> Option<OpenFailure> {
match code? {
5 => Some(OpenFailure::Busy),
2 | 3 => Some(OpenFailure::Missing),
_ => None,
}
}
#[cfg(unix)]
fn classify_open(code: Option<i32>) -> Option<OpenFailure> {
match code? {
libc::EBUSY | libc::EWOULDBLOCK => Some(OpenFailure::Busy),
libc::EACCES => Some(OpenFailure::Forbidden),
libc::ENOENT => Some(OpenFailure::Missing),
_ => None,
}
}
#[cfg(test)]
mod tests;