139 lines
5.5 KiB
Zig
139 lines
5.5 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 device = @import("driver");
|
|
const ipc = @import("ipc");
|
|
const process = @import("process");
|
|
const time = @import("time");
|
|
const input = @import("input-client");
|
|
const memory = @import("memory");
|
|
const logging = @import("logging");
|
|
const ps2 = @import("ps2-library.zig");
|
|
const mouse_packet = @import("mouse-packet.zig");
|
|
const input_protocol = @import("input-protocol");
|
|
|
|
/// 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;
|
|
time.sleepMillis(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 |= input_protocol.mouse_button_left;
|
|
if (packet.right) mask |= input_protocol.mouse_button_right;
|
|
if (packet.middle) mask |= input_protocol.mouse_button_middle;
|
|
return mask;
|
|
}
|
|
|
|
pub fn main(init: process.Init) void {
|
|
const hid = init.arguments.get(1).?;
|
|
|
|
if (hid.len == 0) {
|
|
_ = logging.write("/system/drivers/ps2-bus/mouse: no HID argument\n");
|
|
return;
|
|
}
|
|
std.log.info("starting for hid {s}", .{hid});
|
|
|
|
const buffer = memory.allocator().alloc(device.DeviceDescriptor, 64) catch {
|
|
_ = logging.write("/system/drivers/ps2-bus/mouse: out of memory\n");
|
|
return;
|
|
};
|
|
if (ps2.findMouseDescriptor(buffer) == null) {
|
|
std.log.info("no device for hid {s}", .{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 {
|
|
_ = logging.write("/system/drivers/ps2-bus/mouse: ps2-bus service unavailable\n");
|
|
return;
|
|
};
|
|
const endpoint = ipc.createIpcEndpoint() orelse {
|
|
_ = logging.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 {
|
|
_ = logging.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))
|
|
{
|
|
_ = logging.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 = input.connectSource() orelse {
|
|
_ = logging.write("/system/drivers/ps2-bus/mouse: input service unavailable\n");
|
|
return;
|
|
};
|
|
_ = logging.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{ input_protocol.mouse_button_left, input_protocol.mouse_button_right, input_protocol.mouse_button_middle }) |button| {
|
|
if (changed & button == 0) continue;
|
|
const kind: input_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(input_protocol.MouseEventKind.motion),
|
|
.button = 0,
|
|
.dx = packet.dx,
|
|
.dy = packet.dy,
|
|
.scroll_x = 0,
|
|
.scroll_y = 0,
|
|
.buttons = new_buttons,
|
|
});
|
|
}
|
|
}
|
|
}
|