use crate::capture::Direction;
use crate::resolve::PortInfo;
pub trait Decoder: Send {
fn name(&self) -> &'static str;
fn decode(&mut self, dir: Direction, payload: &[u8], out: &mut Vec<u8>);
}
mod cdc_acm;
mod ch340;
mod ftdi;
mod pl2303;
pub use cdc_acm::CdcAcm;
pub use ch340::Ch340;
pub use ftdi::Ftdi;
pub use pl2303::Pl2303;
const VID_FTDI: u16 = 0x0403;
const VID_PROLIFIC: u16 = 0x067b;
const VID_WCH: u16 = 0x1a86;
#[derive(Default, Copy, Clone, Debug)]
pub struct Options {
pub ftdi_mps_override: Option<u16>,
}
pub fn select(info: &PortInfo, opts: Options) -> Box<dyn Decoder> {
match info.vid {
VID_FTDI => {
let mps = opts
.ftdi_mps_override
.or(info.bulk_in_max_packet)
.unwrap_or_else(|| ftdi_mps_for_pid(info.pid));
Box::new(Ftdi::new(mps))
}
VID_WCH => Box::new(Ch340),
VID_PROLIFIC => Box::new(Pl2303),
_ => Box::new(CdcAcm),
}
}
fn ftdi_mps_for_pid(pid: u16) -> u16 {
match pid {
0x6001 => 64,
0x6010 => 512,
0x6011 => 512,
0x6014 => 512,
0x6015 => 64,
_ => 64,
}
}
#[cfg(test)]
mod tests {
use super::*;
fn info(vid: u16, pid: u16, mps: Option<u16>) -> PortInfo {
PortInfo {
bus: 0,
devnum: 1,
vid,
pid,
interface_number: None,
bulk_in_ep: None,
bulk_out_ep: None,
bulk_in_max_packet: mps,
}
}
#[test]
fn cli_override_wins_over_resolver_value() {
let i = info(VID_FTDI, 0x6001, Some(64));
let opts = Options {
ftdi_mps_override: Some(512),
};
assert_eq!(select(&i, opts).name(), "ftdi");
}
#[test]
fn resolver_value_wins_over_pid_heuristic() {
let i = info(VID_FTDI, 0x6010, Some(64));
let _ = select(&i, Options::default());
}
#[test]
fn pid_heuristic_table() {
assert_eq!(ftdi_mps_for_pid(0x6001), 64);
assert_eq!(ftdi_mps_for_pid(0x6010), 512);
assert_eq!(ftdi_mps_for_pid(0x6011), 512);
assert_eq!(ftdi_mps_for_pid(0x6014), 512);
assert_eq!(ftdi_mps_for_pid(0x6015), 64);
assert_eq!(ftdi_mps_for_pid(0xffff), 64); }
#[test]
fn non_ftdi_vendor_uses_cdc_acm() {
let i = info(0x2341, 0x0001, None);
assert_eq!(select(&i, Options::default()).name(), "cdc-acm");
}
#[test]
fn ch340_vid_selects_ch340() {
let i = info(VID_WCH, 0x7523, None);
assert_eq!(select(&i, Options::default()).name(), "ch340");
}
#[test]
fn pl2303_vid_selects_pl2303() {
let i = info(VID_PROLIFIC, 0x2303, None);
assert_eq!(select(&i, Options::default()).name(), "pl2303");
}
#[test]
fn ch340_passthrough_in_and_out() {
let mut d = Ch340;
let mut out = Vec::new();
d.decode(Direction::In, b"hello", &mut out);
d.decode(Direction::Out, b" world", &mut out);
assert_eq!(out, b"hello world");
}
#[test]
fn pl2303_passthrough_in_and_out() {
let mut d = Pl2303;
let mut out = Vec::new();
d.decode(Direction::In, b"hello", &mut out);
d.decode(Direction::Out, b" world", &mut out);
assert_eq!(out, b"hello world");
}
}