diff --git a/drivers/usb/usbhubd/src/main.rs b/drivers/usb/usbhubd/src/main.rs index ec4efec6d2..5edab889dc 100644 --- a/drivers/usb/usbhubd/src/main.rs +++ b/drivers/usb/usbhubd/src/main.rs @@ -390,16 +390,30 @@ fn main() { // only poll GetPortStatus for ports whose bit is set. // If EP1 is unavailable, fall back to polling all ports. // - // Bitmap size: ceil(ports / 8) bytes. - // Port N is bit (N-1) of byte (N-1)/8. + // Bitmap size: ceil((ports + 1) / 8) bytes — bit 0 is the hub itself, + // bit N is port N (USB 2.0 spec 11.12.4; Linux hub_irq uses bit i for + // port i). The previous revision used bit (N-1) for port N, an + // off-by-one that processed every event on the wrong port. const POLL_FALLBACK_MS: u64 = 200; - let bitmap_size = (ports as usize + 7) / 8; + let bitmap_size = (ports as usize + 8) / 8; let mut bitmap = vec![0u8; bitmap_size]; + // Mask with bits 1..=ports set (every real port; bit 0 = hub, skipped). + let all_ports_mask: u64 = ((1u64 << ports) - 1) << 1; + + // Linux hub_activate() performs a full port scan before arming the + // status-change endpoint — devices already connected at hub power-on + // must be enumerated even if no interrupt ever arrives (a blocking + // first EP1 read would otherwise hang the daemon before any scan). + let mut first_scan = true; + loop { // Build a port-change mask. - // Bit N set = port (N+1) needs processing. - let changed: u64 = if let Some(ref mut ep) = intr_ep_handle { + // Bit N set = port N needs processing (bit 0 = hub status change). + let changed: u64 = if first_scan { + first_scan = false; + all_ports_mask + } else if let Some(ref mut ep) = intr_ep_handle { match ep.transfer_read(&mut bitmap) { Ok(_) => { let mut mask = 0u64; @@ -412,16 +426,16 @@ fn main() { } Err(e) => { log::warn!("usbhubd: interrupt transfer failed ({}), falling back to poll", e); - (1u64 << ports) - 1 + all_ports_mask } } } else { // Polling mode: process all ports. thread::sleep(time::Duration::from_millis(POLL_FALLBACK_MS)); - (1u64 << ports) - 1 + all_ports_mask }; for port in 1..=ports { - let bit = 1u64 << (port - 1); + let bit = 1u64 << port; if (changed & bit) == 0 { continue; }