Files
2026-05-18 04:02:59 -04:00

195 lines
8.9 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#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 (0x000x58 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);
}