usb: [diagnostic] dump the xECP USB2/USB3 port map + all PORTSC

The USB2 companion hub carrying the user's full-speed keyboard never
appears — only the SuperSpeed hub (port 19) and SS storage (port 21)
connect. To find where the USB2 root ports are and whether the companion
is presenting on one, walk the xECP Supported Protocol capabilities
(logging each USB 2.0 / 3.0 root-port range) and dump every port's raw
PORTSC unconditionally (ccs/ped/pls/pp/speed). QEMU confirms the dump
(USB2 ports 5-8, USB3 ports 1-4). Branch-only diagnostic.
This commit is contained in:
Daniel Samson
2026-07-21 22:56:25 +01:00
parent 18408b0666
commit 10956c6660
2 changed files with 36 additions and 11 deletions
+3 -11
View File
@@ -196,17 +196,9 @@ fn scanPorts(manager: runtime.ipc.Handle) void {
};
std.log.info("{d} root-hub ports", .{engine.max_ports});
// Diagnostic: dump every root port's raw PORTSC (CCS = connect, PED =
// enabled, PLS = link state, speed) so a USB2 companion hub that isn't
// enumerating is visible — is it connected on a port we misread, or absent?
{
var p: u32 = 1;
while (p <= engine.max_ports) : (p += 1) {
const portsc = engine.portStatus(p);
if (portsc & 1 != 0 or (portsc >> 5) & 0xF != 5) // connected, or not in the disabled/disconnected link state
std.log.info("PORTSC[{d}] = 0x{x:0>8} ccs={d} ped={d} pls={d} speed={d}", .{ p, portsc, portsc & 1, (portsc >> 1) & 1, (portsc >> 5) & 0xF, (portsc >> 10) & 0xF });
}
}
// Diagnostic: the USB2/USB3 root-port map (xECP) + every port's raw PORTSC,
// to find a USB2 companion hub that isn't presenting a connection.
engine.dumpPortTopology();
var port: u32 = 1;
var connected: u32 = 0;
@@ -484,6 +484,39 @@ pub const Controller = struct {
}
// PORTSC for 1-based port `port`.
/// Diagnostic: walk the xECP list and log each Supported Protocol capability
/// (USB 2.0 vs 3.x, the compatible root-port range), then dump every port's
/// raw PORTSC. Reveals where the USB2 root ports are and their state — for
/// finding a USB2 companion hub that isn't presenting a connection.
pub fn dumpPortTopology(self: *const Controller) void {
const hccparams1 = read32(self.register_base + cap_hccparams1);
var offset: usize = (hccparams1 >> 16) & 0xFFFF; // xECP: dword offset from register_base
var guard: u32 = 0;
while (offset != 0 and guard < 64) : (guard += 1) {
const cap_base = self.register_base + offset * 4;
const dw0 = read32(cap_base);
const id = dw0 & 0xFF;
if (id == 2) { // Supported Protocol
const dw2 = read32(cap_base + 8);
const major = (dw0 >> 24) & 0xFF;
const minor = (dw0 >> 16) & 0xFF;
const port_offset = dw2 & 0xFF;
const port_count = (dw2 >> 8) & 0xFF;
std.log.info("xECP USB {d}.{d}: root ports {d}..{d}", .{ major, minor, port_offset, port_offset + port_count - 1 });
}
const next = (dw0 >> 8) & 0xFF;
if (next == 0) break;
offset += next;
}
var port: u32 = 1;
while (port <= self.max_ports) : (port += 1) {
const portsc = self.portStatus(port);
std.log.info("PORTSC[{d}] = 0x{x:0>8} ccs={d} ped={d} pls={d} pp={d} speed={d}", .{
port, portsc, portsc & 1, (portsc >> 1) & 1, (portsc >> 5) & 0xF, (portsc >> 9) & 1, (portsc >> 10) & 0xF,
});
}
}
pub fn portStatus(self: *const Controller, port: u32) u32 {
return read32(self.op_base + op_portsc_base + op_portsc_stride * (port - 1));
}