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:
Daniel Samson
2026-07-13 14:11:00 +01:00
parent 452080e997
commit 3fb9d5936a
21 changed files with 2856 additions and 110 deletions
+57
View File
@@ -142,6 +142,28 @@ pub fn build(b: *std.Build) void {
.root_source_file = b.path("system/devices/acpi-ids.zig"),
});
// The USB device-framework wire ABI (chapter-9 set-up packets, standard +
// class requests, descriptors) and the USB class-code taxonomy — the flat
// reference the xHCI bus driver, the USB class drivers, and the device
// manager's identity matcher all share. Pure data, like pci-class/acpi-ids.
const usb_abi_module = b.addModule("usb-abi", .{
.root_source_file = b.path("system/devices/usb-abi.zig"),
});
const usb_ids_module = b.addModule("usb-ids", .{
.root_source_file = b.path("system/devices/usb-ids.zig"),
});
// The USB transfer protocol: what a USB class driver says to the xHCI bus
// driver to drive its device (open / control / interrupt / bulk). A protocol
// module like vfs-protocol, shared by the bus driver and every class driver.
const usb_transfer_protocol_module = b.addModule("usb-transfer-protocol", .{
.root_source_file = b.path("system/drivers/usb-xhci-bus/usb-transfer-protocol.zig"),
});
// The block-device protocol: read/write of fixed-size blocks, spoken between a
// filesystem and a block driver (usb-storage). A protocol module like the rest.
const block_protocol_module = b.addModule("block-protocol", .{
.root_source_file = b.path("system/services/block/protocol.zig"),
});
// Kernel tunables (maximum_cpus, stack sizes, tick rate). A dependency-free module of
// compile-time constants, imported wherever a knob is read; keeps the trade-offs
// in one place instead of scattered across the tree. See system/parameters.zig.
@@ -225,6 +247,9 @@ pub fn build(b: *std.Build) void {
.root_source_file = b.path("system/services/device-manager/device-manager-protocol.zig"),
});
runtime_module.addImport("device-manager-protocol", device_manager_protocol_module);
// The USB transfer protocol, so runtime.usb (the class-driver client) can speak
// it, the way runtime.input speaks the input protocol.
runtime_module.addImport("usb-transfer-protocol", usb_transfer_protocol_module);
// The power protocol: system power's domain-named surface (docs/power.md).
const power_protocol_module = b.addModule("power-protocol", .{
@@ -350,6 +375,23 @@ pub fn build(b: *std.Build) void {
const ps2_keyboard_exe = addUserBinary(b, kernel_target, runtime_module, posix_module, mmio_module, xkeyboard_config_module, acpi_ids_module, "ps2-keyboard", "system/drivers/ps2-bus/keyboard.zig");
const ps2_mouse_exe = addUserBinary(b, kernel_target, runtime_module, posix_module, mmio_module, xkeyboard_config_module, acpi_ids_module, "ps2-mouse", "system/drivers/ps2-bus/mouse.zig");
const usb_xhci_bus_exe = addUserBinary(b, kernel_target, runtime_module, posix_module, mmio_module, xkeyboard_config_module, acpi_ids_module, "usb-xhci-bus", "system/drivers/usb-xhci-bus/usb-xhci-bus.zig");
// The xHCI bus driver builds chapter-9 requests and decodes descriptors from
// usb-abi, and reports each interface's (class,subclass,protocol) identity via
// usb-ids.packTriple.
usb_xhci_bus_exe.root_module.addImport("usb-abi", usb_abi_module);
usb_xhci_bus_exe.root_module.addImport("usb-ids", usb_ids_module);
usb_xhci_bus_exe.root_module.addImport("usb-transfer-protocol", usb_transfer_protocol_module);
// The USB HID class drivers: keyboard and mouse. They own no hardware — each
// opens its device through runtime.usb (the transfer protocol) and publishes to
// the input service. They build chapter-9 class requests from usb-abi.
const usb_hid_keyboard_exe = addUserBinary(b, kernel_target, runtime_module, posix_module, mmio_module, xkeyboard_config_module, acpi_ids_module, "usb-hid-keyboard", "system/drivers/usb-hid/keyboard.zig");
usb_hid_keyboard_exe.root_module.addImport("usb-abi", usb_abi_module);
const usb_hid_mouse_exe = addUserBinary(b, kernel_target, runtime_module, posix_module, mmio_module, xkeyboard_config_module, acpi_ids_module, "usb-hid-mouse", "system/drivers/usb-hid/mouse.zig");
usb_hid_mouse_exe.root_module.addImport("usb-abi", usb_abi_module);
// The USB mass-storage class driver: opens its device via runtime.usb, drives it
// with Bulk-Only Transport + SCSI, and serves the block protocol under `.block`.
const usb_storage_exe = addUserBinary(b, kernel_target, runtime_module, posix_module, mmio_module, xkeyboard_config_module, acpi_ids_module, "usb-storage", "system/drivers/usb-storage/usb-storage.zig");
usb_storage_exe.root_module.addImport("block-protocol", block_protocol_module);
const pci_bus_exe = addUserBinary(b, kernel_target, runtime_module, posix_module, mmio_module, xkeyboard_config_module, acpi_ids_module, "pci-bus", "system/drivers/pci-bus/pci-bus.zig");
// The PCI bus driver decodes each function's class triple to human names in its
// boot log (class/subclass/prog-IF), so pull in the shared pci-class reference.
@@ -376,6 +418,9 @@ pub fn build(b: *std.Build) void {
const device_manager_exe = addUserBinary(b, kernel_target, runtime_module, posix_module, mmio_module, xkeyboard_config_module, acpi_ids_module, "device-manager", "system/services/device-manager/device-manager.zig");
// Names the xHCI PCI class triple from the shared taxonomy instead of a bare 0x0C0330.
device_manager_exe.root_module.addImport("pci-class", pci_class_module);
// The manager matches reported USB interfaces by their (class,subclass,protocol)
// triple (usbDriverForIdentity), built from the named usb-ids codes.
device_manager_exe.root_module.addImport("usb-ids", usb_ids_module);
// The input service and its exercisers: the fan-out server, a hardware-free synthetic
// source, and a subscriber that doubles as the `input` test's oracle. See docs/input.md.
const input_exe = addUserBinary(b, kernel_target, runtime_module, posix_module, mmio_module, xkeyboard_config_module, acpi_ids_module, "input", "system/services/input/input.zig");
@@ -402,6 +447,12 @@ pub fn build(b: *std.Build) void {
mk_run.addFileArg(ps2_mouse_exe.getEmittedBin());
mk_run.addArg("usb-xhci-bus");
mk_run.addFileArg(usb_xhci_bus_exe.getEmittedBin());
mk_run.addArg("usb-hid-keyboard");
mk_run.addFileArg(usb_hid_keyboard_exe.getEmittedBin());
mk_run.addArg("usb-hid-mouse");
mk_run.addFileArg(usb_hid_mouse_exe.getEmittedBin());
mk_run.addArg("usb-storage");
mk_run.addFileArg(usb_storage_exe.getEmittedBin());
mk_run.addArg("pci-bus");
mk_run.addFileArg(pci_bus_exe.getEmittedBin());
mk_run.addArg("crash-test");
@@ -433,6 +484,9 @@ pub fn build(b: *std.Build) void {
.{ ps2_keyboard_exe, "system/drivers" },
.{ ps2_mouse_exe, "system/drivers" },
.{ usb_xhci_bus_exe, "system/drivers" },
.{ usb_hid_keyboard_exe, "system/drivers" },
.{ usb_hid_mouse_exe, "system/drivers" },
.{ usb_storage_exe, "system/drivers" },
}) |entry| {
const step = b.addInstallArtifact(entry[0], .{ .dest_dir = .{ .override = .{ .custom = entry[1] } } });
b.getInstallStep().dependOn(&step.step);
@@ -581,6 +635,9 @@ pub fn build(b: *std.Build) void {
"library/mmio/mmio.zig", // barriers assemble + registers round-trip
"system/drivers/ps2-bus/scancode.zig", // set-2 decode + keyboard state machine
"system/drivers/ps2-bus/mouse-packet.zig", // 3-byte mouse packet assembly
"system/drivers/usb-hid/hid-report.zig", // HID boot-report keyboard/mouse decode
"system/drivers/usb-storage/bulk-only-transport.zig", // CBW/CSW wrapper sizes
"system/drivers/usb-storage/scsi.zig", // SCSI CDB encodings (big-endian)
}) |root| {
const mod_tests = b.addTest(.{
.root_module = b.createModule(.{