karlos/src/x86_64/ps2_driver.c

301 lines
7 KiB
C

#include <stdint.h>
#include "cpu.h"
#include "std.h"
#include "x86_64/apic.h"
#include "x86_64/asm.h"
#include "x86_64/ps2_driver.h"
#include "keyboard.h"
#define PORT_STATUS 0x64
#define PORT_CMD 0x64
#define PORT_DATA 0x60
#define CMD_CONFIGBYTE_READ 0x20
#define CMD_CONFIGBYTE_WRITE 0x20
#define CMD_PORT2_DISABLE 0xA7
#define CMD_PORT2_ENABLE 0xA8
#define CMD_PORT2_TEST 0xA9
#define CMD_TEST 0xAA
#define CMD_PORT1_TEST 0xAB
#define CMD_PORT1_DISABLE 0xAD
#define CMD_PORT1_ENABLE 0xAE
#define STATUS_BIT_OUTPUT_FULL 0
#define STATUS_BIT_INPUT_FULL 1
#define STATUS_BIT_SYSTEM 2
#define STATUS_BIT_COMMAND_DATA 3
#define STATUS_BIT_ERR_TIMEOUT 6
#define STATUS_BIT_ERR_PARITY 7
#define CONFIG_BIT_PORT1_INT_ENABLED 0
#define CONFIG_BIT_PORT2_INT_ENABLED 1
#define CONFIG_BIT_SYSTEM 2
#define CONFIG_BIT_PORT1_CLOCK_DISABLED 4
#define CONFIG_BIT_PORT2_CLOCK_DISABLED 5
#define CONFIG_BIT_PORT1_TRANSLATION 6
#ifdef DEBUG_PS2
#define DEBUG_PRINTF(...) printf(__VA_ARGS__)
#else
#define DEBUG_PRINTF(...) do { } while (0)
#endif
uint8_t
ps2_read_status() {
return in8(PORT_STATUS);
}
bool
ps2_get_status_bit(uint8_t bit) {
return (ps2_read_status() >> bit) & 1;
}
bool
ps2_can_read() {
return ps2_get_status_bit(STATUS_BIT_OUTPUT_FULL);
}
bool
ps2_can_write() {
return !ps2_get_status_bit(STATUS_BIT_INPUT_FULL);
}
void ps2_write_cmd(uint8_t cmd) {
while (!ps2_can_write()) {
// wait
}
out8(PORT_CMD, cmd);
}
uint8_t ps2_read_data() {
while(!ps2_can_read()) {
// wait
}
return in8(PORT_DATA);
}
void ps2_empty_output_buffer() {
while (ps2_can_read()) {
in8(PORT_DATA);
}
}
void ps2_write_data(uint8_t data) {
while (!ps2_can_write()) {
// wait
}
out8(PORT_DATA, data);
}
uint8_t ps2_cmd_response(uint8_t cmd) {
ps2_write_cmd(cmd);
return ps2_read_data();
}
void ps2_cmd_with_data(uint8_t cmd, uint8_t data) {
ps2_write_cmd(cmd);
ps2_write_data(data);
}
uint8_t ps2_code_to_keycode_arr[256] = {
[0x01] = KEY_ESC,
[0x29] = KEY_CARET,
[0x02] = KEY_1,
[0x03] = KEY_2,
[0x04] = KEY_3,
[0x05] = KEY_4,
[0x06] = KEY_5,
[0x07] = KEY_6,
[0x08] = KEY_7,
[0x09] = KEY_8,
[0x0a] = KEY_9,
[0x0b] = KEY_0,
[0x0c] = KEY_SZ,
[0x0d] = KEY_ACCENT,
[0x0e] = KEY_BACKSPACE,
[0x0f] = KEY_TAB,
[0x10] = KEY_Q,
[0x11] = KEY_W,
[0x12] = KEY_E,
[0x13] = KEY_R,
[0x14] = KEY_T,
[0x15] = KEY_Z,
[0x16] = KEY_U,
[0x17] = KEY_I,
[0x18] = KEY_O,
[0x19] = KEY_P,
[0x1a] = KEY_UE,
[0x1b] = KEY_PLUS,
[0x1c] = KEY_ENTER,
[0x3a] = KEY_CAPSLOCK,
[0x1e] = KEY_A,
[0x1f] = KEY_S,
[0x20] = KEY_D,
[0x21] = KEY_F,
[0x22] = KEY_G,
[0x23] = KEY_H,
[0x24] = KEY_J,
[0x25] = KEY_K,
[0x26] = KEY_L,
[0x27] = KEY_OE,
[0x28] = KEY_AE,
[0x2b] = KEY_HASH,
[0x2a] = KEY_LSHIFT,
[0x56] = KEY_LESSTHAN,
[0x2c] = KEY_Y,
[0x2d] = KEY_X,
[0x2e] = KEY_C,
[0x2f] = KEY_V,
[0x30] = KEY_B,
[0x31] = KEY_N,
[0x32] = KEY_M,
[0x33] = KEY_COMMA,
[0x34] = KEY_PERIOD,
[0x35] = KEY_DASH,
[0x36] = KEY_RSHIFT,
[0x1d] = KEY_LSTRG,
[0x38] = KEY_LALT,
[0x39] = KEY_SPACE,
};
uint8_t ps2_code_to_keycode(uint8_t ps2_code) {
return ps2_code_to_keycode_arr[ps2_code];
}
uint8_t ps2_code_ext_to_keycode(uint8_t ps2_code) {
switch (ps2_code) {
case 0x38: return KEY_RALT;
case 0x48: return KEY_ARROW_UP;
case 0x4b: return KEY_ARROW_LEFT;
case 0x50: return KEY_ARROW_DOWN;
case 0x4d: return KEY_ARROW_RIGHT;
default: return 0;
}
}
bool have_extend_byte_last = false;
void port1_handler(void) {
bool extended = have_extend_byte_last;
have_extend_byte_last = false;
uint8_t b = ps2_read_data();
DEBUG_PRINTF("PS2 code: %X8\n", b);
if (b == 0xe0) {
have_extend_byte_last = true;
return;
}
if (!extended) {
if (b == 0x01 || ps2_code_to_keycode(b) != 0) {
uint8_t code = ps2_code_to_keycode(b);
DEBUG_PRINTF(" Keycode: (%d, %d), pressed: true\n", KC_ROW(code), KC_COL(code));
on_key_event(code, true);
} else if ((b & 0x7f) == 0x01 || ps2_code_to_keycode(b & 0x7f) != 0) {
uint8_t code = ps2_code_to_keycode(b & 0x7f);
DEBUG_PRINTF(" Keycode: (%d, %d), pressed: false\n", KC_ROW(code), KC_COL(code));
on_key_event(code, false);
} else {
DEBUG_PRINTF("unknown key code: %X8\n", b);
}
} else {
if (ps2_code_ext_to_keycode(b) != 0) {
uint8_t code =ps2_code_ext_to_keycode(b);
DEBUG_PRINTF(" (ext) Keycode: (%d, %d), pressed: true\n", KC_ROW(code), KC_COL(code));
on_key_event(code, true);
} else if (ps2_code_ext_to_keycode(b & 0x7f) != 0) {
uint8_t code = ps2_code_ext_to_keycode(b & 0x7f);
DEBUG_PRINTF(" (ext) Keycode: (%d, %d), pressed: false\n", KC_ROW(code), KC_COL(code));
on_key_event(code, false);
} else {
DEBUG_PRINTF("(ext) unknown key code: %X8\n", b);
}
}
}
void port2_handler(void) {
uint8_t data = ps2_read_data();
DEBUG_PRINTF("keycode from port 2: %X8\n", data);
}
#define PS2_INIT_PRINT
#ifdef PS2_INIT_ASSERT
#define PS2_TEST(ctx, have, want) ASSERT((have) == (want))
#elif defined(PS2_INIT_PRINT)
#define PS2_TEST(ctx, have, want) printf("ps2-init: %s: want %X8, have %X8\n", (ctx), (want), (have))
#else
#define PS2_TEST(ctx, have, want) do { } while (0)
#endif
void ps2_init() {
uint8_t config_byte;
/* Disable devices */
ps2_write_cmd(CMD_PORT1_DISABLE);
ps2_write_cmd(CMD_PORT2_DISABLE);
/* Flush Outputbuffer */
ps2_empty_output_buffer();
/* Set Configbyte */
config_byte = ps2_cmd_response(CMD_CONFIGBYTE_READ);
config_byte &= ~CONFIG_BIT_PORT1_INT_ENABLED;
config_byte &= ~CONFIG_BIT_PORT1_CLOCK_DISABLED;
config_byte &= ~CONFIG_BIT_PORT1_TRANSLATION;
ps2_cmd_with_data(CMD_CONFIGBYTE_WRITE, config_byte);
/* Selftest */
uint8_t test = ps2_cmd_response(CMD_TEST);
PS2_TEST("self-test", test, 0x55);
/* Query for 2nd port */
ps2_write_cmd(CMD_PORT2_ENABLE);
config_byte = ps2_cmd_response(CMD_CONFIGBYTE_READ);
bool port_2_enabled = ((config_byte >> 5) & 1) == 0;
if (port_2_enabled) {
ps2_write_cmd(CMD_PORT2_DISABLE);
config_byte = ps2_cmd_response(CMD_CONFIGBYTE_READ);
config_byte &= ~CONFIG_BIT_PORT2_INT_ENABLED;
config_byte &= ~CONFIG_BIT_PORT2_CLOCK_DISABLED;
ps2_cmd_with_data(CMD_CONFIGBYTE_WRITE, config_byte);
}
/* Test Ports */
uint8_t port_test = ps2_cmd_response(CMD_PORT1_TEST);
PS2_TEST("port1-test", port_test, 0x00);
if (port_2_enabled) {
port_test = ps2_cmd_response(CMD_PORT2_TEST);
PS2_TEST("port2-test", port_test, 0x00);
}
/* Enable devices */
ps2_write_cmd(CMD_PORT1_ENABLE);
if (port_2_enabled) {
ps2_write_cmd(CMD_PORT2_ENABLE);
}
/* Enable interrupts */
config_byte = ps2_cmd_response(CMD_CONFIGBYTE_READ);
config_byte |= CONFIG_BIT_PORT1_INT_ENABLED;
if (port_2_enabled) {
config_byte |= CONFIG_BIT_PORT2_INT_ENABLED;
}
ps2_cmd_with_data(CMD_CONFIGBYTE_WRITE, config_byte);
/* register interrupt handlers */
interrupt_handler_register(101, port1_handler);
interrupt_handler_register(102, port2_handler);
ioapic_configure_irq(1, 101 | IORED_DELIVERY_NORMAL | ((uint64_t)lapic_get_id() << IORED_DESTINATION_SHIFT));
ioapic_configure_irq(12, 102 | IORED_DELIVERY_NORMAL | ((uint64_t)lapic_get_id() << IORED_DESTINATION_SHIFT));
/* Flush Outputbuffer again because it doesn't work otherwise */
ps2_empty_output_buffer();
/* Reset devices */
// TODO
}