diff --git a/kexts/ps2kbd/extension.yaml b/kexts/ps2kbd/extension.yaml new file mode 100644 index 0000000..8741fe5 --- /dev/null +++ b/kexts/ps2kbd/extension.yaml @@ -0,0 +1,8 @@ +name: ps2kbd +description: | + PS2 Keyboard and Mouse Driver +id: net.doorstuck.socks.ps2kbd +license: BSD-3-Clause +copyright: Copyright © Max Wash 2023 +sources: + - main.c diff --git a/kexts/ps2kbd/main.c b/kexts/ps2kbd/main.c new file mode 100644 index 0000000..12d348e --- /dev/null +++ b/kexts/ps2kbd/main.c @@ -0,0 +1,228 @@ +#include +#include +#include +#include +#include +#include + +#define PS2_CMD_P1ON 0xae +#define PS2_CMD_P1OFF 0xad +#define PS2_CMD_P2ON 0xa8 +#define PS2_CMD_P2OFF 0xa7 + +#define PS2_CFG_P1_IRQ (1 << 0) +#define PS2_CFG_P2_IRQ (1 << 1) +#define PS2_CFG_SYSFLAG (1 << 2) +#define PS2_CFG_ZERO1 (1 << 3) +#define PS2_CFG_P1_CLOCK (1 << 4) +#define PS2_CFG_P2_CLOCK (1 << 5) +#define PS2_CFG_P1_TRL (1 << 6) +#define PS2_CFG_ZERO2 (1 << 7) + +#define PS2_SC2_PSBRK 0xe1 +#define PS2_SC2_RELEASED 0xf0 + +#define NEXT_MAP 0xffffffff + +#define PS2_ACK 0xfa +#define PS2_RESEND 0xfe + +#define PS2_IO_DATA 0x60 +#define PS2_IO_STATUS 0x64 +#define PS2_IO_CMD 0x64 + +static struct input_device *keyboard = NULL, *mouse = NULL; + +static void send_cmd(uint8_t cmd) +{ + /* prevent the compiler from optimising this away */ + volatile int _i = 0; + while (inportb(PS2_IO_STATUS) & 0x2) { + _i++; + } + + outportb(PS2_IO_CMD, cmd); +} + +static void send_data(uint8_t data) +{ + /* prevent the compiler from optimising this away */ + volatile int _i = 0; + while (inportb(PS2_IO_STATUS) & 0x2) { + _i++; + } + + outportb(PS2_IO_DATA, data); +} + +static uint8_t recv_data() +{ + /* prevent the compiler from optimising this away */ + volatile int _i = 0; + while (!(inportb(PS2_IO_STATUS) & 0x1)) { + _i++; + } + + return inportb(PS2_IO_DATA); +} + +static int data_avail() +{ + return inportb(PS2_IO_STATUS) & 0x1; +} + +static void flush_buffer() +{ + while (data_avail()) { + recv_data(); + } +} + +static uint8_t read_config() +{ + send_cmd(0x20); + return recv_data(); +} + +static void set_config(uint8_t cfg) +{ + send_cmd(0x60); + send_data(cfg); +} + +static int int_handler(void) +{ + static int scancode_level = 0; + static bool released = false; + + if (scancode_level == PS2_SC2_PSBRK) { + /* TODO pause break keycode */ + scancode_level = 0; + return 0; + } + + while (data_avail()) { + unsigned char scancode = recv_data(); + + /* the only scancode set that begins with 0xe1 is the Pause key. + * Rather than try and interpret a sequence of 8 codes, we just + * assume the pause key was pressed */ + if (scancode == PS2_SC2_PSBRK) { + flush_buffer(); + /* TODO pause break keycode */ + //mdk_input_event ev = { .type = MDK_INPUT_DEVICE_KEYBOARD }; + scancode_level = PS2_SC2_PSBRK; + break; + } + + if (scancode == PS2_SC2_RELEASED) { + released = true; + continue; + } + + printk("ps2: got scancode 0x%02x (%s)", scancode, released ? "up" : "down"); + scancode_level = 0; + } + + return 0; +} + +static int init_controller(void) +{ + /* disable ps2 devices */ + send_cmd(PS2_CMD_P1OFF); + send_cmd(PS2_CMD_P2OFF); + + flush_buffer(); + + /* configure the controller */ + uint8_t config = read_config(); + config &= ~(PS2_CFG_P1_IRQ | PS2_CFG_P2_IRQ | PS2_CFG_P1_TRL); + set_config(config); + + send_cmd(0xaa); + uint8_t response = recv_data(); + if (response != 0x55) { + return -1; + } + + set_config(config); + + /* Switch to scancode set 2 */ + send_data(0xf0); + send_data(0x02); + if (recv_data() != PS2_ACK) { + return -1; + } + + /* enable 1st port */ + send_cmd(PS2_CMD_P1ON); + + /* enable interrupts */ + config = read_config(); + config |= PS2_CFG_P1_IRQ; + set_config(config); + + /* perform keyboard test */ + send_data(0xff); + uint8_t response1 = recv_data(); + uint8_t response2 = recv_data(); + if ((response1 != 0xaa && response2 != 0xfa) && + (response1 != 0xfa && response2 != 0xaa)) { + return -1; + } + + return 0; +} + +static kern_status_t create_devices(void) +{ + struct input_device *kbd = input_device_create(); + struct device *kbd_base = input_device_base(kbd); + snprintf(kbd_base->dev_name, sizeof kbd_base->dev_name, "ps2kbd"); + + struct input_device *ms = input_device_create(); + struct device *ms_base = input_device_base(ms); + snprintf(ms_base->dev_name, sizeof ms_base->dev_name, "ps2ms"); + + kern_status_t status = device_register(kbd_base, misc_device()); + if (status != KERN_OK) { + /* TODO destroy devices */ + return status; + } + + status = device_register(ms_base, misc_device()); + if (status != KERN_OK) { + return status; + } + + keyboard = kbd; + mouse = ms; + + return KERN_OK; +} + +static struct irq_hook ps2_hook = { + .irq_callback = int_handler, +}; + +static kern_status_t online(struct kext *self) +{ + int result = init_controller(); + if (result != 0) { + return KERN_UNSUPPORTED; + } + + kern_status_t status = create_devices(); + if (status != KERN_OK) { + return status; + } + + hook_irq(IRQ1, &ps2_hook); + printk("ps2: controller initialised"); + return KERN_OK; +} + +DEFINE_KEXT("net.doorstuck.socks.ps2kbd", + online, NULL, + KEXT_NO_DEPENDENCIES);