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

@ -36,6 +36,7 @@ kernel/Keyboard.o \
kernel/kmalloc.o \ kernel/kmalloc.o \
kernel/PIC.o \ kernel/PIC.o \
kernel/PIT.o \ kernel/PIT.o \
kernel/RTC.o \
kernel/Serial.o \ kernel/Serial.o \
kernel/SSP.o \ kernel/SSP.o \
icxxabi.o \ icxxabi.o \

View File

@ -18,18 +18,30 @@ namespace Keyboard
Dollar, Pound, Euro, Currency, Enter, Space, Tab, Backspace, LessThan, MoreThan, Tick, BackTick, Section, Half, At, Pipe, Dollar, Pound, Euro, Currency, Enter, Space, Tab, Backspace, LessThan, MoreThan, Tick, BackTick, Section, Half, At, Pipe,
End, Home, Insert, Delete, Super, PageUp, PageDown, PrintScreen, Left, Right, Up, Down, End, Home, Insert, Delete, Super, PageUp, PageDown, PrintScreen, Left, Right, Up, Down,
LeftShift, RightShift, CapsLock, Ctrl, Alt, NumLock, Escape, LeftShift, RightShift, CapsLock, LeftCtrl, RightCtrl, LeftAlt, RightAlt, NumLock, ScrollLock, Escape,
Numpad0, Numpad1, Numpad2, Numpad3, Numpad4, Numpad5, Numpad6, Numpad7, Numpad8, Numpad9, Numpad0, Numpad1, Numpad2, Numpad3, Numpad4, Numpad5, Numpad6, Numpad7, Numpad8, Numpad9,
NumpadComma, NumpadPlus, NumpadMult, NumpadDiv, NumpadMinus, NumpadSep, NumpadPlus, NumpadMult, NumpadDiv, NumpadMinus, NumpadEnter,
Mute, VolumeDown, VolumeUp, Calculator, PlayPause, Stop, PreviousTrack, NextTrack,
F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12, F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12,
Count Count
}; };
void initialize(void (*callback)(Key, uint8_t, bool)); struct KeyEvent
{
Key key;
uint8_t modifiers;
bool pressed;
};
char key_to_ascii(Key, uint8_t); bool initialize(void (*callback)(KeyEvent));
void update_keyboard();
char key_event_to_ascii(KeyEvent);
void led_disco();
} }

View File

@ -5,412 +5,519 @@
namespace Keyboard namespace Keyboard
{ {
static Key scs2_to_key_altgr[0xFF]
constexpr Key scan_code_to_key_extended[0xFF]
{ {
Key::INVALID, Key::INVALID,
Key::F9,
Key::INVALID,
Key::F5,
Key::F3,
Key::F1,
Key::F2,
Key::F12,
Key::INVALID,
Key::F10,
Key::F8,
Key::F6,
Key::F4,
Key::Tab,
Key::None,
Key::INVALID,
Key::INVALID,
Key::Alt,
Key::LeftShift,
Key::INVALID,
Key::Ctrl,
Key::Q,
Key::None,
Key::INVALID, Key::INVALID,
Key::INVALID, Key::INVALID,
Key::INVALID, Key::INVALID,
Key::None, Key::INVALID,
Key::None, Key::INVALID,
Key::None, Key::INVALID,
Key::INVALID,
Key::INVALID,
Key::INVALID,
Key::INVALID,
Key::INVALID,
Key::INVALID,
Key::INVALID,
Key::INVALID,
Key::INVALID,
Key::PreviousTrack,
Key::INVALID,
Key::INVALID,
Key::INVALID,
Key::INVALID,
Key::INVALID,
Key::INVALID,
Key::INVALID,
Key::INVALID,
Key::NextTrack,
Key::INVALID,
Key::INVALID,
Key::NumpadEnter,
Key::RightCtrl,
Key::INVALID,
Key::INVALID,
Key::Mute,
Key::Calculator,
Key::PlayPause,
Key::INVALID,
Key::Stop,
Key::INVALID,
Key::INVALID,
Key::INVALID,
Key::INVALID,
Key::INVALID,
Key::INVALID,
Key::INVALID,
Key::INVALID,
Key::INVALID,
Key::VolumeDown,
Key::INVALID,
Key::VolumeUp,
Key::INVALID,
Key::INVALID,
Key::INVALID,
Key::INVALID,
Key::NumpadDiv,
Key::INVALID,
Key::INVALID,
Key::RightAlt,
Key::INVALID,
Key::INVALID,
Key::INVALID,
Key::INVALID,
Key::INVALID,
Key::INVALID,
Key::INVALID,
Key::INVALID,
Key::INVALID,
Key::INVALID,
Key::INVALID,
Key::INVALID,
Key::INVALID,
Key::INVALID,
Key::Home,
Key::Up,
Key::PageUp,
Key::INVALID,
Key::Left,
Key::INVALID,
Key::Right,
Key::INVALID,
Key::End,
Key::Down,
Key::PageDown,
Key::Insert,
Key::Delete,
Key::INVALID,
Key::INVALID,
Key::INVALID,
Key::INVALID,
Key::INVALID,
Key::INVALID,
Key::INVALID,
Key::Super,
Key::INVALID,
Key::INVALID,
Key::INVALID,
Key::INVALID,
Key::INVALID,
Key::INVALID,
Key::INVALID,
Key::INVALID,
Key::INVALID,
Key::INVALID,
Key::INVALID,
Key::INVALID,
Key::INVALID,
Key::INVALID,
Key::INVALID,
Key::INVALID,
Key::INVALID,
Key::INVALID,
Key::INVALID,
Key::INVALID,
Key::INVALID,
Key::INVALID,
Key::INVALID,
Key::INVALID,
Key::INVALID,
Key::INVALID,
Key::INVALID,
Key::INVALID,
Key::INVALID,
Key::INVALID,
Key::INVALID,
Key::INVALID,
Key::INVALID,
Key::INVALID,
Key::INVALID,
Key::INVALID,
Key::INVALID,
Key::INVALID,
Key::INVALID,
Key::INVALID,
Key::INVALID,
Key::INVALID,
Key::INVALID,
Key::INVALID,
Key::INVALID,
Key::INVALID,
Key::INVALID,
Key::INVALID,
Key::INVALID,
Key::INVALID,
};
constexpr Key scan_code_to_key_altgr[0xFF]
{
Key::INVALID,
Key::Escape,
Key::None, Key::None,
Key::At, Key::At,
Key::INVALID, Key::Pound,
Key::INVALID, Key::Dollar,
Key::None, Key::None,
Key::None, Key::None,
Key::OpenBracket,
Key::OpenBrace,
Key::CloseBrace,
Key::CloseBracket,
Key::BackSlash,
Key::BackTick,
Key::Backspace,
Key::Tab,
Key::None,
Key::None, Key::None,
Key::Euro, Key::Euro,
Key::Dollar,
Key::Pound,
Key::INVALID,
Key::INVALID,
Key::Space,
Key::None,
Key::None,
Key::None,
Key::None,
Key::None,
Key::INVALID,
Key::INVALID,
Key::None, Key::None,
Key::None, Key::None,
Key::None, Key::None,
Key::None, Key::None,
Key::None, Key::None,
Key::None, Key::None,
Key::INVALID,
Key::INVALID,
Key::INVALID,
Key::None, Key::None,
Key::None, Key::None,
Key::None,
Key::OpenBrace,
Key::OpenBracket,
Key::INVALID,
Key::INVALID,
Key::None,
Key::None,
Key::None,
Key::None,
Key::CloseBrace,
Key::CloseBracket,
Key::INVALID,
Key::INVALID,
Key::None,
Key::None,
Key::None,
Key::None,
Key::None,
Key::BackSlash,
Key::INVALID,
Key::INVALID,
Key::INVALID,
Key::None,
Key::INVALID,
Key::None,
Key::None,
Key::INVALID,
Key::INVALID,
Key::CapsLock,
Key::RightShift,
Key::Enter,
Key::Tilde, Key::Tilde,
Key::INVALID, Key::Enter,
Key::LeftCtrl,
Key::None, Key::None,
Key::INVALID, Key::None,
Key::None,
Key::None,
Key::None,
Key::None,
Key::None,
Key::None,
Key::None,
Key::None,
Key::None,
Key::None,
Key::LeftShift,
Key::None,
Key::None,
Key::None,
Key::None,
Key::None,
Key::None,
Key::None,
Key::None,
Key::None,
Key::None,
Key::None,
Key::RightShift,
Key::NumpadMult,
Key::LeftAlt,
Key::Space,
Key::CapsLock,
Key::F1,
Key::F2,
Key::F3,
Key::F4,
Key::F5,
Key::F6,
Key::F7,
Key::F8,
Key::F9,
Key::F10,
Key::NumLock,
Key::ScrollLock,
Key::Numpad7,
Key::Numpad8,
Key::Numpad9,
Key::NumpadMinus,
Key::Numpad4,
Key::Numpad5,
Key::Numpad6,
Key::NumpadPlus,
Key::Numpad1,
Key::Numpad2,
Key::Numpad3,
Key::Numpad0,
Key::NumpadSep,
Key::INVALID, Key::INVALID,
Key::INVALID, Key::INVALID,
Key::Pipe, Key::Pipe,
Key::INVALID,
Key::INVALID,
Key::INVALID,
Key::INVALID,
Key::Backspace,
Key::INVALID,
Key::INVALID,
Key::Numpad1,
Key::INVALID,
Key::Numpad4,
Key::Numpad7,
Key::INVALID,
Key::INVALID,
Key::INVALID,
Key::Numpad0,
Key::NumpadComma,
Key::Numpad2,
Key::Numpad5,
Key::Numpad6,
Key::Numpad8,
Key::Escape,
Key::NumLock,
Key::F11, Key::F11,
Key::NumpadPlus, Key::F12,
Key::Numpad3,
Key::NumpadMinus,
Key::NumpadMult,
Key::Numpad9,
Key::INVALID,
Key::INVALID,
Key::INVALID,
Key::INVALID,
Key::INVALID,
Key::F7,
}; };
static Key scs2_to_key_shift[0xFF] constexpr Key scan_code_to_key_shift[0xFF]
{ {
Key::INVALID, Key::INVALID,
Key::F9, Key::Escape,
Key::INVALID,
Key::F5,
Key::F3,
Key::F1,
Key::F2,
Key::F12,
Key::INVALID,
Key::F10,
Key::F8,
Key::F6,
Key::F4,
Key::Tab,
Key::Half,
Key::INVALID,
Key::INVALID,
Key::Alt,
Key::LeftShift,
Key::INVALID,
Key::Ctrl,
Key::Q,
Key::ExclamationMark, Key::ExclamationMark,
Key::INVALID,
Key::INVALID,
Key::INVALID,
Key::Z,
Key::S,
Key::A,
Key::W,
Key::DoubleQuote, Key::DoubleQuote,
Key::INVALID,
Key::INVALID,
Key::C,
Key::X,
Key::D,
Key::E,
Key::Currency,
Key::Hashtag, Key::Hashtag,
Key::INVALID, Key::Currency,
Key::INVALID,
Key::Space,
Key::V,
Key::F,
Key::T,
Key::R,
Key::Percent, Key::Percent,
Key::INVALID,
Key::INVALID,
Key::N,
Key::B,
Key::H,
Key::G,
Key::Y,
Key::Ampersand, Key::Ampersand,
Key::INVALID,
Key::INVALID,
Key::INVALID,
Key::M,
Key::J,
Key::U,
Key::Slash, Key::Slash,
Key::OpenParen, Key::OpenParen,
Key::INVALID, Key::CloseParen,
Key::INVALID, Key::Equals,
Key::Semicolon, Key::QuestionMark,
Key::K, Key::BackTick,
Key::Backspace,
Key::Tab,
Key::Q,
Key::W,
Key::E,
Key::R,
Key::T,
Key::Y,
Key::U,
Key::I, Key::I,
Key::O, Key::O,
Key::Equals, Key::P,
Key::CloseParen, Key::A_Dot,
Key::INVALID, Key::Caret,
Key::INVALID, Key::Enter,
Key::Colon, Key::LeftCtrl,
Key::Underscore, Key::A,
Key::S,
Key::D,
Key::F,
Key::G,
Key::H,
Key::J,
Key::K,
Key::L, Key::L,
Key::O_Dots, Key::O_Dots,
Key::P,
Key::QuestionMark,
Key::INVALID,
Key::INVALID,
Key::INVALID,
Key::A_Dots, Key::A_Dots,
Key::INVALID, Key::Half,
Key::A_Dot, Key::LeftShift,
Key::BackTick,
Key::INVALID,
Key::INVALID,
Key::CapsLock,
Key::RightShift,
Key::Enter,
Key::Caret,
Key::INVALID,
Key::Asterix, Key::Asterix,
Key::INVALID, Key::Z,
Key::X,
Key::C,
Key::V,
Key::B,
Key::N,
Key::M,
Key::Semicolon,
Key::Colon,
Key::Underscore,
Key::RightShift,
Key::NumpadMult,
Key::LeftAlt,
Key::Space,
Key::CapsLock,
Key::None,
Key::None,
Key::None,
Key::None,
Key::None,
Key::None,
Key::None,
Key::None,
Key::None,
Key::None,
Key::NumLock,
Key::ScrollLock,
Key::Home,
Key::Up,
Key::PageUp,
Key::NumpadMinus,
Key::Left,
Key::None,
Key::Right,
Key::NumpadPlus,
Key::End,
Key::Down,
Key::PageDown,
Key::Insert,
Key::Delete,
Key::INVALID, Key::INVALID,
Key::INVALID, Key::INVALID,
Key::MoreThan, Key::MoreThan,
Key::INVALID,
Key::INVALID,
Key::INVALID,
Key::INVALID,
Key::Backspace,
Key::INVALID,
Key::INVALID,
Key::End,
Key::INVALID,
Key::Left,
Key::Right,
Key::INVALID,
Key::INVALID,
Key::INVALID,
Key::Insert,
Key::Delete,
Key::Down,
Key::None, Key::None,
Key::Right, Key::None,
Key::Up,
Key::Escape,
Key::NumLock,
Key::F11,
Key::NumpadPlus,
Key::PageDown,
Key::NumpadMinus,
Key::NumpadMult,
Key::PageUp,
Key::INVALID,
Key::INVALID,
Key::INVALID,
Key::INVALID,
Key::INVALID,
Key::F7,
}; };
static Key scs2_to_key[0xFF] constexpr Key scan_code_to_key_normal[0xFF]
{ {
Key::INVALID, Key::INVALID,
Key::F9, Key::Escape,
Key::INVALID,
Key::F5,
Key::F3,
Key::F1,
Key::F2,
Key::F12,
Key::INVALID,
Key::F10,
Key::F8,
Key::F6,
Key::F4,
Key::Tab,
Key::Section,
Key::INVALID,
Key::INVALID,
Key::Alt,
Key::LeftShift,
Key::INVALID,
Key::Ctrl,
Key::Q,
Key::_1, Key::_1,
Key::INVALID,
Key::INVALID,
Key::INVALID,
Key::Z,
Key::S,
Key::A,
Key::W,
Key::_2, Key::_2,
Key::INVALID,
Key::INVALID,
Key::C,
Key::X,
Key::D,
Key::E,
Key::_4,
Key::_3, Key::_3,
Key::INVALID, Key::_4,
Key::INVALID,
Key::Space,
Key::V,
Key::F,
Key::T,
Key::R,
Key::_5, Key::_5,
Key::INVALID,
Key::INVALID,
Key::N,
Key::B,
Key::H,
Key::G,
Key::Y,
Key::_6, Key::_6,
Key::INVALID,
Key::INVALID,
Key::INVALID,
Key::M,
Key::J,
Key::U,
Key::_7, Key::_7,
Key::_8, Key::_8,
Key::INVALID, Key::_9,
Key::INVALID, Key::_0,
Key::Comma, Key::Plus,
Key::K, Key::Tick,
Key::Backspace,
Key::Tab,
Key::Q,
Key::W,
Key::E,
Key::R,
Key::T,
Key::Y,
Key::U,
Key::I, Key::I,
Key::O, Key::O,
Key::_0, Key::P,
Key::_9, Key::A_Dot,
Key::INVALID, Key::Caret,
Key::INVALID, Key::Enter,
Key::Period, Key::LeftCtrl,
Key::Hyphen, Key::A,
Key::S,
Key::D,
Key::F,
Key::G,
Key::H,
Key::J,
Key::K,
Key::L, Key::L,
Key::O_Dots, Key::O_Dots,
Key::P,
Key::Plus,
Key::INVALID,
Key::INVALID,
Key::INVALID,
Key::A_Dots, Key::A_Dots,
Key::INVALID, Key::Section,
Key::A_Dot, Key::LeftShift,
Key::Tick,
Key::INVALID,
Key::INVALID,
Key::CapsLock,
Key::RightShift,
Key::Enter,
Key::Caret,
Key::INVALID,
Key::SingleQuote, Key::SingleQuote,
Key::INVALID, Key::Z,
Key::X,
Key::C,
Key::V,
Key::B,
Key::N,
Key::M,
Key::Comma,
Key::Period,
Key::Hyphen,
Key::RightShift,
Key::NumpadMult,
Key::LeftAlt,
Key::Space,
Key::CapsLock,
Key::F1,
Key::F2,
Key::F3,
Key::F4,
Key::F5,
Key::F6,
Key::F7,
Key::F8,
Key::F9,
Key::F10,
Key::NumLock,
Key::ScrollLock,
Key::Numpad7,
Key::Numpad8,
Key::Numpad9,
Key::NumpadMinus,
Key::Numpad4,
Key::Numpad5,
Key::Numpad6,
Key::NumpadPlus,
Key::Numpad1,
Key::Numpad2,
Key::Numpad3,
Key::Numpad0,
Key::NumpadSep,
Key::INVALID, Key::INVALID,
Key::INVALID, Key::INVALID,
Key::LessThan, Key::LessThan,
Key::INVALID,
Key::INVALID,
Key::INVALID,
Key::INVALID,
Key::Backspace,
Key::INVALID,
Key::INVALID,
Key::Numpad1,
Key::INVALID,
Key::Numpad4,
Key::Numpad7,
Key::INVALID,
Key::INVALID,
Key::INVALID,
Key::Numpad0,
Key::NumpadComma,
Key::Numpad2,
Key::Numpad5,
Key::Numpad6,
Key::Numpad8,
Key::Escape,
Key::NumLock,
Key::F11, Key::F11,
Key::NumpadPlus, Key::F12,
Key::Numpad3,
Key::NumpadMinus,
Key::NumpadMult,
Key::Numpad9,
Key::INVALID,
Key::INVALID,
Key::INVALID,
Key::INVALID,
Key::INVALID,
Key::F7,
}; };
} }

View File

@ -1,17 +1,29 @@
#include <kernel/IDT.h> #include <kernel/IDT.h>
#include <kernel/IO.h> #include <kernel/IO.h>
#include <kernel/Keyboard.h> #include <kernel/Keyboard.h>
#include <kernel/PIC.h>
#include <kernel/kprint.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> #include <kernel/KeyboardLayout/FI.h>
#define KB_DEBUG_PRINT 1
#define I8042_DATA_PORT 0x60 #define I8042_DATA_PORT 0x60
#define I8042_STATUS_REGISTER 0x64 #define I8042_STATUS_REGISTER 0x64
#define I8042_COMMAND_REGISTER 0x64 #define I8042_COMMAND_REGISTER 0x64
#define I8042_READ_BYTE0 0x20 #define I8042_STATUS_OUT_FULL (1 << 0)
#define I8042_WRITE_BYTE0 0x60 #define I8042_STATUS_IN_FULL (1 << 1)
#define I8042_READ_CONFIG 0x20
#define I8042_WRITE_CONFIG 0x60
#define I8042_CONFING_IRQ_FIRST (1 << 0)
#define I8042_CONFING_IRQ_SECOND (1 << 1)
#define I8042_CONFING_TRANSLATION (1 << 6)
#define I8042_ENABLE_FIRST 0xAE #define I8042_ENABLE_FIRST 0xAE
#define I8042_ENABLE_SECOND 0xA8 #define I8042_ENABLE_SECOND 0xA8
@ -19,32 +31,59 @@
#define I8042_DISABLE_SECOND 0xA7 #define I8042_DISABLE_SECOND 0xA7
#define I8042_TEST_CONTROLLER 0xAA #define I8042_TEST_CONTROLLER 0xAA
#define I8042_CONTROLLER_TEST_PASS 0x55 #define I8042_TEST_CONTROLLER_PASS 0x55
#define I8042_TEST_FIRST_PORT 0xAB #define I8042_TEST_FIRST_PORT 0xAB
#define I8042_FIRST_PORT_TEST_PASS 0x00 #define I8042_TEST_FIRST_PORT_PASS 0x00
#define I8042_TEST_SECOND 0xA9 #define I8042_TEST_SECOND_PORT 0xA9
#define I8042_SECOND_PORT_TEST_PASS 0x00 #define I8042_TEST_SECOND_PORT_PASS 0x00
#define I8042_ACK 0xfa #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 KEYBOARD_IRQ 0x01
#define MOD_ALT 0b0001 #define MOD_ALT (1 << 0)
#define MOD_CTRL 0b0010 #define MOD_CTRL (1 << 1)
#define MOD_SHIFT 0b0100 #define MOD_SHIFT (1 << 2)
#define MOD_ALTGR 0b1000 #define MOD_ALTGR (1 << 3)
#define MOD_CAPS (1 << 4)
#define KEYBOARD_FI
namespace Keyboard namespace Keyboard
{ {
static bool s_keyboard_state[0xFF] = {}; 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[] static char s_key_to_char[]
{ {
@ -59,110 +98,263 @@ namespace Keyboard
'$', '\0', '\0', '\0', '\n', ' ', '\t', '\b', '<', '>', '\0', '`', '\0', '\0', '@', '|', '$', '\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', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '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', '\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_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; continue;
return IO::inb(I8042_DATA_PORT); 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); 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::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); IO::outb(I8042_DATA_PORT, data);
} }
void irq_handler() static bool i8042_keyboard_command(uint8_t command)
{ {
uint8_t ch; auto timeout = PIT::ms_since_boot() + I8042_KB_TIMEOUT_MS;
while (kb_try_read(ch))
{
bool multimedia = false, pressed = true;
if (ch == 0xE0) while (PIT::ms_since_boot() < timeout)
{ {
multimedia = true; if ((IO::inb(I8042_STATUS_REGISTER) & I8042_STATUS_IN_FULL) == 0)
ch = kb_read(); {
IO::outb(I8042_DATA_PORT, command);
return true;
}
} }
if (ch == 0xF0) return false;
{
pressed = false;
ch = kb_read();
} }
// TODO: Handle multimedia keys void update_keyboard()
if (multimedia)
{ {
if (ch == 17) if (!s_keyboard_command_queue.Empty())
pressed ? (s_modifiers |= MOD_ALTGR) : (s_modifiers &= ~MOD_ALTGR); {
if (pressed && false) auto& command = s_keyboard_command_queue.Front();
kprint("<M{}>", ch);
continue; if (command._sent == 0 && command._ack == 0)
{
if (!i8042_keyboard_command(command.command))
Kernel::panic("oof 1");
command._sent++;
} }
s_keyboard_state[ch] = pressed; if (command._sent == 1 && command._ack == 1 && command.has_data)
switch (ch)
{ {
case 17: if (!i8042_keyboard_command(command.data))
pressed ? (s_modifiers |= MOD_ALT) : (s_modifiers &= ~MOD_ALT); Kernel::panic("oof 2");
command._sent++;
}
if (command._done)
{
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; break;
case 18: case I8042_KB_SET_SCAN_CODE_SET:
case 89:
pressed ? (s_modifiers |= MOD_SHIFT) : (s_modifiers &= ~MOD_SHIFT);
break; break;
case 20: case I8042_KB_SET_LEDS:
pressed ? (s_modifiers |= MOD_CTRL) : (s_modifiers &= ~MOD_CTRL); break;
}
s_keyboard_command_queue.Pop();
}
}
while (!s_key_event_queue.Empty())
{
s_key_callback(s_key_event_queue.Front());
s_key_event_queue.Pop();
}
}
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; break;
default: default:
break; break;
} }
Key key; if (update_leds)
if (s_modifiers & MOD_ALTGR) {
key = scs2_to_key_altgr[ch]; s_keyboard_command_queue.Push({
else if (s_modifiers & MOD_SHIFT) .command = I8042_KB_SET_LEDS,
key = scs2_to_key_shift[ch]; .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 else
key = scs2_to_key[ch]; {
s_keyboard_key_buffer[s_keyboard_key_buffer_size++] = raw;
// Debug print for unregistered keys if (raw != 0xE0)
if (key == Key::INVALID && pressed) keyboard_new_key();
kprint("<{}>", ch);
s_key_callback(key, s_modifiers, pressed);
} }
} }
void initialize(void (*callback)(Key, uint8_t, bool)) bool initialize(void (*callback)(KeyEvent))
{ {
// https://wiki.osdev.org/%228042%22_PS/2_Controller // https://wiki.osdev.org/%228042%22_PS/2_Controller
@ -175,74 +367,102 @@ namespace Keyboard
// TODO // TODO
// Step 3: Disable Devices // Step 3: Disable Devices
kb_command(I8042_DISABLE_FIRST); i8042_controller_command(I8042_DISABLE_FIRST);
kb_command(I8042_DISABLE_SECOND); i8042_controller_command(I8042_DISABLE_SECOND);
// Step 4: Flush The Ouput Buffer // Step 4: Flush The Ouput Buffer
uint8_t tmp; while ((IO::inb(I8042_STATUS_REGISTER) & I8042_STATUS_OUT_FULL) != 0)
while(kb_try_read(tmp)) IO::inb(I8042_DATA_PORT);
continue;
// Step 5: Set the Controller Configuration Byte // Step 5: Set the Controller Configuration Byte
kb_command(I8042_READ_BYTE0); i8042_controller_command(I8042_READ_CONFIG);
uint8_t conf = kb_read(); uint8_t config = wait_and_read();
conf &= 0b10111100; config &= ~(I8042_CONFING_IRQ_FIRST | I8042_CONFING_IRQ_SECOND);
kb_command(I8042_WRITE_BYTE0, conf); i8042_controller_command(I8042_WRITE_CONFIG, config);
// Step 6: Perform Controller Self Test // Step 6: Perform Controller Self Test
kb_command(I8042_TEST_CONTROLLER); i8042_controller_command(I8042_TEST_CONTROLLER);
uint8_t resp = kb_read(); if (wait_and_read() != I8042_TEST_CONTROLLER_PASS)
if (resp != I8042_CONTROLLER_TEST_PASS)
{ {
kprint("ERROR: PS/2 self test failed\n"); kprintln("\e[33mERROR: PS/2 controller self test failed\e[m");
return; return false;
} }
// Step 7: Determine If There Are 2 Channels // Step 7: Determine If There Are 2 Channels
// Step 8: Perform Interface Tests // Step 8: Perform Interface Tests
kb_command(I8042_TEST_FIRST_PORT); i8042_controller_command(I8042_TEST_FIRST_PORT);
resp = kb_read(); if (wait_and_read() != I8042_TEST_FIRST_PORT_PASS)
if (resp != I8042_FIRST_PORT_TEST_PASS)
{ {
kprint("ERROR: PS/2 interface test failed\n"); kprintln("\e[33mERROR: PS/2 first port test failed\e[m");
return; return false;
} }
// Step 9: Enable Devices // Step 9: Enable Devices
kb_command(I8042_WRITE_BYTE0, conf | 0x01); // enable IRQs config |= I8042_CONFING_IRQ_FIRST;
kb_command(I8042_ENABLE_FIRST); i8042_controller_command(I8042_WRITE_CONFIG, config);
i8042_controller_command(I8042_ENABLE_FIRST);
// Step 10: Reset Devices // Step 10: Reset Devices
/* TODO: doesnt seem to respond MUST(s_keyboard_command_queue.Push({
kb_command(0xFF); .command = I8042_KB_RESET,
resp = kb_read(); .extra = true,
if (resp != PS2_ACK) }));
{
kprint("ERROR: PS/2 could not restart devices\n"); // Set scan code set 2
return; 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 // Register callback and IRQ
s_key_callback = callback; 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); PIC::unmask(KEYBOARD_IRQ);
kb_command(0xED, 0b111); return true;
IO::io_wait();
while (kb_try_read(tmp));
} }
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') if (res >= 'A' && res <= 'Z')
res = res - 'A' + 'a'; res = res - 'A' + 'a';
return res; 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(); IDT::initialize();
PIT::initialize(); PIT::initialize();
Keyboard::initialize(on_key_press); if (!Keyboard::initialize(on_key_press))
return;
auto time = RTC::GetCurrentTime(); 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); 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(); ENABLE_INTERRUPTS();
for (;;) for (;;)
{ {
asm("hlt"); Keyboard::update_keyboard();
} }
} }