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:
rajames
2026-10-14 14:04:33 -04:00
co-authored by Claude Opus 5.5
parent 080b7012ba
commit 739cb9c163
30 changed files with 82072 additions and 14 deletions
+53
View File
@@ -5770,3 +5770,56 @@ QEMU's controller has neither. **Still open:** one controller only -- the first
several (the SER5 has more than one) drives that one's ports alone; the stick and keyboard must be
in them until the driver runs several (`xhci_get_dev()` is a single controller throughout).
## XLI. A USB keyboard, a full one (`boards/BRINGUP.md` A3) -- and a hang on any PC without PS/2 (S3) (2026-10-14)
Captain Bob, 2026-10-14: "start on A3", then, mid-way: "use this keyboard as a target. for example
include caps lock, numlock, keypad and arrow ... Note for `later`, let's build keyboard VM's"
(recorded as a concept, nothing built).
**Built.**
- **The device** (`xhci.c`): a configuration whose interface is HID / boot / keyboard (3/1/1) is
taken as a keyboard, not refused as "not a drive": its first interrupt IN endpoint is configured
(`xhci_cmd_configure_hid_endpoint()` -- interval converted from bInterval for full/low and high
speed), `SET_CONFIGURATION`, then `SET_PROTOCOL(boot)` (a keyboard starts in report protocol),
then one Normal TRB at a time on its ring; each report is read and the next queued, deferred like
every doorbell in this driver. Unplugging forgets it. "USB keyboard ready" on the console.
- **The keys** (`usb_kbd.c`): a boot report says which keys are held; the difference from the one
before is the presses and releases, modifiers included, ErrorRollOver ignored. HID usage IDs
become Linux keycodes -- the namespace the PS/2 and virtio keyboards already use -- for a full
keyboard: the main block, F1-F12, Print Screen/Scroll Lock/Pause, the navigation block, arrows,
Num Lock and the whole keypad. `sk_key_event_poll()` (`keyboard_words.c`) reads them first, on
every ISA.
- **What a key means** (`repl.c`, every keyboard alike): Caps Lock and Num Lock are toggles (Num
Lock on at start); Caps Lock acts on letters, Shift reverses it; the keypad types `/ * - +` and
Enter always, digits and `.` with Num Lock on, nothing with it off (navigation -- this REPL's
line editor appends and backspaces only, so arrows and Home/End type nothing either); the 102nd
key types `\` `|`. The lock state goes to a USB keyboard's lights by `SET_REPORT` on its next
report.
- **Speed**: one report at a time, serviced only on the once-a-second idle beat, lost keys -- the
keyboard's own queue overflowed at typing speed (found by the test: of a typed line, only `1`
arrived). While a USB keyboard runs, the console now services the event ring on every timer
tick as it waits for a key.
**S3, found on the way -- a hang.** To have the USB keyboard be QEMU's only keyboard on amd64 the
test turned the PS/2 controller off (`-machine i8042=off`), and the kernel hung at boot just after
the I/O APIC: with no controller port 0x64 reads 0xFF, "output full" for ever, and `i8042_init()`'s
drain never ended (nor would `i8042_drain_stale()`'s). A legacy-free PC -- quite possibly the SER5 --
would have stopped there. Now: 0xFF means no controller, "i8042: no PS/2 controller -- keyboard by
USB only", nothing more done to it; every drain bounded. With a controller, unchanged.
**Verified.** amd64 with the USB keyboard the only keyboard, typed over QMP (`logs/20261014-135243`):
`1 2 + .` on the main keys and again on the keypad with keypad Enter -- `3` both times; Caps Lock,
then `s" ab" type` typed in lower case -- the line reads `S" AB" TYPE` and prints `AB`; Num Lock
off, keypad 7, Enter -- nothing typed. aarch64 and riscv64 (`-135420`, `-135629`): "USB keyboard
ready" and the same three results -- there QEMU also has a virtio keyboard, so those runs show the
USB keyboard configured, not which keyboard each key came through. The hang and its fix:
`-134410` (stops after the I/O APIC), `-135022` (boots). Standard acceptance, all three ISAs: POST
1052 passed, hash unchanged, PS/2 initialised as before on amd64 (`-135808`, `-135918`, `-140123`);
five identities hot-plugged, all attached (`-140240`). Test first? No: this time the test was
written after the code, its "before" being no keyboard support at all (`-134213`, where QMP could
not reach the device, records the enumeration working).
**Limits, not hidden:** the first xHCI controller only (§XL); a keyboard whose first interface is
not its keyboard is not used; no key repeat (a boot keyboard does not repeat, and nothing here
does it for it); the lights are sent on the next report after a lock key, not at once.