runtime: consolidate the device-manager hello into runtime.device_manager

Every supervised driver owes the device manager a hello at startup, and
the retry-lookup-call-check for it had been copied into five drivers:
usb.helloManager (misfiled in the USB client) plus hand-rolled twins in
display, virtio-gpu, pci-bus, and usb-xhci-bus.

Extract it once as a runtime client, runtime.device_manager.hello(role,
device_id) ?Handle — returns the manager endpoint (bus drivers keep it to
report children through), null when there is no manager or it refused the
handshake, and logs the outcome itself so each call site is one line.

Also correct two roles while collapsing their calls: virtio-gpu and the
display driver each claim one PCI function and report no children, so they
are Role.device, not Role.bus. The manager ignores role today, so this is
cosmetic, but it matches the protocol's own definition (bus = reports
children via child_added).

Behavior-preserving otherwise: pci-bus and usb-xhci-bus move their hello
logging from raw serial writes to std.log, which the kernel renders with
the same "<path>: " prefix, so driver-restart still matches
"usb-xhci-bus: hello acknowledged". zig build clean; 8 QEMU cases pass
(driver-restart, pci-scan, usb-hid, usb-storage, virtio-gpu,
display-reattach, device-list, device-manager).
This commit is contained in:
Daniel Samson
2026-07-22 19:27:20 +01:00
parent d27377ab1e
commit ea470afe84
10 changed files with 85 additions and 108 deletions
+6 -25
View File
@@ -131,32 +131,13 @@ fn initialise(endpoint: runtime.ipc.Handle) bool {
return false;
}
// The handshake: role, protocol version, assignment — inside the manager's
// deadline (the lookup retries cover the manager still registering).
var manager: ?runtime.ipc.Handle = null;
var tries: u32 = 0;
while (manager == null and tries < 100) : (tries += 1) {
manager = runtime.ipc.lookup(.device_manager);
if (manager == null) runtime.system.sleep(20);
}
const h = manager orelse {
_ = runtime.system.write("/system/drivers/usb-xhci-bus: no device manager to hello\n");
return false;
};
manager_handle = h; // the tick's hot-plug dispatch reports through this
const hello = protocol.Hello{ .role = @intFromEnum(protocol.Role.bus), .device_id = controller_id };
var reply: [protocol.message_maximum]u8 = undefined;
const n = runtime.ipc.call(h, std.mem.asBytes(&hello), &reply) catch {
_ = runtime.system.write("/system/drivers/usb-xhci-bus: hello call failed\n");
return false;
};
if (n < protocol.reply_size or std.mem.bytesToValue(protocol.HelloReply, reply[0..protocol.reply_size]).status != 0) {
_ = runtime.system.write("/system/drivers/usb-xhci-bus: hello refused\n");
return false;
}
_ = runtime.system.write("/system/drivers/usb-xhci-bus: hello acknowledged\n");
// 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;
manager_handle = handle;
scanPorts(h);
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.