usb: don't spin on a SuperSpeed hub's change bits; log hub port status
Real-hardware finding: a SuperSpeed hub (the user's Genesys, 4 SS downstream ports) HUNG the bus driver at boot — it stopped logging with no fault, and the hub's port power dropped. Cause: a SuperSpeed hub has change bits a USB2 hub lacks (link-state, BH-reset), and the B4 servicing — written and tested against QEMU's USB2 hub — never cleared them, so the hub's status-change endpoint re-reported the same port forever and the driver spun servicing it, never reaching the USB2 companion hub where the full-speed keyboard actually lives. hubPortStatusAck now clears the SuperSpeed-only change features too (gated on hub speed; a USB2 hub is unaffected), and the tick services a bounded batch of hub changes (32) before yielding — so even if a hub's change bits misbehave, the driver cannot spin the machine. A per-port status log line surfaces exactly what a hub reports, so the next real boot shows the SS hub's port states and whether the USB2 companion enumerates. QEMU usb-hub cases still green (the SS clears are gated off for QEMU's USB2 hub).
This commit is contained in:
@@ -951,8 +951,18 @@ pub const Controller = struct {
|
||||
/// bits (so it can signal again). Returns the wPortStatus word.
|
||||
pub fn hubPortStatusAck(self: *Controller, hub: *Device, port: u16) ?u32 {
|
||||
const status = self.readHubPortStatus(hub, port) orelse return null;
|
||||
// Acknowledge every latched change bit. A SuperSpeed hub has extra ones
|
||||
// (link-state, BH-reset) beyond a USB2 hub's connection/reset — leaving
|
||||
// any set makes the hub's status-change endpoint re-report the same port
|
||||
// forever, spinning the driver (a real SuperSpeed hub hung boot here;
|
||||
// QEMU's USB2 hub has none of these). Clearing an inapplicable feature
|
||||
// is harmless (the hub STALLs it and we move on).
|
||||
_ = self.controlTransfer(hub, hubreq.clearPortFeature(hubreq.feature_c_port_connection, port), &.{}, false);
|
||||
_ = self.controlTransfer(hub, hubreq.clearPortFeature(hubreq.feature_c_port_reset, port), &.{}, false);
|
||||
if (hub.speed >= 4) {
|
||||
_ = self.controlTransfer(hub, hubreq.clearPortFeature(hubreq.feature_c_port_link_state, port), &.{}, false);
|
||||
_ = self.controlTransfer(hub, hubreq.clearPortFeature(hubreq.feature_c_bh_port_reset, port), &.{}, false);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user