Kernel: Properly handle finnish keyboard layout.

You can now type any basic ascii character from keyboard.
Multimedia keys are not yet handled.
This commit is contained in:
Bananymous
2022-12-08 17:52:08 +02:00
parent 5ba7af2cf6
commit 817de9f359
7 changed files with 708 additions and 372 deletions

244
kernel/kernel/Keyboard.cpp Normal file
View File

@@ -0,0 +1,244 @@
#include <kernel/IDT.h>
#include <kernel/IO.h>
#include <kernel/Keyboard.h>
#include <kernel/PIC.h>
#include <kernel/kprint.h>
#include <kernel/KeyboardLayout/FI.h>
#define I8042_DATA_PORT 0x60
#define I8042_STATUS_REGISTER 0x64
#define I8042_COMMAND_REGISTER 0x64
#define I8042_READ_BYTE0 0x20
#define I8042_WRITE_BYTE0 0x60
#define I8042_ENABLE_FIRST 0xAE
#define I8042_ENABLE_SECOND 0xA8
#define I8042_DISABLE_FIRST 0xAD
#define I8042_DISABLE_SECOND 0xA7
#define I8042_TEST_CONTROLLER 0xAA
#define I8042_CONTROLLER_TEST_PASS 0x55
#define I8042_TEST_FIRST_PORT 0xAB
#define I8042_FIRST_PORT_TEST_PASS 0x00
#define I8042_TEST_SECOND 0xA9
#define I8042_SECOND_PORT_TEST_PASS 0x00
#define I8042_ACK 0xfa
#define KEYBOARD_IRQ 0x01
#define MOD_ALT 0b0001
#define MOD_CTRL 0b0010
#define MOD_SHIFT 0b0100
#define MOD_ALTGR 0b1000
#define KEYBOARD_FI
namespace Keyboard
{
static bool s_keyboard_state[0xFF] = {};
static uint8_t s_modifiers = 0;
static void (*s_key_callback)(Key, uint8_t, bool) = nullptr;
static char s_key_to_char[]
{
'\0', '\0',
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
'A', 'A', 'O',
',', ':', '.', ';', '-', '_', '\'', '*', '^', '~',
'!', '?', '"', '#', '%', '&', '/', '\\', '+', '=',
'(', ')', '[', ']', '{', '}',
'$', '\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', '1', '2', '3', '4', '5', '6', '7', '8', '9',
',', '+', '*', '/', '-',
'\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()
{
while (!(IO::inb(I8042_STATUS_REGISTER) & 0x01))
continue;
return IO::inb(I8042_DATA_PORT);
}
static bool kb_try_read(uint8_t& out)
{
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)
{
IO::io_wait();
IO::outb(I8042_COMMAND_REGISTER, command);
IO::io_wait();
IO::outb(I8042_DATA_PORT, data);
}
void irq_handler()
{
uint8_t ch;
while (kb_try_read(ch))
{
bool multimedia = false, pressed = true;
if (ch == 0xE0)
{
multimedia = true;
ch = kb_read();
}
if (ch == 0xF0)
{
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;
}
s_keyboard_state[ch] = pressed;
switch (ch)
{
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;
}
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);
}
}
void initialize(void (*callback)(Key, uint8_t, bool))
{
// https://wiki.osdev.org/%228042%22_PS/2_Controller
// TODO: support dual channel
// Step 1: Initialize USB Controllers
// TODO
// Stem 2: Determine if the PS/2 Controller Exists
// TODO
// Step 3: Disable Devices
kb_command(I8042_DISABLE_FIRST);
kb_command(I8042_DISABLE_SECOND);
// Step 4: Flush The Ouput Buffer
uint8_t tmp;
while(kb_try_read(tmp))
continue;
// 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);
// Step 6: Perform Controller Self Test
kb_command(I8042_TEST_CONTROLLER);
uint8_t resp = kb_read();
if (resp != I8042_CONTROLLER_TEST_PASS)
{
kprint("ERROR: PS/2 self test failed\n");
return;
}
// 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)
{
kprint("ERROR: PS/2 interface test failed\n");
return;
}
// Step 9: Enable Devices
kb_command(I8042_WRITE_BYTE0, conf | 0x01); // enable IRQs
kb_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;
}
*/
// Register callback and IRQ
s_key_callback = callback;
IDT::register_irq_handler(KEYBOARD_IRQ, irq_handler);
PIC::unmask(KEYBOARD_IRQ);
}
char key_to_ascii(Key key, uint8_t modifiers)
{
char res = s_key_to_char[static_cast<uint8_t>(key)];
if (!(modifiers & MOD_SHIFT))
if (res >= 'A' && res <= 'Z')
res = res - 'A' + 'a';
return res;
}
}

View File

@@ -1,361 +0,0 @@
#include <kernel/IDT.h>
#include <kernel/IO.h>
#include <kernel/PIC.h>
#include <kernel/PS2.h>
#include <kernel/kprint.h>
#include <string.h>
#define PS2_DATA_PORT 0x60
#define PS2_STATUS_REGISTER 0x64
#define PS2_COMMAND_REGISTER 0x64
#define PS2_ACK 0xfa
#define PS2_IRQ 0x01
#define ALT 0
#define CTRL 1
#define SHIFT 2
#define KEYBOARD_FI
namespace PS2
{
static bool s_keyboard_state[0x7f];
static uint8_t s_modifiers = 0;
#ifdef KEYBOARD_FI
enum class Key
{
Key_INVALID,
Key_ESC,
Key_1,
Key_2,
Key_3,
Key_4,
Key_5,
Key_6,
Key_7,
Key_8,
Key_9,
Key_0,
Key_Plus,
Key_Tick,
Key_Backspace,
Key_Tab,
Key_Q,
Key_W,
Key_E,
Key_R,
Key_T,
Key_Y,
Key_U,
Key_I,
Key_O,
Key_P,
Key_Å,
Key_Caret,
Key_Enter,
Key_Control,
Key_A,
Key_S,
Key_D,
Key_F,
Key_G,
Key_H,
Key_J,
Key_K,
Key_L,
Key_Ö,
Key_Ä,
Key_Section, // §
Key_LeftShift,
Key_SingleQuote,
Key_Z,
Key_X,
Key_C,
Key_V,
Key_B,
Key_N,
Key_M,
Key_Comma,
Key_Period,
Key_Hyphen,
Key_RightShift,
Key_Unknown, // Maybe other alt
Key_Alt,
Key_Space,
Key_CapsLock,
Key_F1,
Key_F2,
Key_F3,
Key_F4,
Key_F5,
Key_F6,
Key_F7,
Key_F8,
Key_F9,
Key_F10,
// TODO
};
#endif
char fi_keymap[0x7f]
{
'\0',
'\0', // esc
'1',
'2',
'3',
'4',
'5',
'6',
'7',
'8',
'9',
'0',
'+',
'`',
'\b',
'\t', // tab
'q',
'w',
'e',
'r',
't',
'y',
'u',
'i',
'o',
'p',
'?',
'^',
'\n', // enter
'\0',
'a',
's',
'd',
'f',
'g',
'h',
'j',
'k',
'l',
'\0', // ö
'\0', // ä
'\0', // §
'\0',
'\'',
'z',
'x',
'c',
'v',
'b',
'n',
'm',
',',
'.',
'-',
'\0',
'\0',
'\0',
' ',
};
char fi_keymap_shift[0x7f]
{
'\0',
'\0', // esc
'!',
'"',
'#',
'\0', // ¤
'%',
'&',
'/',
'(',
')',
'=',
'?',
'`',
'\b',
'\t', // tab
'Q',
'W',
'E',
'R',
'T',
'Y',
'U',
'I',
'O',
'P',
'?',
'^',
'\n', // enter
'\0',
'A',
'S',
'D',
'F',
'G',
'H',
'J',
'K',
'L',
'\0', // ö
'\0', // ä
'\0', // ½
'\0',
'*',
'Z',
'X',
'C',
'V',
'B',
'N',
'M',
';',
':',
'_',
'\0',
'\0',
'\0',
' ',
};
void irq_handler()
{
while (IO::inb(PS2_STATUS_REGISTER) & 0x01)
{
uint8_t raw = IO::inb(PS2_DATA_PORT);
uint8_t ch = raw & 0x7f;
bool pressed = !(raw & 0x80);
switch (ch)
{
case 0x38: pressed ? (s_modifiers |= 1 << ALT ) : (s_modifiers &= ~(1 << ALT )); break;
case 0x1d: pressed ? (s_modifiers |= 1 << CTRL ) : (s_modifiers &= ~(1 << CTRL )); break;
case 0x2a: pressed ? (s_modifiers |= 1 << SHIFT) : (s_modifiers &= ~(1 << SHIFT)); break;
default:
s_keyboard_state[ch] = pressed;
}
if (pressed)
{
char c = s_modifiers & (1 << SHIFT) ? fi_keymap_shift[ch] : fi_keymap[ch];
if (c) kprint("{}", c);
}
IO::io_wait();
}
}
void initialize()
{
// Clear keyboard buffer
while (IO::inb(PS2_STATUS_REGISTER) & 0x01)
IO::inb(PS2_DATA_PORT);
memset(s_keyboard_state, 0, sizeof(s_keyboard_state));
IDT::register_irq_handler(PS2_IRQ, irq_handler);
PIC::unmask(PS2_IRQ);
}
}

View File

@@ -1,11 +1,11 @@
#include <kernel/GDT.h>
#include <kernel/IDT.h>
#include <kernel/Keyboard.h>
#include <kernel/kmalloc.h>
#include <kernel/multiboot.h>
#include <kernel/panic.h>
#include <kernel/PIC.h>
#include <kernel/PIT.h>
#include <kernel/PS2.h>
#include <kernel/tty.h>
#include <kernel/kprint.h>
#include <kernel/IO.h>
@@ -18,6 +18,16 @@
multiboot_info_t* s_multiboot_info;
void on_key_press(Keyboard::Key key, uint8_t modifiers, bool pressed)
{
if (pressed)
{
char ascii = Keyboard::key_to_ascii(key, modifiers);
if (ascii)
kprint("{}", ascii);
}
}
extern "C"
void kernel_main(multiboot_info_t* mbi, uint32_t magic)
{
@@ -37,7 +47,7 @@ void kernel_main(multiboot_info_t* mbi, uint32_t magic)
IDT::initialize();
PIT::initialize();
PS2::initialize();
Keyboard::initialize(on_key_press);
kprint("Hello from the kernel!\n");