feat: partially implement PS/2 input subsystem with keyboard and mouse support
This implementation is incomplete, and doesn't work, but it will soon, to be determined, hopefully, uh, yknow. Signed-off-by kaguya <vpshinomiya@protonmail.com>
This commit is contained in:
Vendored
+2
-1
@@ -56,5 +56,6 @@
|
|||||||
"C_Cpp_Runner.showCompilationTime": false,
|
"C_Cpp_Runner.showCompilationTime": false,
|
||||||
"C_Cpp_Runner.useLinkTimeOptimization": false,
|
"C_Cpp_Runner.useLinkTimeOptimization": false,
|
||||||
"C_Cpp_Runner.msvcSecureNoWarnings": false,
|
"C_Cpp_Runner.msvcSecureNoWarnings": false,
|
||||||
"C_Cpp.errorSquiggles": "enabled"
|
"C_Cpp.errorSquiggles": "enabled",
|
||||||
|
"C_Cpp.default.compileCommands": "${workspaceFolder}/build/compile_commands.json"
|
||||||
}
|
}
|
||||||
@@ -130,7 +130,7 @@ bool ata_read_sectors(uint64_t lba, uint8_t sector_count, void* buffer)
|
|||||||
return true;
|
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;
|
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_LBAHIGH, (uint8_t)(lba >> 16));
|
||||||
x86_64_outb(ATA_PRIMARY_COMMAND, 0x30); // write sectors
|
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++) {
|
for (uint8_t i = 0; i < sector_count; i++) {
|
||||||
if (!ata_poll()) {
|
if (!ata_poll()) {
|
||||||
|
|
||||||
|
|||||||
@@ -10,4 +10,4 @@ void ata_identify(void);
|
|||||||
// Buffer must be large enough (sector_count * 512).
|
// Buffer must be large enough (sector_count * 512).
|
||||||
bool ata_read_sectors(uint64_t lba, uint8_t sector_count, void* buffer);
|
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);
|
bool ata_write_sectors(uint64_t lba, uint8_t sector_count, void* buffer);
|
||||||
@@ -64,3 +64,8 @@ void x86_64_IRQ_RegisterHandler(int irq, IRQHandler handler)
|
|||||||
{
|
{
|
||||||
g_IRQHandlers[irq] = handler;
|
g_IRQHandlers[irq] = handler;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void x86_64_IRQ_Unmask(int irq)
|
||||||
|
{
|
||||||
|
if (g_Driver) g_Driver->Unmask(irq);
|
||||||
|
}
|
||||||
@@ -6,3 +6,4 @@ typedef void (*IRQHandler)(Registers* regs);
|
|||||||
|
|
||||||
void x86_64_IRQ_Initialize();
|
void x86_64_IRQ_Initialize();
|
||||||
void x86_64_IRQ_RegisterHandler(int irq, IRQHandler handler);
|
void x86_64_IRQ_RegisterHandler(int irq, IRQHandler handler);
|
||||||
|
void x86_64_IRQ_Unmask(int irq);
|
||||||
@@ -0,0 +1,332 @@
|
|||||||
|
#include "ps2.h"
|
||||||
|
#include "io.h"
|
||||||
|
#include "irq.h"
|
||||||
|
#include "stdio.h"
|
||||||
|
#include "mp/spinlock.h"
|
||||||
|
#include <stddef.h>
|
||||||
|
#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");
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#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);
|
||||||
@@ -2,12 +2,55 @@
|
|||||||
#include <video/render.h>
|
#include <video/render.h>
|
||||||
#include <arch/x86_64/e9.h>
|
#include <arch/x86_64/e9.h>
|
||||||
#include "mp/spinlock.h"
|
#include "mp/spinlock.h"
|
||||||
|
#include "stdio.h"
|
||||||
|
#include "string.h"
|
||||||
|
|
||||||
static spinlock_t s_vfs_lock = SPINLOCK_INIT;
|
static spinlock_t s_vfs_lock = SPINLOCK_INIT;
|
||||||
static vfs_file_t vfs_fd_table[VFS_MAX_FDS];
|
static vfs_file_t vfs_fd_table[VFS_MAX_FDS];
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
const char* path;
|
||||||
|
int (*read)(void* buf, size_t len);
|
||||||
|
int (*write)(const void* buf, size_t len);
|
||||||
|
} vfs_chardev_t;
|
||||||
|
|
||||||
|
#define VFS_MAX_CHARDEVS 8
|
||||||
|
static vfs_chardev_t s_chardevs[VFS_MAX_CHARDEVS];
|
||||||
|
static int s_num_chardevs = 0;
|
||||||
|
|
||||||
|
int VFS_RegisterCharDev(const char* path,
|
||||||
|
int (*read)(void* buf, size_t len),
|
||||||
|
int (*write)(const void* buf, size_t len))
|
||||||
|
{
|
||||||
|
if (s_num_chardevs >= VFS_MAX_CHARDEVS) {
|
||||||
|
printf("VFS: too many char devices!\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
s_chardevs[s_num_chardevs].path = path;
|
||||||
|
s_chardevs[s_num_chardevs].read = read;
|
||||||
|
s_chardevs[s_num_chardevs].write = write;
|
||||||
|
printf("VFS: registered chardev %s\n", path);
|
||||||
|
s_num_chardevs++;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
fd_t VFS_Open_internal(const char* path)
|
fd_t VFS_Open_internal(const char* path)
|
||||||
{
|
{
|
||||||
|
for (int i = 0; i < s_num_chardevs; i++) {
|
||||||
|
if (strcmp(s_chardevs[i].path, path) == 0) {
|
||||||
|
for (int fd = 4; fd < VFS_MAX_FDS; fd++) {
|
||||||
|
if (!vfs_fd_table[fd].used) {
|
||||||
|
vfs_fd_table[fd].used = true;
|
||||||
|
vfs_fd_table[fd].type = VFS_NODE_CHARDEV;
|
||||||
|
vfs_fd_table[fd].offset = 0;
|
||||||
|
vfs_fd_table[fd].chardev.dev_idx = i;
|
||||||
|
return fd;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
uint32_t ino = ext2_resolve_path(path);
|
uint32_t ino = ext2_resolve_path(path);
|
||||||
if (ino == 0)
|
if (ino == 0)
|
||||||
return -1;
|
return -1;
|
||||||
@@ -92,6 +135,14 @@ int VFS_Read(fd_t fd, uint8_t* buf, size_t size)
|
|||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case VFS_NODE_CHARDEV:
|
||||||
|
{
|
||||||
|
int idx = file->chardev.dev_idx;
|
||||||
|
if (idx < 0 || idx >= s_num_chardevs || !s_chardevs[idx].read)
|
||||||
|
return -1;
|
||||||
|
return s_chardevs[idx].read(buf, size);
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@@ -143,5 +194,13 @@ int VFS_Write(fd_t file, uint8_t* data, size_t size)
|
|||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (f->type == VFS_NODE_CHARDEV)
|
||||||
|
{
|
||||||
|
int idx = f->chardev.dev_idx;
|
||||||
|
if (idx >= 0 && idx < s_num_chardevs && s_chardevs[idx].write)
|
||||||
|
return s_chardevs[idx].write(data, size);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
+9
-1
@@ -16,7 +16,7 @@ typedef int fd_t;
|
|||||||
typedef enum {
|
typedef enum {
|
||||||
VFS_NODE_NONE,
|
VFS_NODE_NONE,
|
||||||
VFS_NODE_EXT2,
|
VFS_NODE_EXT2,
|
||||||
VFS_NODE_DEVICE
|
VFS_NODE_CHARDEV
|
||||||
} vfs_node_type_t;
|
} vfs_node_type_t;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
@@ -27,6 +27,10 @@ typedef struct {
|
|||||||
uint32_t inode_num;
|
uint32_t inode_num;
|
||||||
ext2_inode_t inode;
|
ext2_inode_t inode;
|
||||||
} ext2;
|
} ext2;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
int dev_idx;
|
||||||
|
} chardev;
|
||||||
};
|
};
|
||||||
|
|
||||||
size_t offset;
|
size_t offset;
|
||||||
@@ -38,3 +42,7 @@ fd_t VFS_Open(const char* path);
|
|||||||
int VFS_Read(fd_t fd, uint8_t* buf, size_t size);
|
int VFS_Read(fd_t fd, uint8_t* buf, size_t size);
|
||||||
int VFS_Write(fd_t fd, uint8_t* data, size_t size);
|
int VFS_Write(fd_t fd, uint8_t* data, size_t size);
|
||||||
int VFS_Close(fd_t fd);
|
int VFS_Close(fd_t fd);
|
||||||
|
|
||||||
|
int VFS_RegisterCharDev(const char* path,
|
||||||
|
int (*read)(void* buf, size_t len),
|
||||||
|
int (*write)(const void* buf, size_t len));
|
||||||
@@ -0,0 +1,117 @@
|
|||||||
|
#include "input/input.h"
|
||||||
|
#include "mp/spinlock.h"
|
||||||
|
#include "stdio.h"
|
||||||
|
#include "fs/vfs.h"
|
||||||
|
#include <stddef.h>
|
||||||
|
#include "mm/memory.h"
|
||||||
|
#include "string.h"
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------ *
|
||||||
|
* Event ring-buffer
|
||||||
|
* ------------------------------------------------------------------ */
|
||||||
|
#define INPUT_BUF_SIZE 512
|
||||||
|
|
||||||
|
static input_event_t s_buf[INPUT_BUF_SIZE];
|
||||||
|
static size_t s_rpos = 0;
|
||||||
|
static size_t s_wpos = 0;
|
||||||
|
static spinlock_t s_lock = SPINLOCK_INIT;
|
||||||
|
|
||||||
|
static void push_event(const input_event_t *ev)
|
||||||
|
{
|
||||||
|
spinlock_acquire_or_wait(&s_lock);
|
||||||
|
size_t next = (s_wpos + 1) % INPUT_BUF_SIZE;
|
||||||
|
if (next != s_rpos) { /* drop if full */
|
||||||
|
s_buf[s_wpos] = *ev;
|
||||||
|
s_wpos = next;
|
||||||
|
}
|
||||||
|
spinlock_drop(&s_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool input_poll(input_event_t *out)
|
||||||
|
{
|
||||||
|
spinlock_acquire_or_wait(&s_lock);
|
||||||
|
if (s_rpos == s_wpos) {
|
||||||
|
spinlock_drop(&s_lock);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
*out = s_buf[s_rpos];
|
||||||
|
s_rpos = (s_rpos + 1) % INPUT_BUF_SIZE;
|
||||||
|
spinlock_drop(&s_lock);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool input_has_event(void)
|
||||||
|
{
|
||||||
|
spinlock_acquire_or_wait(&s_lock);
|
||||||
|
bool has = (s_rpos != s_wpos);
|
||||||
|
spinlock_drop(&s_lock);
|
||||||
|
return has;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------ *
|
||||||
|
* Driver-facing helpers
|
||||||
|
* ------------------------------------------------------------------ */
|
||||||
|
void input_push_key(uint8_t scancode, char ascii, bool pressed)
|
||||||
|
{
|
||||||
|
input_event_t ev = {
|
||||||
|
.type = INPUT_EV_KEY,
|
||||||
|
.key = { .scancode = scancode, .ascii = ascii, .pressed = pressed },
|
||||||
|
};
|
||||||
|
push_event(&ev);
|
||||||
|
}
|
||||||
|
|
||||||
|
void input_push_mouse_rel(int16_t dx, int16_t dy)
|
||||||
|
{
|
||||||
|
input_event_t ev = { .type = INPUT_EV_MOUSE_REL, .rel = { dx, dy } };
|
||||||
|
push_event(&ev);
|
||||||
|
}
|
||||||
|
|
||||||
|
void input_push_mouse_btn(uint8_t button, bool pressed)
|
||||||
|
{
|
||||||
|
input_event_t ev = {
|
||||||
|
.type = INPUT_EV_MOUSE_BTN,
|
||||||
|
.btn = { .button = button, .pressed = pressed },
|
||||||
|
};
|
||||||
|
push_event(&ev);
|
||||||
|
}
|
||||||
|
|
||||||
|
void input_push_mouse_wheel(int8_t delta)
|
||||||
|
{
|
||||||
|
input_event_t ev = { .type = INPUT_EV_MOUSE_WHEEL, .wheel = { delta } };
|
||||||
|
push_event(&ev);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------ *
|
||||||
|
* /dev/input/event0 VFS device node
|
||||||
|
*
|
||||||
|
* Adjust VFS_RegisterDevice() to match whatever your VFS layer exposes
|
||||||
|
* for character-device registration.
|
||||||
|
* ------------------------------------------------------------------ */
|
||||||
|
static int dev_input_read(void *buf, size_t len)
|
||||||
|
{
|
||||||
|
size_t count = 0;
|
||||||
|
while (count + sizeof(input_event_t) <= len) {
|
||||||
|
input_event_t ev;
|
||||||
|
if (!input_poll(&ev)) break;
|
||||||
|
memcpy((uint8_t*)buf + count, &ev, sizeof(ev));
|
||||||
|
count += sizeof(ev);
|
||||||
|
}
|
||||||
|
return (int)count;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dev_input_write(const void *buf, size_t len)
|
||||||
|
{
|
||||||
|
(void)buf; (void)len;
|
||||||
|
return -1; /* read-only */
|
||||||
|
}
|
||||||
|
|
||||||
|
void input_register_devnodes(void)
|
||||||
|
{
|
||||||
|
VFS_RegisterCharDev("/dev/input/event0", dev_input_read, dev_input_write);
|
||||||
|
}
|
||||||
|
|
||||||
|
void input_init(void)
|
||||||
|
{
|
||||||
|
input_register_devnodes();
|
||||||
|
printf("input: subsystem initialized\n");
|
||||||
|
}
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------ *
|
||||||
|
* Generic input event types
|
||||||
|
* ------------------------------------------------------------------ */
|
||||||
|
typedef enum {
|
||||||
|
INPUT_EV_NONE = 0,
|
||||||
|
INPUT_EV_KEY, /* key press / release */
|
||||||
|
INPUT_EV_MOUSE_REL, /* relative mouse movement */
|
||||||
|
INPUT_EV_MOUSE_BTN, /* mouse button press/release */
|
||||||
|
INPUT_EV_MOUSE_WHEEL,
|
||||||
|
} input_ev_type_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
input_ev_type_t type;
|
||||||
|
|
||||||
|
union {
|
||||||
|
/* INPUT_EV_KEY */
|
||||||
|
struct {
|
||||||
|
uint8_t scancode; /* raw set-1 scancode (break bit cleared) */
|
||||||
|
char ascii; /* 0 if non-printable */
|
||||||
|
bool pressed;
|
||||||
|
} key;
|
||||||
|
|
||||||
|
/* INPUT_EV_MOUSE_REL */
|
||||||
|
struct {
|
||||||
|
int16_t dx, dy;
|
||||||
|
} rel;
|
||||||
|
|
||||||
|
/* INPUT_EV_MOUSE_BTN */
|
||||||
|
struct {
|
||||||
|
uint8_t button; /* 0=left 1=right 2=middle */
|
||||||
|
bool pressed;
|
||||||
|
} btn;
|
||||||
|
|
||||||
|
/* INPUT_EV_MOUSE_WHEEL */
|
||||||
|
struct {
|
||||||
|
int8_t delta;
|
||||||
|
} wheel;
|
||||||
|
};
|
||||||
|
} input_event_t;
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------ *
|
||||||
|
* Public API
|
||||||
|
* ------------------------------------------------------------------ */
|
||||||
|
void input_init(void);
|
||||||
|
|
||||||
|
/* Called by drivers (PS/2, USB HID, …) to publish events */
|
||||||
|
void input_push_key(uint8_t scancode, char ascii, bool pressed);
|
||||||
|
void input_push_mouse_rel(int16_t dx, int16_t dy);
|
||||||
|
void input_push_mouse_btn(uint8_t button, bool pressed);
|
||||||
|
void input_push_mouse_wheel(int8_t delta);
|
||||||
|
|
||||||
|
/* Consumer API */
|
||||||
|
bool input_poll(input_event_t *out); /* non-blocking; false if empty */
|
||||||
|
bool input_has_event(void);
|
||||||
|
|
||||||
|
/* VFS helpers — call after VFS is ready */
|
||||||
|
void input_register_devnodes(void); /* creates /dev/input/event0 */
|
||||||
+65
@@ -27,6 +27,8 @@
|
|||||||
#include "arch/x86_64/pci.h"
|
#include "arch/x86_64/pci.h"
|
||||||
#include "sound/hda.h"
|
#include "sound/hda.h"
|
||||||
#include "sound/pcm.h"
|
#include "sound/pcm.h"
|
||||||
|
#include "arch/x86_64/ps2.h"
|
||||||
|
#include "input/input.h"
|
||||||
|
|
||||||
|
|
||||||
uintptr_t g_hhdm_offset;
|
uintptr_t g_hhdm_offset;
|
||||||
@@ -201,6 +203,7 @@ void kmain(void) {
|
|||||||
x86_64_ISR_Initialize();
|
x86_64_ISR_Initialize();
|
||||||
x86_64_IRQ_Initialize();
|
x86_64_IRQ_Initialize();
|
||||||
x86_64_PIT_Initialize(1000);
|
x86_64_PIT_Initialize(1000);
|
||||||
|
ps2_init();
|
||||||
asm volatile("sti");
|
asm volatile("sti");
|
||||||
calibrate_tsc();
|
calibrate_tsc();
|
||||||
|
|
||||||
@@ -446,6 +449,68 @@ void kmain(void) {
|
|||||||
//start_userspace();
|
//start_userspace();
|
||||||
printf("tst");
|
printf("tst");
|
||||||
|
|
||||||
|
printf("\n=== Input Subsystem Test ===\n");
|
||||||
|
printf("Opening /dev/input/event0...\n");
|
||||||
|
|
||||||
|
fd_t input_fd = VFS_Open("/dev/input/event0");
|
||||||
|
if (input_fd < 0) {
|
||||||
|
printf("Failed to open /dev/input/event0!\n");
|
||||||
|
} else {
|
||||||
|
printf("Successfully opened /dev/input/event0 (fd=%d)\n", input_fd);
|
||||||
|
printf("Press keys or move mouse... (press ESC to exit test)\n\n");
|
||||||
|
|
||||||
|
input_event_t ev;
|
||||||
|
bool running = true;
|
||||||
|
|
||||||
|
while (running) {
|
||||||
|
// Non-blocking read from the input device
|
||||||
|
int bytes = VFS_Read(input_fd, (uint8_t*)&ev, sizeof(input_event_t));
|
||||||
|
if (bytes == sizeof(input_event_t)) {
|
||||||
|
switch (ev.type) {
|
||||||
|
case INPUT_EV_KEY:
|
||||||
|
printf("KEY: scancode=0x%02X ascii='%c' (%d) pressed=%s\n",
|
||||||
|
ev.key.scancode,
|
||||||
|
ev.key.ascii ? ev.key.ascii : '?',
|
||||||
|
ev.key.ascii,
|
||||||
|
ev.key.pressed ? "YES" : "NO");
|
||||||
|
|
||||||
|
// Exit test on ESC key release
|
||||||
|
if (ev.key.scancode == 0x01 && !ev.key.pressed) {
|
||||||
|
printf("ESC pressed - exiting input test.\n");
|
||||||
|
running = false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case INPUT_EV_MOUSE_REL:
|
||||||
|
if (ev.rel.dx || ev.rel.dy) {
|
||||||
|
printf("MOUSE MOVE: dx=%d dy=%d\n", ev.rel.dx, ev.rel.dy);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case INPUT_EV_MOUSE_BTN:
|
||||||
|
printf("MOUSE BTN: button=%d pressed=%s\n",
|
||||||
|
ev.btn.button,
|
||||||
|
ev.btn.pressed ? "YES" : "NO");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case INPUT_EV_MOUSE_WHEEL:
|
||||||
|
printf("MOUSE WHEEL: delta=%d\n", ev.wheel.delta);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
printf("Unknown event type %d\n", ev.type);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Small yield so we don't spin the CPU too hard
|
||||||
|
// (you can replace with a proper sleep later)
|
||||||
|
asm volatile("pause");
|
||||||
|
}
|
||||||
|
|
||||||
|
VFS_Close(input_fd);
|
||||||
|
printf("Input test finished.\n");
|
||||||
|
}
|
||||||
|
|
||||||
// We're done, just hang...
|
// We're done, just hang...
|
||||||
hcf();
|
hcf();
|
||||||
|
|||||||
+1
-1
@@ -19,7 +19,7 @@ uint64_t rdtsc(void)
|
|||||||
void calibrate_tsc(void)
|
void calibrate_tsc(void)
|
||||||
{
|
{
|
||||||
// Use a stable PIT rate for calibration
|
// Use a stable PIT rate for calibration
|
||||||
const uint32_t pit_freq = 1000; // 100 Hz = 10ms per tick
|
const uint32_t pit_freq = 1000; // 1000 Hz = 1ms per tick
|
||||||
const uint32_t calibration_ticks = 1000; // 1 second total
|
const uint32_t calibration_ticks = 1000; // 1 second total
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user