The AML interpreter now handles the Notify opcode (0x86, previously unhandled): it resolves the target device, evaluates the code, and records the pair in a bounded per-evaluate queue the caller drains with takeNotifications. A host unit test with hand-encoded AML — a method that issues Notify(DEV_, 0x80) — proves the device and code come back; aml.zig joins the zig build test loop so the interpreter is covered on the host. The acpi service's SCI handler now services general-purpose events too: for each set-and-enabled GPE bit it evaluates the \_GPE._Lxx (level) or _Exx (edge) handler method, drains the Notify queue that produced, and publishes a domain event per notified device — PNP0C0A battery, ACPI0003 AC, PNP0C0D lid, else generic notify — then clears the status bit and acks. The embedded controller's _Qxx queries are out of scope (hardware track). QEMU raises no GPEs on this config, so the QEMU suite is the regression net (the power button still works with GPE servicing in the path); correctness is the unit test. Suite 59/59.
145 lines
5.7 KiB
Zig
145 lines
5.7 KiB
Zig
//! PS/2 Mouse Driver
|
|
//!
|
|
//! Spawned by the ps2-bus driver once the controller is initialized and the port
|
|
//! has passed its interface test and device reset. The bus driver hands us our
|
|
//! device HID as argv[1].
|
|
//!
|
|
//! Like the keyboard, this driver never touches the hardware: the 8042's ports
|
|
//! and both port IRQs are owned by the ps2-bus driver (the auxiliary port's
|
|
//! IRQ12 lives on the PNP0F13 node, which the bus claims alongside the
|
|
//! controller). The driver **attaches** to the bus and receives every byte the
|
|
//! mouse sends as a forwarded asynchronous message. The bytes assemble into
|
|
//! three-byte packets, and each packet becomes input-protocol events:
|
|
//!
|
|
//! packet -> button transitions -> button_down / button_up
|
|
//! -> movement -> motion (dx/dy, screen convention)
|
|
|
|
const std = @import("std");
|
|
const runtime = @import("runtime");
|
|
const ps2 = @import("ps2-library.zig");
|
|
const mouse_packet = @import("mouse-packet.zig");
|
|
const device = runtime.device;
|
|
const ipc = runtime.ipc;
|
|
const protocol = runtime.input_protocol;
|
|
|
|
fn writeLine(comptime fmt: []const u8, arguments: anytype) void {
|
|
var line: [128]u8 = undefined;
|
|
_ = runtime.system.write(std.fmt.bufPrint(&line, fmt, arguments) catch return);
|
|
}
|
|
|
|
/// Look up the ps2-bus service, retrying while the bus (which spawned us before
|
|
/// registering) is still coming up.
|
|
fn lookupBus() ?ipc.Handle {
|
|
var attempts: usize = 0;
|
|
while (attempts < 100) : (attempts += 1) {
|
|
if (ipc.lookup(.ps2_bus)) |handle| return handle;
|
|
runtime.system.sleep(50);
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/// The protocol's pressed-button bitmask for a packet.
|
|
fn buttonMask(packet: mouse_packet.Packet) u32 {
|
|
var mask: u32 = 0;
|
|
if (packet.left) mask |= protocol.mouse_button_left;
|
|
if (packet.right) mask |= protocol.mouse_button_right;
|
|
if (packet.middle) mask |= protocol.mouse_button_middle;
|
|
return mask;
|
|
}
|
|
|
|
pub fn main(init: runtime.process.Init) void {
|
|
const hid = init.arguments.get(1).?;
|
|
|
|
if (hid.len == 0) {
|
|
_ = runtime.system.write("/system/drivers/ps2-bus/mouse: no HID argument\n");
|
|
return;
|
|
}
|
|
writeLine("/system/drivers/ps2-bus/mouse: starting for hid {s}\n", .{hid});
|
|
|
|
const buffer = runtime.allocator().alloc(device.DeviceDescriptor, 64) catch {
|
|
_ = runtime.system.write("/system/drivers/ps2-bus/mouse: out of memory\n");
|
|
return;
|
|
};
|
|
if (device.findDeviceDescriptorByHid(buffer, hid) == null) {
|
|
writeLine("/system/drivers/ps2-bus/mouse: no device for hid {s}\n", .{hid});
|
|
return;
|
|
}
|
|
|
|
// Attach to the bus: hand it our endpoint, and it forwards every byte the
|
|
// mouse sends (it owns the controller; we own the decoding).
|
|
const bus = lookupBus() orelse {
|
|
_ = runtime.system.write("/system/drivers/ps2-bus/mouse: ps2-bus service unavailable\n");
|
|
return;
|
|
};
|
|
const endpoint = ipc.createIpcEndpoint() orelse {
|
|
_ = runtime.system.write("/system/drivers/ps2-bus/mouse: no endpoint\n");
|
|
return;
|
|
};
|
|
var attach = ps2.AttachRequest{ .device_type = @intFromEnum(ps2.DeviceType.mouse) };
|
|
var attach_reply: [@sizeOf(ps2.AttachReply)]u8 = undefined;
|
|
const attached = ipc.callCap(bus, std.mem.asBytes(&attach), &attach_reply, endpoint) catch {
|
|
_ = runtime.system.write("/system/drivers/ps2-bus/mouse: attach call failed\n");
|
|
return;
|
|
};
|
|
if (attached.len < @sizeOf(ps2.AttachReply) or
|
|
std.mem.bytesToValue(ps2.AttachReply, attach_reply[0..@sizeOf(ps2.AttachReply)]).status != @intFromEnum(ps2.AttachStatus.ok))
|
|
{
|
|
_ = runtime.system.write("/system/drivers/ps2-bus/mouse: attach refused\n");
|
|
return;
|
|
}
|
|
|
|
// Broadcast mouse events through the input service so programs can listen
|
|
// for them (docs/input.md).
|
|
var source = runtime.input.connectSource() orelse {
|
|
_ = runtime.system.write("/system/drivers/ps2-bus/mouse: input service unavailable\n");
|
|
return;
|
|
};
|
|
_ = runtime.system.write("/system/drivers/ps2-bus/mouse: ok\n");
|
|
|
|
var assembler = mouse_packet.Assembler{};
|
|
var buttons: u32 = 0;
|
|
var receive: [@sizeOf(ps2.ForwardedByte)]u8 = undefined;
|
|
while (true) {
|
|
const got = ipc.replyWait(endpoint, &.{}, &receive, null);
|
|
if (!got.isMessage() or got.len < @sizeOf(ps2.ForwardedByte)) continue;
|
|
const forwarded = std.mem.bytesToValue(ps2.ForwardedByte, receive[0..@sizeOf(ps2.ForwardedByte)]);
|
|
|
|
const packet = assembler.feed(@intCast(forwarded.byte & 0xFF)) orelse continue;
|
|
const new_buttons = buttonMask(packet);
|
|
|
|
// A button transition per changed button, carrying the new whole mask.
|
|
const changed = buttons ^ new_buttons;
|
|
for ([_]u32{ protocol.mouse_button_left, protocol.mouse_button_right, protocol.mouse_button_middle }) |button| {
|
|
if (changed & button == 0) continue;
|
|
const kind: protocol.MouseEventKind = if (new_buttons & button != 0) .button_down else .button_up;
|
|
_ = source.publishMouseEvent(.{
|
|
.kind = @intFromEnum(kind),
|
|
.button = button,
|
|
.dx = 0,
|
|
.dy = 0,
|
|
.scroll_x = 0,
|
|
.scroll_y = 0,
|
|
.buttons = new_buttons,
|
|
});
|
|
}
|
|
buttons = new_buttons;
|
|
|
|
if (packet.dx != 0 or packet.dy != 0) {
|
|
_ = source.publishMouseEvent(.{
|
|
.kind = @intFromEnum(protocol.MouseEventKind.motion),
|
|
.button = 0,
|
|
.dx = packet.dx,
|
|
.dy = packet.dy,
|
|
.scroll_x = 0,
|
|
.scroll_y = 0,
|
|
.buttons = new_buttons,
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
pub const panic = runtime.panic;
|
|
comptime {
|
|
_ = &runtime.start._start;
|
|
}
|