301 lines
7 KiB
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
|
|
}
|