From 2b8a39feacf3cec1e1272d160032da3c4f389ef2 Mon Sep 17 00:00:00 2001 From: antifallobst Date: Sun, 30 Apr 2023 12:07:54 +0200 Subject: [PATCH] feature (PS/2): implemented a basic ps/2 keyboard driver and TTY --- inc/drivers/ps2/controller.h | 21 ++++ inc/drivers/ps2/keyboard.h | 39 ++++++++ inc/drivers/ps2/scancodes.h | 34 +++++++ inc/drivers/tty.h | 26 +++++ src/boot/kmain.c | 7 +- src/drivers/graphics/renderer.c | 11 +++ src/drivers/ps2/controller.c | 70 ++++++++++++++ src/drivers/ps2/keyboard.c | 166 ++++++++++++++++++++++++++++++++ src/drivers/tty.c | 54 +++++++++++ src/platform/interrupts.c | 5 + 10 files changed, 430 insertions(+), 3 deletions(-) create mode 100644 inc/drivers/ps2/controller.h create mode 100644 inc/drivers/ps2/keyboard.h create mode 100644 inc/drivers/ps2/scancodes.h create mode 100644 inc/drivers/tty.h create mode 100644 src/drivers/ps2/controller.c create mode 100644 src/drivers/ps2/keyboard.c create mode 100644 src/drivers/tty.c diff --git a/inc/drivers/ps2/controller.h b/inc/drivers/ps2/controller.h new file mode 100644 index 0000000..92068e2 --- /dev/null +++ b/inc/drivers/ps2/controller.h @@ -0,0 +1,21 @@ +// This file is part of noxos and licensed under the MIT open source license + +#ifndef NOX_CONTROLLER_H +#define NOX_CONTROLLER_H + +#include "utils/stdtypes.h" + +#define PS2_CONTROLLER_DATA_PORT 0x60 +#define PS2_CONTROLLER_STATUS_PORT 0x64 +#define PS2_CONTROLLER_COMMAND_PORT 0x64 + +#define PS2_CONTROLLER_TIMEOUT 0xFFFFFF + +void ps2_controller_init (); +uint8_t ps2_controller_command (uint8_t command); +uint8_t ps2_controller_command_with_data (uint8_t command, uint8_t data); +bool ps2_controller_wait_until_ready_for_input (); +bool ps2_controller_wait_until_ready_for_output (); +uint8_t ps2_controller_read_data (); + +#endif //NOX_CONTROLLER_H diff --git a/inc/drivers/ps2/keyboard.h b/inc/drivers/ps2/keyboard.h new file mode 100644 index 0000000..987d303 --- /dev/null +++ b/inc/drivers/ps2/keyboard.h @@ -0,0 +1,39 @@ +// This file is part of noxos and licensed under the MIT open source license + +#ifndef NOX_KEYBOARD_H +#define NOX_KEYBOARD_H + +#include "utils/stdtypes.h" +#include "utils/string.h" + + +typedef enum { + PS2_KEYBOARD_COMMAND_SET_LEDS = 0xED, + PS2_KEYBOARD_COMMAND_ECHO = 0xEE, + PS2_KEYBOARD_COMMAND_SET_SCANCODE_SET = 0xF0, + PS2_KEYBOARD_COMMAND_IDENTIFY = 0xF2, + PS2_KEYBOARD_COMMAND_SET_TYPEMATIC = 0xF3, + PS2_KEYBOARD_COMMAND_ENABLE_SCAN = 0xF4, + PS2_KEYBOARD_COMMAND_DISABLE_SCAN = 0xF5, + PS2_KEYBOARD_COMMAND_SET_DEFAULT_PARAMS = 0xF6, + PS2_KEYBOARD_COMMAND_RESEND_LAST_BYTE = 0xFE, + PS2_KEYBOARD_COMMAND_SELF_TEST = 0xFF +} ps2_keyboard_command_E; + +typedef enum { + PS2_KEYBOARD_RESPONSE_KEY_DETECTION_ERROR_1 = 0x00, + PS2_KEYBOARD_RESPONSE_SELF_TEST_PASSED = 0xAA, + PS2_KEYBOARD_RESPONSE_ECHO = 0xEE, + PS2_KEYBOARD_RESPONSE_ACKNOWLEDGED = 0xFA, + PS2_KEYBOARD_RESPONSE_SELF_TEST_FAILED_1 = 0xFC, + PS2_KEYBOARD_RESPONSE_SELF_TEST_FAILED_2 = 0xFD, + PS2_KEYBOARD_RESPONSE_RESEND = 0xFE, + PS2_KEYBOARD_RESPONSE_KEY_DETECTION_ERROR_2 = 0xFF +} ps2_keyboard_response_E; + +uint8_t ps2_keyboard_command (ps2_keyboard_command_E command, uint8_t data); +void ps2_keyboard_init (); +void ps2_keyboard_read (); +string_t ps2_keyboard_command_to_string (ps2_keyboard_command_E command); + +#endif //NOX_KEYBOARD_H diff --git a/inc/drivers/ps2/scancodes.h b/inc/drivers/ps2/scancodes.h new file mode 100644 index 0000000..de8dac6 --- /dev/null +++ b/inc/drivers/ps2/scancodes.h @@ -0,0 +1,34 @@ +// This file is part of noxos and licensed under the MIT open source license + +#ifndef NOX_SCANCODES_H +#define NOX_SCANCODES_H + +#define PS2_SCANCODE_SET_1_ESCAPE 0x01 +#define PS2_SCANCODE_SET_1_BACKSPACE 0x0E +#define PS2_SCANCODE_SET_1_TABULATOR 0x0F +#define PS2_SCANCODE_SET_1_RETURN 0x1C +#define PS2_SCANCODE_SET_1_SHIFT_LEFT 0x2A +#define PS2_SCANCODE_SET_1_SHIFT_RIGHT 0x36 +#define PS2_SCANCODE_SET_1_ALT_LEFT 0x38 +#define PS2_SCANCODE_SET_1_SPACE 0x39 +#define PS2_SCANCODE_SET_1_CAPSLOCK 0x3A +#define PS2_SCANCODE_SET_1_NUMLOCK 0x45 +#define PS2_SCANCODE_SET_1_SCROLLLOCK 0x46 +#define PS2_SCANCODE_SET_1_F1 0x3B +#define PS2_SCANCODE_SET_1_F2 0x3C +#define PS2_SCANCODE_SET_1_F3 0x3D +#define PS2_SCANCODE_SET_1_F4 0x3E +#define PS2_SCANCODE_SET_1_F5 0x3F +#define PS2_SCANCODE_SET_1_F6 0x40 +#define PS2_SCANCODE_SET_1_F7 0x41 +#define PS2_SCANCODE_SET_1_F8 0x42 +#define PS2_SCANCODE_SET_1_F9 0x43 +#define PS2_SCANCODE_SET_1_F10 0x44 +#define PS2_SCANCODE_SET_1_F11 0x57 +#define PS2_SCANCODE_SET_1_F12 0x58 + + +#define PS2_SCANCODE_SET_1_RELEASE 0x80 +#define PS2_SCANCODE_SET_1_MODIFIER 0xE0 + +#endif //NOX_SCANCODES_H diff --git a/inc/drivers/tty.h b/inc/drivers/tty.h new file mode 100644 index 0000000..00ebd40 --- /dev/null +++ b/inc/drivers/tty.h @@ -0,0 +1,26 @@ +// This file is part of noxos and licensed under the MIT open source license + +#ifndef NOXOS_TTY_H +#define NOXOS_TTY_H + +#include "drivers/graphics/renderer.h" +#include "utils/stream.h" + +#define TTY_STD_STREAM_SIZE 0x800 // 2048 bytes + +typedef struct { + graphics_buffer_T* graphics_buffer; + stream_T* stream; + position_T cursor; + color_argb_T color; +} tty_T; + +void tty_init (); +tty_T* tty_get_active (); + +tty_T* tty_alloc (uint32_t stream_size); +void tty_destruct (tty_T* tty); +void tty_update (tty_T* tty); +uint32_t tty_write (tty_T* tty, string_t string); + +#endif //NOXOS_TTY_H diff --git a/src/boot/kmain.c b/src/boot/kmain.c index 75a88dc..229ef90 100644 --- a/src/boot/kmain.c +++ b/src/boot/kmain.c @@ -16,6 +16,7 @@ #include "drivers/acpi/acpi.h" #include "drivers/pci.h" #include "drivers/ps2/controller.h" +#include "drivers/tty.h" #include "proc/scheduler.h" void limine_terminal_print(boot_info_T* boot_info, string_t string) { @@ -37,7 +38,7 @@ void kernel_init(boot_info_T* boot_info) { memory_allocator_init((void*)MEM_REGION_KERNEL_HEAP); graphics_renderer_init(boot_info); - graphical_log_init(); +// graphical_log_init(); vfs_init(boot_info); @@ -50,6 +51,8 @@ void kernel_init(boot_info_T* boot_info) { ps2_controller_init(); scheduler_init(boot_info); + + tty_init(); } void kmain(boot_info_T boot_info) { @@ -61,7 +64,5 @@ void kmain(boot_info_T boot_info) { log(LOG_INFO, "!=====[ Kernel Initialized ]=====!\n"); - pci_device_T* ahci_controller = pci_manager_find_device(PCI_CLASS_MASS_STORAGE_CONTROLLER, PCI_SUBCLASS_SERIAL_ATA_CONTROLLER, 1); - CORE_HALT_FOREVER } diff --git a/src/drivers/graphics/renderer.c b/src/drivers/graphics/renderer.c index 8da692d..c0a92f4 100644 --- a/src/drivers/graphics/renderer.c +++ b/src/drivers/graphics/renderer.c @@ -104,6 +104,17 @@ position_T graphics_buffer_draw_string(graphics_buffer_T* graphics_buffer, uint3 pos.x = 0; break; } + case '\b': { + if (pos.x >= g_renderer.font.width) { + pos.x -= g_renderer.font.width; + } else if (pos.y >= g_renderer.font.height) { + pos.y -= g_renderer.font.height; + pos.x = FLOOR_TO(graphics_buffer->width, g_renderer.font.width) - g_renderer.font.width; + } else break; + + graphics_buffer_draw_char(graphics_buffer, pos.x, pos.y, color, ' '); + break; + } default: { if (pos.x + g_renderer.font.width >= graphics_buffer->width) { diff --git a/src/drivers/ps2/controller.c b/src/drivers/ps2/controller.c new file mode 100644 index 0000000..12dfb7f --- /dev/null +++ b/src/drivers/ps2/controller.c @@ -0,0 +1,70 @@ +// This file is part of noxos and licensed under the MIT open source license + +#include "drivers/ps2/controller.h" +#include "drivers/ps2/keyboard.h" +#include "drivers/acpi/fadt.h" +#include "utils/io.h" +#include "utils/logger.h" +#include "boot/config.h" + +void ps2_controller_init() { + if (g_sysconfig->ps2_acpi_validation) { + if (!(g_acpi_table_fadt->ia_boot_architecture_flags & (1 << 1))) { + log(LOG_ERROR, " ps2 controller initialization failed (ps2 controller not set in fadt boot flags)"); + return; + } + } else { + log(LOG_WARNING, " skipping ps2 controller existence acpi check (disabled by sysconfig)"); + } + + ps2_keyboard_init(); +} + +uint8_t ps2_controller_command(uint8_t command) { + io_out_byte(PS2_CONTROLLER_COMMAND_PORT, command); + if (!ps2_controller_wait_until_ready_for_output()) { + return 0; + } + return io_in_byte(PS2_CONTROLLER_DATA_PORT); +} + +uint8_t ps2_controller_command_with_data(uint8_t command, uint8_t data) { + io_out_byte(PS2_CONTROLLER_COMMAND_PORT, command); + if (!ps2_controller_wait_until_ready_for_input()) { + return 0; + } + io_out_byte(PS2_CONTROLLER_DATA_PORT, data); + if (!ps2_controller_wait_until_ready_for_output()) { + return 0; + } + return io_in_byte(PS2_CONTROLLER_DATA_PORT); +} + +bool ps2_controller_wait_until_ready_for_input() { + uint32_t timeout = 0; + while (io_in_byte(PS2_CONTROLLER_STATUS_PORT) & 0b00000010) { + if (timeout > PS2_CONTROLLER_TIMEOUT) { + log(LOG_WARNING, " Controller not ready for input (timeout)"); + return false; + } + timeout++; + } + return true; +} + +bool ps2_controller_wait_until_ready_for_output() { + uint32_t timeout = 0; + log(LOG_DEBUG, "status register: 0b%.8b", io_in_byte(PS2_CONTROLLER_STATUS_PORT)); + while (!(io_in_byte(PS2_CONTROLLER_STATUS_PORT) & 0b00000001)) { + if (timeout > PS2_CONTROLLER_TIMEOUT) { + log(LOG_WARNING, " Controller not ready for output (timeout)"); + return false; + } + timeout++; + } + return true; +} + +uint8_t ps2_controller_read_data() { + return io_in_byte(PS2_CONTROLLER_DATA_PORT); +} diff --git a/src/drivers/ps2/keyboard.c b/src/drivers/ps2/keyboard.c new file mode 100644 index 0000000..92db0a8 --- /dev/null +++ b/src/drivers/ps2/keyboard.c @@ -0,0 +1,166 @@ +// This file is part of noxos and licensed under the MIT open source license + +#include "drivers/ps2/keyboard.h" +#include "drivers/ps2/scancodes.h" +#include "drivers/ps2/controller.h" +#include "drivers/tty.h" +#include "utils/logger.h" +#include "utils/io.h" +#include "platform/interrupts.h" + +char scancode_set_1[] = " 1234567890-= qwertyuiop[] asdfghjkl;'` \\zxcvbnm,./ * 789-456+1230."; +bool shift_enabled = false; +bool extended_key = false; + +uint8_t ps2_keyboard_command(ps2_keyboard_command_E command, uint8_t data) { + uint8_t response = PS2_KEYBOARD_RESPONSE_RESEND; + uint8_t tries = 0; + + while (response == PS2_KEYBOARD_RESPONSE_RESEND) { + response = ps2_controller_command(command); + log(LOG_DEBUG, " '%s' + 0x%xb -> 0x%xb", ps2_keyboard_command_to_string(command), data, response); + tries++; + + if (tries >= 3) { + log(LOG_WARNING, " Command %s not supported by keyboard (to many resend responses)", ps2_keyboard_command_to_string(command)); + return 0; + } + } + + return response; +} + +void ps2_keyboard_init() { + +// uint8_t data = ps2_keyboard_command(PS2_KEYBOARD_COMMAND_ECHO, 0); +// log(LOG_DEBUG, "response: 0x%xb", data); + + io_in_byte(PS2_CONTROLLER_DATA_PORT); + + pic_unmask_irq(IRQ_KEYBOARD); +} + +void ps2_keyboard_read() { + uint8_t scancode = ps2_controller_read_data(); + char chr; + + if (extended_key) { + extended_key = false; + return; + } + + if (scancode == PS2_SCANCODE_SET_1_MODIFIER) { + extended_key = true; + return; + } + + if (scancode >= PS2_SCANCODE_SET_1_RELEASE) { + scancode -= PS2_SCANCODE_SET_1_RELEASE; + switch (scancode) { + case PS2_SCANCODE_SET_1_SHIFT_LEFT: + case PS2_SCANCODE_SET_1_SHIFT_RIGHT: { + shift_enabled = !shift_enabled; + return; + } + } + return; + } + + switch (scancode) { + case PS2_SCANCODE_SET_1_ESCAPE: + case PS2_SCANCODE_SET_1_TABULATOR: + case PS2_SCANCODE_SET_1_ALT_LEFT: + case PS2_SCANCODE_SET_1_NUMLOCK: + case PS2_SCANCODE_SET_1_SCROLLLOCK: + case PS2_SCANCODE_SET_1_F1: + case PS2_SCANCODE_SET_1_F2: + case PS2_SCANCODE_SET_1_F3: + case PS2_SCANCODE_SET_1_F4: + case PS2_SCANCODE_SET_1_F5: + case PS2_SCANCODE_SET_1_F6: + case PS2_SCANCODE_SET_1_F7: + case PS2_SCANCODE_SET_1_F8: + case PS2_SCANCODE_SET_1_F9: + case PS2_SCANCODE_SET_1_F10: + case PS2_SCANCODE_SET_1_F11: + case PS2_SCANCODE_SET_1_F12: { + log(LOG_WARNING, "special keys aren't implemented yet"); + return; + } + + case PS2_SCANCODE_SET_1_BACKSPACE: { + chr = '\b'; + break; + } + + case PS2_SCANCODE_SET_1_SPACE: { + chr = ' '; + break; + } + + case PS2_SCANCODE_SET_1_RETURN: { + chr = '\n'; + break; + } + + case PS2_SCANCODE_SET_1_SHIFT_LEFT: + case PS2_SCANCODE_SET_1_SHIFT_RIGHT: + case PS2_SCANCODE_SET_1_CAPSLOCK: { + shift_enabled = !shift_enabled; + return; + } + + default: { + chr = scancode_set_1[scancode]; + if (shift_enabled) { + if (string_is_char_lowercase(chr)) chr -= 32; + else if (string_is_char_uppercase(chr)) chr += 32; + } + break; + } + } + + char buffer[2]; + buffer[0] = chr; + buffer[1] = '\0'; + + tty_write(tty_get_active(), buffer); +} + +string_t ps2_keyboard_command_to_string(ps2_keyboard_command_E command) { + switch (command) { + case PS2_KEYBOARD_COMMAND_SET_LEDS: { + return "Set LEDs"; + } + case PS2_KEYBOARD_COMMAND_ECHO: { + return "Echo"; + } + case PS2_KEYBOARD_COMMAND_SET_SCANCODE_SET: { + return "Get/Set Scancode Set"; + } + case PS2_KEYBOARD_COMMAND_IDENTIFY: { + return "Identify"; + } + case PS2_KEYBOARD_COMMAND_SET_TYPEMATIC: { + return "Set Typematic"; + } + case PS2_KEYBOARD_COMMAND_ENABLE_SCAN: { + return "Enable Scan"; + } + case PS2_KEYBOARD_COMMAND_DISABLE_SCAN: { + return "Disable Scan"; + } + case PS2_KEYBOARD_COMMAND_SET_DEFAULT_PARAMS: { + return "Set Default Parameter"; + } + case PS2_KEYBOARD_COMMAND_RESEND_LAST_BYTE: { + return "Resend Last Byte"; + } + case PS2_KEYBOARD_COMMAND_SELF_TEST: { + return "Self Test"; + } + default: { + return "Invalid Command"; + } + } +} \ No newline at end of file diff --git a/src/drivers/tty.c b/src/drivers/tty.c new file mode 100644 index 0000000..68dc86b --- /dev/null +++ b/src/drivers/tty.c @@ -0,0 +1,54 @@ +// This file is part of noxos and licensed under the MIT open source license + +#include "drivers/tty.h" +#include "utils/memory.h" + +// this global state tty is just a placeholder +tty_T* g_tty; + +void tty_init() { + g_tty = tty_alloc(TTY_STD_STREAM_SIZE); +} + +tty_T* tty_get_active() { + return g_tty; +} + +tty_T* tty_alloc(uint32_t stream_size) { + tty_T* tty = memory_allocate(sizeof(tty_T)); + + tty->graphics_buffer = graphics_buffer_request(0, 0, graphics_renderer_get_width(), graphics_renderer_get_height(), GRAPHICS_BUFFER_STANDARD); + tty->stream = stream_alloc(stream_size); + tty->cursor.x = 0; + tty->cursor.y = 0; + tty->color = g_color_palette[COLOR_PAL_GREY_LIGHT]; + + return tty; +} + +void tty_destruct(tty_T* tty) { + graphics_buffer_destruct(tty->graphics_buffer); + stream_destruct(tty->stream); + + memory_free(tty); +} + +void tty_update(tty_T* tty) { + char buffer[256]; + uint32_t read_bytes = 255; + while (read_bytes == 255) { + read_bytes = stream_read(tty->stream, buffer, 255); + buffer[read_bytes] = '\0'; + tty->cursor = graphics_buffer_draw_string(tty->graphics_buffer, tty->cursor.x, tty->cursor.y, tty->color, buffer); + } + + graphics_renderer_update(); +} + +uint32_t tty_write(tty_T* tty, string_t string) { + uint32_t num = stream_write(tty->stream, (void*)string, string_length(string)); + + tty_update(tty); + + return num; +} \ No newline at end of file diff --git a/src/platform/interrupts.c b/src/platform/interrupts.c index 44bcea7..a8d88dd 100644 --- a/src/platform/interrupts.c +++ b/src/platform/interrupts.c @@ -9,6 +9,7 @@ #include "utils/logger.h" #include "utils/io.h" #include "proc/scheduler.h" +#include "drivers/ps2/keyboard.h" idt_register_T g_idt_register; uint8_t g_handling_interrupt; @@ -225,6 +226,10 @@ cpu_state_T* irq_handle(cpu_state_T* state, pic_irq_E irq) { // log(LOG_DEBUG, "PIT -> Tick"); break; } + case IRQ_KEYBOARD: { + ps2_keyboard_read(); + break; + } default: { log(LOG_WARNING, "Unhandled IRQ"); break;