#include "ps2.h" #include "drivers/input/input.h" #include "arch/x86_64/sys/irq.h" #include "arch/x86_64/boot/isr.h" #include "arch/x86_64/cpu/io.h" #include "arch/x86_64/sys/ioapic.h" #include "arch/x86_64/sys/apic.h" #include "libk/stdio.h" #include "libk/debug.h" #include "drivers/video/render.h" /* ── PS/2 I/O ports ───────────────────────────────────────────────────────── */ #define PS2_DATA_PORT 0x60 /* Read: scancode / Write: command data */ #define PS2_STATUS_PORT 0x64 /* Read: controller status */ #define PS2_CMD_PORT 0x64 /* Write: controller command */ /* Status register bits */ #define PS2_STATUS_OBF (1 << 0) /* Output buffer full (data ready to read) */ #define PS2_STATUS_IBF (1 << 1) /* Input buffer full (don't write yet) */ /* ── IRQ number for the keyboard ─────────────────────────────────────────── */ #define KBD_IRQ 1 #define KBD_IDT_VECTOR (0x20 + KBD_IRQ) /* 0x21 with PIC_REMAP_OFFSET=0x20 */ /* ── Modifier state ───────────────────────────────────────────────────────── */ static bool s_shift = false; static bool s_ctrl = false; static bool s_alt = false; static bool s_caps_lock = false; static bool s_extended = false; /* true after receiving 0xE0 prefix */ /* ══════════════════════════════════════════════════════════════════════════ * Set-1 keycode → ASCII translation tables * * Index = Set-1 make code (0x00–0x58 covers the full AT-101 layout). * 0x00 = unmapped / non-printable. * ══════════════════════════════════════════════════════════════════════════ */ static const char s_normal[128] = { /*00*/ 0, 0, '1', '2', '3', '4', '5', '6', /*08*/ '7', '8', '9', '0', '-', '=', '\b', '\t', /*10*/ 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', /*18*/ 'o', 'p', '[', ']', '\n', 0, 'a', 's', /*20*/ 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', /*28*/ '\'','`', 0, '\\','z', 'x', 'c', 'v', /*30*/ 'b', 'n', 'm', ',', '.', '/', 0, '*', /*38*/ 0, ' ', 0, 0, 0, 0, 0, 0, /*40*/ 0, 0, 0, 0, 0, 0, 0, '7', /*48*/ '8', '9', '-', '4', '5', '6', '+', '1', /*50*/ '2', '3', '0', '.', 0, 0, 0, 0, /*58*/ 0 }; static const char s_shifted[128] = { /*00*/ 0, 0, '!', '@', '#', '$', '%', '^', /*08*/ '&', '*', '(', ')', '_', '+', '\b', '\t', /*10*/ 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', /*18*/ 'O', 'P', '{', '}', '\n', 0, 'A', 'S', /*20*/ 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', /*28*/ '"', '~', 0, '|', 'Z', 'X', 'C', 'V', /*30*/ 'B', 'N', 'M', '<', '>', '?', 0, '*', /*38*/ 0, ' ', 0, 0, 0, 0, 0, 0, /*40*/ 0, 0, 0, 0, 0, 0, 0, '7', /*48*/ '8', '9', '-', '4', '5', '6', '+', '1', /*50*/ '2', '3', '0', '.', 0, 0, 0, 0, /*58*/ 0 }; /* ══════════════════════════════════════════════════════════════════════════ * Extended (0xE0-prefixed) make codes we care about * ══════════════════════════════════════════════════════════════════════════ */ typedef enum { /* Regular Set-1 make codes (non-extended) */ SC_LSHIFT = 0x2A, SC_RSHIFT = 0x36, SC_LCTRL = 0x1D, SC_LALT = 0x38, SC_CAPSLOCK = 0x3A, SC_BACKSPACE= 0x0E, SC_ENTER = 0x1C, SC_TAB = 0x0F, SC_ESC = 0x01, /* Function keys */ SC_F1 = 0x3B, SC_F2 = 0x3C, SC_F3 = 0x3D, SC_F4 = 0x3E, SC_F5 = 0x3F, SC_F6 = 0x40, SC_F7 = 0x41, SC_F8 = 0x42, SC_F9 = 0x43, SC_F10 = 0x44, SC_F11 = 0x57, SC_F12 = 0x58, } scancode_t; /* ══════════════════════════════════════════════════════════════════════════ * IRQ1 handler * ══════════════════════════════════════════════════════════════════════════ */ void ps2_kbd_handler(Registers *regs) { (void)regs; /* * Always drain the output buffer even if we don't handle the byte, * otherwise the PS/2 controller stalls and stops sending interrupts. */ uint8_t status = x86_64_inb(PS2_STATUS_PORT); if (!(status & PS2_STATUS_OBF)) { /* Spurious interrupt with no data — just return */ return; } uint8_t byte = x86_64_inb(PS2_DATA_PORT); /* ── Handle 0xE0 extended-key prefix ─────────────────────────────── */ if (byte == 0xE0) { s_extended = true; return; } /* ── Decode make / break ─────────────────────────────────────────── */ bool pressed = !(byte & 0x80); /* bit 7 = 0 → make, 1 → break */ uint8_t scancode = byte & 0x7F; /* strip the break bit */ bool extended = s_extended; s_extended = false; /* ── Update modifier keys ────────────────────────────────────────── */ if (!extended) { switch (scancode) { case SC_LSHIFT: case SC_RSHIFT: s_shift = pressed; return; /* don't push a key event for bare modifiers */ case SC_LCTRL: s_ctrl = pressed; return; case SC_LALT: s_alt = pressed; return; case SC_CAPSLOCK: if (pressed) s_caps_lock = !s_caps_lock; return; } } /* ── Translate to ASCII ──────────────────────────────────────────── */ char ascii = 0; if (!extended && scancode < 128) { bool use_upper = s_shift ^ s_caps_lock; /* XOR: caps inverts shift */ ascii = use_upper ? s_shifted[scancode] : s_normal[scancode]; } /* Extended keys (arrows, home, end, etc.) leave ascii=0 */ /* ── Push event ──────────────────────────────────────────────────── */ input_push_key(scancode, ascii, pressed); if (pressed && ascii) { input_push_char(ascii); } } /* ══════════════════════════════════════════════════════════════════════════ * Public helpers * ══════════════════════════════════════════════════════════════════════════ */ uint8_t ps2_kbd_read_scancode(void) { /* Spin until data is available (for early/polled use only) */ while (!(x86_64_inb(PS2_STATUS_PORT) & PS2_STATUS_OBF)) asm volatile("pause"); return x86_64_inb(PS2_DATA_PORT); } /* ══════════════════════════════════════════════════════════════════════════ * Initialisation * ══════════════════════════════════════════════════════════════════════════ */ void ps2_kbd_init(void) { /* * ── Step 1: Flush the PS/2 output buffer ───────────────────────── * * Any leftover bytes from before boot will cause the controller to * withhold interrupts. Drain them now. */ int attempts = 16; while (attempts-- && (x86_64_inb(PS2_STATUS_PORT) & PS2_STATUS_OBF)) (void)x86_64_inb(PS2_DATA_PORT); x86_64_APIC_IRQ_RedirectAndRegister(1, 0x21, ps2_kbd_handler); kprintf("[PS2] Keyboard driver initialised (IRQ%d vector 0x%02x)\n", KBD_IRQ, KBD_IDT_VECTOR); }