Kernel: Update PS/2 controller
This commit is contained in:
parent
fcec793873
commit
a12ddfa12a
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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!");
|
||||
|
|
Loading…
Reference in New Issue