diff --git a/.vscode/settings.json b/.vscode/settings.json index 9b65846..814aae2 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -56,5 +56,6 @@ "C_Cpp_Runner.showCompilationTime": false, "C_Cpp_Runner.useLinkTimeOptimization": false, "C_Cpp_Runner.msvcSecureNoWarnings": false, - "C_Cpp.errorSquiggles": "enabled" + "C_Cpp.errorSquiggles": "enabled", + "C_Cpp.default.compileCommands": "${workspaceFolder}/build/compile_commands.json" } \ No newline at end of file diff --git a/src/arch/x86_64/ata.c b/src/arch/x86_64/ata.c index cf78383..6827ed8 100644 --- a/src/arch/x86_64/ata.c +++ b/src/arch/x86_64/ata.c @@ -130,7 +130,7 @@ bool ata_read_sectors(uint64_t lba, uint8_t sector_count, void* buffer) return true; } -bool ata_write_sectors(uint64_t lba, uint8_t sector_count, const void* buffer) { +bool ata_write_sectors(uint64_t lba, uint8_t sector_count, void* buffer) { if (sector_count == 0) return true; @@ -143,7 +143,7 @@ bool ata_write_sectors(uint64_t lba, uint8_t sector_count, const void* buffer) { x86_64_outb(ATA_PRIMARY_LBAHIGH, (uint8_t)(lba >> 16)); x86_64_outb(ATA_PRIMARY_COMMAND, 0x30); // write sectors - const uint16_t* buf = buffer; + uint16_t* buf = buffer; for (uint8_t i = 0; i < sector_count; i++) { if (!ata_poll()) { diff --git a/src/arch/x86_64/ata.h b/src/arch/x86_64/ata.h index a6f1b82..8078c06 100644 --- a/src/arch/x86_64/ata.h +++ b/src/arch/x86_64/ata.h @@ -10,4 +10,4 @@ void ata_identify(void); // Buffer must be large enough (sector_count * 512). bool ata_read_sectors(uint64_t lba, uint8_t sector_count, void* buffer); -bool ata_write_sectors(uint64_t lba, uint8_t sector_count, const void* buffer); \ No newline at end of file +bool ata_write_sectors(uint64_t lba, uint8_t sector_count, void* buffer); \ No newline at end of file diff --git a/src/arch/x86_64/irq.c b/src/arch/x86_64/irq.c index cb42a08..d9e13c9 100644 --- a/src/arch/x86_64/irq.c +++ b/src/arch/x86_64/irq.c @@ -63,4 +63,9 @@ void x86_64_IRQ_Initialize(void) void x86_64_IRQ_RegisterHandler(int irq, IRQHandler handler) { g_IRQHandlers[irq] = handler; +} + +void x86_64_IRQ_Unmask(int irq) +{ + if (g_Driver) g_Driver->Unmask(irq); } \ No newline at end of file diff --git a/src/arch/x86_64/irq.h b/src/arch/x86_64/irq.h index ea5e046..34051b5 100644 --- a/src/arch/x86_64/irq.h +++ b/src/arch/x86_64/irq.h @@ -5,4 +5,5 @@ typedef void (*IRQHandler)(Registers* regs); void x86_64_IRQ_Initialize(); -void x86_64_IRQ_RegisterHandler(int irq, IRQHandler handler); \ No newline at end of file +void x86_64_IRQ_RegisterHandler(int irq, IRQHandler handler); +void x86_64_IRQ_Unmask(int irq); \ No newline at end of file diff --git a/src/arch/x86_64/ps2.c b/src/arch/x86_64/ps2.c new file mode 100644 index 0000000..770e592 --- /dev/null +++ b/src/arch/x86_64/ps2.c @@ -0,0 +1,332 @@ +#include "ps2.h" +#include "io.h" +#include "irq.h" +#include "stdio.h" +#include "mp/spinlock.h" +#include +#include "input/input.h" + +#define PS2_DATA 0x60 +#define PS2_COMMAND 0x64 + +/* ====================== CONTROLLER HELPERS ====================== */ +static inline bool ps2_wait_write(void) { + uint32_t timeout = 1000000; + while (x86_64_inb(PS2_COMMAND) & 2) { + if (--timeout == 0) { + printf("PS/2: write timeout!\n"); + return false; + } + asm volatile("pause"); + } + return true; +} + +static inline bool ps2_wait_read(void) { + uint32_t timeout = 1000000; + while (!(x86_64_inb(PS2_COMMAND) & 1)) { + if (--timeout == 0) { + printf("PS/2: read timeout!\n"); + return false; + } + asm volatile("pause"); + } + return true; +} + +static inline uint8_t ps2_read(void) { + if (!ps2_wait_read()) return 0xFF; // error value + return x86_64_inb(PS2_DATA); +} + +static inline void ps2_write(uint8_t val) { + if (!ps2_wait_write()) return; + x86_64_outb(PS2_DATA, val); +} + +static inline void ps2_command(uint8_t cmd) { + if (!ps2_wait_write()) return; + x86_64_outb(PS2_COMMAND, cmd); +} + +/* Send command to AUX (mouse) port */ +static void ps2_write_mouse(uint8_t val) { + ps2_command(0xD4); // tell controller next byte goes to mouse + ps2_write(val); +} + +/* ====================== KEYBOARD RING BUFFER ====================== */ +#define KB_BUFFER_SIZE 256 +static uint8_t kb_buffer[KB_BUFFER_SIZE]; +static size_t kb_read_idx = 0; +static size_t kb_write_idx = 0; +static spinlock_t kb_lock = SPINLOCK_INIT; + +/* US QWERTY scancode set 2 -> ASCII (unshifted) */ +static const char kb_map[128] = { +/*00*/ 0, 0, '1', '2', '3', '4', '5', '6', +/*08*/ '7', '8', '9', '0', '-', '=', '\b', '\t', +/*10*/ 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', +/*18*/ 'o', 'p', '[', ']', '\n', 0, 'a', 's', +/*20*/ 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', +/*28*/ '\'', '`', 0, '\\', 'z', 'x', 'c', 'v', +/*30*/ 'b', 'n', 'm', ',', '.', '/', 0, '*', +/*38*/ 0, ' ', 0, 0, 0, 0, 0, 0, +/*40*/ 0, 0, 0, 0, 0, 0, 0, 0, +/*48*/ 0, 0, '-', 0, 0, 0, '+', 0, +/*50*/ 0, 0, 0, 0, 0, 0, 0, 0, +/*58*/ 0, 0, 0, 0, 0, 0, 0, 0, +/*60*/ 0, 0, 0, 0, 0, 0, 0, 0, +/*68*/ 0, 0, 0, 0, 0, 0, 0, 0, +/*70*/ 0, 0, 0, 0, 0, 0, 0, 0, +/*78*/ 0, 0, 0, 0, 0, 0, 0, 0, +}; + +/* Shifted version */ +static const char kb_map_shift[128] = { +/*00*/ 0, 0, '!', '@', '#', '$', '%', '^', +/*08*/ '&', '*', '(', ')', '_', '+', '\b', '\t', +/*10*/ 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', +/*18*/ 'O', 'P', '{', '}', '\n', 0, 'A', 'S', +/*20*/ 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', +/*28*/ '"', '~', 0, '|', 'Z', 'X', 'C', 'V', +/*30*/ 'B', 'N', 'M', '<', '>', '?', 0, '*', +/*38*/ 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,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 bool shift = false; +static bool caps = false; + +static void kb_enqueue(uint8_t sc) { + spinlock_acquire_or_wait(&kb_lock); + size_t next = (kb_write_idx + 1) % KB_BUFFER_SIZE; + if (next != kb_read_idx) { + kb_buffer[kb_write_idx] = sc; + kb_write_idx = next; + } + spinlock_drop(&kb_lock); +} + +bool ps2_keyboard_has_data(void) { + spinlock_acquire_or_wait(&kb_lock); + bool has = (kb_read_idx != kb_write_idx); + spinlock_drop(&kb_lock); + return has; +} + + +/* ====================== MOUSE RING BUFFER ====================== */ +#define MOUSE_BUFFER_SIZE 32 +static mouse_packet_t mouse_buffer[MOUSE_BUFFER_SIZE]; +static size_t mouse_read_idx = 0; +static size_t mouse_write_idx = 0; +static spinlock_t mouse_lock = SPINLOCK_INIT; + +static uint8_t mouse_packet[4]; +static int mouse_idx = 0; +static int mouse_packet_size = 3; // 3 or 4 for wheel + +static void mouse_enqueue(const mouse_packet_t* pkt) { + spinlock_acquire_or_wait(&mouse_lock); + size_t next = (mouse_write_idx + 1) % MOUSE_BUFFER_SIZE; + if (next != mouse_read_idx) { + mouse_buffer[mouse_write_idx] = *pkt; + mouse_write_idx = next; + } + spinlock_drop(&mouse_lock); +} + +bool ps2_mouse_get_packet(mouse_packet_t* out) { + spinlock_acquire_or_wait(&mouse_lock); + if (mouse_read_idx == mouse_write_idx) { + spinlock_drop(&mouse_lock); + return false; + } + *out = mouse_buffer[mouse_read_idx]; + mouse_read_idx = (mouse_read_idx + 1) % MOUSE_BUFFER_SIZE; + spinlock_drop(&mouse_lock); + return true; +} + +bool ps2_mouse_has_packet(void) { + spinlock_acquire_or_wait(&mouse_lock); + bool has = (mouse_read_idx != mouse_write_idx); + spinlock_drop(&mouse_lock); + return has; +} + +/* ====================== IRQ HANDLERS ====================== */ +void keyboard_irq_handler(Registers* regs) { + (void)regs; + uint8_t sc = x86_64_inb(PS2_DATA); + + // Optional: keep the old buffer if you still use the polling API somewhere + kb_enqueue(sc); + + // === NEW: process and push to the input subsystem === + bool released = (sc & 0x80) != 0; + sc &= 0x7F; + + if (sc == 0x2A || sc == 0x36) { // L/R Shift + shift = !released; + return; + } + if (sc == 0x3A && !released) { // Caps Lock + caps = !caps; + return; + } + + bool is_alpha = (sc >= 0x10 && sc <= 0x19) || + (sc >= 0x1E && sc <= 0x26) || + (sc >= 0x2C && sc <= 0x32); + + bool use_upper = shift ^ (caps && is_alpha); + char ascii = use_upper ? kb_map_shift[sc] : kb_map[sc]; + + input_push_key(sc, released ? 0 : ascii, !released); +} + +void mouse_irq_handler(Registers* regs) +{ + (void)regs; + uint8_t data = x86_64_inb(PS2_DATA); + + if (mouse_idx == 0 && !(data & 0x08)) return; + + mouse_packet[mouse_idx++] = data; + + if (mouse_idx == mouse_packet_size) { + mouse_packet_t pkt = {0}; + uint8_t status = mouse_packet[0]; + + pkt.overflow_x = !!(status & (1 << 6)); + pkt.overflow_y = !!(status & (1 << 7)); + + if (pkt.overflow_x || pkt.overflow_y) { + mouse_idx = 0; + return; + } + + pkt.buttons = status & 0x07; + pkt.x_delta = (int8_t)mouse_packet[1]; + pkt.y_delta = -(int8_t)mouse_packet[2]; + pkt.z_delta = (mouse_packet_size == 4) ? (int8_t)mouse_packet[3] : 0; + + mouse_enqueue(&pkt); + + if (pkt.x_delta || pkt.y_delta) + input_push_mouse_rel(pkt.x_delta, pkt.y_delta); + if (pkt.z_delta) + input_push_mouse_wheel(pkt.z_delta); + + static uint8_t prev_buttons = 0; + uint8_t changed = pkt.buttons ^ prev_buttons; + for (int i = 0; i < 3; i++) { + if (changed & (1 << i)) + input_push_mouse_btn(i, !!(pkt.buttons & (1 << i))); + } + prev_buttons = pkt.buttons; + + mouse_idx = 0; + } +} + +/* ====================== DEVICE INITIALIZATION ====================== */ +static bool ps2_controller_init(void) { + ps2_command(0xAD); + ps2_command(0xA7); + + while (x86_64_inb(PS2_COMMAND) & 1) + x86_64_inb(PS2_DATA); + + ps2_command(0xAA); + if (ps2_read() != 0x55) { + printf("PS/2: Controller self-test failed!\n"); + return false; + } + + ps2_command(0xAB); + if (ps2_read() != 0x00) { + printf("PS/2: Keyboard interface test failed!\n"); + return false; + } + ps2_command(0xA9); + if (ps2_read() != 0x00) { + printf("PS/2: Mouse interface test failed (no mouse?)\n"); + } + + ps2_command(0x20); + uint8_t cfg = ps2_read(); + cfg |= (1 << 0) | (1 << 1) | (1 << 6); + cfg &= ~((1 << 4) | (1 << 5)); + ps2_command(0x60); + ps2_write(cfg); + + ps2_command(0xAE); + ps2_command(0xA8); + + printf("PS/2: Controller ready (IRQs enabled, translation on)\n"); + return true; +} + +static void ps2_keyboard_init(void) { + ps2_write(0xFF); + ps2_read(); // ACK + ps2_read(); ps2_read(); // AA 00 + + ps2_write(0xF4); + if (ps2_read() != 0xFA) + printf("PS/2: Keyboard enable failed\n"); + + printf("PS/2: Keyboard initialized (scancode set 2)\n"); +} + +static void ps2_mouse_init(void) { + ps2_write_mouse(0xFF); + ps2_read(); ps2_read(); ps2_read(); // ACK, AA, 00 + + ps2_write_mouse(0xF3); ps2_write_mouse(200); + ps2_write_mouse(0xF3); ps2_write_mouse(100); + ps2_write_mouse(0xF3); ps2_write_mouse(80); + ps2_write_mouse(0xF2); + uint8_t id = ps2_read(); + if (id == 0x03) { + mouse_packet_size = 4; + printf("PS/2: IntelliMouse (wheel) detected\n"); + } else { + mouse_packet_size = 3; + printf("PS/2: Standard mouse detected\n"); + } + + ps2_write_mouse(0xF6); + ps2_write_mouse(0xF4); + + printf("PS/2: Mouse initialized (packet size %d)\n", mouse_packet_size); +} + +/* ====================== MAIN INIT ====================== */ +void ps2_init(void) { + if (!ps2_controller_init()) { + printf("PS/2: Controller initialization failed - input disabled\n"); + return; + } + + input_init(); + + ps2_keyboard_init(); + ps2_mouse_init(); + + + x86_64_IRQ_RegisterHandler(1, keyboard_irq_handler); + x86_64_IRQ_RegisterHandler(12, mouse_irq_handler); + + + x86_64_IRQ_Unmask(1); + x86_64_IRQ_Unmask(12); + + printf("PS/2 subsystem initialized successfully!\n"); +} \ No newline at end of file diff --git a/src/arch/x86_64/ps2.h b/src/arch/x86_64/ps2.h new file mode 100644 index 0000000..39a7a4a --- /dev/null +++ b/src/arch/x86_64/ps2.h @@ -0,0 +1,29 @@ +#pragma once + + +#include +#include +#include "mp/spinlock.h" + +/* ====================== PUBLIC API ====================== */ + +/* Keyboard */ +uint8_t ps2_keyboard_get_scancode(void); // non-blocking, returns 0 if empty +bool ps2_keyboard_has_data(void); +char ps2_keyboard_get_char(void); // processed ASCII (handles shift/caps) + +/* Mouse */ +typedef struct { + int8_t x_delta; + int8_t y_delta; + int8_t z_delta; // wheel (0 if not supported) + uint8_t buttons; // bit 0 = left, bit 1 = right, bit 2 = middle + bool overflow_x; + bool overflow_y; +} mouse_packet_t; + +bool ps2_mouse_get_packet(mouse_packet_t* out_packet); // non-blocking, returns false if empty +bool ps2_mouse_has_packet(void); + +/* Subsystem */ +void ps2_init(void); diff --git a/src/fs/vfs.c b/src/fs/vfs.c index 09b80f4..2ca34a7 100644 --- a/src/fs/vfs.c +++ b/src/fs/vfs.c @@ -2,12 +2,55 @@ #include