diff --git a/system/drivers/usb-xhci-bus/usb-xhci-bus.zig b/system/drivers/usb-xhci-bus/usb-xhci-bus.zig index eef7f91..c2c5f8b 100644 --- a/system/drivers/usb-xhci-bus/usb-xhci-bus.zig +++ b/system/drivers/usb-xhci-bus/usb-xhci-bus.zig @@ -189,6 +189,10 @@ fn speedName(speed: u32) []const u8 { /// class driver against. var manager_handle: ?runtime.ipc.Handle = null; +// Per-root-port connected state from the previous tick, so the poll acts on +// empty->connected transitions (edge), never re-attempting a level every tick. +var prev_connected: [64]bool = [_]bool{false} ** 64; + fn scanPorts(manager: runtime.ipc.Handle) void { const engine = if (controller) |*c| c else { _ = runtime.system.write("/system/drivers/usb-xhci-bus: controller not initialised\n"); @@ -204,6 +208,7 @@ fn scanPorts(manager: runtime.ipc.Handle) void { var connected: u32 = 0; while (port <= engine.max_ports) : (port += 1) { if (!engine.portConnected(port)) continue; + if (port < prev_connected.len) prev_connected[port] = true; // don't re-fire the poll for these connected += 1; bringUpPort(manager, engine, port); } @@ -495,6 +500,29 @@ fn onNotification(badge: u64) void { if (badge & runtime.ipc.notify_timer_bit == 0) return; if (controller) |*engine| { engine.pump(); + // Poll every root port and reconcile — a device present but not yet + // enumerated is brought up; a device gone is torn down. This does NOT + // depend on a Port Status Change EVENT firing: the boot scan runs ~3 ms + // after the controller reset, far too early for a USB2 connection to + // debounce (~100 ms), and the SuperSpeed devices that DO show up early + // proved the event path unreliable for the late USB2 companion hub on + // real hardware. Polling catches it on the next tick regardless. + if (manager_handle) |manager| { + var port: u32 = 1; + while (port <= engine.max_ports and port <= prev_connected.len) : (port += 1) { + const connected = engine.portConnected(port); + const was = prev_connected[port]; + prev_connected[port] = connected; + if (connected and !was and engine.deviceOnPort(port) == null) { + // Rising edge the boot scan missed (it ran before the USB2 + // connection debounced): bring the device up now. + std.log.info("root port {d}: device appeared (PORTSC 0x{x:0>8})", .{ port, engine.portStatus(port) }); + bringUpPort(manager, engine, port); + } else if (!connected and was and engine.deviceOnPort(port) != null) { + tearDownPort(manager, engine, port); + } + } + } while (engine.takePortChange()) |port| { const manager = manager_handle orelse break; const connected = engine.portConnected(port);