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
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
StarForth — Steady-State Virtual Machine Runtime
|
||||
Copyright (c) 2023–2026 Robert A. James. All rights reserved.
|
||||
Licensed under the StarForth License, Version 1.0.
|
||||
*/
|
||||
|
||||
/**
|
||||
* usb_kbd.h - a USB boot-protocol keyboard's keys, as key events
|
||||
* (2026-10-14, boards/BRINGUP.md A3; usb_kbd.c).
|
||||
*
|
||||
* The xHCI driver hands each 8-byte boot report here; the keys pressed
|
||||
* and released since the report before come out as Linux input keycodes,
|
||||
* the namespace every keyboard in this kernel already speaks
|
||||
* (sk_key_event_poll(), keyboard_words.c).
|
||||
*/
|
||||
|
||||
#ifndef STARKERNEL_USB_KBD_H
|
||||
#define STARKERNEL_USB_KBD_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/* One boot report: byte 0 the modifier bits, byte 1 reserved, bytes 2-7
|
||||
* up to six keys held (HID usage IDs). `len` below 3 is ignored. */
|
||||
void usb_kbd_report(const uint8_t *report, uint32_t len);
|
||||
|
||||
/* Forgets the keys held: the keyboard has gone. */
|
||||
void usb_kbd_reset(void);
|
||||
|
||||
/* The next key event: 1 with *keycode (Linux) and *pressed (1/0), or 0. */
|
||||
int usb_kbd_pop_event(uint16_t *keycode, int *pressed);
|
||||
|
||||
/* The lock lights (boot output report bits, HID 1.11 appendix B.1). The
|
||||
* console says which locks are on; the xHCI driver sends them to the
|
||||
* keyboard with SET_REPORT whenever they differ from what it last sent. */
|
||||
#define USB_KBD_LED_NUM 0x01u
|
||||
#define USB_KBD_LED_CAPS 0x02u
|
||||
#define USB_KBD_LED_SCROLL 0x04u
|
||||
void usb_kbd_set_leds(uint8_t leds);
|
||||
/* 1 and the lights to send if they changed since last sent, else 0. */
|
||||
int usb_kbd_leds_pending(uint8_t *leds);
|
||||
/* The driver has sent these. */
|
||||
void usb_kbd_leds_sent(uint8_t leds);
|
||||
|
||||
#endif /* STARKERNEL_USB_KBD_H */
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user