ps2: one instance, every node the machine has

The 8042 is a single controller described by two ACPI nodes — PNP0303
carries the 0x60/0x64 ports, PNP0F13 is the mouse — so it cannot be split
across two processes without them fighting over the same registers. That is
why ps2-bus is a singleton, and why it used to find and claim both nodes
itself.

The manager now gives it every matching node instead. The keyboard node
rides the spawn, because it holds the ports and is needed immediately; the
mouse node is transferred to the already-running instance. Late arrival is
safe here and the ordering is natural rather than lucky: the mouse is not
touched until after the controller handshakes and identify. Measured, the
handover lands at 0.336 and the driver reaches the mouse at 0.456.

Because the count is however many matched, a machine with no PS/2 ports or
only one works without a special case — which matters, since the bus is
mostly emulated now and machines vary.

ps2-bus claims nothing. irq_bind on the mouse node is the proof it holds it:
that call is ownership-gated, so a failure means the handover did not land
rather than a hardware fault, and the log says so.

acpi-ps2 asserts both delegations with the spawned device backreferenced, so
the node that rides the spawn must be the one the driver was spawned for.
Disabling the second delegation fails it.

Two things worth recording. The first discrimination patch was not valid Zig,
so nothing ran and a stale binary reported a pass — checked the build before
believing it. And with the second delegation disabled, acpi-ps2 fails while
input still passes: the mouse works without its IRQ binding, so exactly one
case covers that path.

Suite 118/118.
This commit is contained in:
Daniel Samson
2026-08-08 21:33:35 +01:00
parent 7d8aa51234
commit 6b3a381626
3 changed files with 65 additions and 20 deletions
+18 -16
View File
@@ -114,10 +114,9 @@ pub fn main() void {
_ = logging.write("/system/drivers/ps2-bus: found PS/2 controller\n");
_ = logging.write("/system/drivers/ps2-bus: initializing controller\n");
device.claim(controller_device_descriptor.id) catch |e| {
std.log.warn("unable to claim controller: {s}", .{@errorName(e)});
return;
};
// The controller node arrived with the spawn — the manager holds the hardware
// and names it in the call that creates this process
// (docs/os-development/device-authority.md). Nothing to claim.
const controller = ps2.Controller.init(controller_device_descriptor) orelse {
_ = logging.write("/system/drivers/ps2-bus: controller is missing its IO ports\n");
@@ -255,18 +254,21 @@ pub fn main() void {
if (port_device_types[@intFromEnum(ps2.Port.two)] != null) {
if (ps2.findMouseDescriptor(buffer)) |descriptor| {
if (findInterruptResourceIndex(descriptor)) |auxiliary_index| {
if (device.claim(descriptor.id)) |_| {
if (device.irqBind(descriptor.id, auxiliary_index, endpoint)) {
maybe_auxiliary_interrupt = .{
.device_id = descriptor.id,
.interrupt_index = auxiliary_index,
.gsi = descriptor.resources[auxiliary_index].start,
};
} else {
_ = logging.write("/system/drivers/ps2-bus: auxiliary irq_bind failed\n");
}
} else |e| {
std.log.warn("auxiliary claim failed: {s}", .{@errorName(e)});
// The mouse node is a *second* device for this one instance — the
// 8042 is one controller with two ports, so it cannot be split across
// two processes. It is transferred to us after the spawn, which is
// safe because we only reach it here, long after the controller
// handshakes and identify. irq_bind is the proof we hold it: it is
// gated on ownership, so a failure here means the handover has not
// landed rather than a hardware problem.
if (device.irqBind(descriptor.id, auxiliary_index, endpoint)) {
maybe_auxiliary_interrupt = .{
.device_id = descriptor.id,
.interrupt_index = auxiliary_index,
.gsi = descriptor.resources[auxiliary_index].start,
};
} else {
_ = logging.write("/system/drivers/ps2-bus: auxiliary irq_bind failed (mouse node not delegated?)\n");
}
}
}