feat(usb): every xHCI controller is driven, not only the first

boards/BRINGUP.md A1, FABRIC-3 SXLII.  xhci.c keeps a table of controllers
(up to 8) and services each; kernel_main brings up index 0, 1, ... until
there is none.  repl.c's and blkio_usb.c's per-slot state is per
controller now (a slot ID is a controller's own), and each USB keyboard
keeps its own last report and lights; an unplugged keyboard releases what
it held.

Test first: keyboard and identity on a second controller behind a bridge,
Zuse on the first -- before, only Zuse; after, all three, on all three
ISAs.  Hot-plug and unplug on the second controller.  Acceptance unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
rajames
2026-10-14 14:27:38 -04:00
co-authored by Claude Opus 5.5
parent 739cb9c163
commit e729c64ba3
27 changed files with 81701 additions and 260 deletions
+2 -2
View File
@@ -23,9 +23,9 @@ None has been fixed: each needs your word.
| # | What | Where | Effect on hardware |
|---|---|---|---|
| A1 | **Fixed in part 2026-10-14.** The USB controller was matched by QEMU's own IDs (`1B36:000D`) on PCI bus 0 alone. Now: found by its PCI class (`0C/03/30`) on any bus, slot and function; its register window mapped at the size its BAR reports; taken from the firmware through the USB Legacy Support capability (SMIs off); and 64-byte contexts (`CSZ=1`, common on real controllers) laid out instead of refused. **Still open:** only the first controller found is used -- a PC with several (the SER5 has more than one) drives only that one's ports, so a stick or keyboard has to be in one of them. | `kernel/src/usb/xhci.c`, `kernel/src/pci/pci.c`; `FABRIC-3.md` XL | Rehearsed in QEMU with another vendor's controller behind a bridge, all three ISAs. Not rehearsable: the firmware handoff and 64-byte contexts (QEMU's controller has neither). |
| A1 | **Fixed 2026-10-14.** The USB controller was matched by QEMU's own IDs (`1B36:000D`) on PCI bus 0 alone. Now: found by its PCI class (`0C/03/30`) on any bus, slot and function; its register window mapped at the size its BAR reports; taken from the firmware through the USB Legacy Support capability (SMIs off); and 64-byte contexts (`CSZ=1`, common on real controllers) laid out instead of refused. Every controller is driven, up to 8 (since 2026-10-14, `FABRIC-3.md` XLII): a stick or keyboard may be in any port. | `kernel/src/usb/xhci.c`, `kernel/src/pci/pci.c`; `FABRIC-3.md` XL | Rehearsed in QEMU with another vendor's controller behind a bridge, all three ISAs. Not rehearsable: the firmware handoff and 64-byte contexts (QEMU's controller has neither). |
| A2 | **No prompt without a login.** The production default, `EMERGENCY_CONSOLE_ENABLED=n`, keeps the system headless until an identity logs in by thumbdrive. | `Kconfig.heartbeat:68`; `FABRIC-3.md` VIII.1 | With A1, the v3 system never shows a prompt on hardware. A bring-up build must set `EMERGENCY_CONSOLE_ENABLED=1`. |
| A3 | **Fixed 2026-10-14.** A USB boot-protocol keyboard is driven (xHCI interrupt endpoint, `SET_PROTOCOL(boot)`), its reports become the same key events as the PS/2 and virtio keyboards, and the console treats a full keyboard as one: Caps Lock and Num Lock as toggles shown on the keyboard's lights, the keypad as digits or navigation, the arrows and navigation block mapped (the line editor itself still has no cursor movement). While a USB keyboard runs, the console services its reports every timer tick. | `kernel/src/usb/usb_kbd.c`, `kernel/src/usb/xhci.c`, `kernel/src/repl.c`; `FABRIC-3.md` XLI | Rehearsed in QEMU on all three ISAs; on amd64 with the USB keyboard the only keyboard. Limits: the first controller found (A1); a keyboard whose first interface is not the keyboard is not used; no key repeat. |
| A3 | **Fixed 2026-10-14.** A USB boot-protocol keyboard is driven (xHCI interrupt endpoint, `SET_PROTOCOL(boot)`), its reports become the same key events as the PS/2 and virtio keyboards, and the console treats a full keyboard as one: Caps Lock and Num Lock as toggles shown on the keyboard's lights, the keypad as digits or navigation, the arrows and navigation block mapped (the line editor itself still has no cursor movement). While a USB keyboard runs, the console services its reports every timer tick. | `kernel/src/usb/usb_kbd.c`, `kernel/src/usb/xhci.c`, `kernel/src/repl.c`; `FABRIC-3.md` XLI | Rehearsed in QEMU on all three ISAs; on amd64 with the USB keyboard the only keyboard. Limits: a keyboard whose first interface is not the keyboard is not used; no key repeat. Several keyboards, on any controllers, each its own. |
| A4 | **The only disk driver is virtio.** | `kernel/src/virtio/virtio_blk.c`; `kernel_main.c:493` | On hardware Artemis finds no disk ("Artemis: no virtio-blk disk"). What the fleet then does has not been tried even in QEMU: see step 3.3. |
| A5 | **Entropy:** RDRAND on amd64 only. aarch64 and riscv64 have virtio-rng alone. | `kernel/src/rng/rng.c` | Pi 5 and Mars print the "no entropy backend available" warning; anything that needs random bytes is refused. Minting needs them. |
| A6 | **The board images cannot build the v4 node.** `make boot_image` has no `STARFORTH_V4` path. | root `Makefile`, `kernel/Makefile` | Ruled 2026-10-13: both systems, switchable. Not built yet. |
+30
View File
@@ -5823,3 +5823,33 @@ not reach the device, records the enumeration working).
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.
## XLII. Every USB controller, not the first (`boards/BRINGUP.md` A1, the rest) (2026-10-14)
Captain Bob, 2026-10-14: "do multiple controllers" -- §XL's open item. A PC has several xHCI
controllers, each with its own ports; only the first found was driven, so a stick or keyboard in
another's ports was never seen.
**What assumed one controller, and now does not:**
- `xhci.c`: one latched `g_xhci_dev` → a table of up to `XHCI_MAX_CONTROLLERS` (8);
`xhci_poll_events()` services each in turn; `xhci_get_dev()` → `xhci_dev_count()` /
`xhci_dev_at(i)`; `xhci_find_and_map(dev, index)` finds the index-th controller (1 when there is
none). Everything per device was already per controller (`msc_slots`, the rings, the connect
state machine, the BOT state).
- `kernel_main.c`: brings up controller 0, 1, ... until there is none; one that fails is skipped.
- `repl.c` and `blkio_usb.c`: their per-slot arrays were indexed by slot ID alone -- but a slot ID is
a controller's own, so slot 1 on two controllers would have been one entry. Now one set per
controller (`g_usb_ctrl[]`; `blkio_usb.c` finds its set by the controller's pointer); the attach
acknowledgement looks the device up across all of them.
- `usb_kbd.c`: one "previous report" and one "lights sent" for every keyboard would have diffed two
keyboards against each other. Both are each keyboard's own now (in its slot); a keyboard that
goes releases whatever it still held (no Shift stuck down after unplugging).
**Verified.** Test first: amd64, no PS/2, Zuse's drive on the usual controller, a second (NEC,
behind a root port) with the USB keyboard and identity `00` -- before, only Zuse attached
(`logs/20261014-140826`); after, the keyboard types (`3`, `3`, `AB`), `00` attaches, Zuse is confirmed
(`-141442`) -- both controllers with a device in slot 1, the collision case. Four identities
hot-plugged onto the second controller all attach, and unplugging one detaches that one
(`-141621`). aarch64 and riscv64, the same two-controller boot: all the same results (`-141756`,
`-141953`). Standard acceptance, all three: POST 1052, hash unchanged (`-142120`, `-142237`,
`-142449`); five identities hot-plugged on the first controller, all attached (`-142606`).
+11 -9
View File
@@ -20,25 +20,27 @@
#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);
* up to six keys held (HID usage IDs). `len` below 3 is ignored. `prev`
* is that keyboard's own last report, 8 bytes, kept by the caller -- each
* keyboard is compared with itself, never with another. */
void usb_kbd_report(uint8_t prev[8], const uint8_t *report, uint32_t len);
/* Forgets the keys held: the keyboard has gone. */
void usb_kbd_reset(void);
/* The keyboard has gone: every key it still held is released, and its
* last report forgotten -- no Shift stays down after it. */
void usb_kbd_forget(uint8_t prev[8]);
/* 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
* console says which locks are on; the xHCI driver sends them to each
* 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);
/* The lights every keyboard should show. Each keyboard's driver state
* holds what that keyboard was last sent. */
uint8_t usb_kbd_leds_wanted(void);
#endif /* STARKERNEL_USB_KBD_H */
+23 -16
View File
@@ -147,6 +147,8 @@ typedef struct {
uint32_t hid_ring_cycle;
uint32_t hid_ring_enq;
uint8_t *hid_buf;
uint8_t hid_prev[8]; /* this keyboard's last report (usb_kbd.c) */
uint8_t hid_leds_sent; /* lights last sent to it; 0xFF = none yet */
uint8_t bot_msc_attach_pending;
uint8_t bot_msc_attached;
@@ -419,17 +421,24 @@ typedef struct {
} xhci_dev_t;
/*
* xhci_find_and_map — locate the xHCI controller on PCI bus 0, enable it
* (I/O+MEM+bus-master), map its BAR0 MMIO region, and
* fill in the four register-region pointers in *dev.
* xhci_find_and_map — locate the index-th xHCI controller (0 = the first
* found, by PCI class, on any bus), enable it
* (I/O+MEM+bus-master), take it from the firmware, map
* its BAR0 MMIO region, and fill in the four
* register-region pointers in *dev. Every controller
* is driven since 2026-10-14 (kernel_main.c brings up
* index 0, 1, ... until there is none), each its own
* xhci_dev_t.
*
* dev must point to a zero-initialised xhci_dev_t.
*
* Returns 0 on success.
* Returns -1 if no xHCI device was found on the PCI bus.
* Returns -2 if the BAR0 mapping failed.
* Returns 1 if there is no index-th controller (there are fewer).
* Returns -1 if this one's BAR0 cannot be read, -2 if it cannot be mapped
* (the next controller may still be fine).
*/
int xhci_find_and_map(xhci_dev_t *dev);
#define XHCI_MAX_CONTROLLERS 8u
int xhci_find_and_map(xhci_dev_t *dev, unsigned index);
/*
* xhci_bringup — reset the controller, allocate and program the DCBAA,
@@ -456,17 +465,15 @@ int xhci_find_and_map(xhci_dev_t *dev);
int xhci_bringup(xhci_dev_t *dev);
/*
* xhci_get_dev — return the module-static xhci_dev_t* xhci_poll_events()
* itself reads (only one controller is supported), or NULL
* if xhci_bringup() has not completed successfully yet.
*
* Milestone 2h: callers outside this driver (sk_repl_idle(), eventually
* the block-subsystem attach glue) have no other way to reach the device
* handle -- every earlier caller of this driver's API already had one in
* hand (kernel_main.c's own local xhci_dev_t), which doesn't help code
* that only runs later, on a hotplug event it wasn't the one to observe.
* xhci_dev_count / xhci_dev_at — the controllers xhci_bringup() has
* brought up, in the order found: how many, and the i-th
* (NULL past the end). Callers outside this driver
* (sk_repl_idle(), the block-subsystem attach glue) reach
* each controller this way. Replaced xhci_get_dev(), which
* returned the one controller there used to be (2026-10-14).
*/
xhci_dev_t *xhci_get_dev(void);
uint32_t xhci_dev_count(void); /* controllers brought up */
xhci_dev_t *xhci_dev_at(uint32_t i); /* the i-th, or NULL */
/*
* xhci_msc_slot_for — bounds-checked lookup into dev->msc_slots[slot_id]
+10 -3
View File
@@ -793,9 +793,16 @@ static void kernel_main_deep(BootInfo *boot_info) {
* xhci_poll_events()'s own doc comment for why this driver is polled
* rather than interrupt-driven. */
{
static xhci_dev_t xhci_dev;
(void)(xhci_find_and_map(&xhci_dev) == 0 &&
xhci_bringup(&xhci_dev) == 0);
/* Every xHCI controller, not only the first (2026-10-14): a PC has
* several, each with its own ports, and a stick or keyboard may be
* in any of them. One that fails is skipped; the next is tried. */
static xhci_dev_t xhci_devs[XHCI_MAX_CONTROLLERS];
unsigned xi;
for (xi = 0; xi < XHCI_MAX_CONTROLLERS; xi++) {
int rc = xhci_find_and_map(&xhci_devs[xi], xi);
if (rc == 1) break;
if (rc == 0) (void)xhci_bringup(&xhci_devs[xi]);
}
}
/* FABRIC-0.md item 4.4g (decided 2026-08-11): console_fb_init() moved here,
+202 -186
View File
File diff suppressed because it is too large Load Diff
+19 -6
View File
@@ -30,20 +30,33 @@ typedef struct {
* controller's own max_slots, allocated once on first use here since this
* file has no bringup-time hook of its own to do it earlier). Indexed
* directly by xHCI slot ID, index 0 unused, matching msc_slots[]. */
static BlkioUsbState *g_usb_blk_slots = NULL;
static uint32_t g_usb_blk_slot_count = 0;
/* One registry per controller (2026-10-14: every xHCI controller is now
* driven, and a slot ID is a controller's own -- slot 1 on two
* controllers is two devices). Found by the controller's own pointer. */
static struct {
xhci_dev_t *xdev;
BlkioUsbState *slots;
uint32_t count;
} g_usb_blk[XHCI_MAX_CONTROLLERS];
static BlkioUsbState *usb_blk_state_for(xhci_dev_t *xdev, uint32_t slot_id) {
uint32_t c;
if (!xdev || slot_id < 1 || slot_id > xdev->max_slots) return NULL;
if (!g_usb_blk_slots || g_usb_blk_slot_count < xdev->max_slots + 1) {
for (c = 0; c < XHCI_MAX_CONTROLLERS; c++) {
if (g_usb_blk[c].xdev == xdev || !g_usb_blk[c].xdev) break;
}
if (c == XHCI_MAX_CONTROLLERS) return NULL;
if (!g_usb_blk[c].slots || g_usb_blk[c].count < xdev->max_slots + 1) {
size_t bytes = (size_t)(xdev->max_slots + 1) * sizeof(BlkioUsbState);
BlkioUsbState *fresh = (BlkioUsbState *)kmalloc_aligned(bytes, 64);
if (!fresh) return NULL;
memset(fresh, 0, bytes);
g_usb_blk_slots = fresh;
g_usb_blk_slot_count = xdev->max_slots + 1;
g_usb_blk[c].xdev = xdev;
g_usb_blk[c].slots = fresh;
g_usb_blk[c].count = xdev->max_slots + 1;
}
return &g_usb_blk_slots[slot_id];
return &g_usb_blk[c].slots[slot_id];
}
static int usb_blk_open(blkio_dev_t *dev, const blkio_params_t *p) {
+10 -21
View File
@@ -62,11 +62,8 @@ static const uint8_t modifier_key[8] = { 29, 42, 56, 125, 97, 54, 100, 126 };
#define KBD_EVENTS 64u
static struct { uint16_t keycode; uint8_t pressed; } g_events[KBD_EVENTS];
static uint32_t g_head, g_count;
static uint8_t g_prev[8];
/* Num Lock on to start, matching the console (repl.c); 0xFF = nothing
* sent yet, so a new keyboard is always told. */
/* Num Lock on to start, matching the console (repl.c). */
static uint8_t g_leds_want = USB_KBD_LED_NUM;
static uint8_t g_leds_sent = 0xFFu;
static void push(uint16_t keycode, int pressed)
{
@@ -89,7 +86,7 @@ static int held(const uint8_t *keys, uint32_t n, uint8_t usage)
return 0;
}
void usb_kbd_report(const uint8_t *report, uint32_t len)
void usb_kbd_report(uint8_t prev[8], const uint8_t *report, uint32_t len)
{
uint8_t now[8] = {0};
uint32_t i, n;
@@ -101,34 +98,26 @@ void usb_kbd_report(const uint8_t *report, uint32_t len)
for (i = 0; i < 8; i++) {
uint8_t bit = (uint8_t)(1u << i);
if ((now[0] & bit) != (g_prev[0] & bit)) push(modifier_key[i], (now[0] & bit) != 0);
if ((now[0] & bit) != (prev[0] & bit)) push(modifier_key[i], (now[0] & bit) != 0);
}
n = 6;
for (i = 2; i < 8; i++) /* released first, then pressed */
if (g_prev[i] > 0x01 && !held(now + 2, n, g_prev[i])) push(key_for(g_prev[i]), 0);
if (prev[i] > 0x01 && !held(now + 2, n, prev[i])) push(key_for(prev[i]), 0);
for (i = 2; i < 8; i++)
if (now[i] > 0x01 && !held(g_prev + 2, n, now[i])) push(key_for(now[i]), 1);
if (now[i] > 0x01 && !held(prev + 2, n, now[i])) push(key_for(now[i]), 1);
for (i = 0; i < 8; i++) g_prev[i] = now[i];
for (i = 0; i < 8; i++) prev[i] = now[i];
}
void usb_kbd_reset(void)
void usb_kbd_forget(uint8_t prev[8])
{
uint32_t i;
for (i = 0; i < 8; i++) g_prev[i] = 0;
g_leds_sent = 0xFFu;
static const uint8_t none[8] = {0};
usb_kbd_report(prev, none, 8); /* everything held, released */
}
void usb_kbd_set_leds(uint8_t leds) { g_leds_want = leds; }
int usb_kbd_leds_pending(uint8_t *leds)
{
if (g_leds_want == g_leds_sent) return 0;
*leds = g_leds_want;
return 1;
}
void usb_kbd_leds_sent(uint8_t leds) { g_leds_sent = leds; }
uint8_t usb_kbd_leds_wanted(void) { return g_leds_want; }
int usb_kbd_pop_event(uint16_t *keycode, int *pressed)
{
+33 -17
View File
@@ -88,15 +88,14 @@ static void xhci_take_from_bios(xhci_dev_t *dev)
}
}
int xhci_find_and_map(xhci_dev_t *dev)
int xhci_find_and_map(xhci_dev_t *dev, unsigned index)
{
uint64_t map_size;
if (!dev) return -1;
if (pci_find_class(XHCI_PCI_CLASS, XHCI_PCI_SUBCLASS, XHCI_PCI_PROG_IF, 0, &dev->pci) != 0) {
log_message(LOG_DEBUG, "xhci: no xHCI controller on any PCI bus");
return -1;
if (pci_find_class(XHCI_PCI_CLASS, XHCI_PCI_SUBCLASS, XHCI_PCI_PROG_IF, index, &dev->pci) != 0) {
return 1; /* no index-th controller: there are fewer */
}
xhci_log_hex32("xhci: controller vendor/device=",
((uint32_t)dev->pci.vendor_id << 16) | dev->pci.device_id);
@@ -188,10 +187,12 @@ static void xhci_log_hex32(const char *label, uint32_t val)
log_message(LOG_DEBUG, "%s%s", label, buf);
}
/* Only one controller is supported (matches xhci_dev_t's own doc comment);
* latched at the end of a successful xhci_bringup() for
* xhci_poll_events()'s use. */
static xhci_dev_t *g_xhci_dev = NULL;
/* Every controller brought up, in the order found (2026-10-14: a PC has
* several, and a stick or keyboard may be in any of their ports -- until
* then only the first was ever driven). Latched at the end of a
* successful xhci_bringup(); xhci_poll_events() services each in turn. */
static xhci_dev_t *g_xhci_devs[XHCI_MAX_CONTROLLERS];
static uint32_t g_xhci_dev_count;
/* Forward declaration -- xhci_bringup() below issues one Enable Slot as a
* command-ring smoke test; the implementation lives after xhci_bringup()
@@ -428,7 +429,7 @@ int xhci_bringup(xhci_dev_t *dev)
? "xhci: context size = 64 bytes"
: "xhci: context size = 32 bytes");
g_xhci_dev = dev;
if (g_xhci_dev_count < XHCI_MAX_CONTROLLERS) g_xhci_devs[g_xhci_dev_count++] = dev;
/* Catch a device already connected at launch -- xhci_poll_events() is
* purely event-ring-driven (Port Status Change events only), and a
@@ -442,9 +443,14 @@ int xhci_bringup(xhci_dev_t *dev)
return 0;
}
xhci_dev_t *xhci_get_dev(void)
uint32_t xhci_dev_count(void)
{
return g_xhci_dev;
return g_xhci_dev_count;
}
xhci_dev_t *xhci_dev_at(uint32_t i)
{
return i < g_xhci_dev_count ? g_xhci_devs[i] : NULL;
}
xhci_msc_slot_t *xhci_msc_slot_for(xhci_dev_t *dev, uint32_t slot_id)
@@ -1510,7 +1516,7 @@ static int xhci_ep0_hid_set_leds(xhci_dev_t *dev, uint32_t slot_id, uint8_t leds
XHCI_TRB_CONTROL_DIR_IN);
ms->transfer_purpose = XHCI_XFER_HID_SET_LEDS;
usb_kbd_leds_sent(leds); /* once asked: a keyboard that refuses is not asked again and again */
ms->hid_leds_sent = leds; /* once asked: a keyboard that refuses is not asked again and again */
dev->doorbell[slot_id] = XHCI_DB_TARGET(1);
return 0;
}
@@ -1859,9 +1865,17 @@ static void xhci_handle_bulk_stall(xhci_dev_t *dev, uint32_t slot_id,
static uint32_t g_xhci_keyboards;
int xhci_keyboard_running(void) { return g_xhci_keyboards != 0; }
static void xhci_poll_one(xhci_dev_t *dev);
void xhci_poll_events(void)
{
xhci_dev_t *dev = g_xhci_dev;
uint32_t i;
for (i = 0; i < g_xhci_dev_count; i++) xhci_poll_one(g_xhci_devs[i]);
}
/* One controller's Event Ring -- everything below, per controller. */
static void xhci_poll_one(xhci_dev_t *dev)
{
if (!dev) return;
uint32_t evt_processed = 0;
@@ -2004,7 +2018,7 @@ void xhci_poll_events(void)
{
xhci_msc_slot_t *dms2 = xhci_msc_slot_for(dev, dev->pending_disable_slot_id);
if (dms2) {
if (dms2->hid_kbd) usb_kbd_reset(); /* A3: no key stays held */
if (dms2->hid_kbd) usb_kbd_forget(dms2->hid_prev); /* A3: no key stays held */
if (dms2->hid_running && g_xhci_keyboards) g_xhci_keyboards--;
dms2->hid_kbd = 0;
dms2->hid_running = 0;
@@ -2134,7 +2148,7 @@ void xhci_poll_events(void)
uint32_t asked = xms->hid_max_packet ? xms->hid_max_packet : 8u;
uint32_t left = trb->status & 0xFFFFFFu;
if (asked > 64u) asked = 64u;
usb_kbd_report(xms->hid_buf, left < asked ? asked - left : 0u);
usb_kbd_report(xms->hid_prev, xms->hid_buf, left < asked ? asked - left : 0u);
xms->next_action = XHCI_NEXT_ACTION_HID_POLL;
} else {
console_println("USB keyboard: report failed -- keyboard stopped");
@@ -2361,7 +2375,8 @@ void xhci_poll_events(void)
case XHCI_XFER_HID_SET_PROTOCOL: {
xms->hid_running = 1;
g_xhci_keyboards++;
usb_kbd_reset();
memset(xms->hid_prev, 0, sizeof(xms->hid_prev));
xms->hid_leds_sent = 0xFFu; /* tell it the lights at once */
console_println("USB keyboard ready");
xms->next_action = XHCI_NEXT_ACTION_HID_POLL;
break;
@@ -2669,7 +2684,8 @@ void xhci_poll_events(void)
uint8_t leds;
if (!nms->hid_running) break;
xhci_hid_queue_report(dev, next_slot_id, nms);
if (nms->transfer_purpose == XHCI_XFER_NONE && usb_kbd_leds_pending(&leds))
leds = usb_kbd_leds_wanted();
if (nms->transfer_purpose == XHCI_XFER_NONE && leds != nms->hid_leds_sent)
(void)xhci_ep0_hid_set_leds(dev, next_slot_id, leds);
break;
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff