Kernel: Update PS/2 controller

This commit is contained in:
Bananymous 2023-01-04 19:22:23 +02:00
parent fcec793873
commit a12ddfa12a
2 changed files with 160 additions and 128 deletions

View File

@ -9,7 +9,10 @@
#include <kernel/KeyboardLayout/FI.h>
#define KB_DEBUG_PRINT 1
#define DEBUG_ALL_IRQ 0
#define KEYBOARD_SHOW_UNKNOWN 1
#define MOUSE_ENABLED 0
#define I8042_DATA_PORT 0x60
#define I8042_STATUS_REGISTER 0x64
@ -46,7 +49,6 @@
#define I8042_KB_SELF_TEST_PASS 0xAA
#define I8042_KB_SET_SCAN_CODE_SET 0xF0
#define I8042_KB_SET_LEDS 0xED
#define I8042_KB_TIMEOUT_MS 1000
#define I8042_KB_LED_SCROLL_LOCK (1 << 0)
#define I8042_KB_LED_NUM_LOCK (1 << 1)
#define I8042_KB_LED_CAPS_LOCK (1 << 2)
@ -57,6 +59,8 @@
#define I8042_MOUSE_ENABLE 0xF4
#define I8042_MOUSE_DISABLE 0xF5
#define I8042_TIMEOUT_MS 1000
#define KEYBOARD_IRQ 0x01
#define MOUSE_IRQ 0x0C
@ -85,6 +89,7 @@ namespace Input
uint8_t _ack = 0;
bool _done = false;
};
static uint64_t s_command_sent = 0;
static BAN::Queue<Command> s_command_queue;
static uint8_t s_command_response[3] = {};
static uint8_t s_command_response_index = 0;
@ -153,6 +158,11 @@ namespace Input
};
static_assert(sizeof(s_key_to_utf8_upper) == (int)Key::Count * sizeof(*s_key_to_utf8_upper));
static void keyboard_new_key();
static uint8_t wait_and_read()
{
while ((IO::inb(I8042_STATUS_REGISTER) & I8042_STATUS_OUT_FULL) == 0)
@ -176,15 +186,16 @@ namespace Input
static bool i8042_command(uint8_t target, uint8_t command)
{
if (target == TARGET_MOUSE)
IO::outb(I8042_STATUS_REGISTER, 0xD4);
IO::outb(I8042_COMMAND_REGISTER, 0xD4);
auto timeout = PIT::ms_since_boot() + I8042_KB_TIMEOUT_MS;
auto timeout = PIT::ms_since_boot() + I8042_TIMEOUT_MS;
while (PIT::ms_since_boot() < timeout)
{
if ((IO::inb(I8042_STATUS_REGISTER) & I8042_STATUS_IN_FULL) == 0)
{
IO::outb(I8042_DATA_PORT, command);
s_command_sent = PIT::ms_since_boot();
return true;
}
}
@ -192,6 +203,114 @@ namespace Input
return false;
}
static void i8042_handle_byte(uint8_t target, uint8_t raw)
{
bool waiting_response = false;
if (!s_command_queue.Empty())
{
auto& command = s_command_queue.Front();
if (command.target == target && command._sent && !command._done)
waiting_response = true;
}
if (target == TARGET_KEYBOARD)
{
if (waiting_response)
{
auto& command = s_command_queue.Front();
if (raw == I8042_KB_RESEND)
{
dprintln("PS/2 Keyboard: Resend 0x{H}", command._sent == 2 ? command.data : command.command);
command._sent--;
}
else if (raw == I8042_KB_ACK)
{
command._ack++;
if (command.resp_cnt == 0)
command._done = (command._ack >= (1 + command.has_data));
}
else if (raw == 0x00)
{
dprintln("\e[33mKey detection error or internal buffer overrun\e[m");
command._sent = 0;
command._ack = 0;
command._done = false;
s_command_response_index = 0;
}
else if (raw == 0xEE && command.command == 0xEE)
{
s_command_queue.Pop();
}
else
{
s_command_response[s_command_response_index++] = raw;
if (s_command_response_index >= command.resp_cnt)
command._done = true;
}
}
else
{
s_keyboard_key_buffer[s_keyboard_key_buffer_size++] = raw;
if (raw != 0xE0)
keyboard_new_key();
}
}
else if (target == TARGET_MOUSE)
{
if (waiting_response)
{
auto& command = s_command_queue.Front();
if (raw == I8042_MOUSE_ACK)
{
command._ack++;
if (command.resp_cnt == 0)
command._done = (command._ack >= (1 + command.has_data));
}
else
{
s_command_response[s_command_response_index++] = raw;
if (s_command_response_index >= command.resp_cnt)
command._done = true;
}
}
else
{
s_mouse_data_buffer[s_mouse_data_buffer_index++] = raw;
if (s_mouse_data_buffer_index >= 3)
{
if (s_mouse_data_buffer[0] & 0x07)
{
bool left = s_mouse_data_buffer[0] & (1 << 0);
bool right = s_mouse_data_buffer[0] & (1 << 1);
bool middle = s_mouse_data_buffer[0] & (1 << 2);
if (left) s_mouse_button_event_queue.Push({ .button = MouseButton::Left });
if (right) s_mouse_button_event_queue.Push({ .button = MouseButton::Right });
if (middle) s_mouse_button_event_queue.Push({ .button = MouseButton::Middle });
}
if (s_mouse_data_buffer[1] || s_mouse_data_buffer[2])
{
int16_t rel_x = (int16_t)s_mouse_data_buffer[1] - ((s_mouse_data_buffer[0] << 4) & 0x100);
int16_t rel_y = (int16_t)s_mouse_data_buffer[2] - ((s_mouse_data_buffer[0] << 3) & 0x100);
s_mouse_move_event_queue.Push({ .dx = rel_x, .dy = rel_y });
}
s_mouse_data_buffer_index = 0;
}
}
}
else
{
Kernel::panic("Unknown target");
}
}
void update()
{
if (!s_command_queue.Empty())
@ -202,18 +321,26 @@ namespace Input
if (command._sent == 0 && command._ack == 0)
{
command._sent++;
if (!i8042_command(command.target, command.command))
Kernel::panic("PS/2 command oof {}, 0x{2H}", command.target, command.command);
command._sent++;
}
if (command._sent == 1 && command._ack == 1 && command.has_data)
{
command._sent++;
if (!i8042_command(command.target, command.data))
Kernel::panic("PS/2 data oof {}, 0x{2H}", command.target, command.data);
command._sent++;
}
if (command._sent > 0 && PIT::ms_since_boot() > s_command_sent + 1000)
{
kprintln("PS/2 command 0x{2H} timed out on {}", command.command, command.target);
// Discard command on timeout?
command._done = true;
command.target = 0;
}
if (command._done)
{
if (command.target == TARGET_KEYBOARD)
@ -250,6 +377,7 @@ namespace Input
Kernel::panic("PS/2 Mouse unhandled command");
}
}
s_command_response_index = 0;
s_command_queue.Pop();
}
@ -328,7 +456,7 @@ namespace Input
}
#if KB_DEBUG_PRINT
#if KEYBOARD_SHOW_UNKNOWN
if (key == Key::INVALID)
kprintln("{} {}", ch, extended ? 'E' : ' ');
#endif
@ -360,6 +488,7 @@ namespace Input
if (update_leds)
{
s_command_queue.Push({
.target = TARGET_KEYBOARD,
.command = I8042_KB_SET_LEDS,
.data = s_led_states,
.has_data = true,
@ -392,120 +521,28 @@ namespace Input
static void keyboard_irq_handler()
{
uint8_t raw = IO::inb(I8042_DATA_PORT);
bool waiting_response = false;
if (!s_command_queue.Empty())
{
auto& command = s_command_queue.Front();
if (command.target == TARGET_MOUSE)
Kernel::panic("Please no :( (PS/2 Keyboard got irq while Mouse was waiting)");
waiting_response = (command._sent > 0 && !command._done);
}
if (waiting_response)
{
auto& command = s_command_queue.Front();
if (raw == I8042_KB_RESEND)
{
dprintln("PS/2 Keyboard: Resend 0x{H}", command._sent == 2 ? command.data : command.command);
command._sent--;
}
else if (raw == I8042_KB_ACK)
{
command._ack++;
if (command.resp_cnt == 0)
command._done = (command._ack >= (1 + command.has_data));
}
else if (raw == 0x00)
{
dprintln("\e[33mKey detection error or internal buffer overrun\e[m");
command._sent = 0;
command._ack = 0;
command._done = false;
s_command_response_index = 0;
}
else if (raw == 0xEE && command.command == 0xEE)
{
s_command_queue.Pop();
}
else
{
s_command_response[s_command_response_index++] = raw;
if (s_command_response_index >= command.resp_cnt)
command._done = true;
}
}
else
{
s_keyboard_key_buffer[s_keyboard_key_buffer_size++] = raw;
if (raw != 0xE0)
keyboard_new_key();
}
#if DEBUG_ALL_IRQ
dprintln("k 0x{2H}", raw);
#endif
i8042_handle_byte(TARGET_KEYBOARD, raw);
}
static void mouse_irq_handler()
{
uint8_t raw = IO::inb(I8042_DATA_PORT);
bool waiting_response = false;
if (!s_command_queue.Empty())
{
auto& command = s_command_queue.Front();
if (command.target == TARGET_KEYBOARD)
Kernel::panic("Please no :( (PS/2 Mouse got irq while Keyboard was waiting)");
waiting_response = (command._sent > 0 && !command._done);
}
if (waiting_response)
{
auto& command = s_command_queue.Front();
if (raw == I8042_MOUSE_ACK)
{
command._ack++;
if (command.resp_cnt == 0)
command._done = (command._ack >= (1 + command.has_data));
}
else
{
s_command_response[s_command_response_index++] = raw;
if (s_command_response_index >= command.resp_cnt)
command._done = true;
}
}
else
{
s_mouse_data_buffer[s_mouse_data_buffer_index++] = raw;
if (s_mouse_data_buffer_index >= 3)
{
if (s_mouse_data_buffer[0] & 0x07)
{
bool left = s_mouse_data_buffer[0] & (1 << 0);
bool right = s_mouse_data_buffer[0] & (1 << 1);
bool middle = s_mouse_data_buffer[0] & (1 << 2);
if (left) s_mouse_button_event_queue.Push({ .button = MouseButton::Left });
if (right) s_mouse_button_event_queue.Push({ .button = MouseButton::Right });
if (middle) s_mouse_button_event_queue.Push({ .button = MouseButton::Middle });
}
if (s_mouse_data_buffer[1] || s_mouse_data_buffer[2])
{
int16_t rel_x = (int16_t)s_mouse_data_buffer[1] - ((s_mouse_data_buffer[0] << 4) & 0x100);
int16_t rel_y = (int16_t)s_mouse_data_buffer[2] - ((s_mouse_data_buffer[0] << 3) & 0x100);
s_mouse_move_event_queue.Push({ .dx = rel_x, .dy = rel_y });
}
s_mouse_data_buffer_index = 0;
}
}
#if DEBUG_ALL_IRQ
dprintln("m 0x{2H}", raw);
#endif
i8042_handle_byte(TARGET_MOUSE, raw);
}
static void initialize_keyboard()
{
// Register callback and IRQ
IDT::register_irq_handler(KEYBOARD_IRQ, keyboard_irq_handler);
APIC::EnableIRQ(KEYBOARD_IRQ);
i8042_controller_command(I8042_ENABLE_FIRST_PORT);
MUST(s_command_queue.Push({
.target = TARGET_KEYBOARD,
.command = I8042_KB_RESET,
@ -527,14 +564,15 @@ namespace Input
.data = s_led_states,
.has_data = true,
}));
// Register callback and IRQ
IDT::register_irq_handler(KEYBOARD_IRQ, keyboard_irq_handler);
APIC::EnableIRQ(KEYBOARD_IRQ);
}
static void initialize_mouse()
{
// Register callback and IRQ
IDT::register_irq_handler(MOUSE_IRQ, mouse_irq_handler);
APIC::EnableIRQ(MOUSE_IRQ);
i8042_controller_command(I8042_ENABLE_SECOND_PORT);
MUST(s_command_queue.Push({
.target = TARGET_MOUSE,
.command = I8042_MOUSE_RESET,
@ -546,10 +584,6 @@ namespace Input
.target = TARGET_MOUSE,
.command = I8042_MOUSE_ENABLE,
}));
// Register callback and IRQ
IDT::register_irq_handler(MOUSE_IRQ, mouse_irq_handler);
APIC::EnableIRQ(MOUSE_IRQ);
}
bool initialize()
@ -586,7 +620,7 @@ namespace Input
}
// Step 7: Determine If There Are 2 Channels
bool dual_channel = config & I8042_CONFING_DUAL_CHANNEL;
bool dual_channel = MOUSE_ENABLED ? config & I8042_CONFING_DUAL_CHANNEL : false;
if (dual_channel)
{
i8042_controller_command(I8042_ENABLE_SECOND_PORT);
@ -620,13 +654,11 @@ namespace Input
if (dual_channel)
config |= I8042_CONFING_IRQ_SECOND;
i8042_controller_command(I8042_WRITE_CONFIG, config);
i8042_controller_command(I8042_ENABLE_FIRST_PORT);
i8042_controller_command(I8042_ENABLE_SECOND_PORT);
// Step 10: Reset Devices
initialize_keyboard();
if (dual_channel)
initialize_mouse();
initialize_mouse();
return true;
}

View File

@ -80,15 +80,15 @@ extern "C" void kernel_main(multiboot_info_t* mbi, uint32_t magic)
gdt_initialize();
IDT::initialize();
PIT::initialize();
kmalloc_initialize();
PIT::initialize();
if (!Input::initialize())
return;
TTY* tty1 = new TTY;
tty1->SetCursorPosition(0, 2);
if (!Input::initialize())
return;
ENABLE_INTERRUPTS();
kprintln("Hello from the kernel!");