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
+16
View File
@@ -279,6 +279,14 @@ typedef struct {
#define USB_CLASS_MASS_STORAGE 0x08u
#define USB_SUBCLASS_SCSI 0x06u /* SCSI transparent command set */
#define USB_PROTOCOL_BOT 0x50u /* Bulk-Only Transport */
#define USB_IFACE_OFF_NUMBER 2u /* bInterfaceNumber */
/* A boot-protocol keyboard (HID 1.11 section 4): the class a BIOS can use,
* and what this driver reads keys from (2026-10-14, boards/BRINGUP.md A3). */
#define USB_CLASS_HID 0x03u
#define USB_SUBCLASS_HID_BOOT 0x01u
#define USB_PROTOCOL_HID_KEYBOARD 0x01u
#define USB_HID_REQ_SET_PROTOCOL 0x0Bu /* class, interface recipient */
#define USB_HID_PROTOCOL_BOOT 0u
/* Standard USB Endpoint descriptor field offsets (7 bytes, USB 2.0 spec
* table 9-13) -- found the same way as the Interface descriptor: walked
@@ -292,6 +300,8 @@ typedef struct {
#define USB_EP_ADDR_NUM_MASK 0x0Fu
#define USB_EP_ATTR_TYPE_MASK 0x03u
#define USB_EP_TYPE_BULK 0x02u
#define USB_EP_TYPE_INTERRUPT 0x03u
#define USB_EP_OFF_INTERVAL 6u /* bInterval */
/* Bulk-Only Transport Command Block Wrapper (USB Mass Storage Class Bulk-
* Only Transport spec, section 5.1) -- sent host-to-device on the bulk OUT
@@ -400,6 +410,10 @@ typedef struct {
#define XHCI_EVT_SLOT_ID(control) (((uint32_t)(control) >> 24) & 0xFFu)
#define XHCI_COMPLETION_CODE_SUCCESS 1u
#define XHCI_COMPLETION_CODE_STALL_ERROR 6u
#define XHCI_COMPLETION_CODE_SHORT_PACKET 13u
/* Transfer Event: the Endpoint ID (DCI) the transfer was on, bits 20:16 of
* the control word (xHCI 1.2 table 6-38). */
#define XHCI_EVT_EP_ID(control) (((uint32_t)(control) >> 16) & 0x1Fu)
/* Port Status Change Event TRB layout (xHCI 1.2 spec table 6-34):
* parameter[31:24] = Port ID (1-based, matches PORTSC array indexing
@@ -440,6 +454,8 @@ typedef struct {
#define XHCI_EP_CTX_TYPE_CONTROL_BIDI 4u /* EP0 */
#define XHCI_EP_CTX_TYPE_BULK_OUT 2u /* xHCI 1.2 spec table 6-9 */
#define XHCI_EP_CTX_TYPE_BULK_IN 6u
#define XHCI_EP_CTX_TYPE_INTERRUPT_IN 7u
#define XHCI_EP_CTX_INTERVAL_SHIFT 16 /* dword0: period 2^Interval x 125 us */
#define XHCI_EP_CTX_CERR_SHIFT 1
#define XHCI_EP_CTX_MAX_PACKET_SHIFT 16