From e14858f91840591e4149c887251bbcf97fb843a8 Mon Sep 17 00:00:00 2001 From: Jiajie Chen Date: Fri, 1 Aug 2025 16:35:06 +0800 Subject: [PATCH 01/10] Apply cargo clippy --- src/device.rs | 8 ++++---- src/host.rs | 9 +++------ src/lib.rs | 33 ++++++++++++++------------------- src/usbip_protocol.rs | 6 ++---- 4 files changed, 23 insertions(+), 33 deletions(-) diff --git a/src/device.rs b/src/device.rs index 4005f14..19b135c 100644 --- a/src/device.rs +++ b/src/device.rs @@ -291,7 +291,7 @@ impl UsbDevice { match (FromPrimitive::from_u8(ep.attributes), ep.direction()) { (Some(Control), In) => { // control in - debug!("Control IN setup={:x?}", setup_packet); + debug!("Control IN setup={setup_packet:x?}"); match ( setup_packet.request_type, FromPrimitive::from_u8(setup_packet.request), @@ -436,7 +436,7 @@ impl UsbDevice { } else { Err(std::io::Error::new( std::io::ErrorKind::InvalidInput, - format!("Invalid string index: {}", index), + format!("Invalid string index: {index}"), )) } } @@ -463,7 +463,7 @@ impl UsbDevice { Ok(desc) } _ => { - warn!("unknown desc type: {:x?}", setup_packet); + warn!("unknown desc type: {setup_packet:x?}"); Ok(vec![]) } } @@ -488,7 +488,7 @@ impl UsbDevice { } (Some(Control), Out) => { // control out - debug!("Control OUT setup={:x?}", setup_packet); + debug!("Control OUT setup={setup_packet:x?}"); match ( setup_packet.request_type, FromPrimitive::from_u8(setup_packet.request), diff --git a/src/host.rs b/src/host.rs index 20df83f..ed8b2e5 100644 --- a/src/host.rs +++ b/src/host.rs @@ -22,10 +22,7 @@ impl UsbInterfaceHandler for RusbUsbHostInterfaceHandler { setup: SetupPacket, req: &[u8], ) -> Result> { - debug!( - "To host device: ep={:?} setup={:?} req={:?}", - ep, setup, req - ); + debug!("To host device: ep={ep:?} setup={setup:?} req={req:?}",); let mut buffer = vec![0u8; transfer_buffer_length as usize]; let timeout = std::time::Duration::new(1, 0); let handle = self.handle.lock().unwrap(); @@ -111,7 +108,7 @@ impl UsbDeviceHandler for RusbUsbHostDeviceHandler { setup: SetupPacket, req: &[u8], ) -> Result> { - debug!("To host device: setup={:?} req={:?}", setup, req); + debug!("To host device: setup={setup:?} req={req:?}"); let mut buffer = vec![0u8; transfer_buffer_length as usize]; let timeout = std::time::Duration::new(1, 0); let handle = self.handle.lock().unwrap(); @@ -260,7 +257,7 @@ impl UsbDeviceHandler for NusbUsbHostDeviceHandler { setup: SetupPacket, req: &[u8], ) -> Result> { - debug!("To host device: setup={:?} req={:?}", setup, req); + debug!("To host device: setup={setup:?} req={req:?}"); let mut buffer = vec![0u8; transfer_buffer_length as usize]; let timeout = std::time::Duration::new(1, 0); let handle = self.handle.lock().unwrap(); diff --git a/src/lib.rs b/src/lib.rs index 46da1dd..a43b09a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -61,10 +61,7 @@ impl UsbIpServer { let dev = match device_info.open() { Ok(dev) => dev, Err(err) => { - warn!( - "Impossible to open device {:?}: {}, ignoring device", - device_info, err - ); + warn!("Impossible to open device {device_info:?}: {err}, ignoring device",); continue; } }; @@ -72,8 +69,7 @@ impl UsbIpServer { Ok(cfg) => cfg, Err(err) => { warn!( - "Impossible to get active configuration {:?}: {}, ignoring device", - device_info, err + "Impossible to get active configuration {device_info:?}: {err}, ignoring device", ); continue; } @@ -178,8 +174,7 @@ impl UsbIpServer { Ok(desc) => desc, Err(err) => { warn!( - "Impossible to get device descriptor for {:?}: {}, ignoring device", - dev, err + "Impossible to get device descriptor for {dev:?}: {err}, ignoring device", ); continue; } @@ -319,7 +314,7 @@ impl UsbIpServer { let open_device = match dev.open() { Ok(dev) => dev, Err(err) => { - warn!("Impossible to share {:?}: {}, ignoring device", dev, err); + warn!("Impossible to share {dev:?}: {err}, ignoring device"); continue; } }; @@ -377,7 +372,7 @@ impl UsbIpServer { } else { Err(std::io::Error::new( ErrorKind::NotFound, - format!("Device {} not found", bus_id), + format!("Device {bus_id} not found"), )) } } @@ -471,13 +466,13 @@ pub async fn handler( let res = match device.find_ep(real_ep as u8) { None => { - warn!("Endpoint {:02x?} not found", real_ep); + warn!("Endpoint {real_ep:02x?} not found"); UsbIpResponse::usbip_ret_submit_fail(&header) } Some((ep, intf)) => { - trace!("->Endpoint {:02x?}", ep); - trace!("->Setup {:02x?}", setup); - trace!("->Request {:02x?}", data); + trace!("->Endpoint {ep:02x?}"); + trace!("->Setup {setup:02x?}"); + trace!("->Request {data:02x?}"); let resp = device .handle_urb( ep, @@ -493,12 +488,12 @@ pub async fn handler( if out { trace!("<-Wrote {}", data.len()); } else { - trace!("<-Resp {:02x?}", resp); + trace!("<-Resp {resp:02x?}"); } UsbIpResponse::usbip_ret_submit_success(&header, 0, 0, resp, vec![]) } Err(err) => { - warn!("Error handling URB: {}", err); + warn!("Error handling URB: {err}"); UsbIpResponse::usbip_ret_submit_fail(&header) } } @@ -511,7 +506,7 @@ pub async fn handler( mut header, unlink_seqnum, } => { - trace!("Got USBIP_CMD_UNLINK for {:10x?}", unlink_seqnum); + trace!("Got USBIP_CMD_UNLINK for {unlink_seqnum:10x?}"); header.command = USBIP_RET_UNLINK.into(); @@ -535,11 +530,11 @@ pub async fn server(addr: SocketAddr, server: Arc) { let new_server = server.clone(); tokio::spawn(async move { let res = handler(&mut socket, new_server).await; - info!("Handler ended with {:?}", res); + info!("Handler ended with {res:?}"); }); } Err(err) => { - warn!("Got error {:?}", err); + warn!("Got error {err:?}"); } } } diff --git a/src/usbip_protocol.rs b/src/usbip_protocol.rs index c47d37b..c961316 100644 --- a/src/usbip_protocol.rs +++ b/src/usbip_protocol.rs @@ -149,8 +149,7 @@ impl UsbIpCommand { if version != 0 && version != USBIP_VERSION { return Err(std::io::Error::other(format!( - "Unknown version: {:#04X}", - version + "Unknown version: {version:#04X}" ))); } @@ -243,8 +242,7 @@ impl UsbIpCommand { }) } _ => Err(std::io::Error::other(format!( - "Unknown command: {:#04X}", - command + "Unknown command: {command:#04X}" ))), } } From 156ec45a812706e85af49abce875d15e916a2bfb Mon Sep 17 00:00:00 2001 From: Jiajie Chen Date: Fri, 1 Aug 2025 16:38:47 +0800 Subject: [PATCH 02/10] Apply more cargo clippy --- src/host.rs | 5 +---- src/lib.rs | 3 +-- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/src/host.rs b/src/host.rs index ed8b2e5..cfdd241 100644 --- a/src/host.rs +++ b/src/host.rs @@ -175,10 +175,7 @@ impl UsbInterfaceHandler for NusbUsbHostInterfaceHandler { setup: SetupPacket, req: &[u8], ) -> Result> { - debug!( - "To host device: ep={:?} setup={:?} req={:?}", - ep, setup, req - ); + debug!("To host device: ep={ep:?} setup={setup:?} req={req:?}",); let mut buffer = vec![0u8; transfer_buffer_length as usize]; let timeout = std::time::Duration::new(1, 0); let handle = self.handle.lock().unwrap(); diff --git a/src/lib.rs b/src/lib.rs index a43b09a..6dd9e58 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -183,8 +183,7 @@ impl UsbIpServer { Ok(desc) => desc, Err(err) => { warn!( - "Impossible to get config descriptor for {:?}: {}, ignoring device", - dev, err + "Impossible to get config descriptor for {dev:?}: {err}, ignoring device", ); continue; } From 3ff5df5c2ebe6da25d930f622aeba75606566ac1 Mon Sep 17 00:00:00 2001 From: Jiajie Chen Date: Wed, 24 Dec 2025 23:17:02 +0800 Subject: [PATCH 03/10] Migrate to nusb 0.2.1 --- Cargo.toml | 2 +- src/host.rs | 183 +++++++++++++++++++++++++++++++++++++++------------- src/lib.rs | 13 ++-- 3 files changed, 145 insertions(+), 53 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 6dc0e7b..387a6f5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,7 +16,7 @@ num-traits = "0.2.15" num-derive = "0.4.2" rusb = "0.9.3" serde = { version = "1.0", features = ["derive"], optional = true } -nusb = "0.1.10" +nusb = "0.2.1" [dev-dependencies] tokio = { version = "1.22.0", features = ["full"] } diff --git a/src/host.rs b/src/host.rs index cfdd241..d95f715 100644 --- a/src/host.rs +++ b/src/host.rs @@ -1,5 +1,6 @@ //! Host USB use super::*; +use nusb::MaybeFuture; /// A handler to pass requests to interface of a rusb USB device of the host #[derive(Clone, Debug)] @@ -176,44 +177,111 @@ impl UsbInterfaceHandler for NusbUsbHostInterfaceHandler { req: &[u8], ) -> Result> { debug!("To host device: ep={ep:?} setup={setup:?} req={req:?}",); - let mut buffer = vec![0u8; transfer_buffer_length as usize]; let timeout = std::time::Duration::new(1, 0); let handle = self.handle.lock().unwrap(); - let control = nusb::transfer::Control { - control_type: match (setup.request_type >> 5) & 0b11 { - 0 => nusb::transfer::ControlType::Standard, - 1 => nusb::transfer::ControlType::Class, - 2 => nusb::transfer::ControlType::Vendor, - _ => unimplemented!(), - }, - recipient: match setup.request_type & 0b11111 { - 0 => nusb::transfer::Recipient::Device, - 1 => nusb::transfer::Recipient::Interface, - 2 => nusb::transfer::Recipient::Endpoint, - 3 => nusb::transfer::Recipient::Other, - _ => unimplemented!(), - }, - request: setup.request, - value: setup.value, - index: setup.index, - }; if ep.attributes == EndpointAttributes::Control as u8 { // control if let Direction::In = ep.direction() { // control in - if let Ok(len) = handle.control_in_blocking(control, &mut buffer, timeout) { - return Ok(Vec::from(&buffer[..len])); + let control_in = nusb::transfer::ControlIn { + control_type: match (setup.request_type >> 5) & 0b11 { + 0 => nusb::transfer::ControlType::Standard, + 1 => nusb::transfer::ControlType::Class, + 2 => nusb::transfer::ControlType::Vendor, + _ => unimplemented!(), + }, + recipient: match setup.request_type & 0b11111 { + 0 => nusb::transfer::Recipient::Device, + 1 => nusb::transfer::Recipient::Interface, + 2 => nusb::transfer::Recipient::Endpoint, + 3 => nusb::transfer::Recipient::Other, + _ => unimplemented!(), + }, + request: setup.request, + value: setup.value, + index: setup.index, + length: transfer_buffer_length as u16, + }; + if let Ok(data) = handle.control_in(control_in, timeout).wait() { + return Ok(data); } } else { // control out - handle.control_out_blocking(control, req, timeout).ok(); + let control_out = nusb::transfer::ControlOut { + control_type: match (setup.request_type >> 5) & 0b11 { + 0 => nusb::transfer::ControlType::Standard, + 1 => nusb::transfer::ControlType::Class, + 2 => nusb::transfer::ControlType::Vendor, + _ => unimplemented!(), + }, + recipient: match setup.request_type & 0b11111 { + 0 => nusb::transfer::Recipient::Device, + 1 => nusb::transfer::Recipient::Interface, + 2 => nusb::transfer::Recipient::Endpoint, + 3 => nusb::transfer::Recipient::Other, + _ => unimplemented!(), + }, + request: setup.request, + value: setup.value, + index: setup.index, + data: req, + }; + handle.control_out(control_out, timeout).wait().ok(); } } else if ep.attributes == EndpointAttributes::Interrupt as u8 { // interrupt - todo!("Missing blocking api for interrupt transfer in nusb") + if let Direction::In = ep.direction() { + // interrupt in + let mut endpoint = handle + .endpoint::(ep.address) + .map_err(|e| { + std::io::Error::other(format!("Failed to open interrupt endpoint: {}", e)) + })?; + let buffer = endpoint.allocate(transfer_buffer_length as usize); + let completion = endpoint.transfer_blocking(buffer, timeout); + if completion.status.is_ok() { + return Ok(completion.buffer.to_vec()); + } + } else { + // interrupt out + let mut endpoint = handle + .endpoint::(ep.address) + .map_err(|e| { + std::io::Error::other(format!("Failed to open interrupt endpoint: {}", e)) + })?; + if !req.is_empty() { + let mut buffer = endpoint.allocate(req.len()); + buffer.copy_from_slice(req); + endpoint.transfer_blocking(buffer, timeout); + } + } } else if ep.attributes == EndpointAttributes::Bulk as u8 { // bulk - todo!("Missing blocking api for bulk transfer in nusb") + if let Direction::In = ep.direction() { + // bulk in + let mut endpoint = handle + .endpoint::(ep.address) + .map_err(|e| { + std::io::Error::other(format!("Failed to open bulk endpoint: {}", e)) + })?; + let buffer = endpoint.allocate(transfer_buffer_length as usize); + let completion = endpoint.transfer_blocking(buffer, timeout); + if completion.status.is_ok() { + return Ok(completion.buffer.to_vec()); + } + } else { + // bulk out + let mut endpoint = handle + .endpoint::(ep.address) + .map_err(|e| { + std::io::Error::other(format!("Failed to open bulk endpoint: {}", e)) + })?; + if !req.is_empty() { + let mut buffer = endpoint.allocate(req.len()); + buffer.copy_from_slice(req); + endpoint.transfer_blocking(buffer, timeout); + } + } } Ok(vec![]) } @@ -255,38 +323,61 @@ impl UsbDeviceHandler for NusbUsbHostDeviceHandler { req: &[u8], ) -> Result> { debug!("To host device: setup={setup:?} req={req:?}"); - let mut buffer = vec![0u8; transfer_buffer_length as usize]; let timeout = std::time::Duration::new(1, 0); let handle = self.handle.lock().unwrap(); - let control = nusb::transfer::Control { - control_type: match (setup.request_type >> 5) & 0b11 { - 0 => nusb::transfer::ControlType::Standard, - 1 => nusb::transfer::ControlType::Class, - 2 => nusb::transfer::ControlType::Vendor, - _ => unimplemented!(), - }, - recipient: match setup.request_type & 0b11111 { - 0 => nusb::transfer::Recipient::Device, - 1 => nusb::transfer::Recipient::Interface, - 2 => nusb::transfer::Recipient::Endpoint, - 3 => nusb::transfer::Recipient::Other, - _ => unimplemented!(), - }, - request: setup.request, - value: setup.value, - index: setup.index, - }; // control if cfg!(not(target_os = "windows")) { if setup.request_type & 0x80 == 0 { // control out #[cfg(not(target_os = "windows"))] - handle.control_out_blocking(control, req, timeout).ok(); + { + let control_out = nusb::transfer::ControlOut { + control_type: match (setup.request_type >> 5) & 0b11 { + 0 => nusb::transfer::ControlType::Standard, + 1 => nusb::transfer::ControlType::Class, + 2 => nusb::transfer::ControlType::Vendor, + _ => unimplemented!(), + }, + recipient: match setup.request_type & 0b11111 { + 0 => nusb::transfer::Recipient::Device, + 1 => nusb::transfer::Recipient::Interface, + 2 => nusb::transfer::Recipient::Endpoint, + 3 => nusb::transfer::Recipient::Other, + _ => unimplemented!(), + }, + request: setup.request, + value: setup.value, + index: setup.index, + data: req, + }; + handle.control_out(control_out, timeout).wait().ok(); + } } else { // control in #[cfg(not(target_os = "windows"))] - if let Ok(len) = handle.control_in_blocking(control, &mut buffer, timeout) { - return Ok(Vec::from(&buffer[..len])); + { + let control_in = nusb::transfer::ControlIn { + control_type: match (setup.request_type >> 5) & 0b11 { + 0 => nusb::transfer::ControlType::Standard, + 1 => nusb::transfer::ControlType::Class, + 2 => nusb::transfer::ControlType::Vendor, + _ => unimplemented!(), + }, + recipient: match setup.request_type & 0b11111 { + 0 => nusb::transfer::Recipient::Device, + 1 => nusb::transfer::Recipient::Interface, + 2 => nusb::transfer::Recipient::Endpoint, + 3 => nusb::transfer::Recipient::Other, + _ => unimplemented!(), + }, + request: setup.request, + value: setup.value, + index: setup.index, + length: transfer_buffer_length as u16, + }; + if let Ok(data) = handle.control_in(control_in, timeout).wait() { + return Ok(data); + } } } } else { diff --git a/src/lib.rs b/src/lib.rs index 6dd9e58..f02bdab 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,6 +3,7 @@ use log::*; use num_derive::FromPrimitive; use num_traits::FromPrimitive; +use nusb::MaybeFuture; use rusb::*; use std::any::Any; use std::collections::{HashMap, VecDeque}; @@ -58,7 +59,7 @@ impl UsbIpServer { pub fn with_nusb_devices(nusb_device_infos: Vec) -> Vec { let mut devices = vec![]; for device_info in nusb_device_infos { - let dev = match device_info.open() { + let dev = match device_info.open().wait() { Ok(dev) => dev, Err(err) => { warn!("Impossible to open device {device_info:?}: {err}, ignoring device",); @@ -78,7 +79,7 @@ impl UsbIpServer { for intf in cfg.interfaces() { // ignore alternate settings let intf_num = intf.interface_number(); - let intf = dev.claim_interface(intf_num).unwrap(); + let intf = dev.claim_interface(intf_num).wait().unwrap(); let alt_setting = intf.descriptors().next().unwrap(); let mut endpoints = vec![]; @@ -100,7 +101,7 @@ impl UsbIpServer { interface_subclass: alt_setting.subclass(), interface_protocol: alt_setting.protocol(), endpoints, - string_interface: alt_setting.string_index().unwrap_or(0), + string_interface: alt_setting.string_index().map(|nz| nz.get()).unwrap_or(0), class_specific_descriptor: Vec::new(), handler, }); @@ -108,17 +109,17 @@ impl UsbIpServer { let mut device = UsbDevice { path: format!( "/sys/bus/{}/{}/{}", - device_info.bus_number(), + device_info.busnum(), device_info.device_address(), 0 ), bus_id: format!( "{}-{}-{}", - device_info.bus_number(), + device_info.busnum(), device_info.device_address(), 0, ), - bus_num: device_info.bus_number() as u32, + bus_num: device_info.busnum() as u32, dev_num: 0, speed: device_info.speed().unwrap() as u32, vendor_id: device_info.vendor_id(), From fdbb9574aad8f6dd9bbff2e79645ffe22d339ff8 Mon Sep 17 00:00:00 2001 From: Jiajie Chen Date: Wed, 24 Dec 2025 23:17:29 +0800 Subject: [PATCH 04/10] Fix missing max packet size handling --- src/cdc.rs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/cdc.rs b/src/cdc.rs index 9b57c4b..7308a28 100644 --- a/src/cdc.rs +++ b/src/cdc.rs @@ -75,9 +75,17 @@ impl UsbInterfaceHandler for UsbCdcAcmHandler { return Ok(vec![]); } else { // bulk in - // TODO: handle max packet size - let resp = self.tx_buffer.clone(); - self.tx_buffer.clear(); + // Handle max packet size - return data in chunks of max_packet_size + let max_packet_size = ep.max_packet_size as usize; + let resp = if self.tx_buffer.len() > max_packet_size { + // Return only the first chunk (max_packet_size bytes) + self.tx_buffer.drain(..max_packet_size).collect::>() + } else { + // Return all data if it fits in one packet + let resp = self.tx_buffer.clone(); + self.tx_buffer.clear(); + resp + }; return Ok(resp); } } From b83f58439101b1593141e1f4cf62cedc4fcb5cc2 Mon Sep 17 00:00:00 2001 From: Jiajie Chen Date: Wed, 24 Dec 2025 23:18:11 +0800 Subject: [PATCH 05/10] Handle upper case in hid keyboard report --- src/hid.rs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/hid.rs b/src/hid.rs index 0dc84ea..4dbd534 100644 --- a/src/hid.rs +++ b/src/hid.rs @@ -35,16 +35,17 @@ pub struct UsbHidKeyboardReport { impl UsbHidKeyboardReport { pub fn from_ascii(ascii: u8) -> UsbHidKeyboardReport { - // TODO: casing - let key = match ascii { - b'a'..=b'z' => ascii - b'a' + 4, - b'1'..=b'9' => ascii - b'1' + 30, - b'0' => 39, - b'\r' | b'\n' => 40, + let (modifier, key) = match ascii { + b'a'..=b'z' => (0, ascii - b'a' + 4), + b'A'..=b'Z' => (0x02, ascii - b'A' + 4), // Left Shift modifier + b'1'..=b'9' => (0, ascii - b'1' + 30), + b'0' => (0, 39), + b'\r' | b'\n' => (0, 40), + b' ' => (0, 44), // Space _ => unimplemented!("Unrecognized ascii {}", ascii), }; UsbHidKeyboardReport { - modifier: 0, + modifier, keys: [key, 0, 0, 0, 0, 0], } } From 48d0d9ca342f5bf4638a16764ffaa42328d34a57 Mon Sep 17 00:00:00 2001 From: Jiajie Chen Date: Wed, 24 Dec 2025 23:23:25 +0800 Subject: [PATCH 06/10] Fix bus num on non-Linux platform --- src/lib.rs | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index f02bdab..b9fae1a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -106,20 +106,24 @@ impl UsbIpServer { handler, }); } + + // Platform-specific bus number (Linux-only) + let bus_num_val: u32; + #[cfg(target_os = "linux")] + { + bus_num_val = device_info.busnum() as u32; + } + #[cfg(not(target_os = "linux"))] + { + bus_num_val = 0; + } + + let device_address = device_info.device_address(); + let mut device = UsbDevice { - path: format!( - "/sys/bus/{}/{}/{}", - device_info.busnum(), - device_info.device_address(), - 0 - ), - bus_id: format!( - "{}-{}-{}", - device_info.busnum(), - device_info.device_address(), - 0, - ), - bus_num: device_info.busnum() as u32, + path: format!("/sys/bus/{}/{}/{}", bus_num_val, device_address, 0), + bus_id: format!("{}-{}-{}", bus_num_val, device_address, 0), + bus_num: bus_num_val, dev_num: 0, speed: device_info.speed().unwrap() as u32, vendor_id: device_info.vendor_id(), From c8cd214e1dbe8bb959ce2da156f6682111760dd2 Mon Sep 17 00:00:00 2001 From: Jiajie Chen Date: Thu, 25 Dec 2025 20:52:45 +0800 Subject: [PATCH 07/10] Fix clippy warning: cloned_ref_to_slice_refs Replace &[device.clone()] with std::slice::from_ref(&device) in test to address clippy::cloned_ref_to_slice_refs warning. Co-authored-by: Qwen-Coder --- src/usbip_protocol.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/usbip_protocol.rs b/src/usbip_protocol.rs index c961316..9414cf6 100644 --- a/src/usbip_protocol.rs +++ b/src/usbip_protocol.rs @@ -621,7 +621,7 @@ mod tests { fn byte_serialize_op_rep_devlist() { setup_test_logger(); let device = example_device(); - let res = UsbIpResponse::op_rep_devlist(&[device.clone()]); + let res = UsbIpResponse::op_rep_devlist(std::slice::from_ref(&device)); assert_eq!( res.to_bytes(), [ From a19bfe8301e09da75fb7417033ccb686dd970eb4 Mon Sep 17 00:00:00 2001 From: Jiajie Chen Date: Thu, 25 Dec 2025 20:58:48 +0800 Subject: [PATCH 08/10] Simplify documentation (fixes #59) Remove redundant sections from README and update LICENSE copyright year. Co-authored-by: Qwen-Coder --- LICENSE | 4 ++-- README.md | 55 ++++++++++++++++++++++++++++++++++++++++--------------- 2 files changed, 42 insertions(+), 17 deletions(-) diff --git a/LICENSE b/LICENSE index 2f9967a..8d0ea9b 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2020 Jiajie Chen +Copyright (c) 2020-2025 Jiajie Chen Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -18,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file +SOFTWARE. diff --git a/README.md b/README.md index fdd75db..f7db312 100644 --- a/README.md +++ b/README.md @@ -3,33 +3,58 @@ [![Coverage Status](https://coveralls.io/repos/github/jiegec/usbip/badge.svg?branch=master)](https://coveralls.io/github/jiegec/usbip?branch=master) [![crates.io](https://img.shields.io/crates/v/usbip.svg)](https://crates.io/crates/usbip) -A Rust library to run a USB/IP server to simulate USB devices. +A Rust library to run a USB/IP server to simulate USB devices and share real USB devices over a network. -It also enables sharing devices from an OS supporting libusb(libusb claims that it supports Linux, macOS, Windows, OpenBSD/NetBSD, Haiku and Solaris) to another OS supporting USB/IP(Linux, Windows). Sharing an CCID SmartCard from macOS to Linux is tested by running `gpg --card-status`. +## What is USB/IP? + +USB/IP is a network protocol that allows USB devices to be shared between computers over a network. It enables: + +- **Device simulation**: Create virtual USB devices that can be accessed remotely +- **Device sharing**: Share physical USB devices from one machine to another +- **Cross-platform**: Works across different operating systems (Linux, etc.) + +## Installation + +### Prerequisites + +Install Rust from the [official documentation](https://www.rust-lang.org/tools/install). + +### Building from source + +```bash +git clone https://github.com/jiegec/usbip.git +cd usbip +cargo build --release +``` ## How to use -See examples directory. Three examples are provided: +### Examples -1. hid_keyboard: Simulate a hid keyboard that types something every second. -2. cdc_acm_serial: Simulate a serial that gets a character every second. -3. host: Act like original usb/ip sharing server, sharing one device from one machine to another. Also supports sharing from macOS to Linux! +The `examples/` directory contains three example programs: -To run example, run: +1. **hid_keyboard**: Simulate a HID keyboard that types something every second +2. **cdc_acm_serial**: Simulate a CDC ACM serial device that receives a character every second +3. **host**: Act as a USB/IP server, sharing physical devices from the host machine to remote clients + +#### Running an example ```bash -$ env RUST_LOG=info cargo run --example hid_keyboard +cargo run --example hid_keyboard ``` -Then, in a USB/IP client environment: +#### Connecting from a USB/IP client + +On the client machine (e.g. Linux with USB/IP support): ```bash -$ usbip list -r $remote_ip -$ usbip attach -r $remote_ip -b $bus_id +# List available devices +usbip list -r $remote_ip + +# Attach to a device +usbip attach -r $remote_ip -b $bus_id ``` -Then, you can inspect the simulated USB device behavior in both sides. +## License -## API - -See code comments. Not finalized yet, so get prepared for api breaking changes. +MIT License - see [LICENSE](LICENSE) file for details. From 0878920532267972542554ffbd555376085ea9f2 Mon Sep 17 00:00:00 2001 From: Jiajie Chen Date: Tue, 27 Jan 2026 13:00:46 +0800 Subject: [PATCH 09/10] Release v0.8.0 --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 387a6f5..6ebb2cd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "usbip" -version = "0.7.1" +version = "0.8.0" authors = ["Jiajie Chen "] edition = "2024" license = "MIT" From 4df87e3b841821ea0cce2bf9412a8859a3f11a1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dav=C3=AD=C3=B0=20Steinn=20Geirsson?= Date: Wed, 18 Mar 2026 23:54:25 +0000 Subject: [PATCH 10/10] Add flake.nix and fix test that only passed in debug builds - Add flake.nix with package (including examples) and dev shell - Track Cargo.lock (removed from .gitignore) as required by the nix build - Change debug_assert! to assert! in UsbIpResponse::to_bytes() so invariant checks fire in release mode, fixing the byte_serialize_invalid_usbip_ret_submit #[should_panic] test Co-Authored-By: Claude Opus 4.6 (1M context) --- .gitignore | 2 +- Cargo.lock | 614 ++++++++++++++++++++++++++++++++++++++++++ flake.lock | 61 +++++ flake.nix | 65 +++++ src/usbip_protocol.rs | 6 +- 5 files changed, 744 insertions(+), 4 deletions(-) create mode 100644 Cargo.lock create mode 100644 flake.lock create mode 100644 flake.nix diff --git a/.gitignore b/.gitignore index 96ef6c0..d787b70 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,2 @@ /target -Cargo.lock +/result diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..f18aec5 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,614 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + +[[package]] +name = "anstream" +version = "0.6.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000" + +[[package]] +name = "anstyle-parse" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" +dependencies = [ + "anstyle", + "once_cell_polyfill", + "windows-sys", +] + +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + +[[package]] +name = "bitflags" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" + +[[package]] +name = "bytes" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" + +[[package]] +name = "cc" +version = "1.2.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a0dd1ca384932ff3641c8718a02769f1698e7563dc6974ffd03346116310423" +dependencies = [ + "find-msvc-tools", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "colorchoice" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570" + +[[package]] +name = "core-foundation" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "env_filter" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a1c3cc8e57274ec99de65301228b537f1e4eedc1b8e0f9411c6caac8ae7308f" +dependencies = [ + "log", + "regex", +] + +[[package]] +name = "env_logger" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2daee4ea451f429a58296525ddf28b45a3b64f1acf6587e2067437bb11e218d" +dependencies = [ + "anstream", + "anstyle", + "env_filter", + "jiff", + "log", +] + +[[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "find-msvc-tools" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" + +[[package]] +name = "futures-core" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" + +[[package]] +name = "io-kit-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06d3a048d09fbb6597dbf7c69f40d14df4a49487db1487191618c893fc3b1c26" +dependencies = [ + "core-foundation-sys", + "mach2", +] + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" + +[[package]] +name = "jiff" +version = "0.2.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a3546dc96b6d42c5f24902af9e2538e82e39ad350b0c766eb3fbf2d8f3d8359" +dependencies = [ + "jiff-static", + "log", + "portable-atomic", + "portable-atomic-util", + "serde_core", +] + +[[package]] +name = "jiff-static" +version = "0.2.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a8c8b344124222efd714b73bb41f8b5120b27a7cc1c75593a6ff768d9d05aa4" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "libc" +version = "0.2.183" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5b646652bf6661599e1da8901b3b9522896f01e736bad5f723fe7a3a27f899d" + +[[package]] +name = "libusb1-sys" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da050ade7ac4ff1ba5379af847a10a10a8e284181e060105bf8d86960ce9ce0f" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "linux-raw-sys" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" + +[[package]] +name = "lock_api" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" + +[[package]] +name = "mach2" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a1b95cd5421ec55b445b5ae102f5ea0e768de1f82bd3001e11f426c269c3aea" +dependencies = [ + "libc", +] + +[[package]] +name = "memchr" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" + +[[package]] +name = "mio" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" +dependencies = [ + "libc", + "wasi", + "windows-sys", +] + +[[package]] +name = "num-derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "nusb" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a330b3bc7f8b4fc729a4c63164b3927eeeaced198222a3ce6b8b6e034851b7a" +dependencies = [ + "core-foundation", + "core-foundation-sys", + "futures-core", + "io-kit-sys", + "linux-raw-sys", + "log", + "once_cell", + "rustix", + "slab", + "windows-sys", +] + +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + +[[package]] +name = "once_cell_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" + +[[package]] +name = "parking_lot" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-link", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "pkg-config" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" + +[[package]] +name = "portable-atomic" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49" + +[[package]] +name = "portable-atomic-util" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "091397be61a01d4be58e7841595bd4bfedb15f1cd54977d79b8271e94ed799a3" +dependencies = [ + "portable-atomic", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "redox_syscall" +version = "0.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" +dependencies = [ + "bitflags", +] + +[[package]] +name = "regex" +version = "1.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" + +[[package]] +name = "rusb" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab9f9ff05b63a786553a4c02943b74b34a988448671001e9a27e2f0565cc05a4" +dependencies = [ + "libc", + "libusb1-sys", + "serde", +] + +[[package]] +name = "rustix" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "signal-hook-registry" +version = "1.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b" +dependencies = [ + "errno", + "libc", +] + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + +[[package]] +name = "socket2" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tokio" +version = "1.50.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27ad5e34374e03cfffefc301becb44e9dc3c17584f414349ebe29ed26661822d" +dependencies = [ + "bytes", + "libc", + "mio", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys", +] + +[[package]] +name = "tokio-macros" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c55a2eff8b69ce66c84f85e1da1c233edc36ceb85a2058d11b0d6a3c7e7569c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "usbip" +version = "0.8.0" +dependencies = [ + "env_logger", + "log", + "num-derive", + "num-traits", + "nusb", + "rusb", + "serde", + "tokio", +] + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..752172b --- /dev/null +++ b/flake.lock @@ -0,0 +1,61 @@ +{ + "nodes": { + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1731533236, + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1773628058, + "narHash": "sha256-hpXH0z3K9xv0fHaje136KY872VT2T5uwxtezlAskQgY=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "f8573b9c935cfaa162dd62cc9e75ae2db86f85df", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..98fb844 --- /dev/null +++ b/flake.nix @@ -0,0 +1,65 @@ +{ + description = "A library to run USB/IP server"; + + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable"; + flake-utils.url = "github:numtide/flake-utils"; + }; + + outputs = { self, nixpkgs, flake-utils }: + flake-utils.lib.eachDefaultSystem (system: + let + pkgs = nixpkgs.legacyPackages.${system}; + + nativeBuildInputs = with pkgs; [ + rustc + cargo + pkg-config + ]; + + buildInputs = with pkgs; [ + libusb1 + ] ++ pkgs.lib.optionals pkgs.stdenv.hostPlatform.isLinux [ + udev + ]; + + usbip = pkgs.rustPlatform.buildRustPackage { + pname = "usbip"; + version = "0.8.0"; + + src = self; + + cargoHash = "sha256-PiJvE9CWbNIZif/ku3G+A7g5vSzl2O80a33NZdgmFL4="; + + inherit nativeBuildInputs buildInputs; + + buildFeatures = [ "serde" ]; + + # Build both the library and all examples + cargoBuildFlags = [ "--examples" ]; + + postInstall = '' + for f in target/*/release/examples/{hid_keyboard,cdc_acm_serial,host}; do + [ -f "$f" ] && install -Dm755 "$f" "$out/bin/$(basename $f)" + done + ''; + + meta = with pkgs.lib; { + description = "A library to run USB/IP server"; + homepage = "https://github.com/jiegec/usbip"; + license = licenses.mit; + mainProgram = "host"; + }; + }; + in + { + packages = { + default = usbip; + inherit usbip; + }; + + devShells.default = pkgs.mkShell { + inherit nativeBuildInputs buildInputs; + }; + }); +} diff --git a/src/usbip_protocol.rs b/src/usbip_protocol.rs index 9414cf6..aaebf9a 100644 --- a/src/usbip_protocol.rs +++ b/src/usbip_protocol.rs @@ -384,8 +384,8 @@ impl UsbIpResponse { let mut result = Vec::with_capacity(48 + transfer_buffer.len() + iso_packet_descriptor.len()); - debug_assert!(header.command == USBIP_RET_SUBMIT.into()); - debug_assert!(if header.direction == Direction::In as u32 { + assert!(header.command == USBIP_RET_SUBMIT.into()); + assert!(if header.direction == Direction::In as u32 { actual_length == transfer_buffer.len() as u32 } else { actual_length == 0 @@ -405,7 +405,7 @@ impl UsbIpResponse { Self::UsbIpRetUnlink { ref header, status } => { let mut result = Vec::with_capacity(48); - debug_assert!(header.command == USBIP_RET_UNLINK.into()); + assert!(header.command == USBIP_RET_UNLINK.into()); result.extend_from_slice(&header.to_bytes()); result.extend_from_slice(&status.to_be_bytes());