#[cfg(all(unix, feature = "libusb"))]
use crate::server::usb::descriptors::{ControlLineState, LineCoding};
#[cfg(all(unix, feature = "libusb"))]
use tokio::sync::mpsc;
#[cfg(all(unix, feature = "libusb"))]
use tracing::{debug, trace, warn};
#[cfg(all(unix, feature = "libusb"))]
mod cdc_request {
pub const SET_LINE_CODING: u8 = 0x20;
pub const GET_LINE_CODING: u8 = 0x21;
pub const SET_CONTROL_LINE_STATE: u8 = 0x22;
pub const SEND_BREAK: u8 = 0x23;
}
#[cfg(all(unix, feature = "libusb"))]
mod request_type {
pub const CLASS_INTERFACE_OUT: u8 = 0b0010_0001;
pub const CLASS_INTERFACE_IN: u8 = 0b1010_0001;
}
#[cfg(all(unix, feature = "libusb"))]
mod cdc_notification {
pub const REQUEST_TYPE: u8 = 0b1010_0001;
pub const SERIAL_STATE: u8 = 0x20;
pub const UART_STATE_OVERRUN: u16 = 1 << 6;
}
#[cfg(all(unix, feature = "libusb"))]
const MAX_TX_BUFFER: usize = 64 * 1024;
#[cfg(all(unix, feature = "libusb"))]
#[derive(Debug)]
pub struct UsbCdcAcmSerialHandler {
tx_buffer: Vec<u8>,
rx_tx: mpsc::UnboundedSender<Vec<u8>>,
line_coding: LineCoding,
control_lines: ControlLineState,
notifications: std::collections::VecDeque<Vec<u8>>,
}
#[cfg(all(unix, feature = "libusb"))]
impl UsbCdcAcmSerialHandler {
pub fn new(rx_tx: mpsc::UnboundedSender<Vec<u8>>) -> Self {
Self {
tx_buffer: Vec::new(),
rx_tx,
line_coding: LineCoding::default_115200_8n1(),
control_lines: ControlLineState::from_value(0),
notifications: std::collections::VecDeque::new(),
}
}
pub fn queue_serial_state_overrun(&mut self) {
let state = cdc_notification::UART_STATE_OVERRUN;
let mut message = Vec::with_capacity(10);
message.push(cdc_notification::REQUEST_TYPE);
message.push(cdc_notification::SERIAL_STATE);
message.extend_from_slice(&0u16.to_le_bytes()); message.extend_from_slice(&0u16.to_le_bytes()); message.extend_from_slice(&2u16.to_le_bytes()); message.extend_from_slice(&state.to_le_bytes());
debug!("USB serial queueing SERIAL_STATE notification with bOverRun set");
self.notifications.push_back(message);
}
#[allow(dead_code)]
pub fn pending_notifications(&self) -> usize {
self.notifications.len()
}
pub fn endpoints() -> Vec<usbip::UsbEndpoint> {
usbip::cdc::UsbCdcAcmHandler::endpoints()
}
pub fn queue_tx(&mut self, data: &[u8]) -> usize {
let room = MAX_TX_BUFFER.saturating_sub(self.tx_buffer.len());
let taken = data.len().min(room);
self.tx_buffer.extend_from_slice(&data[..taken]);
let dropped = data.len() - taken;
if dropped > 0 {
warn!(
"USB serial transmit buffer is full at {} byte(s); dropped {} of {} queued",
self.tx_buffer.len(),
dropped,
data.len()
);
} else {
trace!("USB serial queue {} byte(s) for the host", data.len());
}
dropped
}
pub fn set_line_coding(&mut self, line_coding: LineCoding) {
debug!(
"USB serial line coding set by handler: {} baud, {} data bits, parity {}, stop {}",
line_coding.baud_rate, line_coding.data_bits, line_coding.parity, line_coding.stop_bits
);
self.line_coding = line_coding;
}
pub fn line_coding(&self) -> LineCoding {
self.line_coding
}
#[allow(dead_code)]
pub fn pending_tx(&self) -> usize {
self.tx_buffer.len()
}
fn handle_control(
&mut self,
setup: usbip::SetupPacket,
req: &[u8],
) -> std::result::Result<Vec<u8>, std::io::Error> {
match (setup.request_type, setup.request) {
(request_type::CLASS_INTERFACE_OUT, cdc_request::SET_LINE_CODING) => {
match LineCoding::from_bytes(req) {
Some(coding) => {
self.line_coding = coding;
debug!(
"USB serial host set line coding: {} baud, {} data bits, parity {}, \
stop {}",
self.line_coding.baud_rate,
self.line_coding.data_bits,
self.line_coding.parity,
self.line_coding.stop_bits
);
}
None => warn!(
"USB serial SET_LINE_CODING carried {} bytes, expected {} - ignored",
req.len(),
LineCoding::WIRE_LEN
),
}
Ok(vec![])
}
(request_type::CLASS_INTERFACE_IN, cdc_request::GET_LINE_CODING) => {
Ok(self.line_coding.to_bytes().to_vec())
}
(request_type::CLASS_INTERFACE_OUT, cdc_request::SET_CONTROL_LINE_STATE) => {
self.control_lines = ControlLineState::from_value(setup.value);
debug!(
"USB serial host set control lines: DTR={} RTS={}",
self.control_lines.dtr, self.control_lines.rts
);
Ok(vec![])
}
(request_type::CLASS_INTERFACE_OUT, cdc_request::SEND_BREAK) => {
debug!("USB serial host sent a break of {} ms", setup.value);
Ok(vec![])
}
_ => {
debug!(
"USB serial: unhandled control request type={:#04x} request={:#04x}",
setup.request_type, setup.request
);
Ok(vec![])
}
}
}
}
#[cfg(all(unix, feature = "libusb"))]
impl usbip::UsbInterfaceHandler for UsbCdcAcmSerialHandler {
fn handle_urb(
&mut self,
_interface: &usbip::UsbInterface,
ep: usbip::UsbEndpoint,
transfer_buffer_length: u32,
setup: usbip::SetupPacket,
req: &[u8],
) -> std::result::Result<Vec<u8>, std::io::Error> {
if ep.is_ep0() {
return self.handle_control(setup, req);
}
match ep.direction() {
usbip::Direction::Out => {
if !req.is_empty() {
trace!("USB serial received {} byte(s) from the host", req.len());
if self.rx_tx.send(req.to_vec()).is_err() {
warn!(
"USB serial dropped {} byte(s): connection task has exited",
req.len()
);
}
}
Ok(vec![])
}
usbip::Direction::In => {
if ep.attributes == usbip::EndpointAttributes::Interrupt as u8 {
let limit = (ep.max_packet_size as usize).min(transfer_buffer_length as usize);
let Some(mut message) = self.notifications.pop_front() else {
return Ok(vec![]);
};
if message.len() > limit {
let remainder = message.split_off(limit);
self.notifications.push_front(remainder);
}
trace!(
"USB serial sent a {} byte CDC notification to the host",
message.len()
);
return Ok(message);
}
let limit = (ep.max_packet_size as usize).min(transfer_buffer_length as usize);
let take = self.tx_buffer.len().min(limit);
let resp: Vec<u8> = self.tx_buffer.drain(..take).collect();
if !resp.is_empty() {
trace!("USB serial sent {} byte(s) to the host", resp.len());
}
Ok(resp)
}
}
}
fn get_class_specific_descriptor(&self) -> Vec<u8> {
usbip::UsbInterfaceHandler::get_class_specific_descriptor(
&usbip::cdc::UsbCdcAcmHandler::new(),
)
}
fn as_any(&mut self) -> &mut dyn std::any::Any {
self
}
}