From 0806b5e170ce0749a45b3310be643cd5bfff61ca Mon Sep 17 00:00:00 2001 From: Oliver Po Date: Wed, 29 Jan 2025 18:56:25 +1100 Subject: [PATCH] Implement keyboard driver --- .../arch/x86_64/drivers/keyboard/keyboard.c | 142 +++++++++++++++++- .../arch/x86_64/drivers/keyboard/keyboard.h | 10 +- kernel/src/arch/x86_64/usermode/usermode.h | 2 +- kernel/src/arch/x86_64/vga/vga.c | 53 +++++++ kernel/src/arch/x86_64/vga/vga.h | 9 ++ kernel/src/kernel_main.c | 18 ++- 6 files changed, 224 insertions(+), 10 deletions(-) diff --git a/kernel/src/arch/x86_64/drivers/keyboard/keyboard.c b/kernel/src/arch/x86_64/drivers/keyboard/keyboard.c index abbc827..10e2435 100644 --- a/kernel/src/arch/x86_64/drivers/keyboard/keyboard.c +++ b/kernel/src/arch/x86_64/drivers/keyboard/keyboard.c @@ -2,12 +2,148 @@ #include "../../../../io.h" #include "../../apic/lapic.h" +#include "../../apic/ioapic.h" +#include "../../cpu/idt.h" #include "../../serial/serial.h" -__attribute__((interrupt)) void keyboard_handler(struct interrupt_frame *frame) { +#include + +// clang-format off +enum scan_set_one { + // todo: abbreviate names + S1_ESC = 1, S1_1, S1_2, S1_3, S1_4, + S1_5, S1_6, S1_7, S1_8, S1_9, + S1_0, S1_MINUS, S1_EQUAL, S1_BACKSPACE, S1_TAB, + S1_Q, S1_W, S1_E, S1_R, S1_T, + S1_Y, S1_U, S1_I, S1_O, S1_P, S1_LEFT_BRACKET, + S1_RIGHT_BRACKET, S1_ENTER, S1_LEFT_CTRL, S1_A, S1_S, + S1_D, S1_F, S1_G, S1_H, S1_J, + S1_K, S1_L, S1_SEMI, S1_SINGLE_QUOTE, S1_BACK_TICK, + S1_LEFT_SHIFT, S1_LEFT_SLASH, S1_Z, S1_X, S1_C, + S1_V, S1_B, S1_N, S1_M, S1_COMMA, + S1_DOT, S1_RIGHT_SLASH, S1_RIGHT_SHIFT, S1_KEYPAD_STAR, + S1_LEFT_ALT, S1_SPACE, S1_CAPSLOCK, S1_F1, S1_F2, + S1_F3, S1_F4, S1_F5, S1_F6, S1_F7, + S1_F8, S1_F9, S1_F10, S1_NUMLOCK, S1_SCROLLLOCK, + S1_KEYPAD_7, S1_KEYPAD_8, S1_KEYPAD_9, S1_KEYPAD_MINUS, + S1_KEYPAD_4, S1_KEYPAD_5, S1_KEYPAD_6, S1_KEYPAD_PLUS, + S1_KEYPAD_1, S1_KEYPAD_2, S1_KEYPAD_3, S1_KEYPAD_0, + S1_KEYPAD_DOT, S1_F11, S1_F12, + // RELEASED + // todo: make a map for each key held down + S1_ESC_RELEASED = 0x81, + S1_1_RELEASED, S1_2_RELEASED, S1_3_RELEASED, S1_4_RELEASED, + S1_5_RELEASED, S1_6_RELEASED, S1_7_RELEASED, S1_8_RELEASED, + S1_9_RELEASED, S1_0_RELEASED, S1_MINUS_RELEASED, S1_EQUALS_RELEASED, + S1_BACKSPACE_RELEASED, S1_TAB_RELEASED, S1_Q_RELEASED, S1_W_RELEASED, + S1_E_RELEASED, S1_R_RELEASED, S1_T_RELEASED, S1_Y_RELEASED, S1_U_RELEASED, + S1_I_RELEASED, S1_O_RELEASED, S1_P_RELEASED, S1_LEFT_BRACKET_RELEASED, + S1_RIGHT_BRACKET_RELEASED, S1_ENTER_RELEASED, S1_LEFT_CTRL_RELEASED, S1_A_RELEASED, + S1_S_RELEASED, S1_D_RELEASED, S1_F_RELEASED, S1_G_RELEASED, S1_H_RELEASED, + S1_J_RELEASED, S1_K_RELEASED, S1_L_RELEASED, S1_SEMI_RELEASED, S1_SINGLE_QUOTE_RELEASED, + S1_BACK_TICK_RELEASED, S1_LEFT_SHIFT_RELEASED, S1_LEFT_SLASH_RELEASED, S1_Z_RELEASED, + S1_X_RELEASED, S1_C_RELEASED, S1_V_RELEASED, S1_B_RELEASED, S1_N_RELEASED, + S1_M_RELEASED, S1_COMMA_RELEASED, S1_DOT_RELEASED, S1_RIGHT_SLASH_RELEASED, + S1_RIGHT_SHIFT_RELEASED, S1_KEYPAD_STAR_RELEASED, S1_LEFT_ALT_RELEASED, S1_SPACE_RELEASED, + S1_CAPSLOCK_RELEASED, S1_F1_RELEASED, S1_F2_RELEASED, S1_F3_RELEASED, S1_F4_RELEASED, + S1_F5_RELEASED, S1_F6_RELEASED, S1_F7_RELEASED, S1_F8_RELEASED, S1_F9_RELEASED, + S1_F10_RELEASED, S1_NUMLOCK_RELEASED, S1_SCROLLLOCK_RELEASED, S1_KEYPAD_7_RELEASED, + S1_KEYPAD_8_RELEASED, S1_KEYPAD_9_RELEASED, S1_KEYPAD_MINUS_RELEASED, + S1_KEYPAD_4_RELEASED, S1_KEYPAD_5_RELEASED, S1_KEYPAD_6_RELEASED, S1_KEYPAD_PLUS_RELEASED, + S1_KEYPAD_1_RELEASED, S1_KEYPAD_2_RELEASED, S1_KEYPAD_3_RELEASED, S1_KEYPAD_0_RELEASED, + S1_KEYPAD_DOT_RELEASED, S1_F11_RELEASED, S1_F12_RELEASED +}; +// clang-format on + +#define MAX_BUFFER_SIZE 512 +static char buffer[MAX_BUFFER_SIZE]; +static size_t buffer_pos = 0; + +static __attribute__((interrupt)) void keyboard_handler(struct interrupt_frame *frame); + +static uint8_t keyboard_code_set_1(const uint8_t data); + +static bool caps_lock = 0; + +// TODO: use acpi to check if 8042 ps/2 controller is supported +// todo: poll for keyboard status +// todo: check keyboard type (1, 2, 3) +// todo: detect if key is capital or nah (need to detect caps lock and shift) + +void keyboard_init(void) { + // Disable any devices connected to ps/2 ports + //outb(0x64, 0xAD); + //outb(0x64, 0xA7); + // todo: do the steps specified by osdev later + + int apic_id = apic_get_id(); + ioapic_set_redirect((uintptr_t *) IOAPIC_VIRTUAL_ADDRESS, 1, 0x21, apic_id); + setup_interrupt_gate(0x21, keyboard_handler, INTERRUPT_64_GATE, 0, 0); +} + +// Keycodes from: https://wiki.osdev.org/PS/2_Keyboard +// CODE SET 1 +// ! for some reason 2,3,4 in qemu r different keys +static uint8_t keyboard_code_set_1(const uint8_t data) { + if (data >= S1_1 && data <= S1_EQUAL) { + return "1234567890-="[data - S1_1]; + } else if (data >= S1_Q && data <= S1_ENTER) { + return "QWERTYUIOP[]\n"[data - S1_Q]; + } else if (data >= S1_A && data <= S1_BACK_TICK) { + return "ASDFGHJKL;'`"[data - S1_A]; + } else if (data >= S1_LEFT_SLASH && data <= S1_RIGHT_SLASH) { + return "\\ZXCVBNM,./"[data - S1_LEFT_SLASH]; + } else if (data >= S1_KEYPAD_7 && data <= S1_KEYPAD_DOT) { + return "789-456+1230."[data - S1_KEYPAD_7]; + } else if (data == S1_SPACE) { + return ' '; + } else if (data == S1_KEYPAD_STAR) { + return '*'; + } else if (data == S1_CAPSLOCK) { + caps_lock = !caps_lock; + // enable caps lock LED + if (caps_lock) { + outb(0xED, inb(0xED) | (1 << 2)); + } else { + outb(0xED, inb(0xED) & ~(1 << 2)); + } + } + + return data; +} + +// TODO: have switch for different code sets +static char keyboard_convert_code(char data) { + return keyboard_code_set_1(data); +} + +// ! figure out how to make it pause/wait for getchar(); + +// todo: WRITE A FRONTEND FUNCTION FOR KLIB +char getc(void) { + while (buffer_pos == 0) { + __builtin_ia32_pause(); + } + char data = buffer[--buffer_pos]; + putc(data); + return data; +} + +void getstr(char *str, size_t len) { + for (size_t i = 0; i < len; i++) { + str[i] = getc(); + } +} + +static void keyboard_handler(struct interrupt_frame *frame) { // Consume a key, not doing this will only let // the interrupt trigger once - inb(0x60); - puts("Keyboard pressed"); + uint8_t data = keyboard_code_set_1(inb(0x60)); + if (data >= 'A' && data <= 'Z' && !caps_lock) data += 32; + if (data >= ' ' && data <= '~') buffer[buffer_pos++] = data; + // ! minor bug: could loop over and getc wont be able to get the prev 512 chars + buffer_pos %= MAX_BUFFER_SIZE; + //if (data >= ' ' && data <= '~') + // krintf("Keyboard pressed %c\n", data); send_apic_eoi(); } diff --git a/kernel/src/arch/x86_64/drivers/keyboard/keyboard.h b/kernel/src/arch/x86_64/drivers/keyboard/keyboard.h index 2f0e0a6..b3e0334 100644 --- a/kernel/src/arch/x86_64/drivers/keyboard/keyboard.h +++ b/kernel/src/arch/x86_64/drivers/keyboard/keyboard.h @@ -1,6 +1,12 @@ #pragma once +#include + struct interrupt_frame; -// Keyboard Handler -void keyboard_handler(struct interrupt_frame *frame); +// Initialise the keyboard driver +void keyboard_init(void); + +char getc(void); + +void getstr(char *str, size_t len); diff --git a/kernel/src/arch/x86_64/usermode/usermode.h b/kernel/src/arch/x86_64/usermode/usermode.h index 6124024..ee0fc24 100644 --- a/kernel/src/arch/x86_64/usermode/usermode.h +++ b/kernel/src/arch/x86_64/usermode/usermode.h @@ -102,4 +102,4 @@ struct gdt { } __attribute__((packed)); // Enters usermode -void enter_usermode(void *function_address); +void _Noreturn enter_usermode(void *function_address); diff --git a/kernel/src/arch/x86_64/vga/vga.c b/kernel/src/arch/x86_64/vga/vga.c index fc15c3a..88da778 100644 --- a/kernel/src/arch/x86_64/vga/vga.c +++ b/kernel/src/arch/x86_64/vga/vga.c @@ -1,5 +1,7 @@ #include "vga.h" +#include "../serial/serial.h" + #include #include @@ -91,6 +93,7 @@ void vga_putchar(char c) { break; } } + vga_cursor_update(terminal_column, terminal_row); } void vga_write_string(const char *data) { @@ -98,3 +101,53 @@ void vga_write_string(const char *data) { vga_putchar(*data++); } } + +// Found names here: http://www.osdever.net/FreeVGA/vga/crtcreg.htm +#define CURSOR_ADDRESS_REGISTER 0x3D4 +#define CURSOR_INDEX_REGISTER 0x3D5 + +// Cursor address +#define CURSOR_START 0x0A +#define CURSOR_END 0x0B + +#define CURSOR_LOCATION_HIGH 0x0E +#define CURSOR_LOCATION_LOW 0x0F + +void vga_cursor_enable(size_t start, size_t end) { + // Writes to CURSOR_ADDRESS_REGISTER first + // to specify address + + // start determines start of cursor height + // end determines end of cursor height + + outb(CURSOR_ADDRESS_REGISTER, CURSOR_START); + // retain bits 6 and 7 because they are reserved. + outb(CURSOR_INDEX_REGISTER, inb(CURSOR_INDEX_REGISTER) & 0xC0 | (uint8_t) (start & 0x3F)); + + outb(CURSOR_ADDRESS_REGISTER, CURSOR_END); + // retain bits 5, 6, 7 because they are reserved. + outb(CURSOR_INDEX_REGISTER, inb(CURSOR_INDEX_REGISTER) & 0xE0 | (uint8_t) (end & 0x1f)); +} + +void vga_cursor_disable(void) { + outb(CURSOR_ADDRESS_REGISTER, CURSOR_START); + // set 5th bit to 1 to disable + outb(CURSOR_INDEX_REGISTER, inb(CURSOR_INDEX_REGISTER) & 0xC0 | 0x20); +} + +void vga_cursor_update(size_t x, size_t y) { + // todo: ensure 8 bits + uint16_t pos = y * VGA_WIDTH + x; + outb(CURSOR_ADDRESS_REGISTER, CURSOR_LOCATION_LOW); + outb(CURSOR_INDEX_REGISTER, (uint8_t) (pos & 0xFF)); + outb(CURSOR_ADDRESS_REGISTER, CURSOR_LOCATION_HIGH); + outb(CURSOR_INDEX_REGISTER, (uint8_t) ((pos >> 8) & 0xFF)); +} + +uint16_t vga_cursor_position(void) { + uint16_t pos = 0; + outb(CURSOR_ADDRESS_REGISTER, CURSOR_LOCATION_LOW); + pos |= inb(CURSOR_INDEX_REGISTER); + outb(CURSOR_ADDRESS_REGISTER, CURSOR_LOCATION_HIGH); + pos |= inb(CURSOR_INDEX_REGISTER); +} diff --git a/kernel/src/arch/x86_64/vga/vga.h b/kernel/src/arch/x86_64/vga/vga.h index ff7a2d0..9d6d231 100644 --- a/kernel/src/arch/x86_64/vga/vga.h +++ b/kernel/src/arch/x86_64/vga/vga.h @@ -36,3 +36,12 @@ void vga_putchar(char c); // Prints a null-terminated string void vga_write_string(const char *data); + +// Enables text cursor +void vga_cursor_enable(size_t start, size_t end); + +// Disables text cursor +void vga_cursor_disable(void); + +// Updates text cursor +void vga_cursor_update(size_t x, size_t y); diff --git a/kernel/src/kernel_main.c b/kernel/src/kernel_main.c index 29b8d54..c3626e5 100644 --- a/kernel/src/kernel_main.c +++ b/kernel/src/kernel_main.c @@ -96,9 +96,21 @@ void _Noreturn kernel_main(struct boot_parameters parameters) { outw(0xf4, 0x10); _die(); #else + puts("Enter a character: "); + char c = getc(); + krintf("Char: %c\n", c); + + puts("Enter a string: "); + char *str = kmalloc(10); + getstr(str, 9); + str[9] = '\0'; + krintf("String: %s\n", str); + free(str); + + char message[] = "X Hello world!\n"; - for (size_t i = 0; i < 1; i++) { + for (size_t i = 0; i < 5; i++) { message[0] = '0' + i; i = (i + 1) % 10; @@ -180,8 +192,6 @@ static void setup_idt(void) { // system timer ioapic_set_redirect((uintptr_t *) IOAPIC_VIRTUAL_ADDRESS, 0, 0x20, apic_id); - // keyboard - ioapic_set_redirect((uintptr_t *) IOAPIC_VIRTUAL_ADDRESS, 1, 0x21, apic_id); for (size_t vector = 0; vector < 32; vector++) { setup_interrupt_gate(vector, exception_handler, INTERRUPT_64_GATE, 0, @@ -190,7 +200,7 @@ static void setup_idt(void) { // we have interrupt after 31 since 0-31 are reserved for errors setup_interrupt_gate(0x20, timer_handler, INTERRUPT_64_GATE, 0, 0); - setup_interrupt_gate(0x21, keyboard_handler, INTERRUPT_64_GATE, 0, 0); + keyboard_init(); setup_interrupt_gate(0x80, trap, TRAP_64_GATE, 3, 0); load_idt();