Files
claude-os/src/keyboard.c
AI 3512e937ae Fix keyboard init: add timeout to controller flush loop
The PS/2 keyboard controller flush loop could hang infinitely on some
VMs (UTM) where the controller keeps reporting data. Add a 1024-byte
timeout to prevent the kernel from hanging during keyboard_init().

Also add debug prints showing flush progress for diagnostics.
2026-02-23 13:48:33 +00:00

199 lines
6.1 KiB
C

/**
* @file keyboard.c
* @brief PS/2 keyboard driver implementation.
*
* Reads scancodes from I/O port 0x60 on IRQ1, translates scancode set 1
* to ASCII using a simple lookup table, stores characters in a ring buffer,
* and wakes any process blocked waiting for keyboard input.
*/
#include "keyboard.h"
#include "port_io.h"
#include "pic.h"
#include "process.h"
#include <stddef.h>
#include <string.h>
/* Debug print helpers defined in kernel.c */
extern void offset_print(const char *str);
extern void print_hex(uint32_t val);
/** PS/2 keyboard data port. */
#define KB_DATA_PORT 0x60
/** Ring buffer for keyboard input. */
static char kb_buffer[KB_BUFFER_SIZE];
static volatile uint32_t kb_head = 0;
static volatile uint32_t kb_tail = 0;
/** Process waiting for keyboard input (PID, or 0 if none). */
static volatile uint32_t kb_waiting_pid = 0;
/** Shift key state. */
static int shift_pressed = 0;
/**
* Scancode set 1 to ASCII lookup table (unshifted).
* Index = scancode, value = ASCII character (0 = unmapped).
*/
static const char scancode_ascii[128] = {
0, 27, '1', '2', '3', '4', '5', '6', /* 0x00 - 0x07 */
'7', '8', '9', '0', '-', '=', '\b', '\t', /* 0x08 - 0x0F */
'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', /* 0x10 - 0x17 */
'o', 'p', '[', ']', '\n', 0, 'a', 's', /* 0x18 - 0x1F */
'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', /* 0x20 - 0x27 */
'\'', '`', 0, '\\', 'z', 'x', 'c', 'v', /* 0x28 - 0x2F */
'b', 'n', 'm', ',', '.', '/', 0, '*', /* 0x30 - 0x37 */
0, ' ', 0, 0, 0, 0, 0, 0, /* 0x38 - 0x3F */
0, 0, 0, 0, 0, 0, 0, 0, /* 0x40 - 0x47 */
0, 0, 0, 0, 0, 0, 0, 0, /* 0x48 - 0x4F */
0, 0, 0, 0, 0, 0, 0, 0, /* 0x50 - 0x57 */
0, 0, 0, 0, 0, 0, 0, 0, /* 0x58 - 0x5F */
0, 0, 0, 0, 0, 0, 0, 0, /* 0x60 - 0x67 */
0, 0, 0, 0, 0, 0, 0, 0, /* 0x68 - 0x6F */
0, 0, 0, 0, 0, 0, 0, 0, /* 0x70 - 0x77 */
0, 0, 0, 0, 0, 0, 0, 0, /* 0x78 - 0x7F */
};
/**
* Scancode set 1 to ASCII lookup table (shifted).
*/
static const char scancode_ascii_shift[128] = {
0, 27, '!', '@', '#', '$', '%', '^', /* 0x00 - 0x07 */
'&', '*', '(', ')', '_', '+', '\b', '\t', /* 0x08 - 0x0F */
'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', /* 0x10 - 0x17 */
'O', 'P', '{', '}', '\n', 0, 'A', 'S', /* 0x18 - 0x1F */
'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', /* 0x20 - 0x27 */
'"', '~', 0, '|', 'Z', 'X', 'C', 'V', /* 0x28 - 0x2F */
'B', 'N', 'M', '<', '>', '?', 0, '*', /* 0x30 - 0x37 */
0, ' ', 0, 0, 0, 0, 0, 0, /* 0x38 - 0x3F */
0, 0, 0, 0, 0, 0, 0, 0, /* 0x40 - 0x47 */
0, 0, 0, 0, 0, 0, 0, 0, /* 0x48 - 0x4F */
0, 0, 0, 0, 0, 0, 0, 0, /* 0x50 - 0x57 */
0, 0, 0, 0, 0, 0, 0, 0, /* 0x58 - 0x5F */
0, 0, 0, 0, 0, 0, 0, 0, /* 0x60 - 0x67 */
0, 0, 0, 0, 0, 0, 0, 0, /* 0x68 - 0x6F */
0, 0, 0, 0, 0, 0, 0, 0, /* 0x70 - 0x77 */
0, 0, 0, 0, 0, 0, 0, 0, /* 0x78 - 0x7F */
};
/** Left/right shift scancodes. */
#define SC_LSHIFT_PRESS 0x2A
#define SC_LSHIFT_RELEASE 0xAA
#define SC_RSHIFT_PRESS 0x36
#define SC_RSHIFT_RELEASE 0xB6
/**
* Put a character into the ring buffer.
*/
static void kb_buffer_put(char c) {
uint32_t next = (kb_head + 1) % KB_BUFFER_SIZE;
if (next == kb_tail) {
return; /* Buffer full, drop character */
}
kb_buffer[kb_head] = c;
kb_head = next;
}
void keyboard_init(void) {
kb_head = 0;
kb_tail = 0;
kb_waiting_pid = 0;
shift_pressed = 0;
offset_print(" KEYBOARD: flushing controller...\n");
/* Flush any pending data from the keyboard controller.
* Use a timeout to avoid hanging if the controller keeps reporting data
* (some emulators/VMs behave differently). */
int flush_count = 0;
while ((inb(0x64) & 0x01) && flush_count < 1024) {
inb(KB_DATA_PORT);
flush_count++;
}
offset_print(" KEYBOARD: flushed ");
print_hex((uint32_t)flush_count);
offset_print(" KEYBOARD: bytes, unmasking IRQ1...\n");
/* Unmask IRQ1 (keyboard) in the PIC */
pic_clear_mask(1);
offset_print(" KEYBOARD: initialized\n");
}
void keyboard_irq(registers_t *regs) {
(void)regs;
uint8_t scancode = inb(KB_DATA_PORT);
/* Handle shift keys */
if (scancode == SC_LSHIFT_PRESS || scancode == SC_RSHIFT_PRESS) {
shift_pressed = 1;
return;
}
if (scancode == SC_LSHIFT_RELEASE || scancode == SC_RSHIFT_RELEASE) {
shift_pressed = 0;
return;
}
/* Ignore key releases (bit 7 set) */
if (scancode & 0x80) {
return;
}
/* Translate scancode to ASCII */
char c;
if (shift_pressed) {
c = scancode_ascii_shift[scancode];
} else {
c = scancode_ascii[scancode];
}
if (c == 0) {
return; /* Unmapped key */
}
/* Put character in buffer */
kb_buffer_put(c);
/* Wake any process waiting for keyboard input */
if (kb_waiting_pid != 0) {
process_t *waiter = process_get(kb_waiting_pid);
if (waiter && waiter->state == PROCESS_BLOCKED) {
waiter->state = PROCESS_READY;
}
kb_waiting_pid = 0;
}
}
uint32_t keyboard_read(char *buf, uint32_t count) {
uint32_t n = 0;
while (n < count && kb_tail != kb_head) {
buf[n++] = kb_buffer[kb_tail];
kb_tail = (kb_tail + 1) % KB_BUFFER_SIZE;
}
return n;
}
int keyboard_has_data(void) {
return kb_head != kb_tail;
}
void keyboard_block_for_input(registers_t *regs) {
process_t *cur = process_current();
if (!cur) return;
cur->state = PROCESS_BLOCKED;
cur->saved_regs = *regs;
/* Rewind EIP by 2 bytes so that when the process is unblocked and
* scheduled, the CPU re-executes the INT 0x80 instruction. At that
* point keyboard_has_data() will return true and the read succeeds. */
cur->saved_regs.eip -= 2;
kb_waiting_pid = cur->pid;
/* Schedule next process */
schedule_tick(regs);
}