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
+30 -2
View File
@@ -61,7 +61,9 @@ typedef enum {
XHCI_XFER_BOT_DATA_OUT,
XHCI_XFER_CSW_RECEIVED,
XHCI_XFER_CLEAR_HALT,
XHCI_XFER_BOT_RESET
XHCI_XFER_BOT_RESET,
XHCI_XFER_HID_SET_PROTOCOL,
XHCI_XFER_HID_SET_LEDS
} xhci_transfer_purpose_t;
typedef enum {
@@ -78,7 +80,9 @@ typedef enum {
XHCI_NEXT_ACTION_BOT_SEND_READ_CAPACITY10,
XHCI_NEXT_ACTION_BOT_SEND_WRITE10,
XHCI_NEXT_ACTION_CLEAR_HALT,
XHCI_NEXT_ACTION_BOT_RESET
XHCI_NEXT_ACTION_BOT_RESET,
XHCI_NEXT_ACTION_HID_SET_PROTOCOL,
XHCI_NEXT_ACTION_HID_POLL
} xhci_next_action_t;
/* FABRIC-3.md §VII (2026-09-05): per-slot device state, formerly scalar
@@ -129,6 +133,21 @@ typedef struct {
* per attached device now (was a single flag per kind, one device
* assumed) -- sk_repl_idle() scans every slot for these each idle
* tick, cheap and bounded by max_slots. */
/* A boot-protocol keyboard (2026-10-14, boards/BRINGUP.md A3): its
* interrupt IN endpoint, a ring for it, the 8-byte report buffer one
* Normal TRB at a time reads into, and the report before it (keys are
* found pressed or released by comparing the two -- usb_kbd.c). */
uint8_t hid_kbd;
uint8_t hid_running;
uint8_t hid_iface;
uint8_t hid_ep_addr;
uint16_t hid_max_packet;
uint8_t hid_interval;
xhci_trb_t *hid_ring;
uint32_t hid_ring_cycle;
uint32_t hid_ring_enq;
uint8_t *hid_buf;
uint8_t bot_msc_attach_pending;
uint8_t bot_msc_attached;
uint8_t bot_msc_detach_pending;
@@ -583,6 +602,15 @@ int xhci_cmd_address_device(xhci_dev_t *dev, uint32_t slot_id,
*/
int xhci_cmd_configure_endpoint(xhci_dev_t *dev, uint32_t slot_id);
/* A boot keyboard (2026-10-14, boards/BRINGUP.md A3): Configure Endpoint
* for its one interrupt IN endpoint, and SET_PROTOCOL(boot) on EP0 once
* it is configured. Deferred dispatch only, like the rest. */
int xhci_cmd_configure_hid_endpoint(xhci_dev_t *dev, uint32_t slot_id);
int xhci_ep0_hid_set_protocol(xhci_dev_t *dev, uint32_t slot_id);
/* 1 while a boot keyboard is running: the console then services the event
* ring every timer tick while it waits for a key. */
int xhci_keyboard_running(void);
/*
* xhci_cmd_reset_endpoint — submit a Reset Endpoint command TRB for
* slot_id's endpoint `ep_id` (a bEndpointAddress,