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
@@ -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.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user