diff --git a/examples/ftdi.rs b/examples/ftdi.rs new file mode 100644 index 0000000..4c094da --- /dev/null +++ b/examples/ftdi.rs @@ -0,0 +1,107 @@ +use env_logger; +use std::any::Any; +use std::net::*; +use std::sync::{Arc, Mutex}; +use std::time::Duration; +use usbip::{ + ftdi::FtdiDeviceHandler, Direction, EndpointAttributes, SetupPacket, UsbDevice, UsbEndpoint, + UsbInterface, UsbInterfaceHandler, +}; + +const FTDI_VID: u16 = 0x0403; +const FTDI_PID: u16 = 0x6011; + +#[derive(Clone)] +pub struct PseudoFtdiHandler { + tx_buffer: Vec, +} + +impl PseudoFtdiHandler { + pub fn new() -> Self { + Self { tx_buffer: vec![] } + } +} + +// This implemention does not +impl UsbInterfaceHandler for PseudoFtdiHandler { + fn handle_urb( + &mut self, + _interface: &UsbInterface, + ep: UsbEndpoint, + _setup: SetupPacket, + req: &[u8], + ) -> Result, std::io::Error> { + // interrupt + if ep.attributes == EndpointAttributes::Interrupt as u8 { + Ok(vec![]) + } + // bulk + else if let Direction::Out = ep.direction() { + // write to device/file here... + println!("Write endpoint {:02x}: {:?}", ep.address, req); + Ok(vec![]) + } else { + // Read from the device. + // The first two bytes are device status. + // These must always be present, otherwise the ftdi driver on the remote side will + // consume these bytes. + if self.tx_buffer.len() > 0 { + let mut ftdi_packet: Vec = vec![0x01, 0x00]; + ftdi_packet.extend(&self.tx_buffer); + self.tx_buffer = vec![]; + println!("Read endpoint {:02x}: {:?}", ep.address, ftdi_packet); + Ok(ftdi_packet) + } else { + Ok(vec![0x01, 0x00]) + } + } + } + + fn get_class_specific_descriptor(&self) -> Vec { + vec![] + } + + fn as_any(&mut self) -> &mut dyn Any { + self + } +} + +fn ftdi_device() -> ( + UsbDevice, + Arc>>, +) { + let pseudo = Arc::new(Mutex::new( + Box::new(PseudoFtdiHandler::new()) as Box + )); + let device_handler = Arc::new(Mutex::new( + Box::new(FtdiDeviceHandler::new()) as Box + )); + let endpoints = FtdiDeviceHandler::endpoints(4); + let mut device = UsbDevice::new(0) + .with_interface(255, 255, 255, "", endpoints[0..2].to_vec(), pseudo.clone()) + .with_interface(255, 255, 255, "", endpoints[2..4].to_vec(), pseudo.clone()) + .with_interface(255, 255, 255, "", endpoints[4..6].to_vec(), pseudo.clone()) + .with_interface(255, 255, 255, "", endpoints[6..8].to_vec(), pseudo.clone()) + .with_device_handler(device_handler); + + device.product_id = FTDI_PID; + device.vendor_id = FTDI_VID; + (device, pseudo) +} + +#[tokio::main] +async fn main() { + env_logger::init(); + let (device, handler) = ftdi_device(); + let server = usbip::UsbIpServer::new_simulated(vec![device]); + let addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 3240); + tokio::spawn(usbip::server(addr, server)); + + loop { + tokio::time::sleep(Duration::from_secs(1)).await; + let mut handler = handler.lock().unwrap(); + if let Some(ftdi) = handler.as_any().downcast_mut::() { + ftdi.tx_buffer.extend(b"hello\0"); + } + } +} diff --git a/src/device.rs b/src/device.rs index a37db4c..5b078f4 100644 --- a/src/device.rs +++ b/src/device.rs @@ -1,4 +1,28 @@ use super::*; +use rusb::Version as rusbVersion; + +#[derive(Clone, Default)] +pub struct Version { + pub major: u8, + pub minor: u8, + pub patch: u8, +} + +impl From for Version { + fn from(value: rusbVersion) -> Self { + Self { + major: value.major(), + minor: value.minor(), + patch: value.sub_minor(), + } + } +} + +impl Into for Version { + fn into(self) -> rusbVersion { + rusbVersion(self.major, self.minor, self.patch) + } +} /// Represent a USB device #[derive(Clone, Default)] @@ -10,7 +34,7 @@ pub struct UsbDevice { pub speed: u32, pub vendor_id: u16, pub product_id: u16, - pub device_bcd: u16, + pub device_bcd: Version, pub device_class: u8, pub device_subclass: u8, pub device_protocol: u8, @@ -18,6 +42,8 @@ pub struct UsbDevice { pub num_configurations: u8, pub interfaces: Vec, pub device_handler: Option>>>, + pub usb_version: Version, + pub(crate) ep0_in: UsbEndpoint, pub(crate) ep0_out: UsbEndpoint, // strings @@ -130,7 +156,9 @@ impl UsbDevice { socket.write_u32(self.speed).await?; socket.write_u16(self.vendor_id).await?; socket.write_u16(self.product_id).await?; - socket.write_u16(self.device_bcd).await?; + socket + .write_u16((self.device_bcd.major as u16) << 8 | self.device_bcd.minor as u16) + .await?; socket.write_u8(self.device_class).await?; socket.write_u8(self.device_subclass).await?; socket.write_u8(self.device_protocol).await?; @@ -198,18 +226,18 @@ impl UsbDevice { let mut desc = vec![ 0x12, // bLength Device as u8, // bDescriptorType: Device - 0x10, - 0x02, // bcdUSB: USB 2.1 - self.device_class, // bDeviceClass - self.device_subclass, // bDeviceSubClass - self.device_protocol, // bDeviceProtocol + self.usb_version.minor as u8, + self.usb_version.major as u8, // bcdUSB: USB 2.0 + self.device_class, // bDeviceClass + self.device_subclass, // bDeviceSubClass + self.device_protocol, // bDeviceProtocol self.ep0_in.max_packet_size as u8, // bMaxPacketSize0 - self.vendor_id as u8, // idVendor + self.vendor_id as u8, // idVendor (self.vendor_id >> 8) as u8, self.product_id as u8, // idProduct (self.product_id >> 8) as u8, - self.device_bcd as u8, // bcdDevice - (self.device_bcd >> 8) as u8, + self.device_bcd.minor as u8, // bcdDevice + self.device_bcd.major as u8, self.string_manufacturer, // iManufacturer self.string_product, // iProduct self.string_serial, // iSerial @@ -331,14 +359,14 @@ impl UsbDevice { let mut desc = vec![ 0x0A, // bLength DeviceQualifier as u8, // bDescriptorType: Device Qualifier - 0x10, - 0x02, // bcdUSB USB 2.1 - self.device_class, // bDeviceClass - self.device_subclass, // bDeviceSUbClass - self.device_protocol, // bDeviceProtocol + self.usb_version.minor as u8, + self.usb_version.major as u8, + self.device_class, // bDeviceClass + self.device_subclass, // bDeviceSUbClass + self.device_protocol, // bDeviceProtocol self.ep0_in.max_packet_size as u8, // bMaxPacketSize0 - self.num_configurations, // bNumConfigurations - 0x00, // reserved + self.num_configurations, // bNumConfigurations + 0x00, // reserved ]; // requested len too short: wLength < real length diff --git a/src/ftdi.rs b/src/ftdi.rs new file mode 100644 index 0000000..b3b2f58 --- /dev/null +++ b/src/ftdi.rs @@ -0,0 +1,117 @@ +use crate::{EndpointAttributes, SetupPacket, UsbDeviceHandler, UsbEndpoint}; +use std::any::Any; + +const FTDI_DEVICE_REQ_TYPE: u8 = 0xC0; +const FTDI_SIO_RESET: u8 = 0; /* Reset the port */ +const FTDI_SIO_MODEM_CTRL: u8 = 1; /* Set the modem control register */ +const FTDI_SIO_SET_FLOW_CTRL: u8 = 2; /* Set flow control register */ +const FTDI_SIO_SET_BAUD_RATE: u8 = 3; /* Set baud rate */ +const FTDI_SIO_SET_DATA: u8 = 4; /* Set the data characteristics ofthe port */ +const FTDI_SIO_GET_MODEM_STATUS: u8 = 5; /* Retrieve current value of modem status register */ +const FTDI_SIO_SET_EVENT_CHAR: u8 = 6; /* Set the event character */ +const FTDI_SIO_SET_ERROR_CHAR: u8 = 7; /* Set the error character */ +const FTDI_SIO_SET_LATENCY_TIMER: u8 = 9; /* Set the latency timer */ +const FTDI_SIO_GET_LATENCY_TIMER: u8 = 0x0a; /* Get the latency timer */ +const FTDI_SIO_SET_BITMODE: u8 = 0x0b; /* Set bitbang mode */ +const FTDI_SIO_READ_PINS: u8 = 0x0c; /* Read immediate value of pins */ +const FTDI_SIO_READ_EEPROM: u8 = 0x90; /* Read EEPROM */ + +const EP_MAX_PACKET_SIZE: u16 = 512; + +#[derive(Debug)] +enum FTDISIORequestTypes { + Reset, + ModemCtrl, + SetFlowCtrl, + SetBaudRate, + SetData, + GetModemStatus, + SetEventChar, + SetErrorChar, + SetLatencyTimer, + GetLatencyTimer, + SetBitmode, + ReadPins, + ReadEEPROM, + Unknown, +} + +impl From for FTDISIORequestTypes { + fn from(orig: u8) -> Self { + match orig { + FTDI_SIO_RESET => FTDISIORequestTypes::Reset, + FTDI_SIO_MODEM_CTRL => FTDISIORequestTypes::ModemCtrl, + FTDI_SIO_SET_FLOW_CTRL => FTDISIORequestTypes::SetFlowCtrl, + FTDI_SIO_SET_BAUD_RATE => FTDISIORequestTypes::SetBaudRate, + FTDI_SIO_SET_DATA => FTDISIORequestTypes::SetData, + FTDI_SIO_GET_MODEM_STATUS => FTDISIORequestTypes::GetModemStatus, + FTDI_SIO_SET_EVENT_CHAR => FTDISIORequestTypes::SetEventChar, + FTDI_SIO_SET_ERROR_CHAR => FTDISIORequestTypes::SetErrorChar, + FTDI_SIO_SET_LATENCY_TIMER => FTDISIORequestTypes::SetLatencyTimer, + FTDI_SIO_GET_LATENCY_TIMER => FTDISIORequestTypes::GetLatencyTimer, + FTDI_SIO_SET_BITMODE => FTDISIORequestTypes::SetBitmode, + FTDI_SIO_READ_PINS => FTDISIORequestTypes::ReadPins, + FTDI_SIO_READ_EEPROM => FTDISIORequestTypes::ReadEEPROM, + _ => FTDISIORequestTypes::Unknown, + } + } +} + +#[derive(Clone)] +pub struct FtdiDeviceHandler {} + +impl FtdiDeviceHandler { + pub fn new() -> Self { + Self {} + } + + pub fn endpoints(interface_count: u8) -> Vec { + let mut ret_eps = vec![]; + + for i in 0..interface_count { + ret_eps.push(UsbEndpoint { + /// bEndpointAddress + address: 0x81 + (0x2 * i), + /// bmAttributes + attributes: EndpointAttributes::Bulk as u8, + /// wMaxPacketSize + max_packet_size: EP_MAX_PACKET_SIZE as u16, + /// bInterval + interval: 0, + }); + + ret_eps.push(UsbEndpoint { + /// bEndpointAddress + address: 0x2 + (0x2 * i), + /// bmAttributes + attributes: EndpointAttributes::Bulk as u8, + /// wMaxPacketSize + max_packet_size: EP_MAX_PACKET_SIZE as u16, + /// bInterval + interval: 0, + }) + } + ret_eps + } +} + +impl UsbDeviceHandler for FtdiDeviceHandler { + fn handle_urb(&mut self, setup: SetupPacket, _: &[u8]) -> Result, std::io::Error> { + match setup.request_type { + FTDI_DEVICE_REQ_TYPE => match setup.request.into() { + FTDISIORequestTypes::GetModemStatus => Ok(vec![0x00]), + // 1 ms + FTDISIORequestTypes::GetLatencyTimer => Ok(vec![0x01]), + request => { + println!("Unhandled: {:?}", request); + Ok(vec![]) + } + }, + _ => Ok(vec![]), + } + } + + fn as_any(&mut self) -> &mut dyn Any { + self + } +} diff --git a/src/host.rs b/src/host.rs index f8947e8..29268ce 100644 --- a/src/host.rs +++ b/src/host.rs @@ -25,7 +25,7 @@ impl UsbInterfaceHandler for UsbHostInterfaceHandler { "To host device: ep={:?} setup={:?} req={:?}", ep, setup, req ); - let mut buffer = [0u8; 1024]; + let mut buffer = vec![0u8; ep.max_packet_size as usize]; let timeout = std::time::Duration::new(1, 0); let handle = self.handle.lock().unwrap(); if ep.attributes == EndpointAttributes::Control as u8 { diff --git a/src/lib.rs b/src/lib.rs index d1ad134..a939b1c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -17,6 +17,7 @@ pub mod cdc; mod consts; mod device; mod endpoint; +pub mod ftdi; pub mod hid; mod host; mod interface; @@ -41,134 +42,168 @@ impl UsbIpServer { Self { devices } } - /// Create a [UsbIpServer] exposing devices in the host, and redirect all USB transfers to them using libusb - pub fn new_from_host() -> Self { + fn with_devices(device_list: Vec>) -> Vec { let mut devices = vec![]; - if let Ok(list) = rusb::devices() { - for dev in list.iter() { - let open_device = match dev.open() { - Ok(dev) => dev, - Err(err) => { - println!("Impossible to share {:?}: {}", dev, err); - continue; - } - }; - let handle = Arc::new(Mutex::new(open_device)); - let desc = dev.device_descriptor().unwrap(); - let cfg = dev.active_config_descriptor().unwrap(); - let mut interfaces = vec![]; + + for dev in device_list { + let open_device = match dev.open() { + Ok(dev) => dev, + Err(err) => { + println!("Impossible to share {:?}: {}", dev, err); + continue; + } + }; + let handle = Arc::new(Mutex::new(open_device)); + let desc = dev.device_descriptor().unwrap(); + let cfg = dev.active_config_descriptor().unwrap(); + let mut interfaces = vec![]; + handle + .lock() + .unwrap() + .set_auto_detach_kernel_driver(true) + .ok(); + for intf in cfg.interfaces() { + // ignore alternate settings + let intf_desc = intf.descriptors().next().unwrap(); handle .lock() .unwrap() - .set_auto_detach_kernel_driver(true) + .claim_interface(intf_desc.interface_number()) .ok(); - for intf in cfg.interfaces() { - // ignore alternate settings - let intf_desc = intf.descriptors().next().unwrap(); - handle - .lock() - .unwrap() - .claim_interface(intf_desc.interface_number()) - .ok(); - let mut endpoints = vec![]; + let mut endpoints = vec![]; - for ep_desc in intf_desc.endpoint_descriptors() { - endpoints.push(UsbEndpoint { - address: ep_desc.address(), - attributes: ep_desc.transfer_type() as u8, - max_packet_size: ep_desc.max_packet_size(), - interval: ep_desc.interval(), - }); - } - - let handler = Arc::new(Mutex::new(Box::new(UsbHostInterfaceHandler::new( - handle.clone(), - )) - as Box)); - interfaces.push(UsbInterface { - interface_class: intf_desc.class_code(), - interface_subclass: intf_desc.sub_class_code(), - interface_protocol: intf_desc.protocol_code(), - endpoints, - string_interface: intf_desc.description_string_index().unwrap_or(0), - class_specific_descriptor: Vec::from(intf_desc.extra()), - handler, + for ep_desc in intf_desc.endpoint_descriptors() { + endpoints.push(UsbEndpoint { + address: ep_desc.address(), + attributes: ep_desc.transfer_type() as u8, + max_packet_size: ep_desc.max_packet_size(), + interval: ep_desc.interval(), }); } - let mut device = UsbDevice { - path: format!( - "/sys/bus/{}/{}/{}", - dev.bus_number(), - dev.address(), - dev.port_number() - ), - bus_id: format!( - "{}-{}-{}", - dev.bus_number(), - dev.address(), - dev.port_number() - ), - bus_num: dev.bus_number() as u32, - dev_num: dev.port_number() as u32, - speed: dev.speed() as u32, - vendor_id: desc.vendor_id(), - product_id: desc.product_id(), - device_class: desc.class_code(), - device_subclass: desc.sub_class_code(), - device_protocol: desc.protocol_code(), - configuration_value: cfg.number(), - num_configurations: desc.num_configurations(), - ep0_in: UsbEndpoint { - address: 0x80, - attributes: EndpointAttributes::Control as u8, - max_packet_size: desc.max_packet_size() as u16, - interval: 0, - }, - ep0_out: UsbEndpoint { - address: 0x00, - attributes: EndpointAttributes::Control as u8, - max_packet_size: desc.max_packet_size() as u16, - interval: 0, - }, - interfaces, - device_handler: Some(Arc::new(Mutex::new(Box::new( - UsbHostDeviceHandler::new(handle.clone()), - )))), - ..UsbDevice::default() - }; - // set strings - if let Some(index) = desc.manufacturer_string_index() { - device.string_manufacturer = device.new_string( - &handle - .lock() - .unwrap() - .read_string_descriptor_ascii(index) - .unwrap(), - ) + let handler = Arc::new(Mutex::new(Box::new(UsbHostInterfaceHandler::new( + handle.clone(), + )) + as Box)); + interfaces.push(UsbInterface { + interface_class: intf_desc.class_code(), + interface_subclass: intf_desc.sub_class_code(), + interface_protocol: intf_desc.protocol_code(), + endpoints, + string_interface: intf_desc.description_string_index().unwrap_or(0), + class_specific_descriptor: Vec::from(intf_desc.extra()), + handler, + }); + } + let mut device = UsbDevice { + path: format!( + "/sys/bus/{}/{}/{}", + dev.bus_number(), + dev.address(), + dev.port_number() + ), + bus_id: format!( + "{}-{}-{}", + dev.bus_number(), + dev.address(), + dev.port_number() + ), + bus_num: dev.bus_number() as u32, + dev_num: dev.port_number() as u32, + speed: dev.speed() as u32, + vendor_id: desc.vendor_id(), + product_id: desc.product_id(), + device_class: desc.class_code(), + device_subclass: desc.sub_class_code(), + device_protocol: desc.protocol_code(), + device_bcd: device::Version::from(desc.device_version()), + configuration_value: cfg.number(), + num_configurations: desc.num_configurations(), + ep0_in: UsbEndpoint { + address: 0x80, + attributes: EndpointAttributes::Control as u8, + max_packet_size: desc.max_packet_size() as u16, + interval: 0, + }, + ep0_out: UsbEndpoint { + address: 0x00, + attributes: EndpointAttributes::Control as u8, + max_packet_size: desc.max_packet_size() as u16, + interval: 0, + }, + interfaces, + device_handler: Some(Arc::new(Mutex::new(Box::new(UsbHostDeviceHandler::new( + handle.clone(), + ))))), + usb_version: device::Version::from(desc.usb_version()), + ..UsbDevice::default() + }; + + // set strings + if let Some(index) = desc.manufacturer_string_index() { + device.string_manufacturer = device.new_string( + &handle + .lock() + .unwrap() + .read_string_descriptor_ascii(index) + .unwrap(), + ) + } + if let Some(index) = desc.product_string_index() { + device.string_product = device.new_string( + &handle + .lock() + .unwrap() + .read_string_descriptor_ascii(index) + .unwrap(), + ) + } + if let Some(index) = desc.serial_number_string_index() { + device.string_serial = device.new_string( + &handle + .lock() + .unwrap() + .read_string_descriptor_ascii(index) + .unwrap(), + ) + } + devices.push(device); + } + devices + } + + /// Create a [UsbIpServer] exposing devices in the host, and redirect all USB transfers to them using libusb + pub fn new_from_host() -> Self { + match rusb::devices() { + Ok(list) => { + let mut devs = vec![]; + for d in list.iter() { + devs.push(d) + } + Self { + devices: Self::with_devices(devs), } - if let Some(index) = desc.product_string_index() { - device.string_product = device.new_string( - &handle - .lock() - .unwrap() - .read_string_descriptor_ascii(index) - .unwrap(), - ) + } + Err(_) => Self { devices: vec![] }, + } + } + + pub fn new_from_host_with_filter(filter: F) -> Self + where + F: FnMut(&Device) -> bool, + { + match rusb::devices() { + Ok(list) => { + let mut devs = vec![]; + for d in list.iter().filter(filter) { + devs.push(d) } - if let Some(index) = desc.serial_number_string_index() { - device.string_serial = device.new_string( - &handle - .lock() - .unwrap() - .read_string_descriptor_ascii(index) - .unwrap(), - ) + Self { + devices: Self::with_devices(devs), } - devices.push(device); } + Err(_) => Self { devices: vec![] }, } - Self { devices } } } @@ -291,7 +326,8 @@ async fn handler( socket.write_u32(direction).await?; socket.write_u32(ep).await?; // status - socket.write_u32(0).await?; + socket.write_i32(0).await?; + socket.write_all(&mut padding).await?; } _ => warn!("Got unknown command {:?}", command), }