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:
@@ -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()) {
|
||||
|
||||
|
||||
@@ -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);
|
||||
bool ata_write_sectors(uint64_t lba, uint8_t sector_count, void* buffer);
|
||||
@@ -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);
|
||||
}
|
||||
@@ -5,4 +5,5 @@
|
||||
typedef void (*IRQHandler)(Registers* regs);
|
||||
|
||||
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);
|
||||
Reference in New Issue
Block a user