feat(usb): a USB keyboard -- a full one -- and no hang on a PC without PS/2
boards/BRINGUP.md A3 and S3, FABRIC-3 SXLI. A HID boot keyboard is configured (interrupt IN endpoint, SET_PROTOCOL boot) and its reports become the same Linux-keycode events as PS/2 and virtio (usb_kbd.c): the full layout -- F-keys, navigation block, arrows, Num Lock, keypad. The console treats Caps and Num Lock as toggles shown on the keyboard's lights, and the keypad as digits or navigation. While a USB keyboard runs, its event ring is serviced every tick, not once a second. Found on the way: with no i8042 (port 0x64 reads 0xFF) the kernel hung at boot in an endless drain -- a legacy-free PC would stop there. Detected and skipped now; drains bounded. QEMU, amd64 with the USB keyboard the only one: 1 2 + . on main keys and keypad, Caps Lock, Num Lock all right; aarch64/riscv64 configure it too. Acceptance on three ISAs unchanged; identities hot-plug. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
080b7012ba
commit
739cb9c163
@@ -62,13 +62,33 @@ static inline uint8_t inb(uint16_t port) {
|
||||
return val;
|
||||
}
|
||||
|
||||
/* Is there a controller at all? A PC without one -- "legacy-free", common
|
||||
* now, the keyboard on USB alone -- answers port 0x64 with 0xFF, every bit
|
||||
* set, "output full" included, for ever: until 2026-10-14 the drains below
|
||||
* then never ended, and the kernel hung right here (boards/BRINGUP.md S3,
|
||||
* found rehearsing A3 with QEMU's i8042=off). */
|
||||
static int g_i8042_present;
|
||||
|
||||
/* Read and drop what the controller holds -- a bounded number of bytes:
|
||||
* a real controller holds at most a few. */
|
||||
static void i8042_drain(void) {
|
||||
int i;
|
||||
for (i = 0; i < 64 && (inb(I8042_STATUS_PORT) & I8042_STATUS_OUTPUT_FULL); i++) {
|
||||
(void)inb(I8042_DATA_PORT);
|
||||
}
|
||||
}
|
||||
|
||||
void i8042_init(void) {
|
||||
if (inb(I8042_STATUS_PORT) == 0xFF) {
|
||||
console_println("i8042: no PS/2 controller -- keyboard by USB only");
|
||||
return;
|
||||
}
|
||||
g_i8042_present = 1;
|
||||
|
||||
/* Drain any stale output byte left by firmware before enabling IRQ1 —
|
||||
* otherwise the first "interrupt" would really be this leftover byte
|
||||
* read via polling, not a real IRQ1 delivery. */
|
||||
while (inb(I8042_STATUS_PORT) & I8042_STATUS_OUTPUT_FULL) {
|
||||
(void)inb(I8042_DATA_PORT);
|
||||
}
|
||||
i8042_drain();
|
||||
|
||||
outb(I8042_CMD_PORT, I8042_CMD_READ_CONFIG);
|
||||
uint8_t config = inb(I8042_DATA_PORT);
|
||||
@@ -110,9 +130,7 @@ void i8042_drain_stale(void) {
|
||||
* edge ever again (the textbook "fires once, then never" pattern).
|
||||
* i8042_init()'s drain happens at M4; VM bootstrap runs for real wall
|
||||
* time between then and unmask, so re-drain immediately before. */
|
||||
while (inb(I8042_STATUS_PORT) & I8042_STATUS_OUTPUT_FULL) {
|
||||
(void)inb(I8042_DATA_PORT);
|
||||
}
|
||||
if (g_i8042_present) i8042_drain();
|
||||
}
|
||||
|
||||
/* Ring buffer: single producer (the ISR), single reader (i8042_pop_scancode(),
|
||||
|
||||
Reference in New Issue
Block a user