//! shared definitions between the different PS/2 drivers const runtime = @import("runtime"); const device = runtime.device; /// PS-2 io ports: /// The PS/2 Controller itself uses 2 IO ports (usually, IO ports 0x60 and 0x64). Like many IO /// ports, reads and writes may access different internal registers. /// /// Historical note: The PC-XT PPI had used port 0x61 to reset the keyboard interrupt request /// signal (among other unrelated functions). Port 0x61 has no keyboard related functions on AT and /// PS/2 compatibles. /// /// The Data Port (typically IO Port 0x60) is used for reading data that was received from a PS/2 /// device or from the PS/2 controller itself and writing data to a PS/2 device or to the PS/2 /// controller itself. // Access type: Read/Write pub const dataPort = 0x60; // Access type: Read pub const statusRegisterPort = 0x64; // Access type: Write pub const CommandRegisterPort = 0x64; const spin_limit: u32 = 5000; fn waitWritable(id: u64, cmd_index: u64) bool { var tries: u32 = 0; while (tries < spin_limit) : (tries += 1) { if (status(id, cmd_index) & 0x02 == 0) return true; // IBF clear -> ok to write } return false; // timed out } fn waitReadable(id: u64, cmd_index: u64) bool { var tries: u32 = 0; while (tries < spin_limit) : (tries += 1) { if (status(id, cmd_index) & 0x01 != 0) return true; // OBF set -> data ready } return false; } fn scanResourcesIndexes(d: device.DeviceDescriptor) struct { data_port_resource_index: u64, command_or_status_resource_index: u64 } { const resources = d.resources; const total_resources = d.resource_count; var data_port_resource_index: ?u64 = null; var command_or_status_resource_index: ?u64 = null; for (resources, 0..total_resources) |resource, resource_index| { if (resource.kind == @intFromEnum(device.ResourceKind.io_port) and resource.start == dataPort) { data_port_resource_index = @intCast(resource_index); } else if (resource.kind == @intFromEnum(device.ResourceKind.io_port) and resource.start == statusRegisterPort) { command_or_status_resource_index = @intCast(resource_index); } } return .{ .data_port_resource_index = data_port_resource_index orelse 0, .command_or_status_resource_index = command_or_status_resource_index orelse 0 }; } fn status(id: u64, cmd_index: u64) u8 { return @intCast(device.ioRead(id, cmd_index, 0, 1) orelse 0); } fn sendCommand(id: u64, cmd_index: u64, byte: u8) bool { // wait IBF clear if (!waitWritable(id, cmd_index)) return false; return device.ioWrite(id, cmd_index, 0, 1, byte); } fn readData(id: u64, data_index: u64) u8 { // wait OBF set if (!waitReadable(id, data_index)) return null; return @intCast(device.ioRead(id, data_index, 0, 1) orelse 0); } pub fn disablePorts(d: device.DeviceDescriptor) void { const indexes = scanResourcesIndexes(d); // disable port 1 (keyboard) _ = sendCommand(d.id, indexes.data_port_resource_index, 0xAD); // disable port 2 (aux/mouse) _ = sendCommand(d.id, indexes.command_or_status_resource_index, 0xA7); } pub fn flushOutputBuffer(d: device.DeviceDescriptor) void { const indexes = scanResourcesIndexes(d); // flush any stale byte the controller buffered _ = device.ioRead(d.id, indexes.data_port_resource_index, 0, 1); }