diff --git a/library/runtime/block.zig b/library/runtime/block.zig index 4ac2c62..b0d1734 100644 --- a/library/runtime/block.zig +++ b/library/runtime/block.zig @@ -61,7 +61,10 @@ pub fn open() ?Device { // enumeration, mass-storage bring-up) must complete first, which can take // tens of seconds under emulation. var attempts: usize = 0; - while (attempts < 1200) : (attempts += 1) { + // 30 s covers the slowest observed healthy chain (a flaky QEMU enumeration + // completed at ~24 s); a machine whose stick genuinely failed setup should + // not sit a further minute pretending otherwise. + while (attempts < 600) : (attempts += 1) { if (ipc.lookup(.block)) |handle| return .{ .endpoint = handle }; system.sleep(50); } diff --git a/system/drivers/usb-xhci-bus/usb-xhci-library.zig b/system/drivers/usb-xhci-bus/usb-xhci-library.zig index 7d2f511..c25f662 100644 --- a/system/drivers/usb-xhci-bus/usb-xhci-library.zig +++ b/system/drivers/usb-xhci-bus/usb-xhci-library.zig @@ -620,8 +620,15 @@ pub const Controller = struct { .parameter = device.input_context.physical, .control = trbControl(.address_device, @as(u32, device.slot_id) << 24), }); - const code = self.awaitCommand(physical) orelse return false; - return code == @intFromEnum(CompletionCode.success); + const code = self.awaitCommand(physical) orelse { + std.log.info("port {d} setup: Address Device timed out", .{device.port}); + return false; + }; + if (code != @intFromEnum(CompletionCode.success)) { + std.log.info("port {d} setup: Address Device completion code {d}", .{ device.port, code }); + return false; + } + return true; } /// Reset the port, enable a slot, and address the device on it: after this the @@ -629,9 +636,25 @@ pub const Controller = struct { /// or null on any failure. The EP0 MPS is taken from the speed default and /// corrected from the device descriptor by `refreshMaxPacketSize0` if needed. pub fn setupDevice(self: *Controller, port: u32, speed: u32) ?*Device { - if (!self.resetPort(port)) return null; - const slot_id = self.enableSlot() orelse return null; - const device = self.allocateDevice() orelse return null; + // A SuperSpeed port that has trained its link is ALREADY enabled — the + // xHCI advances USB3 ports to Enabled with no reset (spec 4.3). Driving + // a hot reset into a live SS link drops PED mid-reset on real silicon + // (observed: "setup failed" in the same millisecond as "connected"). + // Only a not-yet-enabled port — every USB2 device, or a stuck SS link — + // needs the reset to enable. + const already_enabled = speed >= 4 and self.portStatus(port) & portsc_enabled != 0; + if (!already_enabled and !self.resetPort(port)) { + std.log.info("port {d} setup: port reset failed (PORTSC 0x{x:0>8})", .{ port, self.portStatus(port) }); + return null; + } + const slot_id = self.enableSlot() orelse { + std.log.info("port {d} setup: Enable Slot failed", .{port}); + return null; + }; + const device = self.allocateDevice() orelse { + std.log.info("port {d} setup: no free device slot", .{port}); + return null; + }; device.* = .{ .used = true, .slot_id = slot_id, @@ -652,6 +675,7 @@ pub const Controller = struct { return device; } + fn abandon(self: *Controller, device: *Device) ?*Device { _ = self; device.used = false;