176 lines
7.5 KiB
Zig
176 lines
7.5 KiB
Zig
//! The PS/2 Controller is located on the mainboard.
|
|
//! In the early days the controller was a single chip (Intel 8042).
|
|
//! As of today it is part of the Advanced Integrated Peripheral.
|
|
//!
|
|
//! It shows up in the device discovery as:
|
|
//! KBD_ [acpi_device] hid=PNP0303 (PS/2 Keyboard)
|
|
//! - io_port 0x60 len 0x1
|
|
//! - io_port 0x64 len 0x1
|
|
//! - irq 0x1 len 0x1
|
|
//! MOU_ [acpi_device] hid=PNP0F13 (PS/2 Mouse)
|
|
//! - irq 0xc len 0x1
|
|
const std = @import("std");
|
|
const runtime = @import("runtime");
|
|
const ps2 = @import("ps2-library.zig");
|
|
const device = runtime.device;
|
|
|
|
/// Format one whole log line and emit it in a single `debug_write`, so output
|
|
/// from the child drivers (which run concurrently) can never interleave with it.
|
|
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);
|
|
}
|
|
|
|
/// Ask the device on `port` what it is, then spawn the matching driver from the
|
|
/// initial-ramdisk, handing it the device's HID as argv[1]. The driver is chosen
|
|
/// from what the device reports, not from the port number.
|
|
fn spawnIdentifiedDriver(controller: ps2.Controller, port: ps2.Port) void {
|
|
const device_type = controller.identifyDevice(port) orelse {
|
|
writeLine("system/drivers/ps2-bus: identify timed out on port {s}\n", .{@tagName(port)});
|
|
return;
|
|
};
|
|
const driver_name = device_type.driverName() orelse {
|
|
writeLine("system/drivers/ps2-bus: unrecognized device on port {s}\n", .{@tagName(port)});
|
|
return;
|
|
};
|
|
const hid = device_type.hid() orelse "";
|
|
if (runtime.system.spawnWithArguments(driver_name, &.{hid}) != null) {
|
|
writeLine("system/drivers/ps2-bus: port {s} is a {s}, spawned {s}\n", .{ @tagName(port), hid, driver_name });
|
|
} else {
|
|
writeLine("system/drivers/ps2-bus: failed to spawn {s}\n", .{driver_name});
|
|
}
|
|
}
|
|
|
|
pub fn main() void {
|
|
const buffer = runtime.allocator().alloc(device.DeviceDescriptor, 64) catch {
|
|
_ = runtime.system.write("system/drivers/ps2-bus: out of memory\n");
|
|
return;
|
|
};
|
|
|
|
var has_two_channels = false;
|
|
// The 8042's IO ports (0x60/0x64) are enumerated under the keyboard ACPI node
|
|
// (PNP0303), so we init the controller from that descriptor — but which device
|
|
// is on which port is decided later by identify, not by this HID.
|
|
const maybe_controller_device_descriptor = device.findDeviceDescriptorByHid(buffer, "PNP0303");
|
|
if (maybe_controller_device_descriptor) |controller_device_descriptor| {
|
|
_ = runtime.system.write("system/drivers/ps2-bus: found PS/2 controller\n");
|
|
_ = runtime.system.write("system/drivers/ps2-bus: initializing controller\n");
|
|
|
|
if (!device.claim(controller_device_descriptor.id)) {
|
|
_ = runtime.system.write("system/drivers/ps2-bus: unable to claim controller \n");
|
|
return;
|
|
}
|
|
|
|
const controller = ps2.Controller.init(controller_device_descriptor) orelse {
|
|
_ = runtime.system.write("system/drivers/ps2-bus: controller is missing its IO ports\n");
|
|
return;
|
|
};
|
|
|
|
controller.disablePort(.One);
|
|
controller.disablePort(.Two);
|
|
controller.flushOutputBuffer();
|
|
|
|
const current = controller.readConfigurationByte() orelse {
|
|
_ = runtime.system.write("system/drivers/ps2-bus: controller configuration timed out\n");
|
|
return;
|
|
};
|
|
|
|
const update = current & ~(ps2.configuration_first_port_interrupt |
|
|
ps2.configuration_second_port_interrupt |
|
|
ps2.configuration_first_port_translation);
|
|
|
|
if (controller.writeConfigurationByte(update) == null) {
|
|
_ = runtime.system.write("system/drivers/ps2-bus: controller configuration timed out\n");
|
|
return;
|
|
}
|
|
|
|
if (controller.performSelfTest()) | reply | {
|
|
if (reply != ps2.response_controller_test_passed) {
|
|
_ = runtime.system.write("system/drivers/ps2-bus: perform controller self test failed\n");
|
|
return;
|
|
}
|
|
} else {
|
|
_ = runtime.system.write("system/drivers/ps2-bus: controller self test timed out\n");
|
|
return;
|
|
}
|
|
|
|
has_two_channels = controller.hasTwoChannels() orelse {
|
|
_ = runtime.system.write("system/drivers/ps2-bus: controller channels timed out\n");
|
|
return;
|
|
};
|
|
|
|
if (has_two_channels) {
|
|
_ = runtime.system.write("system/drivers/ps2-bus: has two channels\n");
|
|
// keep the bus quiet until we have tested the ports and are ready to use them
|
|
controller.disablePort(.Two);
|
|
} else {
|
|
_ = runtime.system.write("system/drivers/ps2-bus: has one channel\n");
|
|
}
|
|
|
|
// interface tests: always test port 1, test port 2 only if it exists
|
|
const port_one_works = (controller.testPort(.One) orelse {
|
|
_ = runtime.system.write("system/drivers/ps2-bus: port 1 test timed out\n");
|
|
return;
|
|
}) == ps2.response_port_test_passed;
|
|
|
|
var port_two_works = false;
|
|
if (has_two_channels) {
|
|
port_two_works = (controller.testPort(.Two) orelse {
|
|
_ = runtime.system.write("system/drivers/ps2-bus: port 2 test timed out\n");
|
|
return;
|
|
}) == ps2.response_port_test_passed;
|
|
}
|
|
|
|
if (!port_one_works and !port_two_works) {
|
|
_ = runtime.system.write("system/drivers/ps2-bus: no usable ports\n");
|
|
return;
|
|
}
|
|
|
|
// enable the working ports and, via a read-modify-write, their interrupts
|
|
controller.enablePort(.One);
|
|
if (port_two_works) controller.enablePort(.Two);
|
|
|
|
var configuration = controller.readConfigurationByte() orelse {
|
|
_ = runtime.system.write("system/drivers/ps2-bus: controller configuration timed out\n");
|
|
return;
|
|
};
|
|
if (port_one_works) configuration |= ps2.Port.One.interruptBit();
|
|
if (port_two_works) configuration |= ps2.Port.Two.interruptBit();
|
|
_ = controller.writeConfigurationByte(configuration);
|
|
|
|
// reset each working device; a failing device is logged but does not
|
|
// abort bring-up of the other one
|
|
if (port_one_works) {
|
|
if (controller.resetDevice(.One)) |passed| {
|
|
if (!passed) _ = runtime.system.write("system/drivers/ps2-bus: port 1 device reset failed\n");
|
|
} else {
|
|
_ = runtime.system.write("system/drivers/ps2-bus: port 1 device reset timed out\n");
|
|
}
|
|
}
|
|
if (port_two_works) {
|
|
if (controller.resetDevice(.Two)) |passed| {
|
|
if (!passed) _ = runtime.system.write("system/drivers/ps2-bus: port 2 device reset failed\n");
|
|
} else {
|
|
_ = runtime.system.write("system/drivers/ps2-bus: port 2 device reset timed out\n");
|
|
}
|
|
}
|
|
|
|
// Identify the device on each working port and hand it off to the driver
|
|
// that matches what it reported — a port is not assumed to be a keyboard
|
|
// or a mouse by its number.
|
|
if (port_one_works) spawnIdentifiedDriver(controller, .One);
|
|
if (port_two_works) spawnIdentifiedDriver(controller, .Two);
|
|
} else {
|
|
_ = runtime.system.write("system/drivers/ps2-bus: no PS/2 controller found\n");
|
|
return;
|
|
}
|
|
|
|
_ = runtime.system.write("system/drivers/ps2-bus: ok\n");
|
|
while (true) runtime.system.sleep(1000);
|
|
}
|
|
|
|
pub const panic = runtime.panic;
|
|
comptime {
|
|
_ = &runtime.start._start;
|
|
}
|