usb: enumerate devices behind a hub — route strings + transaction translators (B4b)
The core of hub support (docs/usb-hub.md): a device on a hub's downstream port now reaches its class driver exactly like one on a root port. The hub's interrupt status-change endpoint is armed with an in-process subscription — completions set the hub's pending-port mask instead of queuing a class-driver report — and setupHub seeds every downstream port pending so a STATIC topology (a device present at power-on) enumerates without waiting on the initial interrupt edge. On the bus tick, each pending hub port is serviced: GET_STATUS + clear the change bits, and on a connect reset the port, read the speed, then enable a slot and Address Device with the composed route string ((parent_route<<4)|port), the inherited root port, and the parent hub's slot/port as the TRANSACTION TRANSLATOR — so the controller routes a full/low-speed device's split transactions through the hub's TT. The device then enumerates and registers its interfaces through the normal path (a compact topology-unique port key keeps the id tag within its 8-byte cap), recursing setupHub if it is itself a hub. Verified in QEMU (a keyboard behind a USB2 hub on a second controller): 'hub slot 1 port 1 device vendor 0x0627 ... usb-hid-keyboard: ok (device 35)'. Full suite 89/89. The user's SuperSpeed Genesys hub + full-speed keyboard/mouse on its USB2 companion is the real-hardware target, flagged separately.
This commit is contained in:
@@ -242,6 +242,33 @@ fn bringUpPort(manager: runtime.ipc.Handle, engine: *library.Controller, port: u
|
||||
if (deviceIsHub(usb_device)) _ = engine.setupHub(usb_device);
|
||||
}
|
||||
|
||||
/// Bring up a device on hub downstream `port`: setup+address (with route string
|
||||
/// and TT), enumerate, register its interfaces, and — if it is itself a hub —
|
||||
/// set it up (recursion). Mirrors bringUpPort for a root-port device.
|
||||
fn bringUpBehindHub(manager: runtime.ipc.Handle, engine: *library.Controller, hub: *library.Device, port: u16) void {
|
||||
const usb_device = engine.serviceHubPort(hub, port) orelse return; // empty/disconnect/failure
|
||||
if (!engine.enumerate(usb_device)) {
|
||||
std.log.info("hub slot {d} port {d}: enumeration failed", .{ hub.slot_id, port });
|
||||
return;
|
||||
}
|
||||
std.log.info("hub slot {d} port {d} device vendor 0x{x:0>4} product 0x{x:0>4}, {d} interface(s)", .{
|
||||
hub.slot_id, port,
|
||||
usb_device.device_descriptor.vendor_id,
|
||||
usb_device.device_descriptor.product_id,
|
||||
usb_device.interface_count,
|
||||
});
|
||||
for (usb_device.interfaces[0..usb_device.interface_count]) |*interface| {
|
||||
// A compact topology-unique port key: 1000 + slot*100 + port. Stays a few
|
||||
// digits (the hid tag "P<key>I<iface>" has an 8-byte cap) while never
|
||||
// colliding with a root port (1..N) or another (hub, port).
|
||||
const port_key = 1000 + @as(u32, hub.slot_id) * 100 + port;
|
||||
if (reportInterface(manager, port_key, interface.*)) |registered| {
|
||||
interface.registered_device_id = registered;
|
||||
}
|
||||
}
|
||||
if (deviceIsHub(usb_device)) _ = engine.setupHub(usb_device);
|
||||
}
|
||||
|
||||
/// Whether an enumerated device is a hub — class 9 at the device or the
|
||||
/// interface level (a hub's single interface is class 9/0/0).
|
||||
fn deviceIsHub(usb_device: *const library.Device) bool {
|
||||
@@ -434,6 +461,13 @@ fn onNotification(badge: u64) void {
|
||||
tearDownPort(manager, engine, port);
|
||||
}
|
||||
}
|
||||
// Downstream hub-port changes (docs/usb-hub.md): a device connected on a
|
||||
// hub's downstream port is enumerated and registered here, so a keyboard
|
||||
// behind a hub reaches its class driver like one on a root port.
|
||||
while (engine.takeHubChange()) |change| {
|
||||
const manager = manager_handle orelse break;
|
||||
bringUpBehindHub(manager, engine, change.hub, change.port);
|
||||
}
|
||||
while (engine.takeReport()) |report| {
|
||||
var message = transfer.InterruptReport{
|
||||
.device_token = report.device_token,
|
||||
|
||||
Reference in New Issue
Block a user