fix: USB spec conformance and reliability improvements
Spec conformance: - Add missing standard control requests for simulated devices: GetConfiguration, GetStatus (device/interface/endpoint), ClearFeature, SetFeature, SetAddress - Replace debug_assert with truncate for path/bus_id wire format to prevent protocol desync in release builds Reliability: - server() now returns Result instead of panicking on bind failure Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
3f4e8effce
commit
804a4910a0
3 changed files with 41 additions and 7 deletions
|
|
@ -42,7 +42,7 @@
|
|||
### 7. Missing standard control requests (simulated devices)
|
||||
- **File:** `lib/src/device.rs:346-778`
|
||||
- **Issue:** `GetStatus`, `ClearFeature`, `SetFeature`, `GetConfiguration`, `SetAddress` are unhandled for simulated devices.
|
||||
- **Status:** [ ] TODO
|
||||
- **Status:** [x] DONE — Added GetConfiguration, GetStatus (device/interface/endpoint), ClearFeature, SetFeature, SetAddress handlers.
|
||||
|
||||
### 8. ISO `actual_length` sum not validated
|
||||
- **File:** `lib/src/lib.rs:551-552`
|
||||
|
|
@ -60,7 +60,7 @@
|
|||
### 10. `server()` panics on bind failure
|
||||
- **File:** `lib/src/lib.rs:773`
|
||||
- **Issue:** `expect("bind to addr")` panics instead of returning `Result`.
|
||||
- **Status:** [ ] TODO
|
||||
- **Status:** [x] DONE — Changed return type to `Result<()>`, replaced `expect` with `?`.
|
||||
|
||||
### 11. Error status mapping via string matching
|
||||
- **File:** `lib/src/lib.rs:584-592`
|
||||
|
|
@ -75,4 +75,4 @@
|
|||
### 13. `debug_assert` for path/bus_id length
|
||||
- **File:** `lib/src/device.rs:252-260`
|
||||
- **Issue:** `debug_assert!` compiled out in release; silent truncation if path > 256 bytes.
|
||||
- **Status:** [ ] TODO
|
||||
- **Status:** [x] DONE — Replaced `debug_assert!` + `resize` with `truncate` + `resize`.
|
||||
|
|
|
|||
|
|
@ -257,12 +257,12 @@ impl UsbDevice {
|
|||
let mut result = Vec::with_capacity(312);
|
||||
|
||||
let mut path = self.path.as_bytes().to_vec();
|
||||
debug_assert!(path.len() <= 256);
|
||||
path.truncate(256);
|
||||
path.resize(256, 0);
|
||||
result.extend_from_slice(path.as_slice());
|
||||
|
||||
let mut bus_id = self.bus_id.as_bytes().to_vec();
|
||||
debug_assert!(bus_id.len() <= 32);
|
||||
bus_id.truncate(32);
|
||||
bus_id.resize(32, 0);
|
||||
result.extend_from_slice(bus_id.as_slice());
|
||||
|
||||
|
|
@ -609,6 +609,25 @@ impl UsbDevice {
|
|||
}
|
||||
}
|
||||
}
|
||||
(0b10000000, Some(GetConfiguration)) => {
|
||||
debug!("Get configuration value");
|
||||
Ok(UrbResponse { data: vec![self.configuration_value], ..Default::default() })
|
||||
}
|
||||
(0b10000000, Some(GetStatus)) => {
|
||||
// Device recipient: self-powered=0, remote-wakeup=0
|
||||
debug!("Get status (device)");
|
||||
Ok(UrbResponse { data: vec![0x00, 0x00], ..Default::default() })
|
||||
}
|
||||
(0b10000001, Some(GetStatus)) => {
|
||||
// Interface recipient: reserved, always zero
|
||||
debug!("Get status (interface)");
|
||||
Ok(UrbResponse { data: vec![0x00, 0x00], ..Default::default() })
|
||||
}
|
||||
(0b10000010, Some(GetStatus)) => {
|
||||
// Endpoint recipient: halt=0
|
||||
debug!("Get status (endpoint)");
|
||||
Ok(UrbResponse { data: vec![0x00, 0x00], ..Default::default() })
|
||||
}
|
||||
(0b10000001, Some(GetInterface)) => {
|
||||
let intf_index = setup_packet.index as usize & 0xFF;
|
||||
match self.interface_states.get(intf_index) {
|
||||
|
|
@ -718,6 +737,21 @@ impl UsbDevice {
|
|||
)),
|
||||
}
|
||||
}
|
||||
(0b00000010, Some(ClearFeature)) => {
|
||||
// Endpoint recipient: no-op (simulated device doesn't stall)
|
||||
debug!("Clear feature (endpoint)");
|
||||
Ok(UrbResponse::default())
|
||||
}
|
||||
(0b00000010, Some(SetFeature)) => {
|
||||
// Endpoint recipient: no-op
|
||||
debug!("Set feature (endpoint)");
|
||||
Ok(UrbResponse::default())
|
||||
}
|
||||
(0b00000000, Some(SetAddress)) => {
|
||||
// No-op: address already assigned by bus
|
||||
debug!("Set address (no-op)");
|
||||
Ok(UrbResponse::default())
|
||||
}
|
||||
_ if setup_packet.request_type & 0xF == 1 => {
|
||||
// to interface
|
||||
// see https://www.beyondlogic.org/usbnutshell/usb6.shtml
|
||||
|
|
|
|||
|
|
@ -874,8 +874,8 @@ pub async fn handler<T: AsyncReadExt + AsyncWriteExt + Unpin + Send + 'static>(
|
|||
}
|
||||
|
||||
/// Spawn a USB/IP server at `addr` using [TcpListener]
|
||||
pub async fn server(addr: SocketAddr, server: Arc<UsbIpServer>) {
|
||||
let listener = TcpListener::bind(addr).await.expect("bind to addr");
|
||||
pub async fn server(addr: SocketAddr, server: Arc<UsbIpServer>) -> Result<()> {
|
||||
let listener = TcpListener::bind(addr).await?;
|
||||
|
||||
let server = async move {
|
||||
loop {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue