#![allow(unused_imports)]
use std::time::Duration;
use crate::constants::*;
use crate::error::{Result, UsbSidError};
pub(crate) trait Transport: Send {
fn send(&mut self, data: &[u8]) -> Result<usize>;
fn recv(&mut self, buf: &mut [u8]) -> Result<usize>;
}
#[cfg(feature = "usb")]
pub(crate) mod usb {
use super::*;
use rusb::{Context, DeviceHandle, UsbContext};
pub struct UsbTransport {
pub handle: DeviceHandle<Context>,
#[allow(dead_code)]
ctx: Context, }
impl UsbTransport {
pub fn open() -> Result<Self> {
let ctx = Context::new().map_err(UsbSidError::Usb)?;
let handle = ctx.open_device_with_vid_pid(VENDOR_ID, PRODUCT_ID).ok_or(
UsbSidError::DeviceNotFound {
vid: VENDOR_ID,
pid: PRODUCT_ID,
},
)?;
for if_num in 0..2u8 {
#[cfg(target_os = "linux")]
{
if handle.kernel_driver_active(if_num).unwrap_or(false) {
let _ = handle.detach_kernel_driver(if_num);
}
}
#[cfg(target_os = "macos")]
{
let _ = handle.set_auto_detach_kernel_driver(true);
}
handle.claim_interface(if_num)?;
}
handle.write_control(
0x21,
0x22,
ACM_CTRL_DTR | ACM_CTRL_RTS,
0,
&[],
Duration::from_secs(1),
)?;
handle.write_control(0x21, 0x20, 0, 0, &LINE_ENCODING, Duration::from_secs(1))?;
Ok(Self { handle, ctx })
}
}
impl Transport for UsbTransport {
fn send(&mut self, data: &[u8]) -> Result<usize> {
self.handle
.write_bulk(EP_OUT_ADDR, data, Duration::from_secs(1))
.map_err(UsbSidError::Usb)
}
fn recv(&mut self, buf: &mut [u8]) -> Result<usize> {
self.handle
.read_bulk(EP_IN_ADDR, buf, Duration::from_secs(1))
.map_err(UsbSidError::Usb)
}
}
}
#[cfg(feature = "serial")]
pub(crate) mod serial {
use super::*;
use serialport::SerialPort;
const BAUD_RATE: u32 = 9_000_000;
const TIMEOUT: Duration = Duration::from_secs(1);
pub struct SerialTransport {
port: Box<dyn SerialPort>,
}
impl SerialTransport {
pub fn open(port_name: &str) -> Result<Self> {
let port = serialport::new(port_name, BAUD_RATE)
.timeout(TIMEOUT)
.data_bits(serialport::DataBits::Eight)
.parity(serialport::Parity::None)
.stop_bits(serialport::StopBits::One)
.flow_control(serialport::FlowControl::None)
.open()
.map_err(|e| UsbSidError::Thread(format!("Serial open failed: {e}")))?;
Ok(Self { port })
}
pub fn open_auto() -> Result<Self> {
let ports = serialport::available_ports()
.map_err(|e| UsbSidError::Thread(format!("Port enumeration failed: {e}")))?;
for port in &ports {
if let serialport::SerialPortType::UsbPort(info) = &port.port_type {
if info.vid == VENDOR_ID && info.pid == PRODUCT_ID {
log::debug!(
"[USBSID] Found device on {} (VID={:04X} PID={:04X})",
port.port_name,
info.vid,
info.pid
);
return Self::open(&port.port_name);
}
}
}
Err(UsbSidError::DeviceNotFound {
vid: VENDOR_ID,
pid: PRODUCT_ID,
})
}
#[allow(dead_code)]
pub fn try_clone(&self) -> Result<Self> {
let port = self
.port
.try_clone()
.map_err(|e| UsbSidError::Thread(format!("Serial clone failed: {e}")))?;
Ok(Self { port })
}
}
impl Transport for SerialTransport {
fn send(&mut self, data: &[u8]) -> Result<usize> {
use std::io::Write;
self.port
.write(data)
.map_err(|e| UsbSidError::Thread(format!("Serial write failed: {e}")))
}
fn recv(&mut self, buf: &mut [u8]) -> Result<usize> {
use std::io::Read;
self.port
.read(buf)
.map_err(|e| UsbSidError::Thread(format!("Serial read failed: {e}")))
}
}
}