/* * Keyboard.cpp * PS/2 Keyboard driver -- Scancode Set 1 * Copyright (c) 2025 Daniel Hammer */ #include "Keyboard.hpp" #include "PS2Controller.hpp" #include #include #include #include #include #include namespace Drivers::PS2::Keyboard { // Scancode Set 1 to ASCII lookup table (unshifted) static const char g_ScancodeToAscii[128] = { 0, 0x1B, '1', '2', '3', '4', '5', '6', // 0x00 - 0x07 '7', '8', '9', '0', '-', '=', '\b', '\t', // 0x08 - 0x0F 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', // 0x10 - 0x17 'o', 'p', '[', ']', '\n', 0, 'a', 's', // 0x18 - 0x1F 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', // 0x20 - 0x27 '\'', '`', 0, '\\', 'z', 'x', 'c', 'v', // 0x28 - 0x2F 'b', 'n', 'm', ',', '.', '/', 0, '*', // 0x30 - 0x37 0, ' ', 0, 0, 0, 0, 0, 0, // 0x38 - 0x3F 0, 0, 0, 0, 0, 0, 0, '7', // 0x40 - 0x47 '8', '9', '-', '4', '5', '6', '+', '1', // 0x48 - 0x4F '2', '3', '0', '.', 0, 0, 0, 0, // 0x50 - 0x57 0, 0, 0, 0, 0, 0, 0, 0, // 0x58 - 0x5F 0, 0, 0, 0, 0, 0, 0, 0, // 0x60 - 0x67 0, 0, 0, 0, 0, 0, 0, 0, // 0x68 - 0x6F 0, 0, 0, 0, 0, 0, 0, 0, // 0x70 - 0x77 0, 0, 0, 0, 0, 0, 0, 0 // 0x78 - 0x7F }; // Scancode Set 1 to ASCII lookup table (shifted) static const char g_ScancodeToAsciiShifted[128] = { 0, 0x1B, '!', '@', '#', '$', '%', '^', // 0x00 - 0x07 '&', '*', '(', ')', '_', '+', '\b', '\t', // 0x08 - 0x0F 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', // 0x10 - 0x17 'O', 'P', '{', '}', '\n', 0, 'A', 'S', // 0x18 - 0x1F 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', // 0x20 - 0x27 '"', '~', 0, '|', 'Z', 'X', 'C', 'V', // 0x28 - 0x2F 'B', 'N', 'M', '<', '>', '?', 0, '*', // 0x30 - 0x37 0, ' ', 0, 0, 0, 0, 0, 0, // 0x38 - 0x3F 0, 0, 0, 0, 0, 0, 0, '7', // 0x40 - 0x47 '8', '9', '-', '4', '5', '6', '+', '1', // 0x48 - 0x4F '2', '3', '0', '.', 0, 0, 0, 0, // 0x50 - 0x57 0, 0, 0, 0, 0, 0, 0, 0, // 0x58 - 0x5F 0, 0, 0, 0, 0, 0, 0, 0, // 0x60 - 0x67 0, 0, 0, 0, 0, 0, 0, 0, // 0x68 - 0x6F 0, 0, 0, 0, 0, 0, 0, 0, // 0x70 - 0x77 0, 0, 0, 0, 0, 0, 0, 0 // 0x78 - 0x7F }; // Scancode constants for modifier keys (Set 1) constexpr uint8_t ScLeftShift = 0x2A; constexpr uint8_t ScRightShift = 0x36; constexpr uint8_t ScLeftCtrl = 0x1D; constexpr uint8_t ScLeftAlt = 0x38; constexpr uint8_t ScCapsLock = 0x3A; constexpr uint8_t ScNumLock = 0x45; constexpr uint8_t ScScrollLock = 0x46; // Break code flag (bit 7 set means key release) constexpr uint8_t BreakCodeBit = 0x80; // Ring buffer for key events static KeyEvent g_KeyBuffer[KeyBufferSize]; static volatile uint32_t g_BufferHead = 0; static volatile uint32_t g_BufferTail = 0; static kcp::Spinlock g_BufferLock; // Current modifier state static ModifierState g_Modifiers = {}; // Track extended scancode prefix (0xE0) static bool g_ExtendedScancode = false; static bool IsShiftActive() { bool shift = g_Modifiers.LeftShift || g_Modifiers.RightShift; bool caps = g_Modifiers.CapsLock; return shift ^ caps; } static void BufferPush(const KeyEvent& event) { uint32_t nextHead = (g_BufferHead + 1) & (KeyBufferSize - 1); if (nextHead == g_BufferTail) { // Buffer full, drop the event return; } g_KeyBuffer[g_BufferHead] = event; g_BufferHead = nextHead; } static bool BufferPop(KeyEvent& event) { if (g_BufferTail == g_BufferHead) { return false; } event = g_KeyBuffer[g_BufferTail]; g_BufferTail = (g_BufferTail + 1) & (KeyBufferSize - 1); return true; } void Initialize() { Kt::KernelLogStream(Kt::INFO, "PS2/KB") << "Initializing keyboard driver"; // Reset keyboard to default state // Send 0xFF (Reset) to the keyboard Drivers::PS2::SendData(0xFF); // Expect 0xFA (ACK) then 0xAA (self-test passed) uint8_t ack = Drivers::PS2::ReadData(); if (ack != 0xFA) { Kt::KernelLogStream(Kt::WARNING, "PS2/KB") << "Keyboard reset: unexpected ACK: " << base::hex << (uint64_t)ack; } uint8_t selfTest = Drivers::PS2::ReadData(); if (selfTest != 0xAA) { Kt::KernelLogStream(Kt::WARNING, "PS2/KB") << "Keyboard self-test: unexpected result: " << base::hex << (uint64_t)selfTest; } // Enable scanning (in case it was disabled) Drivers::PS2::SendData(0xF4); ack = Drivers::PS2::ReadData(); g_Modifiers = {}; g_BufferHead = 0; g_BufferTail = 0; g_ExtendedScancode = false; // Register IRQ handler and unmask IRQ1 on the IOAPIC Hal::RegisterIrqHandler(Hal::IRQ_KEYBOARD, HandleIRQ); Hal::IoApic::UnmaskIrq(Hal::IoApic::GetGsiForIrq(Hal::IRQ_KEYBOARD)); Kt::KernelLogStream(Kt::OK, "PS2/KB") << "Keyboard driver initialized"; } void HandleIRQ(uint8_t irq) { (void)irq; uint8_t scancode = Io::In8(DataPort); // Handle extended scancode prefix if (scancode == 0xE0) { g_ExtendedScancode = true; return; } bool released = (scancode & BreakCodeBit) != 0; uint8_t keycode = scancode & ~BreakCodeBit; if (g_ExtendedScancode) { g_ExtendedScancode = false; // Extended scancodes: Right Ctrl (0x1D), Right Alt (0x38), etc. if (keycode == ScLeftCtrl) { g_Modifiers.RightCtrl = !released; } else if (keycode == ScLeftAlt) { g_Modifiers.RightAlt = !released; } // Extended keys don't produce ASCII characters for now KeyEvent event = { .Scancode = scancode, .Ascii = 0, .Pressed = !released, .Shift = g_Modifiers.LeftShift || g_Modifiers.RightShift, .Ctrl = g_Modifiers.LeftCtrl || g_Modifiers.RightCtrl, .Alt = g_Modifiers.LeftAlt || g_Modifiers.RightAlt, .CapsLock = g_Modifiers.CapsLock }; g_BufferLock.Acquire(); BufferPush(event); g_BufferLock.Release(); return; } // Handle modifier keys (update state, but still push event to buffer) bool isModifier = false; switch (keycode) { case ScLeftShift: g_Modifiers.LeftShift = !released; isModifier = true; break; case ScRightShift: g_Modifiers.RightShift = !released; isModifier = true; break; case ScLeftCtrl: g_Modifiers.LeftCtrl = !released; isModifier = true; break; case ScLeftAlt: g_Modifiers.LeftAlt = !released; isModifier = true; break; case ScCapsLock: if (!released) { g_Modifiers.CapsLock = !g_Modifiers.CapsLock; } isModifier = true; break; case ScNumLock: if (!released) { g_Modifiers.NumLock = !g_Modifiers.NumLock; } isModifier = true; break; case ScScrollLock: if (!released) { g_Modifiers.ScrollLock = !g_Modifiers.ScrollLock; } isModifier = true; break; default: break; } // Modifiers still need events in the buffer (for apps like doom) // but lock keys (caps/num/scroll) don't need buffer events if (isModifier && keycode != ScCapsLock && keycode != ScNumLock && keycode != ScScrollLock) { KeyEvent event = { .Scancode = scancode, .Ascii = 0, .Pressed = !released, .Shift = g_Modifiers.LeftShift || g_Modifiers.RightShift, .Ctrl = g_Modifiers.LeftCtrl || g_Modifiers.RightCtrl, .Alt = g_Modifiers.LeftAlt || g_Modifiers.RightAlt, .CapsLock = g_Modifiers.CapsLock }; g_BufferLock.Acquire(); BufferPush(event); g_BufferLock.Release(); return; } if (isModifier) return; // Translate scancode to ASCII char ascii = 0; if (keycode < 128) { if (IsShiftActive()) { ascii = g_ScancodeToAsciiShifted[keycode]; } else { ascii = g_ScancodeToAscii[keycode]; } } KeyEvent event = { .Scancode = scancode, .Ascii = ascii, .Pressed = !released, .Shift = g_Modifiers.LeftShift || g_Modifiers.RightShift, .Ctrl = g_Modifiers.LeftCtrl || g_Modifiers.RightCtrl, .Alt = g_Modifiers.LeftAlt || g_Modifiers.RightAlt, .CapsLock = g_Modifiers.CapsLock }; g_BufferLock.Acquire(); BufferPush(event); g_BufferLock.Release(); } bool IsKeyAvailable() { return g_BufferHead != g_BufferTail; } KeyEvent GetKey() { KeyEvent event = {}; g_BufferLock.Acquire(); BufferPop(event); g_BufferLock.Release(); return event; } char GetChar() { // Blocks until a printable key press is available while (true) { if (IsKeyAvailable()) { KeyEvent event = GetKey(); if (event.Pressed && event.Ascii != 0) { return event.Ascii; } } // Yield the CPU while waiting asm volatile("hlt"); } } const ModifierState& GetModifiers() { return g_Modifiers; } void InjectKeyEvent(const KeyEvent& event) { g_BufferLock.Acquire(); BufferPush(event); g_BufferLock.Release(); } };