// ps2.c - PS/2 keyboard/mouse event decoding and kernel input binding. #include "ps2.h" static KDPC gMouseDpc; static volatile bool gKeyboardNextExtended; static volatile uint8_t gMousePacket[3]; static volatile int gMousePacketIndex; static PS2_MOUSE_PACKET gMouseDpcQueue[MOUSE_DPC_QUEUE_SIZE]; static volatile int gMouseDpcHead; static volatile int gMouseDpcTail; static INPUT_KEY Ps2Set1ToKey(uint8_t make, bool extended) { if (extended) { switch (make) { case 0x1C: return INPUT_KEY_KP_ENTER; case 0x1D: return INPUT_KEY_RIGHT_CTRL; case 0x35: return INPUT_KEY_KP_DIVIDE; case 0x38: return INPUT_KEY_RIGHT_ALT; case 0x47: return INPUT_KEY_HOME; case 0x48: return INPUT_KEY_UP; case 0x49: return INPUT_KEY_PAGE_UP; case 0x4B: return INPUT_KEY_LEFT; case 0x4D: return INPUT_KEY_RIGHT; case 0x4F: return INPUT_KEY_END; case 0x50: return INPUT_KEY_DOWN; case 0x51: return INPUT_KEY_PAGE_DOWN; case 0x52: return INPUT_KEY_INSERT; case 0x53: return INPUT_KEY_DELETE; default: return INPUT_KEY_NONE; } } switch (make) { case 0x01: return INPUT_KEY_ESCAPE; case 0x02: return INPUT_KEY_1; case 0x03: return INPUT_KEY_2; case 0x04: return INPUT_KEY_3; case 0x05: return INPUT_KEY_4; case 0x06: return INPUT_KEY_5; case 0x07: return INPUT_KEY_6; case 0x08: return INPUT_KEY_7; case 0x09: return INPUT_KEY_8; case 0x0A: return INPUT_KEY_9; case 0x0B: return INPUT_KEY_0; case 0x0C: return INPUT_KEY_MINUS; case 0x0D: return INPUT_KEY_EQUAL; case 0x0E: return INPUT_KEY_BACKSPACE; case 0x0F: return INPUT_KEY_TAB; case 0x10: return INPUT_KEY_Q; case 0x11: return INPUT_KEY_W; case 0x12: return INPUT_KEY_E; case 0x13: return INPUT_KEY_R; case 0x14: return INPUT_KEY_T; case 0x15: return INPUT_KEY_Y; case 0x16: return INPUT_KEY_U; case 0x17: return INPUT_KEY_I; case 0x18: return INPUT_KEY_O; case 0x19: return INPUT_KEY_P; case 0x1A: return INPUT_KEY_LEFT_BRACKET; case 0x1B: return INPUT_KEY_RIGHT_BRACKET; case 0x1C: return INPUT_KEY_ENTER; case 0x1D: return INPUT_KEY_LEFT_CTRL; case 0x1E: return INPUT_KEY_A; case 0x1F: return INPUT_KEY_S; case 0x20: return INPUT_KEY_D; case 0x21: return INPUT_KEY_F; case 0x22: return INPUT_KEY_G; case 0x23: return INPUT_KEY_H; case 0x24: return INPUT_KEY_J; case 0x25: return INPUT_KEY_K; case 0x26: return INPUT_KEY_L; case 0x27: return INPUT_KEY_SEMICOLON; case 0x28: return INPUT_KEY_APOSTROPHE; case 0x29: return INPUT_KEY_GRAVE; case 0x2A: return INPUT_KEY_LEFT_SHIFT; case 0x2B: return INPUT_KEY_BACKSLASH; case 0x2C: return INPUT_KEY_Z; case 0x2D: return INPUT_KEY_X; case 0x2E: return INPUT_KEY_C; case 0x2F: return INPUT_KEY_V; case 0x30: return INPUT_KEY_B; case 0x31: return INPUT_KEY_N; case 0x32: return INPUT_KEY_M; case 0x33: return INPUT_KEY_COMMA; case 0x34: return INPUT_KEY_PERIOD; case 0x35: return INPUT_KEY_SLASH; case 0x36: return INPUT_KEY_RIGHT_SHIFT; case 0x37: return INPUT_KEY_KP_MULTIPLY; case 0x38: return INPUT_KEY_LEFT_ALT; case 0x39: return INPUT_KEY_SPACE; case 0x3A: return INPUT_KEY_CAPS_LOCK; case 0x3B: return INPUT_KEY_F1; case 0x3C: return INPUT_KEY_F2; case 0x3D: return INPUT_KEY_F3; case 0x3E: return INPUT_KEY_F4; case 0x3F: return INPUT_KEY_F5; case 0x40: return INPUT_KEY_F6; case 0x41: return INPUT_KEY_F7; case 0x42: return INPUT_KEY_F8; case 0x43: return INPUT_KEY_F9; case 0x44: return INPUT_KEY_F10; case 0x45: return INPUT_KEY_NUM_LOCK; case 0x47: return INPUT_KEY_KP_7; case 0x48: return INPUT_KEY_KP_8; case 0x49: return INPUT_KEY_KP_9; case 0x4A: return INPUT_KEY_KP_MINUS; case 0x4B: return INPUT_KEY_KP_4; case 0x4C: return INPUT_KEY_KP_5; case 0x4D: return INPUT_KEY_KP_6; case 0x4E: return INPUT_KEY_KP_PLUS; case 0x4F: return INPUT_KEY_KP_1; case 0x50: return INPUT_KEY_KP_2; case 0x51: return INPUT_KEY_KP_3; case 0x52: return INPUT_KEY_KP_0; case 0x53: return INPUT_KEY_KP_PERIOD; case 0x57: return INPUT_KEY_F11; case 0x58: return INPUT_KEY_F12; default: return INPUT_KEY_NONE; } } static void Ps2KeyboardIsr(void) { uint8_t sc = __inbyte(PS2_DATA); if (sc == SC1_EXT_PREFIX) { gKeyboardNextExtended = true; return; } bool extended = gKeyboardNextExtended; bool released = (sc & SC1_BREAK_BIT) != 0; uint8_t make = sc & SC1_MAKE_MASK; gKeyboardNextExtended = false; INPUT_KEY key = Ps2Set1ToKey(make, extended); if (key == INPUT_KEY_NONE) return; INPUT_KEY_EVENT event = { .Key = key, .Pressed = !released, }; KeInputSubmitKey(&event); } static void Ps2MouseDpcRoutine(KDPC* dpc, void* context, void* systemArgument1, void* systemArgument2) { (void)dpc; (void)context; (void)systemArgument1; (void)systemArgument2; while (gMouseDpcHead != gMouseDpcTail) { PS2_MOUSE_PACKET packet = gMouseDpcQueue[gMouseDpcHead]; gMouseDpcHead = (gMouseDpcHead + 1) & (int)MOUSE_DPC_QUEUE_MASK; INPUT_MOUSE_EVENT event = { .dx = packet.dx, .dy = packet.dy, .wheel = 0, .buttons = packet.buttons, }; KeInputSubmitMouse(&event); } } static void Ps2MouseIsr(void) { uint8_t data = __inbyte(PS2_DATA); if (gMousePacketIndex == 0 && !(data & 0x08u)) return; gMousePacket[gMousePacketIndex++] = data; if (gMousePacketIndex < 3) return; gMousePacketIndex = 0; uint8_t flags = gMousePacket[0]; if (flags & 0xC0u) return; int dx = (int)gMousePacket[1] - ((flags & 0x10u) ? 256 : 0); int dy = (int)gMousePacket[2] - ((flags & 0x20u) ? 256 : 0); dy = -dy; int next = (gMouseDpcTail + 1) & (int)MOUSE_DPC_QUEUE_MASK; if (next != gMouseDpcHead) { gMouseDpcQueue[gMouseDpcTail].dx = dx; gMouseDpcQueue[gMouseDpcTail].dy = dy; gMouseDpcQueue[gMouseDpcTail].buttons = flags & 0x07u; gMouseDpcTail = next; KeInsertQueueDpc(&gMouseDpc, 0, 0); } } static void Ps2ControllerPrepare(void) { Ps2WriteCommand(0xADu); // disable keyboard port Ps2WriteCommand(0xA7u); // disable aux port Ps2FlushOutput(); uint8_t commandByte = Ps2ReadCommandByte(); commandByte &= (uint8_t)~0x01u; // keep keyboard IRQ disabled during polled init commandByte &= (uint8_t)~0x02u; // keep mouse IRQ disabled during polled init commandByte |= 0x40u; // translate keyboard stream to set 1 commandByte &= (uint8_t)~0x10u; // keyboard clock enabled commandByte &= (uint8_t)~0x20u; // mouse clock enabled Ps2WriteCommandByte(commandByte); Ps2WriteCommand(0xAEu); // enable keyboard port Ps2WriteCommand(0xA8u); // enable aux port Ps2FlushOutput(); } static void Ps2ControllerEnableInterrupts(void) { uint8_t commandByte = Ps2ReadCommandByte(); commandByte |= 0x01u; commandByte |= 0x02u; Ps2WriteCommandByte(commandByte); } static void Ps2KeyboardInitialize(void) { // Enable scanning. If the keyboard is absent, the timeout/ACK failure is // non-fatal; the IRQ path simply never produces events. if (!Ps2SendKeyboard(0xF4u)) DbgPrint("ps2: keyboard enable did not ACK\n"); } static void Ps2MouseInitialize(void) { if (!Ps2SendMouse(0xFFu)) { DbgPrint("ps2: mouse reset did not ACK\n"); return; } (void)Ps2ReadData(); // BAT completion, usually 0xAA (void)Ps2ReadData(); // device id, usually 0x00 if (!Ps2SendMouse(0xF6u)) DbgPrint("ps2: mouse set-defaults did not ACK\n"); if (!Ps2SendMouse(0xF4u)) DbgPrint("ps2: mouse enable did not ACK\n"); } KSTATUS DriverEntry(const HAL_API* hal) { (void)hal; uint32_t keyboardId = 0, mouseId = 0; KeInputRegisterDevice("ps2-keyboard", 0, &keyboardId); KeInputRegisterDevice("ps2-mouse", 0, &mouseId); KeInitializeDpc(&gMouseDpc, Ps2MouseDpcRoutine, 0); // Keep 8042 IRQ generation disabled while doing polled controller/device // setup. The old ordering routed IRQ1/IRQ12 first, which let ACK/BAT bytes // race the ISR and get consumed as fake key/mouse input before the polled // init path could read them. Ps2ControllerPrepare(); Ps2KeyboardInitialize(); Ps2MouseInitialize(); KeIrqRegister(PS2_IRQ_KEYBOARD, Ps2KeyboardIsr); KeIrqRegister(PS2_IRQ_MOUSE, Ps2MouseIsr); Ps2ControllerEnableInterrupts(); DbgPrint("ps2: initialized keyboard=%u mouse=%u\n", keyboardId, mouseId); return STATUS_SUCCESS; }