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

View File

@ -32,10 +32,10 @@ BUILDDIR=$(abspath build)
KERNEL_OBJS=\ KERNEL_OBJS=\
$(KERNEL_ARCH_OBJS) \ $(KERNEL_ARCH_OBJS) \
kernel/kernel.o \ kernel/kernel.o \
kernel/Keyboard.o \
kernel/kmalloc.o \ kernel/kmalloc.o \
kernel/PIC.o \ kernel/PIC.o \
kernel/PIT.o \ kernel/PIT.o \
kernel/PS2.o \
kernel/SSP.o \ kernel/SSP.o \
OBJS=\ OBJS=\

View File

@ -0,0 +1,35 @@
#pragma once
#include <stdint.h>
namespace Keyboard
{
enum class Key : uint8_t
{
INVALID, None,
_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_Dot, A_Dots, O_Dots,
Comma, Colon, Period, Semicolon, Hyphen, Underscore, SingleQuote, Asterix, Caret, Tilde,
ExclamationMark, QuestionMark, DoubleQuote, Hashtag, Percent, Ampersand, Slash, BackSlash, Plus, Equals,
OpenParen, CloseParen, OpenBracket, CloseBracket, OpenBrace, CloseBrace,
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,
LeftShift, RightShift, CapsLock, Ctrl, Alt, NumLock, Escape,
Numpad0, Numpad1, Numpad2, Numpad3, Numpad4, Numpad5, Numpad6, Numpad7, Numpad8, Numpad9,
NumpadComma, NumpadPlus, NumpadMult, NumpadDiv, NumpadMinus,
F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12,
Count
};
void initialize(void (*callback)(Key, uint8_t, bool));
char key_to_ascii(Key, uint8_t);
}

View File

@ -0,0 +1,416 @@
#pragma once
#include <kernel/Keyboard.h>
namespace Keyboard
{
static Key scs2_to_key_altgr[0xFF]
{
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::None,
Key::None,
Key::None,
Key::None,
Key::At,
Key::INVALID,
Key::INVALID,
Key::None,
Key::None,
Key::None,
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::INVALID,
Key::INVALID,
Key::INVALID,
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::INVALID,
Key::None,
Key::INVALID,
Key::INVALID,
Key::INVALID,
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::NumpadPlus,
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]
{
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::Half,
Key::INVALID,
Key::INVALID,
Key::Alt,
Key::LeftShift,
Key::INVALID,
Key::Ctrl,
Key::Q,
Key::ExclamationMark,
Key::INVALID,
Key::INVALID,
Key::INVALID,
Key::Z,
Key::S,
Key::A,
Key::W,
Key::DoubleQuote,
Key::INVALID,
Key::INVALID,
Key::C,
Key::X,
Key::D,
Key::E,
Key::Currency,
Key::Hashtag,
Key::INVALID,
Key::INVALID,
Key::Space,
Key::V,
Key::F,
Key::T,
Key::R,
Key::Percent,
Key::INVALID,
Key::INVALID,
Key::N,
Key::B,
Key::H,
Key::G,
Key::Y,
Key::Ampersand,
Key::INVALID,
Key::INVALID,
Key::INVALID,
Key::M,
Key::J,
Key::U,
Key::Slash,
Key::OpenParen,
Key::INVALID,
Key::INVALID,
Key::Semicolon,
Key::K,
Key::I,
Key::O,
Key::Equals,
Key::CloseParen,
Key::INVALID,
Key::INVALID,
Key::Colon,
Key::Underscore,
Key::L,
Key::O_Dots,
Key::P,
Key::QuestionMark,
Key::INVALID,
Key::INVALID,
Key::INVALID,
Key::A_Dots,
Key::INVALID,
Key::A_Dot,
Key::BackTick,
Key::INVALID,
Key::INVALID,
Key::CapsLock,
Key::RightShift,
Key::Enter,
Key::Caret,
Key::INVALID,
Key::Asterix,
Key::INVALID,
Key::INVALID,
Key::INVALID,
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::Right,
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]
{
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::Section,
Key::INVALID,
Key::INVALID,
Key::Alt,
Key::LeftShift,
Key::INVALID,
Key::Ctrl,
Key::Q,
Key::_1,
Key::INVALID,
Key::INVALID,
Key::INVALID,
Key::Z,
Key::S,
Key::A,
Key::W,
Key::_2,
Key::INVALID,
Key::INVALID,
Key::C,
Key::X,
Key::D,
Key::E,
Key::_4,
Key::_3,
Key::INVALID,
Key::INVALID,
Key::Space,
Key::V,
Key::F,
Key::T,
Key::R,
Key::_5,
Key::INVALID,
Key::INVALID,
Key::N,
Key::B,
Key::H,
Key::G,
Key::Y,
Key::_6,
Key::INVALID,
Key::INVALID,
Key::INVALID,
Key::M,
Key::J,
Key::U,
Key::_7,
Key::_8,
Key::INVALID,
Key::INVALID,
Key::Comma,
Key::K,
Key::I,
Key::O,
Key::_0,
Key::_9,
Key::INVALID,
Key::INVALID,
Key::Period,
Key::Hyphen,
Key::L,
Key::O_Dots,
Key::P,
Key::Plus,
Key::INVALID,
Key::INVALID,
Key::INVALID,
Key::A_Dots,
Key::INVALID,
Key::A_Dot,
Key::Tick,
Key::INVALID,
Key::INVALID,
Key::CapsLock,
Key::RightShift,
Key::Enter,
Key::Caret,
Key::INVALID,
Key::SingleQuote,
Key::INVALID,
Key::INVALID,
Key::INVALID,
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::NumpadPlus,
Key::Numpad3,
Key::NumpadMinus,
Key::NumpadMult,
Key::Numpad9,
Key::INVALID,
Key::INVALID,
Key::INVALID,
Key::INVALID,
Key::INVALID,
Key::F7,
};
}

View File

@ -1,8 +0,0 @@
#pragma once
namespace PS2
{
void initialize();
}

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/GDT.h>
#include <kernel/IDT.h> #include <kernel/IDT.h>
#include <kernel/Keyboard.h>
#include <kernel/kmalloc.h> #include <kernel/kmalloc.h>
#include <kernel/multiboot.h> #include <kernel/multiboot.h>
#include <kernel/panic.h> #include <kernel/panic.h>
#include <kernel/PIC.h> #include <kernel/PIC.h>
#include <kernel/PIT.h> #include <kernel/PIT.h>
#include <kernel/PS2.h>
#include <kernel/tty.h> #include <kernel/tty.h>
#include <kernel/kprint.h> #include <kernel/kprint.h>
#include <kernel/IO.h> #include <kernel/IO.h>
@ -18,6 +18,16 @@
multiboot_info_t* s_multiboot_info; 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" extern "C"
void kernel_main(multiboot_info_t* mbi, uint32_t magic) 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(); IDT::initialize();
PIT::initialize(); PIT::initialize();
PS2::initialize(); Keyboard::initialize(on_key_press);
kprint("Hello from the kernel!\n"); kprint("Hello from the kernel!\n");