major refactorings
Signed-off-by: kaguya3311 <kaguya3311@national.shitposting.agency>
This commit is contained in:
@@ -0,0 +1,195 @@
|
||||
#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);
|
||||
}
|
||||
Reference in New Issue
Block a user