Wire real PS/2 scancodes through to input events and characters
Replace the keyboard driver's synthetic stream with the real path: - ps2-bus binds IRQ1 (interrupt bits set only after the bind), drains port 0x60 on each interrupt, and forwards every byte to the attached child driver over async ipc_send, routed by the status register's auxiliary-output bit. Children attach via the new well-known ps2_bus service, handing over their endpoint as a capability. - scancode.zig (new, pure, host-tested): scancode set 2 -> USB HID usage decoding (F0/E0/E1 prefix state machine) plus keyboard state — pressed-key bitmap, typematic-repeat classification, modifier and caps-lock tracking. - keyboard.zig decodes the forwarded stream and publishes real key_down/key_press/key_up events, filling key_press characters via xkeyboard-config (layout from argv[2], default us) and synthesizing ASCII control characters for Enter/Tab/Backspace/Escape. - protocol.zig names the full HID usage set in Keycode; build.zig threads the xkeyboard-config module into user binaries. Verified end to end in QEMU via monitor sendkey: shift, caps lock, and control-character synthesis all decode correctly. The mouse driver still publishes its synthetic stream; attaching it to the bus the same way is the follow-up.
This commit is contained in:
@@ -49,12 +49,15 @@ pub const cmd_write_second_port_output: u8 = 0xD3; // write next data byte to th
|
||||
pub const cmd_write_second_port_input: u8 = 0xD4; // write next data byte to the second port input buffer (to the mouse)
|
||||
pub const cmd_pulse_system_reset: u8 = 0xFE; // pulse output line 0 low: resets the CPU
|
||||
|
||||
/// PS/2 status register bits (read from the status port, 0x64). Bits 4 and 5
|
||||
/// are chipset-specific and intentionally omitted.
|
||||
/// PS/2 status register bits (read from the status port, 0x64). Bit 4 is
|
||||
/// chipset-specific and intentionally omitted.
|
||||
pub const status_output_buffer_full: u8 = 1 << 0; // 1 = a byte is waiting to be read from the data port
|
||||
pub const status_input_buffer_full: u8 = 1 << 1; // 1 = the controller has not yet consumed the last write
|
||||
pub const status_system_flag: u8 = 1 << 2; // set once the controller passes POST
|
||||
pub const status_command_or_data: u8 = 1 << 3; // 1 = last write was a command, 0 = data
|
||||
/// Chipset-specific in the original spec, universal in practice on dual-channel
|
||||
/// controllers: set = the waiting byte came from the second port (the mouse).
|
||||
pub const status_auxiliary_output: u8 = 1 << 5;
|
||||
pub const status_timeout_error: u8 = 1 << 6; // 1 = time-out error
|
||||
pub const status_parity_error: u8 = 1 << 7; // 1 = parity error
|
||||
|
||||
@@ -237,11 +240,12 @@ pub const Port = enum(u2) {
|
||||
};
|
||||
|
||||
/// The kind of device attached to a port, as reported by the device itself in
|
||||
/// response to the identify command — not assumed from the port number.
|
||||
pub const DeviceType = enum {
|
||||
keyboard,
|
||||
mouse,
|
||||
unknown,
|
||||
/// response to the identify command — not assumed from the port number. Fixed
|
||||
/// `u32` values because the type also travels in an `AttachRequest`.
|
||||
pub const DeviceType = enum(u32) {
|
||||
keyboard = 0,
|
||||
mouse = 1,
|
||||
unknown = 2,
|
||||
|
||||
/// Initial-ramdisk name of the driver that serves this device type, or null
|
||||
/// if we could not classify it.
|
||||
@@ -264,6 +268,37 @@ pub const DeviceType = enum {
|
||||
}
|
||||
};
|
||||
|
||||
// --- the bus <-> child-driver forwarding protocol -----------------------------
|
||||
//
|
||||
// The 8042's ports and IRQ1 live on the PNP0303 node that only the ps2-bus driver
|
||||
// claims, so the child device drivers (ps2-keyboard, ps2-mouse) cannot read port
|
||||
// 0x60 themselves. Instead each child **attaches**: it calls the bus's well-known
|
||||
// `ps2_bus` endpoint with an `AttachRequest`, handing over its own endpoint as the
|
||||
// call's capability. From then on the bus forwards every byte the device sends as
|
||||
// a `ForwardedByte` via the asynchronous `ipc.send` — the IRQ path in the bus can
|
||||
// never block on a slow child, and the child never touches the controller.
|
||||
|
||||
/// A child driver registering for its device's bytes. `device_type` is a
|
||||
/// `DeviceType` value; the child's receive endpoint travels as the call's
|
||||
/// capability (`send_cap`).
|
||||
pub const AttachRequest = extern struct {
|
||||
device_type: u32,
|
||||
};
|
||||
|
||||
/// Reply to an `AttachRequest`. `status` is 0 on success or a negative errno.
|
||||
pub const AttachReply = extern struct {
|
||||
status: i32,
|
||||
_padding: u32 = 0,
|
||||
};
|
||||
|
||||
/// One raw byte read from the data port, forwarded to the attached child whose
|
||||
/// port it came from (routed by the status register's auxiliary-output bit).
|
||||
pub const ForwardedByte = extern struct {
|
||||
/// The `Port` the byte came from, as `@intFromEnum`.
|
||||
port: u32,
|
||||
byte: u32,
|
||||
};
|
||||
|
||||
/// A single PS/2 (8042) controller. Construct one with `Controller.init` and
|
||||
/// drive the controller through its methods; there is only ever one 8042 per
|
||||
/// machine, but holding the resolved resource indices in an instance keeps the
|
||||
|
||||
Reference in New Issue
Block a user