Kernel: Improve keyboard input by a lot

Seems to work on my 2 computerss
This commit is contained in:
Bananymous
2022-12-13 10:42:49 +02:00
parent 7ebe727a29
commit e62a626b39
5 changed files with 818 additions and 476 deletions

View File

@@ -1,50 +1,89 @@
#include <kernel/IDT.h>
#include <kernel/IO.h>
#include <kernel/Keyboard.h>
#include <kernel/PIC.h>
#include <kernel/kprint.h>
#include <kernel/PIC.h>
#include <kernel/PIT.h>
#include <kernel/Queue.h>
#include <kernel/Serial.h>
#include <kernel/KeyboardLayout/FI.h>
#define I8042_DATA_PORT 0x60
#define I8042_STATUS_REGISTER 0x64
#define I8042_COMMAND_REGISTER 0x64
#define KB_DEBUG_PRINT 1
#define I8042_READ_BYTE0 0x20
#define I8042_WRITE_BYTE0 0x60
#define I8042_DATA_PORT 0x60
#define I8042_STATUS_REGISTER 0x64
#define I8042_COMMAND_REGISTER 0x64
#define I8042_ENABLE_FIRST 0xAE
#define I8042_ENABLE_SECOND 0xA8
#define I8042_DISABLE_FIRST 0xAD
#define I8042_DISABLE_SECOND 0xA7
#define I8042_STATUS_OUT_FULL (1 << 0)
#define I8042_STATUS_IN_FULL (1 << 1)
#define I8042_TEST_CONTROLLER 0xAA
#define I8042_CONTROLLER_TEST_PASS 0x55
#define I8042_READ_CONFIG 0x20
#define I8042_WRITE_CONFIG 0x60
#define I8042_TEST_FIRST_PORT 0xAB
#define I8042_FIRST_PORT_TEST_PASS 0x00
#define I8042_CONFING_IRQ_FIRST (1 << 0)
#define I8042_CONFING_IRQ_SECOND (1 << 1)
#define I8042_CONFING_TRANSLATION (1 << 6)
#define I8042_TEST_SECOND 0xA9
#define I8042_SECOND_PORT_TEST_PASS 0x00
#define I8042_ENABLE_FIRST 0xAE
#define I8042_ENABLE_SECOND 0xA8
#define I8042_DISABLE_FIRST 0xAD
#define I8042_DISABLE_SECOND 0xA7
#define I8042_ACK 0xfa
#define I8042_TEST_CONTROLLER 0xAA
#define I8042_TEST_CONTROLLER_PASS 0x55
#define KEYBOARD_IRQ 0x01
#define I8042_TEST_FIRST_PORT 0xAB
#define I8042_TEST_FIRST_PORT_PASS 0x00
#define MOD_ALT 0b0001
#define MOD_CTRL 0b0010
#define MOD_SHIFT 0b0100
#define MOD_ALTGR 0b1000
#define I8042_TEST_SECOND_PORT 0xA9
#define I8042_TEST_SECOND_PORT_PASS 0x00
#define KEYBOARD_FI
#define I8042_KB_ACK 0xFA
#define I8042_KB_RESEND 0xFE
#define I8042_KB_RESET 0xFF
#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)
#define KEYBOARD_IRQ 0x01
#define MOD_ALT (1 << 0)
#define MOD_CTRL (1 << 1)
#define MOD_SHIFT (1 << 2)
#define MOD_ALTGR (1 << 3)
#define MOD_CAPS (1 << 4)
namespace Keyboard
{
static bool s_keyboard_state[0xFF] = {};
static uint8_t s_modifiers = 0;
static void (*s_key_callback)(Key, uint8_t, bool) = nullptr;
struct Command
{
uint8_t command = 0;
uint8_t data = 0;
bool has_data = false;
bool extra = false;
uint8_t _sent = 0;
uint8_t _ack = 0;
bool _done = false;
};
static Queue<Command> s_keyboard_command_queue;
static uint8_t s_keyboard_command_extra = 0x00;
static Queue<KeyEvent> s_key_event_queue;
static uint8_t s_keyboard_key_buffer[10] = {};
static uint8_t s_keyboard_key_buffer_size = 0;
static uint8_t s_led_states = 0b000;
static uint8_t s_modifiers = 0x00;
static void (*s_key_callback)(KeyEvent) = nullptr;
static char s_key_to_char[]
{
@@ -59,110 +98,263 @@ namespace Keyboard
'$', '\0', '\0', '\0', '\n', ' ', '\t', '\b', '<', '>', '\0', '`', '\0', '\0', '@', '|',
'\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
'\0', '\0', '\0', '\0', '\0', '\0', '\0',
'\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
',', '+', '*', '/', '-',
',', '+', '*', '/', '-', '\n',
'\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
'\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
};
static_assert(sizeof(s_key_to_char) == static_cast<int>(Key::Count));
static uint8_t kb_read()
static uint8_t wait_and_read()
{
while (!(IO::inb(I8042_STATUS_REGISTER) & 0x01))
while ((IO::inb(I8042_STATUS_REGISTER) & I8042_STATUS_OUT_FULL) == 0)
continue;
return IO::inb(I8042_DATA_PORT);
}
static bool kb_try_read(uint8_t& out)
static void i8042_controller_command(uint8_t command)
{
if (IO::inb(I8042_STATUS_REGISTER) & 0x01)
{
out = IO::inb(I8042_DATA_PORT);
return true;
}
return false;
}
static void kb_command(uint8_t command)
{
IO::io_wait();
IO::outb(I8042_COMMAND_REGISTER, command);
}
static void kb_command(uint8_t command, uint8_t data)
static void i8042_controller_command(uint8_t command, uint8_t data)
{
IO::io_wait();
IO::outb(I8042_COMMAND_REGISTER, command);
IO::io_wait();
while ((IO::inb(I8042_STATUS_REGISTER) & I8042_STATUS_IN_FULL) != 0)
continue;
IO::outb(I8042_DATA_PORT, data);
}
void irq_handler()
static bool i8042_keyboard_command(uint8_t command)
{
uint8_t ch;
while (kb_try_read(ch))
auto timeout = PIT::ms_since_boot() + I8042_KB_TIMEOUT_MS;
while (PIT::ms_since_boot() < timeout)
{
bool multimedia = false, pressed = true;
if (ch == 0xE0)
if ((IO::inb(I8042_STATUS_REGISTER) & I8042_STATUS_IN_FULL) == 0)
{
multimedia = true;
ch = kb_read();
IO::outb(I8042_DATA_PORT, command);
return true;
}
}
return false;
}
void update_keyboard()
{
if (!s_keyboard_command_queue.Empty())
{
auto& command = s_keyboard_command_queue.Front();
if (command._sent == 0 && command._ack == 0)
{
if (!i8042_keyboard_command(command.command))
Kernel::panic("oof 1");
command._sent++;
}
if (ch == 0xF0)
if (command._sent == 1 && command._ack == 1 && command.has_data)
{
pressed = false;
ch = kb_read();
}
// TODO: Handle multimedia keys
if (multimedia)
{
if (ch == 17)
pressed ? (s_modifiers |= MOD_ALTGR) : (s_modifiers &= ~MOD_ALTGR);
if (pressed && false)
kprint("<M{}>", ch);
continue;
if (!i8042_keyboard_command(command.data))
Kernel::panic("oof 2");
command._sent++;
}
s_keyboard_state[ch] = pressed;
switch (ch)
if (command._done)
{
case 17:
pressed ? (s_modifiers |= MOD_ALT) : (s_modifiers &= ~MOD_ALT);
break;
case 18:
case 89:
pressed ? (s_modifiers |= MOD_SHIFT) : (s_modifiers &= ~MOD_SHIFT);
break;
case 20:
pressed ? (s_modifiers |= MOD_CTRL) : (s_modifiers &= ~MOD_CTRL);
break;
default:
break;
switch (command.command)
{
case I8042_KB_RESET:
if (s_keyboard_command_extra != I8042_KB_SELF_TEST_PASS)
Kernel::panic("PS/2 Keyboard self test failed");
break;
case I8042_KB_SET_SCAN_CODE_SET:
break;
case I8042_KB_SET_LEDS:
break;
}
s_keyboard_command_queue.Pop();
}
}
Key key;
if (s_modifiers & MOD_ALTGR)
key = scs2_to_key_altgr[ch];
else if (s_modifiers & MOD_SHIFT)
key = scs2_to_key_shift[ch];
else
key = scs2_to_key[ch];
// Debug print for unregistered keys
if (key == Key::INVALID && pressed)
kprint("<{}>", ch);
s_key_callback(key, s_modifiers, pressed);
while (!s_key_event_queue.Empty())
{
s_key_callback(s_key_event_queue.Front());
s_key_event_queue.Pop();
}
}
void initialize(void (*callback)(Key, uint8_t, bool))
static void keyboard_new_key()
{
uint8_t index = 0;
bool extended = (s_keyboard_key_buffer[index] == 0xE0);
if (extended)
index++;
bool pressed = (s_keyboard_key_buffer[index] & 0x80) == 0;
uint8_t ch = s_keyboard_key_buffer[index] & ~0x80;
Key key = Key::INVALID;
if (extended)
{
key = scan_code_to_key_extended[ch];
}
else
{
if (s_modifiers & MOD_SHIFT)
key = scan_code_to_key_shift[ch];
else if (s_modifiers & MOD_ALTGR)
key = scan_code_to_key_altgr[ch];
else
key = scan_code_to_key_normal[ch];
}
if ((s_led_states & I8042_KB_LED_NUM_LOCK))
{
switch (key)
{
case Key::Numpad0: key = Key::Insert; break;
case Key::Numpad1: key = Key::End; break;
case Key::Numpad2: key = Key::Down; break;
case Key::Numpad3: key = Key::PageDown; break;
case Key::Numpad4: key = Key::Left; break;
case Key::Numpad5: key = Key::None; break;
case Key::Numpad6: key = Key::Right; break;
case Key::Numpad7: key = Key::Home; break;
case Key::Numpad8: key = Key::Up; break;
case Key::Numpad9: key = Key::PageUp; break;
case Key::NumpadSep: key = Key::Delete; break;
default: break;
}
}
#if KB_DEBUG_PRINT
if (key == Key::INVALID)
kprintln("{} {}", ch, extended ? 'E' : ' ');
#endif
s_keyboard_state[static_cast<int>(key)] = pressed;
bool update_leds = false;
switch (key)
{
case Key::ScrollLock:
update_leds = pressed;
if (update_leds)
s_led_states ^= I8042_KB_LED_SCROLL_LOCK;
break;
case Key::NumLock:
update_leds = pressed;
if (update_leds)
s_led_states ^= I8042_KB_LED_NUM_LOCK;
break;
case Key::CapsLock:
update_leds = pressed;
if (update_leds)
s_led_states ^= I8042_KB_LED_CAPS_LOCK;
break;
default:
break;
}
if (update_leds)
{
s_keyboard_command_queue.Push({
.command = I8042_KB_SET_LEDS,
.data = s_led_states,
.has_data = true,
});
}
uint8_t modifiers = 0;
if (s_keyboard_state[(int)Key::LeftShift] || s_keyboard_state[(int)Key::RightShift])
modifiers |= MOD_SHIFT;
if (s_keyboard_state[(int)Key::LeftCtrl] || s_keyboard_state[(int)Key::RightCtrl])
modifiers |= MOD_CTRL;
if (s_keyboard_state[(int)Key::LeftAlt])
modifiers |= MOD_ALT;
if (s_keyboard_state[(int)Key::RightAlt])
modifiers |= MOD_ALTGR;
if (s_led_states & I8042_KB_LED_CAPS_LOCK)
modifiers |= MOD_CAPS;
s_modifiers = modifiers;
if (key != Key::INVALID)
{
auto error_or = s_key_event_queue.Push({ .key = key, .modifiers = modifiers, .pressed = pressed });
if (error_or.HasError())
dprintln("PS/2 Keyboard: {}", error_or.GetError().message);
}
s_keyboard_key_buffer_size -= index + 1;
memmove(s_keyboard_key_buffer, s_keyboard_key_buffer + index, s_keyboard_key_buffer_size);
}
void keyboard_irq_handler()
{
uint8_t raw = IO::inb(I8042_DATA_PORT);
bool command_waiting = false;
if (!s_keyboard_command_queue.Empty())
{
auto& command = s_keyboard_command_queue.Front();
command_waiting = (command._sent > 0 && !command._done);
}
if (command_waiting)
{
auto& command = s_keyboard_command_queue.Front();
if (raw == I8042_KB_RESEND)
{
dprintln("PS/2 Keyboard: Resend 0x{H}", command._sent == 2 ? command.data : command.command);
command._sent--;
return;
}
if (raw == I8042_KB_ACK)
{
command._ack++;
if (command.extra > 0)
return;
command._done = command.has_data ? (command._ack == 2) : true;
return;
}
if (raw == 0x00)
{
dprintln("\e[33mKey detection error or internal buffer overrun\e[m");
command._sent = 0;
command._ack = 0;
command._done = false;
return;
}
if (raw == 0xEE && command.command == 0xEE)
{
s_keyboard_command_queue.Pop();
return;
}
s_keyboard_command_extra = raw;
command._done = true;
return;
}
else
{
s_keyboard_key_buffer[s_keyboard_key_buffer_size++] = raw;
if (raw != 0xE0)
keyboard_new_key();
}
}
bool initialize(void (*callback)(KeyEvent))
{
// https://wiki.osdev.org/%228042%22_PS/2_Controller
@@ -175,74 +367,102 @@ namespace Keyboard
// TODO
// Step 3: Disable Devices
kb_command(I8042_DISABLE_FIRST);
kb_command(I8042_DISABLE_SECOND);
i8042_controller_command(I8042_DISABLE_FIRST);
i8042_controller_command(I8042_DISABLE_SECOND);
// Step 4: Flush The Ouput Buffer
uint8_t tmp;
while(kb_try_read(tmp))
continue;
while ((IO::inb(I8042_STATUS_REGISTER) & I8042_STATUS_OUT_FULL) != 0)
IO::inb(I8042_DATA_PORT);
// Step 5: Set the Controller Configuration Byte
kb_command(I8042_READ_BYTE0);
uint8_t conf = kb_read();
conf &= 0b10111100;
kb_command(I8042_WRITE_BYTE0, conf);
i8042_controller_command(I8042_READ_CONFIG);
uint8_t config = wait_and_read();
config &= ~(I8042_CONFING_IRQ_FIRST | I8042_CONFING_IRQ_SECOND);
i8042_controller_command(I8042_WRITE_CONFIG, config);
// Step 6: Perform Controller Self Test
kb_command(I8042_TEST_CONTROLLER);
uint8_t resp = kb_read();
if (resp != I8042_CONTROLLER_TEST_PASS)
i8042_controller_command(I8042_TEST_CONTROLLER);
if (wait_and_read() != I8042_TEST_CONTROLLER_PASS)
{
kprint("ERROR: PS/2 self test failed\n");
return;
kprintln("\e[33mERROR: PS/2 controller self test failed\e[m");
return false;
}
// Step 7: Determine If There Are 2 Channels
// Step 8: Perform Interface Tests
kb_command(I8042_TEST_FIRST_PORT);
resp = kb_read();
if (resp != I8042_FIRST_PORT_TEST_PASS)
i8042_controller_command(I8042_TEST_FIRST_PORT);
if (wait_and_read() != I8042_TEST_FIRST_PORT_PASS)
{
kprint("ERROR: PS/2 interface test failed\n");
return;
kprintln("\e[33mERROR: PS/2 first port test failed\e[m");
return false;
}
// Step 9: Enable Devices
kb_command(I8042_WRITE_BYTE0, conf | 0x01); // enable IRQs
kb_command(I8042_ENABLE_FIRST);
config |= I8042_CONFING_IRQ_FIRST;
i8042_controller_command(I8042_WRITE_CONFIG, config);
i8042_controller_command(I8042_ENABLE_FIRST);
// Step 10: Reset Devices
/* TODO: doesnt seem to respond
kb_command(0xFF);
resp = kb_read();
if (resp != PS2_ACK)
{
kprint("ERROR: PS/2 could not restart devices\n");
return;
}
*/
MUST(s_keyboard_command_queue.Push({
.command = I8042_KB_RESET,
.extra = true,
}));
// Set scan code set 2
MUST(s_keyboard_command_queue.Push({
.command = I8042_KB_SET_SCAN_CODE_SET,
.data = 0x02,
.has_data = true,
}));
// Turn LEDs off
MUST(s_keyboard_command_queue.Push({
.command = I8042_KB_SET_LEDS,
.data = s_led_states,
.has_data = true,
}));
// Register callback and IRQ
s_key_callback = callback;
IDT::register_irq_handler(KEYBOARD_IRQ, irq_handler);
IDT::register_irq_handler(KEYBOARD_IRQ, keyboard_irq_handler);
PIC::unmask(KEYBOARD_IRQ);
kb_command(0xED, 0b111);
IO::io_wait();
while (kb_try_read(tmp));
return true;
}
char key_to_ascii(Key key, uint8_t modifiers)
char key_event_to_ascii(KeyEvent event)
{
char res = s_key_to_char[static_cast<uint8_t>(key)];
char res = s_key_to_char[static_cast<uint8_t>(event.key)];
if (!(modifiers & MOD_SHIFT))
if (!(event.modifiers & (MOD_SHIFT | MOD_CAPS)))
if (res >= 'A' && res <= 'Z')
res = res - 'A' + 'a';
return res;
}
void led_disco()
{
uint64_t time = PIT::ms_since_boot();
uint64_t freq = 100;
bool state = false;
for(;;)
{
if (PIT::ms_since_boot() > time + freq)
{
time += freq;
state = !state;
MUST(s_keyboard_command_queue.Push({
.command = I8042_KB_SET_LEDS,
.data = (uint8_t)(state ? 0b111 : 0b000),
.has_data = true,
}));
}
update_keyboard();
}
}
}

View File

@@ -62,7 +62,8 @@ void kernel_main(multiboot_info_t* mbi, uint32_t magic)
IDT::initialize();
PIT::initialize();
Keyboard::initialize(on_key_press);
if (!Keyboard::initialize(on_key_press))
return;
auto time = RTC::GetCurrentTime();
kprintln("Today is {2}:{2}:{2} {2}.{2}.{4}", time.hour, time.minute, time.second, time.day, time.month, time.year);
@@ -71,8 +72,9 @@ void kernel_main(multiboot_info_t* mbi, uint32_t magic)
ENABLE_INTERRUPTS();
for (;;)
{
asm("hlt");
Keyboard::update_keyboard();
}
}