usb: post-reset recovery delay + Address Device retry on transaction error
Real-hardware root-cause, from the now-readable log: the tick-poll fix DID catch the user's full-speed devices (USB2 root ports 3, 9, 11 'device appeared — Full-speed'), but every one failed 'Address Device completion code 4' = USB Transaction Error. Cause: danos addressed the device immediately after the port reset, but USB 2.0 (spec 7.1.7.5) requires a reset-recovery interval (TRSTRCY, 10 ms) before a device answers SET_ADDRESS. QEMU tolerates the omission; real full-speed devices do not. setupDevice now waits 10 ms after a port reset before addressing, and addressDeviceCommand retries up to 3 times on a transaction error, re-resetting a root-port device between attempts (xHCI 4.6.5 recovery). QEMU USB cases still green. Real-hardware confirmation pending — this is the specific fix for the user's keyboard/mouse failing to address.
This commit is contained in:
@@ -101,6 +101,7 @@ pub const TrbType = enum(u6) {
|
|||||||
pub const CompletionCode = enum(u8) {
|
pub const CompletionCode = enum(u8) {
|
||||||
invalid = 0,
|
invalid = 0,
|
||||||
success = 1,
|
success = 1,
|
||||||
|
usb_transaction_error = 4,
|
||||||
short_packet = 13,
|
short_packet = 13,
|
||||||
_,
|
_,
|
||||||
};
|
};
|
||||||
@@ -836,19 +837,31 @@ pub const Controller = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn addressDeviceCommand(self: *Controller, device: *Device) bool {
|
fn addressDeviceCommand(self: *Controller, device: *Device) bool {
|
||||||
|
// 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(.{
|
const physical = self.submitCommand(.{
|
||||||
.parameter = device.input_context.physical,
|
.parameter = device.input_context.physical,
|
||||||
.control = trbControl(.address_device, @as(u32, device.slot_id) << 24),
|
.control = trbControl(.address_device, @as(u32, device.slot_id) << 24),
|
||||||
});
|
});
|
||||||
const code = self.awaitCommand(physical) orelse {
|
const code = self.awaitCommand(physical) orelse {
|
||||||
std.log.info("port {d} setup: Address Device timed out", .{device.port});
|
std.log.info("port {d} setup: Address Device timed out (attempt {d})", .{ device.port, attempt + 1 });
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
if (code != @intFromEnum(CompletionCode.success)) {
|
if (code == @intFromEnum(CompletionCode.success)) return true;
|
||||||
std.log.info("port {d} setup: Address Device completion code {d}", .{ device.port, code });
|
std.log.info("port {d} setup: Address Device completion code {d} (attempt {d})", .{ device.port, code, attempt + 1 });
|
||||||
return false;
|
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
|
/// 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).
|
// (pre-reset reads misreport on real controllers; M20).
|
||||||
effective_speed = (self.portStatus(port) >> 10) & 0xF;
|
effective_speed = (self.portStatus(port) >> 10) & 0xF;
|
||||||
if (effective_speed == 0) effective_speed = speed; // defensive: keep the caller's read
|
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 {
|
const slot_id = self.enableSlot() orelse {
|
||||||
std.log.info("port {d} setup: Enable Slot failed", .{port});
|
std.log.info("port {d} setup: Enable Slot failed", .{port});
|
||||||
|
|||||||
Reference in New Issue
Block a user