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:
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user