Files
nt-lite/code/drivers/input/ps2/ps2.c
T

310 lines
11 KiB
C

// PS/2 input driver package entry point. This file registers keyboard and mouse devices.
#include "ps2.h"
static KDPC gKeyboardDpc;
static KDPC gMouseDpc;
static volatile bool gKeyboardNextExtended;
static INPUT_KEY_EVENT gKeyboardDpcQueue[KEYBOARD_DPC_QUEUE_SIZE];
static volatile int gKeyboardDpcHead;
static volatile int gKeyboardDpcTail;
static volatile uint8_t gKeyboardDown[256];
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;
// Implements the ps2 Set1 To Key operation.
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;
}
}
// Implements the ps2 Keyboard Dpc Routine operation.
static void Ps2KeyboardDpcRoutine(KDPC* dpc, void* context, void* systemArgument1, void* systemArgument2) {
(void)dpc;
(void)context;
(void)systemArgument1;
(void)systemArgument2;
while (gKeyboardDpcHead != gKeyboardDpcTail) {
INPUT_KEY_EVENT event = gKeyboardDpcQueue[gKeyboardDpcHead];
gKeyboardDpcHead = (gKeyboardDpcHead + 1) & (int)KEYBOARD_DPC_QUEUE_MASK;
KeInputSubmitKey(&event);
}
}
// Implements the ps2 Queue Keyboard Event operation.
static void Ps2QueueKeyboardEvent(INPUT_KEY key, bool pressed) {
if (key >= sizeof(gKeyboardDown))
return;
bool wasDown = gKeyboardDown[key] != 0;
if (pressed == wasDown)
return;
int next = (gKeyboardDpcTail + 1) & (int)KEYBOARD_DPC_QUEUE_MASK;
if (next == gKeyboardDpcHead)
return;
gKeyboardDown[key] = pressed ? 1u : 0u;
gKeyboardDpcQueue[gKeyboardDpcTail].Key = key;
gKeyboardDpcQueue[gKeyboardDpcTail].Pressed = pressed;
gKeyboardDpcTail = next;
KeInsertQueueDpc(&gKeyboardDpc, 0, 0);
}
// Implements the ps2 Keyboard Isr operation.
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;
Ps2QueueKeyboardEvent(key, !released);
}
// Implements the ps2 Mouse Dpc Routine operation.
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);
}
}
// Implements the ps2 Mouse Isr operation.
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);
}
}
// Implements the ps2 Controller Prepare operation.
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();
}
// Implements the ps2 Controller Enable Interrupts operation.
static void Ps2ControllerEnableInterrupts(void) {
uint8_t commandByte = Ps2ReadCommandByte();
commandByte |= 0x01u;
commandByte |= 0x02u;
Ps2WriteCommandByte(commandByte);
}
// Implements the ps2 Keyboard Initialize operation.
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");
}
// Implements the ps2 Mouse Initialize operation.
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");
}
// Implements the driver Entry operation.
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(&gKeyboardDpc, Ps2KeyboardDpcRoutine, 0);
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;
}