diff --git a/system/drivers/usb-xhci-bus/usb-xhci-library.zig b/system/drivers/usb-xhci-bus/usb-xhci-library.zig index feaef0b..656ca98 100644 --- a/system/drivers/usb-xhci-bus/usb-xhci-library.zig +++ b/system/drivers/usb-xhci-bus/usb-xhci-library.zig @@ -101,6 +101,7 @@ pub const TrbType = enum(u6) { pub const CompletionCode = enum(u8) { invalid = 0, success = 1, + usb_transaction_error = 4, short_packet = 13, _, }; @@ -836,19 +837,31 @@ pub const Controller = struct { } fn addressDeviceCommand(self: *Controller, device: *Device) bool { - const physical = self.submitCommand(.{ - .parameter = device.input_context.physical, - .control = trbControl(.address_device, @as(u32, device.slot_id) << 24), - }); - 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; + // Retry on a USB Transaction Error (code 4): a freshly-reset device can + // miss the first SET_ADDRESS; re-reset the port and try again (xHCI + // 4.6.5). Up to 3 attempts. + var attempt: u32 = 0; + while (attempt < 3) : (attempt += 1) { + const physical = self.submitCommand(.{ + .parameter = device.input_context.physical, + .control = trbControl(.address_device, @as(u32, device.slot_id) << 24), + }); + const code = self.awaitCommand(physical) orelse { + std.log.info("port {d} setup: Address Device timed out (attempt {d})", .{ device.port, attempt + 1 }); + return false; + }; + if (code == @intFromEnum(CompletionCode.success)) return true; + std.log.info("port {d} setup: Address Device completion code {d} (attempt {d})", .{ device.port, code, attempt + 1 }); + if (code != @intFromEnum(CompletionCode.usb_transaction_error)) return false; + // Re-reset a root-port device and wait the recovery interval before + // retrying. (A device behind a hub is reset through the hub — not + // retried here; its port was reset in serviceHubPort.) + if (device.parent_slot == 0) { + if (!self.resetPort(device.port)) return false; + system.sleep(10); + } else return false; } - return true; + return false; } /// Reset the port, enable a slot, and address the device on it: after this the @@ -874,6 +887,11 @@ pub const Controller = struct { // (pre-reset reads misreport on real controllers; M20). effective_speed = (self.portStatus(port) >> 10) & 0xF; if (effective_speed == 0) effective_speed = speed; // defensive: keep the caller's read + // USB 2.0 spec 7.1.7.5: a device needs a reset-recovery interval + // (TRSTRCY, 10 ms) after reset before it answers SET_ADDRESS. + // Addressing immediately gives a USB Transaction Error (code 4) on + // real full-speed devices; QEMU tolerates the omission. + system.sleep(10); } const slot_id = self.enableSlot() orelse { std.log.info("port {d} setup: Enable Slot failed", .{port});