USB driver stack: xHCI transfers, HID keyboard/mouse, mass storage
Flesh out the xHCI host-controller driver into a full transfer engine and build the three USB class drivers on top, all verified end to end under QEMU. - xHCI engine (usb-xhci-library.zig): controller reset, command/event rings with cycle-bit bookkeeping (gated on a No-Op-command proof), device slots, Address Device, control transfers, full chapter-9 enumeration, Configure Endpoint, and interrupt/bulk transfers. Each interface is device_registered with its (class,subclass,protocol) identity, unique per (port,interface). - Bus<->class transfer protocol (usb-transfer-protocol.zig + runtime.usb): open / control / interrupt-subscribe (async report pump on a poll timer) / bulk-by- physical-address, so sector data never crosses the 256-byte IPC limit. - USB HID keyboard + mouse (usb-hid/): decode boot-protocol reports and publish to the input service. A USB usage is already the input protocol's keycode. - USB mass storage (usb-storage/): Bulk-Only Transport + transparent SCSI, serving a block device under the new .block service id (block-protocol). - device-manager matches USB interfaces to class drivers (usbDriverForIdentity). - usb-abi / usb-ids made importable modules; add HID and mass-storage class requests, packTriple, and a usb_device DeviceClass. - Fix test/qemu_test.py on macOS: the QMP unix-socket path was built from the deep worktree path and exceeded the 104-byte sun_path limit, so QEMU exited before booting. It now lives under a short temp path. Tests: usb-report, usb-hid, usb-storage pass under python3 test/qemu_test.py; host units (usb-abi, usb-ids, hid-report, bulk-only-transport, scsi) green.
This commit is contained in:
@@ -144,6 +144,10 @@ pub fn run(case: []const u8, boot_information: *const BootInformation) void {
|
||||
driverRestartTest(boot_information);
|
||||
} else if (eql(case, "usb-report")) {
|
||||
usbReportTest(boot_information);
|
||||
} else if (eql(case, "usb-hid")) {
|
||||
usbHidTest(boot_information);
|
||||
} else if (eql(case, "usb-storage")) {
|
||||
usbStorageTest(boot_information);
|
||||
} else if (eql(case, "device-list")) {
|
||||
deviceListTest(boot_information);
|
||||
} else if (eql(case, "pci-scan")) {
|
||||
@@ -1968,6 +1972,40 @@ fn pciScanTest(boot_information: *const BootInformation) void {
|
||||
/// the acpi service publishes it; init runs the stop sequence over its children
|
||||
/// and asks the power service for S5; the machine powers off (QEMU exits). The
|
||||
/// kernel test only spawns init — the ordered chain is the harness assertion.
|
||||
/// The USB HID chain, end to end: boot the full service tree (init spawns vfs,
|
||||
/// input, device-manager), and let discovery run — the manager matches the PCI
|
||||
/// host bridge to pci-bus, pci-bus reports the xHCI controller, usb-xhci-bus
|
||||
/// enumerates the HID interfaces, and the manager spawns the class drivers. The
|
||||
/// harness's expect regex requires usb-xhci-bus to register the boot-keyboard
|
||||
/// interface, the manager to spawn usb-hid-keyboard, and that driver to come up
|
||||
/// (open its device, ask for boot protocol, subscribe) — proof the transfer
|
||||
/// protocol works class-driver to controller.
|
||||
fn usbHidTest(boot_information: *const BootInformation) void {
|
||||
bootServiceTreeTest(boot_information, "usb-hid");
|
||||
}
|
||||
|
||||
/// The USB storage chain: same full-tree boot, but the harness attaches a
|
||||
/// usb-storage device and the expect regex requires usb-storage to come up
|
||||
/// (open its device, run the BOT bring-up, read its capacity, and read block 0).
|
||||
fn usbStorageTest(boot_information: *const BootInformation) void {
|
||||
bootServiceTreeTest(boot_information, "usb-storage");
|
||||
}
|
||||
|
||||
fn bootServiceTreeTest(boot_information: *const BootInformation, comptime label: []const u8) void {
|
||||
log("DANOS-TEST-BEGIN: " ++ label ++ "\n", .{});
|
||||
if (boot_information.init_len == 0 or boot_information.initial_ramdisk_len == 0) {
|
||||
check("bootloader handed over init and the initial_ramdisk", false);
|
||||
result();
|
||||
return;
|
||||
}
|
||||
const ramdisk = @as([*]const u8, @ptrFromInt(boot_handoff.physicalToVirtual(boot_information.initial_ramdisk_base)))[0..boot_information.initial_ramdisk_len];
|
||||
process.setInitialRamdisk(ramdisk);
|
||||
const image = @as([*]const u8, @ptrFromInt(boot_handoff.physicalToVirtual(boot_information.init_base)))[0..boot_information.init_len];
|
||||
const spawned = if (process.spawnProcess(image, 4, &.{"/system/services/init"})) true else |_| false;
|
||||
check("init spawned (boots vfs, input, device-manager, and the USB chain)", spawned);
|
||||
result();
|
||||
}
|
||||
|
||||
fn orderlyShutdownTest(boot_information: *const BootInformation) void {
|
||||
log("DANOS-TEST-BEGIN: orderly-shutdown\n", .{});
|
||||
if (boot_information.init_len == 0 or boot_information.initial_ramdisk_len == 0) {
|
||||
|
||||
Reference in New Issue
Block a user