C2: migrate consumers off the runtime shim to direct concern-module imports

Every user binary and the two device-logic library modules (pci, usb) now
`@import` the concern modules directly instead of aliasing through `runtime`:

  runtime.ipc/process/time/service/input/block/display  -> @import("<module>")
  runtime.device / runtime.device_manager               -> @import("driver")
  runtime.fs                                             -> @import("file-system")
  runtime.Thread                                         -> @import("thread").Thread
  runtime.system.{write,writeRecord,klog*}              -> logging.*
  runtime.system.{sleep,timerOnce,wallClock,clock}     -> time.*
  runtime.system.{spawn*,kill,exit,yield,processes,...}-> process.*
  runtime.system.{mmap,munmap,PROT_*}                  -> memory.*
  runtime.dma.* / runtime.shared_memory.* / runtime.allocator -> memory.*

Each consumer keeps its own alias name (e.g. `const device = @import("driver")`),
so call sites are unchanged and there are no collisions with local `driver`
variables. build.zig now injects the concern modules into every user binary via
`default_imports`; pci/usb module import lists were updated to match.

The `runtime` and `system` shims remain for one more step (root.zig still uses
runtime); they are deleted in C5. Nothing but root.zig imports `runtime` now.

Verified: zig build, zig build test, and 17 QEMU cases (smoke, device-manager,
logger, fat-mount, fat-mutations, usb-storage, usb-hid, display-native,
virtio-gpu, input, thread-spawn, thread-mutex, process-kill, shared-memory,
driver-restart, acpi-ps2, pci-scan).
This commit is contained in:
Daniel Samson
2026-07-22 23:28:34 +01:00
parent dded46726b
commit 23bcd77c58
37 changed files with 783 additions and 692 deletions
+39 -32
View File
@@ -14,9 +14,16 @@
//! not the controller is running.
const std = @import("std");
const runtime = @import("runtime");
const device = @import("driver");
const ipc = @import("ipc");
const process = @import("process");
const service = @import("service");
const time = @import("time");
const input = @import("input");
const device_manager = @import("driver");
const memory = @import("memory");
const logging = @import("logging");
const device_manager_protocol = @import("device-manager-protocol");
const device = runtime.device;
const usb_ids = @import("usb-ids");
const usb_abi = @import("usb-abi");
const usb_transfer_protocol = @import("usb-transfer-protocol");
@@ -27,7 +34,7 @@ var controller: ?library.Controller = null;
/// This driver's service endpoint (registered as `.usb_bus`), where class-driver
/// requests, signals, and the interrupt-poll timer all arrive.
var service_endpoint: runtime.ipc.Handle = 0;
var service_endpoint: ipc.Handle = 0;
/// How often the driver drains the event ring for interrupt reports (~125 Hz),
/// re-armed each tick. Frequent enough for responsive input.
@@ -69,7 +76,7 @@ var controller_id: u64 = device_manager_protocol.no_device;
/// Claim the assigned controller, find its register window, and hello the
/// manager. Any failure returns false: the process exits cleanly, which the
/// manager reads as "meant to stop" — a missing assignment is not a crash loop.
fn initialise(endpoint: runtime.ipc.Handle) bool {
fn initialise(endpoint: ipc.Handle) bool {
service_endpoint = endpoint;
if (!device.claim(controller_id)) {
std.log.info("unable to claim controller device {d}", .{controller_id});
@@ -77,8 +84,8 @@ fn initialise(endpoint: runtime.ipc.Handle) bool {
}
// Fetch our own descriptor back for the controller's resources.
const buffer = runtime.allocator().alloc(device.DeviceDescriptor, 64) catch {
_ = runtime.system.write("/system/drivers/usb-xhci-bus: out of memory\n");
const buffer = memory.allocator().alloc(device.DeviceDescriptor, 64) catch {
_ = logging.write("/system/drivers/usb-xhci-bus: out of memory\n");
return false;
};
const total = device.enumerate(buffer);
@@ -107,14 +114,14 @@ fn initialise(endpoint: runtime.ipc.Handle) bool {
register_window.len,
});
register_base = device.mmioMap(controller_id, register_index) orelse {
_ = runtime.system.write("/system/drivers/usb-xhci-bus: mmio_map failed\n");
_ = logging.write("/system/drivers/usb-xhci-bus: mmio_map failed\n");
return false;
};
// Bring the controller up: reset it, stand up the command and event rings,
// and start it running (the hardware half lives in usb-xhci-library.zig).
controller = library.Controller.init(register_base) orelse {
_ = runtime.system.write("/system/drivers/usb-xhci-bus: controller reset/bring-up failed\n");
_ = logging.write("/system/drivers/usb-xhci-bus: controller reset/bring-up failed\n");
return false;
};
std.log.info("controller running ({d} slots, {d}-byte contexts)", .{
@@ -125,23 +132,23 @@ fn initialise(endpoint: runtime.ipc.Handle) bool {
// ring, the doorbell, and the cycle-bit bookkeeping. If this completes, the
// engine is sound; transfers build on exactly this machinery.
if (controller.?.noOpCommand()) {
_ = runtime.system.write("/system/drivers/usb-xhci-bus: command ring running (no-op ok)\n");
_ = logging.write("/system/drivers/usb-xhci-bus: command ring running (no-op ok)\n");
} else {
_ = runtime.system.write("/system/drivers/usb-xhci-bus: no-op command did not complete\n");
_ = logging.write("/system/drivers/usb-xhci-bus: no-op command did not complete\n");
return false;
}
// The handshake (role: bus — we enumerate USB ports and report the devices
// behind them), inside the manager's hello deadline. Keep the handle: the
// tick's hot-plug dispatch reports through it.
const handle = runtime.device_manager.hello(.bus, controller_id) orelse return false;
const handle = device_manager.hello(.bus, controller_id) orelse return false;
manager_handle = handle;
scanPorts(handle);
// Arm the poll timer that drains interrupt reports from the event ring. It is
// re-armed on each tick in onNotification; class drivers subscribe later.
_ = runtime.system.timerOnce(service_endpoint, poll_interval_ms);
_ = time.timerOnce(service_endpoint, poll_interval_ms);
return true;
}
@@ -168,15 +175,15 @@ fn speedName(speed: u32) []const u8 {
/// report one child per interface — carrying the interface's (class, subclass,
/// protocol) triple as identity, which is what the device manager matches a
/// class driver against.
var manager_handle: ?runtime.ipc.Handle = null;
var manager_handle: ?ipc.Handle = null;
// Per-root-port connected state from the previous tick, so the poll acts on
// empty->connected transitions (edge), never re-attempting a level every tick.
var prev_connected: [64]bool = [_]bool{false} ** 64;
fn scanPorts(manager: runtime.ipc.Handle) void {
fn scanPorts(manager: ipc.Handle) void {
const engine = if (controller) |*c| c else {
_ = runtime.system.write("/system/drivers/usb-xhci-bus: controller not initialised\n");
_ = logging.write("/system/drivers/usb-xhci-bus: controller not initialised\n");
return;
};
std.log.info("{d} root-hub ports", .{engine.max_ports});
@@ -190,7 +197,7 @@ fn scanPorts(manager: runtime.ipc.Handle) void {
bringUpPort(manager, engine, port);
}
if (connected == 0) {
_ = runtime.system.write("/system/drivers/usb-xhci-bus: no devices connected\n");
_ = logging.write("/system/drivers/usb-xhci-bus: no devices connected\n");
engine.dumpPortTopology(); // help diagnose an empty scan: the xECP map + raw PORTSC
}
}
@@ -198,7 +205,7 @@ fn scanPorts(manager: runtime.ipc.Handle) void {
/// Bring up whatever is on `port`: setup + enumerate + register/report one child
/// per interface. Shared by the boot scan and hot-plug (a port-change event with
/// the port now connected).
fn bringUpPort(manager: runtime.ipc.Handle, engine: *library.Controller, port: u32) void {
fn bringUpPort(manager: ipc.Handle, engine: *library.Controller, port: u32) void {
const speed = (engine.portStatus(port) >> 10) & 0xF; // the PORTSC port-speed class
std.log.info("port {d} connected — {s} (speed class {d})", .{ port, speedName(speed), speed });
@@ -249,7 +256,7 @@ fn hubPortKey(hub_slot: u8, port: u16) u32 {
/// new device) or a disconnect (tear the old one down). Recurses for a hub
/// behind a hub — a nested hub is set up on connect and its downstream devices
/// torn down first on disconnect.
fn bringUpBehindHub(manager: runtime.ipc.Handle, engine: *library.Controller, hub: *library.Device, port: u16) void {
fn bringUpBehindHub(manager: ipc.Handle, engine: *library.Controller, hub: *library.Device, port: u16) void {
const status = engine.hubPortStatusAck(hub, port) orelse return;
const connected = library.Controller.hubPortConnected(status);
const existing = engine.deviceOnHubPort(hub, port);
@@ -291,7 +298,7 @@ fn bringUpBehindHub(manager: runtime.ipc.Handle, engine: *library.Controller, hu
/// Tear down a device that disconnected from a hub: recursively tear down its
/// own downstream devices first if it is a hub, report each interface removed,
/// then Disable Slot. Mirrors tearDownPort for a hub-attached device.
fn tearDownHubDevice(manager: runtime.ipc.Handle, engine: *library.Controller, dev: *library.Device) void {
fn tearDownHubDevice(manager: ipc.Handle, engine: *library.Controller, dev: *library.Device) void {
// A hub that left takes its whole subtree with it — tear children down first.
if (dev.is_hub) {
while (engine.nextChildOf(dev.slot_id, 0)) |child| tearDownHubDevice(manager, engine, child);
@@ -305,7 +312,7 @@ fn tearDownHubDevice(manager: runtime.ipc.Handle, engine: *library.Controller, d
.bus_address = (@as(u64, key) << 8) | interface.number,
};
var reply: [device_manager_protocol.message_maximum]u8 = undefined;
_ = runtime.ipc.call(manager, std.mem.asBytes(&event), &reply) catch {};
_ = ipc.call(manager, std.mem.asBytes(&event), &reply) catch {};
interface.registered_device_id = 0;
}
engine.tearDownDevice(dev);
@@ -325,7 +332,7 @@ fn deviceIsHub(usb_device: *const library.Device) bool {
/// interface as removed (the manager prunes the node, notifies watchers, and
/// stops the class driver's world honestly), then release the controller-side
/// device state (Disable Slot).
fn tearDownPort(manager: runtime.ipc.Handle, engine: *library.Controller, port: u32) void {
fn tearDownPort(manager: ipc.Handle, engine: *library.Controller, port: u32) void {
const usb_device = engine.deviceOnPort(port) orelse return;
std.log.info("port {d} disconnected", .{port});
for (usb_device.interfaces[0..usb_device.interface_count]) |*interface| {
@@ -335,7 +342,7 @@ fn tearDownPort(manager: runtime.ipc.Handle, engine: *library.Controller, port:
.bus_address = (@as(u64, port) << 8) | interface.number,
};
var reply: [device_manager_protocol.message_maximum]u8 = undefined;
_ = runtime.ipc.call(manager, std.mem.asBytes(&event), &reply) catch {
_ = ipc.call(manager, std.mem.asBytes(&event), &reply) catch {
std.log.info("child-removed report for port {d} interface {d} failed", .{ port, interface.number });
};
interface.registered_device_id = 0;
@@ -348,7 +355,7 @@ fn tearDownPort(manager: runtime.ipc.Handle, engine: *library.Controller, port:
/// manager can match a class driver (HID keyboard, mouse, mass storage); the
/// registered device id becomes that driver's argv[1] assignment. Returns the
/// registered device id, or null if registration or the report failed.
fn reportInterface(manager: runtime.ipc.Handle, port: u32, interface: library.InterfaceInfo) ?u64 {
fn reportInterface(manager: ipc.Handle, port: u32, interface: library.InterfaceInfo) ?u64 {
const identity = usb_ids.packTriple(interface.class, interface.subclass, interface.protocol);
// A USB device is reached through its controller, not by MMIO, so the child
@@ -378,7 +385,7 @@ fn reportInterface(manager: runtime.ipc.Handle, port: u32, interface: library.In
.device_id = registered,
};
var reply: [device_manager_protocol.message_maximum]u8 = undefined;
_ = runtime.ipc.call(manager, std.mem.asBytes(&report), &reply) catch {
_ = ipc.call(manager, std.mem.asBytes(&report), &reply) catch {
std.log.info("child report for port {d} interface {d} failed", .{ port, interface.number });
return null;
};
@@ -396,7 +403,7 @@ fn reportInterface(manager: runtime.ipc.Handle, port: u32, interface: library.In
/// Serve the USB transfer protocol: a class driver opens its device, then issues
/// control / interrupt-subscribe / bulk requests against it.
fn onMessage(message: []const u8, reply: []u8, sender: u32, capability: ?runtime.ipc.Handle) usize {
fn onMessage(message: []const u8, reply: []u8, sender: u32, capability: ?ipc.Handle) usize {
_ = sender;
if (message.len < 4) return 0;
const operation = std.mem.readInt(u32, message[0..4], .little);
@@ -418,7 +425,7 @@ fn writeReply(reply: []u8, value: anytype) usize {
/// open: resolve the assigned device id to an interface, remember the caller's
/// endpoint (for interrupt reports), and answer with a device token + the
/// interface's endpoints so the class driver need not re-read the config.
fn handleOpen(message: []const u8, reply: []u8, capability: ?runtime.ipc.Handle) usize {
fn handleOpen(message: []const u8, reply: []u8, capability: ?ipc.Handle) usize {
if (message.len < @sizeOf(usb_transfer_protocol.OpenRequest)) return writeReply(reply, usb_transfer_protocol.OpenReply{ .status = -1, .endpoint_count = 0, .device_token = 0, .interface_class = 0, .interface_subclass = 0, .interface_protocol = 0, .interface_number = 0 });
const request = std.mem.bytesToValue(usb_transfer_protocol.OpenRequest, message[0..@sizeOf(usb_transfer_protocol.OpenRequest)]);
const engine = if (controller) |*c| c else return writeReply(reply, usb_transfer_protocol.OpenReply{ .status = -1, .endpoint_count = 0, .device_token = 0, .interface_class = 0, .interface_subclass = 0, .interface_protocol = 0, .interface_number = 0 });
@@ -493,7 +500,7 @@ fn handleBulk(message: []const u8, reply: []u8) usize {
/// The poll timer landed: drain any interrupt reports off the event ring and push
/// each to the class driver that subscribed, then re-arm the timer.
fn onNotification(badge: u64) void {
if (badge & runtime.ipc.notify_timer_bit == 0) return;
if (badge & ipc.notify_timer_bit == 0) return;
if (controller) |*engine| {
engine.pump();
// Poll every root port and reconcile — a device present but not yet
@@ -550,22 +557,22 @@ fn onNotification(badge: u64) void {
};
const n = @min(report.length, usb_transfer_protocol.max_report_data);
@memcpy(message.data[0..n], report.data[0..n]);
_ = runtime.ipc.send(report.report_endpoint, std.mem.asBytes(&message));
_ = ipc.send(report.report_endpoint, std.mem.asBytes(&message));
}
}
_ = runtime.system.timerOnce(service_endpoint, poll_interval_ms);
_ = time.timerOnce(service_endpoint, poll_interval_ms);
}
pub fn main(init: runtime.process.Init) void {
pub fn main(init: process.Init) void {
const argument = init.arguments.get(1) orelse {
_ = runtime.system.write("/system/drivers/usb-xhci-bus: missing controller device id (argv[1])\n");
_ = logging.write("/system/drivers/usb-xhci-bus: missing controller device id (argv[1])\n");
return;
};
controller_id = std.fmt.parseInt(u64, argument, 10) catch {
std.log.info("malformed controller device id '{s}'", .{argument});
return;
};
runtime.service.run(usb_transfer_protocol.message_maximum, .{
service.run(usb_transfer_protocol.message_maximum, .{
.service = .usb_bus,
.init = initialise,
.on_message = onMessage,
@@ -19,12 +19,11 @@
//! proven.
const std = @import("std");
const runtime = @import("runtime");
const time = @import("time");
const memory = @import("memory");
const mmio = @import("mmio");
const usb_abi = @import("usb-abi");
const usb_ids = @import("usb-ids");
const dma = runtime.dma;
const system = runtime.system;
// --- register offsets -------------------------------------------------------
@@ -134,7 +133,7 @@ const trbs_per_ring = page_size / @sizeOf(Trb); // 256
// A producer ring (command ring, or a transfer ring): a page of TRBs whose last
// entry is a Link TRB back to the start. `cycle` is the producer cycle state.
const ProducerRing = struct {
region: dma.Region,
region: memory.DmaRegion,
enqueue_index: usize = 0,
cycle: bool = true,
@@ -182,8 +181,8 @@ const ProducerRing = struct {
// The event ring: a single segment the controller fills and the driver drains.
// `cycle` is the consumer cycle state, flipped each time the dequeue wraps.
const EventRing = struct {
segment: dma.Region,
table: dma.Region,
segment: memory.DmaRegion,
table: memory.DmaRegion,
dequeue_index: usize = 0,
cycle: bool = true,
@@ -269,12 +268,12 @@ pub const Device = struct {
port: u32 = 0,
speed: u32 = 0,
max_packet_size_0: u32 = 8,
input_context: dma.Region = .{ .virtual = 0, .physical = 0 },
device_context: dma.Region = .{ .virtual = 0, .physical = 0 },
input_context: memory.DmaRegion = .{ .virtual = 0, .physical = 0 },
device_context: memory.DmaRegion = .{ .virtual = 0, .physical = 0 },
ep0_ring: ProducerRing = .{ .region = .{ .virtual = 0, .physical = 0 } },
// A page-sized bounce buffer for control-transfer data (descriptors are read
// here, then copied out to the caller).
control_buffer: dma.Region = .{ .virtual = 0, .physical = 0 },
control_buffer: memory.DmaRegion = .{ .virtual = 0, .physical = 0 },
device_descriptor: usb_abi.DeviceDescriptor = std.mem.zeroes(usb_abi.DeviceDescriptor),
configuration_value: u8 = 0,
interface_count: u8 = 0,
@@ -308,7 +307,7 @@ const Subscription = struct {
dci: u32 = 0,
endpoint_address: u8 = 0,
ring: *ProducerRing = undefined,
buffer: dma.Region = .{ .virtual = 0, .physical = 0 },
buffer: memory.DmaRegion = .{ .virtual = 0, .physical = 0 },
max_length: u16 = 0,
armed_trb_physical: u64 = 0,
// The bus layer's per-subscription IPC state (opaque here): the class driver's
@@ -447,7 +446,7 @@ pub const Controller = struct {
max_ports: u32,
context_size: usize, // 32 or 64 (CSZ)
device_context_array: dma.Region,
device_context_array: memory.DmaRegion,
command_ring: ProducerRing,
event_ring: EventRing,
devices: [max_devices]Device = [_]Device{.{}} ** max_devices,
@@ -601,19 +600,19 @@ pub const Controller = struct {
write32(self.operational(op_config), self.max_slots);
// The Device Context Base Address Array (entry 0 = scratchpad array).
self.device_context_array = dma.alloc(page_size, dma.coherent) orelse return null;
self.device_context_array = memory.dmaAlloc(page_size, memory.dma_coherent) orelse return null;
self.setupScratchpad(register_base);
write64(self.operational(op_dcbaap), self.device_context_array.physical);
// The command ring: a page of TRBs, last entry a Link back to the start.
self.command_ring = .{ .region = dma.alloc(page_size, dma.coherent) orelse return null };
self.command_ring = .{ .region = memory.dmaAlloc(page_size, memory.dma_coherent) orelse return null };
self.command_ring.installLink();
write64(self.operational(op_crcr), self.command_ring.region.physical | cycle_bit);
// The event ring: one segment + a one-entry segment table.
self.event_ring = .{
.segment = dma.alloc(page_size, dma.coherent) orelse return null,
.table = dma.alloc(page_size, dma.coherent) orelse return null,
.segment = memory.dmaAlloc(page_size, memory.dma_coherent) orelse return null,
.table = memory.dmaAlloc(page_size, memory.dma_coherent) orelse return null,
};
const table: *volatile ErstEntry = @ptrFromInt(self.event_ring.table.virtual);
table.ring_segment_base = self.event_ring.segment.physical;
@@ -663,11 +662,11 @@ pub const Controller = struct {
return;
}
// One page per scratchpad buffer, plus a page holding their address array.
const pointers = dma.alloc(page_size, dma.coherent) orelse return;
const pointers = memory.dmaAlloc(page_size, memory.dma_coherent) orelse return;
const pointer_array: [*]volatile u64 = @ptrFromInt(pointers.virtual);
var index: u32 = 0;
while (index < count) : (index += 1) {
const buffer = dma.alloc(page_size, dma.coherent) orelse return;
const buffer = memory.dmaAlloc(page_size, memory.dma_coherent) orelse return;
pointer_array[index] = buffer.physical;
}
array[0] = pointers.physical;
@@ -675,16 +674,16 @@ pub const Controller = struct {
// Spin (with a deadline) until every bit in `mask` reads back as zero / one.
fn waitClear(address: usize, mask: u32) bool {
const deadline = system.clock() + 1_000_000_000; // 1 s
const deadline = time.clock() + 1_000_000_000; // 1 s
while (read32(address) & mask != 0) {
if (system.clock() >= deadline) return false;
if (time.clock() >= deadline) return false;
}
return true;
}
fn waitSet(address: usize, mask: u32) bool {
const deadline = system.clock() + 1_000_000_000;
const deadline = time.clock() + 1_000_000_000;
while (read32(address) & mask == 0) {
if (system.clock() >= deadline) return false;
if (time.clock() >= deadline) return false;
}
return true;
}
@@ -723,7 +722,7 @@ pub const Controller = struct {
write64(self.interrupter(event_ring_dequeue_pointer), dequeue | (1 << 3));
return event;
}
if (system.clock() >= deadline_ns) return null;
if (time.clock() >= deadline_ns) return null;
}
}
@@ -732,7 +731,7 @@ pub const Controller = struct {
/// serviced even during a command); other events are ignored. Returns the
/// completion code, or null on timeout.
fn awaitCommand(self: *Controller, command_physical: u64) ?u8 {
const deadline = system.clock() + 1_000_000_000;
const deadline = time.clock() + 1_000_000_000;
while (true) {
const event = self.nextEvent(deadline) orelse return null;
const kind = trbType(event.control);
@@ -764,9 +763,9 @@ pub const Controller = struct {
// bits and PED are untouched) and preserving PP.
const before = self.portStatus(port);
self.writePortStatus(port, (before & ~portsc_write_1_to_clear) | portsc_reset);
const deadline = system.clock() + 500_000_000;
const deadline = time.clock() + 500_000_000;
while (self.portStatus(port) & portsc_reset_change == 0) {
if (system.clock() >= deadline) return false;
if (time.clock() >= deadline) return false;
}
// Clear the Port Reset Change bit (write 1 to PRC, 0 to the rest).
const after = self.portStatus(port);
@@ -778,7 +777,7 @@ pub const Controller = struct {
/// assigned (carried in bits 31:24 of the completion event's control field).
fn enableSlot(self: *Controller) ?u8 {
const physical = self.submitCommand(.{ .control = trbControl(.enable_slot, 0) });
const deadline = system.clock() + 1_000_000_000;
const deadline = time.clock() + 1_000_000_000;
while (true) {
const event = self.nextEvent(deadline) orelse return null;
if (trbType(event.control) == @intFromEnum(TrbType.command_completion_event) and
@@ -858,7 +857,7 @@ pub const Controller = struct {
// retried here; its port was reset in serviceHubPort.)
if (device.parent_slot == 0) {
if (!self.resetPort(device.port)) return false;
system.sleep(10);
time.sleepMillis(10);
} else return false;
}
return false;
@@ -891,7 +890,7 @@ pub const Controller = struct {
// (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);
time.sleepMillis(10);
}
const slot_id = self.enableSlot() orelse {
std.log.info("port {d} setup: Enable Slot failed", .{port});
@@ -909,11 +908,11 @@ pub const Controller = struct {
.max_packet_size_0 = defaultMaxPacketSize0(effective_speed),
.root_port = port, // a root-port device: the chain root IS this port
};
device.input_context = dma.alloc(page_size, dma.coherent) orelse return self.abandon(device);
device.device_context = dma.alloc(page_size, dma.coherent) orelse return self.abandon(device);
device.ep0_ring = .{ .region = dma.alloc(page_size, dma.coherent) orelse return self.abandon(device) };
device.input_context = memory.dmaAlloc(page_size, memory.dma_coherent) orelse return self.abandon(device);
device.device_context = memory.dmaAlloc(page_size, memory.dma_coherent) orelse return self.abandon(device);
device.ep0_ring = .{ .region = memory.dmaAlloc(page_size, memory.dma_coherent) orelse return self.abandon(device) };
device.ep0_ring.installLink();
device.control_buffer = dma.alloc(page_size, dma.coherent) orelse return self.abandon(device);
device.control_buffer = memory.dmaAlloc(page_size, memory.dma_coherent) orelse return self.abandon(device);
self.buildAddressInputContext(device);
const array: [*]volatile u64 = @ptrFromInt(self.device_context_array.virtual);
@@ -991,7 +990,7 @@ pub const Controller = struct {
if (!is_interrupt or !is_in) continue;
const ring = self.getOrConfigureEndpoint(device, endpoint) orelse return;
const subscription = self.allocateSubscription() orelse return;
const buffer = dma.alloc(page_size, dma.coherent) orelse return;
const buffer = memory.dmaAlloc(page_size, memory.dma_coherent) orelse return;
const number: u8 = endpoint.address & 0x0F;
subscription.* = .{
.active = true,
@@ -1068,7 +1067,7 @@ pub const Controller = struct {
_ = self.controlTransfer(hub, hubreq.setPortFeature(hubreq.feature_port_reset, port), &.{}, false);
var tries: u32 = 0;
while (tries < 200) : (tries += 1) {
system.sleep(5);
time.sleepMillis(5);
const s = self.readHubPortStatus(hub, port) orelse return null;
if (s & hubreq.status_enable != 0) break;
}
@@ -1113,11 +1112,11 @@ pub const Controller = struct {
.parent_slot = hub.slot_id,
.parent_port = @intCast(port),
};
device.input_context = dma.alloc(page_size, dma.coherent) orelse return self.abandon(device);
device.device_context = dma.alloc(page_size, dma.coherent) orelse return self.abandon(device);
device.ep0_ring = .{ .region = dma.alloc(page_size, dma.coherent) orelse return self.abandon(device) };
device.input_context = memory.dmaAlloc(page_size, memory.dma_coherent) orelse return self.abandon(device);
device.device_context = memory.dmaAlloc(page_size, memory.dma_coherent) orelse return self.abandon(device);
device.ep0_ring = .{ .region = memory.dmaAlloc(page_size, memory.dma_coherent) orelse return self.abandon(device) };
device.ep0_ring.installLink();
device.control_buffer = dma.alloc(page_size, dma.coherent) orelse return self.abandon(device);
device.control_buffer = memory.dmaAlloc(page_size, memory.dma_coherent) orelse return self.abandon(device);
self.buildAddressInputContext(device);
const array: [*]volatile u64 = @ptrFromInt(self.device_context_array.virtual);
@@ -1164,7 +1163,7 @@ pub const Controller = struct {
/// a way that survived every driver restart (the 1-in-3 READ CAPACITY
/// failure at boot, with a USB keyboard and mouse polling concurrently).
fn awaitTransfer(self: *Controller, slot_id: u8, dci: u32, requested_length: u32) ?u8 {
const deadline = system.clock() + 1_000_000_000;
const deadline = time.clock() + 1_000_000_000;
while (true) {
const event = self.nextEvent(deadline) orelse return null;
if (trbType(event.control) != @intFromEnum(TrbType.transfer_event)) continue;
@@ -1401,7 +1400,7 @@ pub const Controller = struct {
const configured = &device.endpoint_rings[device.endpoint_ring_count];
configured.dci = dci;
configured.ring = .{ .region = dma.alloc(page_size, dma.coherent) orelse return null };
configured.ring = .{ .region = memory.dmaAlloc(page_size, memory.dma_coherent) orelse return null };
configured.ring.installLink();
self.buildConfigureEndpointInputContext(device, endpoint, dci, &configured.ring);
if (!self.configureEndpointCommand(device)) return null;
@@ -1481,7 +1480,7 @@ pub const Controller = struct {
pub fn subscribeInterrupt(self: *Controller, device: *Device, endpoint: EndpointInfo, device_token: u64, report_endpoint: usize) bool {
const ring = self.getOrConfigureEndpoint(device, endpoint) orelse return false;
const subscription = self.allocateSubscription() orelse return false;
const buffer = dma.alloc(page_size, dma.coherent) orelse return false;
const buffer = memory.dmaAlloc(page_size, memory.dma_coherent) orelse return false;
const number: u8 = endpoint.address & 0x0F;
const direction_in = endpoint.address & 0x80 != 0;
subscription.* = .{
@@ -1570,7 +1569,7 @@ pub const Controller = struct {
/// driver's timer tick.
pub fn pump(self: *Controller) void {
while (true) {
const event = self.nextEvent(system.clock()) orelse return; // deadline=now: null when empty
const event = self.nextEvent(time.clock()) orelse return; // deadline=now: null when empty
const kind = trbType(event.control);
if (kind == @intFromEnum(TrbType.transfer_event)) {
_ = self.serviceInterruptEvent(event);