fix: align bulk IN buffers and forward unknown string descriptors

Two fixes for mass storage passthrough:

1. Bulk IN buffer allocation now rounds up to the endpoint's
   max_packet_size. nusb (and the USB spec) require IN transfers to be
   multiples of max_packet_size. Without this, SCSI INQUIRY (36 bytes on
   a 512-byte max_packet_size endpoint) was rejected, causing the kernel
   to repeatedly reset the device.

2. String descriptor requests for indices not in the local string pool
   are now forwarded to the device handler. This fixes interface and
   configuration string descriptors (e.g., iInterface=5) that exist on
   the real device but weren't populated in the synthetic string pool.

Tested: USB mass storage gadget via dummy_hcd successfully enumerates,
mounts, reads, and writes through the USBIP passthrough.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Davíð Steinn Geirsson 2026-03-22 11:13:45 +00:00
parent 889017be3b
commit 63e9faf82d
2 changed files with 17 additions and 2 deletions

View file

@ -507,6 +507,18 @@ impl UsbDevice {
desc.resize(setup_packet.length as usize, 0);
}
Ok(UrbResponse { data: desc, ..Default::default() })
} else if self.device_handler.is_some() {
// Forward unknown string indices to the device handler
// (host passthrough: the real device knows its own strings)
let lock = self.device_handler.as_ref().unwrap();
let mut handler = lock.lock().unwrap();
handler.handle_urb(UrbRequest {
ep,
transfer_buffer_length,
setup: setup_packet,
data: out_data,
..Default::default()
})
} else {
Err(std::io::Error::new(
std::io::ErrorKind::InvalidInput,

View file

@ -334,17 +334,20 @@ impl UsbInterfaceHandler for NusbUsbHostInterfaceHandler {
} else if ep.attributes == EndpointAttributes::Bulk as u8 {
// bulk
if let Direction::In = ep.direction() {
// bulk in
// bulk in - round up to max_packet_size as required by USB spec
let mps = ep.max_packet_size.max(1) as usize;
let alloc_len = ((transfer_buffer_length as usize) + mps - 1) / mps * mps;
let mut endpoint = handle
.endpoint::<nusb::transfer::Bulk, nusb::transfer::In>(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 buffer = endpoint.allocate(alloc_len);
let completion = endpoint.transfer_blocking(buffer, timeout);
completion.status.map_err(|e| {
std::io::Error::new(std::io::ErrorKind::Other, format!("USB bulk IN failed: {e}"))
})?;
// Return only the actual bytes received (may be less than alloc_len)
return Ok(UrbResponse { data: completion.buffer.to_vec(), ..Default::default() });
} else {
// bulk out