310 lines
11 KiB
C
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;
|
|
}
|