//! 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, }); } } }