Skip to content

usb_hid: honor a boot protocol request that arrives after startup - #11282

Open
mikeysklar wants to merge 1 commit into
adafruit:mainfrom
mikeysklar:fix-1136-boot-hid
Open

usb_hid: honor a boot protocol request that arrives after startup#11282
mikeysklar wants to merge 1 commit into
adafruit:mainfrom
mikeysklar:fix-1136-boot-hid

Conversation

@mikeysklar

@mikeysklar mikeysklar commented Sep 1, 2026

Copy link
Copy Markdown
Collaborator

What

send_report() now drops the report ID while the host has the interface in boot protocol.

Why

I went looking for old issues. #1136 is eight years old, 69 comments.

The bug usb_hid_setup_devices() swaps in the boot device, whose report ID is 0, but runs only from usb_setup_with_vm() at VM start
Effect A SET_PROTOCOL(boot) arriving while code.py runs is missed until the next restart, so reports keep the prefix while the host reads 8-byte boot reports
Symptom The 0x01 lands in the modifier byte, a held left Ctrl. Matches bitboy85's get_boot_device() returning 1 alongside a phantom Ctrl

Hardware tested

Metro RP2040 on Linux. Not tested: real BIOS, GRUB, KVM, Windows, non-RP2 ports, zephyr-cp.

How I tested it

Two devices, so report IDs stay in use:

usb_hid.enable((usb_hid.Device.KEYBOARD, usb_hid.Device.CONSUMER_CONTROL), boot_device=1)

code.py sent 'a' once per second, read from USB DPRAM over SWD.

'a' press report Baseline d897c15f24 This branch
Report protocol, before 01 00 00 04 ..., 9 bytes 01 00 00 04 ..., 9 bytes
Boot protocol, after unchanged, 9 bytes 00 00 04 ..., 8 bytes
Default config, no boot.py IDs 1/2/3 byte-identical to baseline

Scope

Item Status
The host request Simulated by setting TinyUSB's protocol_mode over SWD, not sent by a real host
BIOS, GRUB, KVM Not tested, none available
macOS pre-boot Unrelated. An M2 Startup Options screen behaves the same with and without this change, so the macOS reports in #1136 have another cause
zephyr-cp Has the same defect, unchanged here. No available board enables its USB device stack. Happy to mirror it
Keyboard LED reports Still dropped in boot protocol with multiple devices. IN direction only
Dropped from this PR Report-ID-less descriptor variants. The hand-rolled descriptors in the thread would mask this timing bug too, so they are not evidence that a host skipped SET_PROTOCOL

AI assistance

Written with an LLM agent (Claude). I ran the boards myself.

Prompt Root-cause #1136 from the thread and source, fix it, verify on hardware with controls
Numbers above From the SWD instrumentation, not the model

@dhalbert

dhalbert commented Sep 1, 2026

Copy link
Copy Markdown
Collaborator

I worked on this a lot years ago, as you can tell. I am surprised you need a new report descriptor without the Report ID slot. Part of the point of boot keyboard and boot mouse is that the descriptor sent by the device is ignored. Instead the host assumes the standard descriptors, as described in https://www.usb.org/sites/default/files/hid1_12.pdf in the boot devices sections

So is this a work-around for something that Macs are not doing properly in their "BIOS"? I think the original code solved the issue on PC's.

@mikeysklar

Copy link
Copy Markdown
Collaborator Author

You're right about the descriptor. The bug is timing: the swap runs only at VM start, so a later SET_PROTOCOL is missed. Not Mac specific. I'll cut it to that.

usb_hid_setup_devices() swaps in the boot keyboard or mouse, whose report
ID is 0, but it only runs from usb_setup_with_vm() at VM start. A
SET_PROTOCOL(boot) arriving while code.py is already running is not acted
on until the next VM restart, so reports keep their report-ID prefix while
the host is reading them as 8-byte boot reports. That matches bitboy85's
report in adafruit#1136: get_boot_device() returns 1 yet a phantom left Ctrl is
held, because the 0x01 prefix lands in the modifier byte.

Check tud_hid_get_protocol() in send_report() instead, and drop the report
ID while the host has the interface in boot protocol.

Measured on a Metro RP2040, with the host request simulated by setting
TinyUSB's protocol_mode over SWD: before, reports stay 9 bytes after the
switch; after, they become 8 bytes on the next send. Default HID
configuration is unchanged.
@mikeysklar mikeysklar changed the title usb_hid: drop the report-ID prefix for a sole boot keyboard or mouse usb_hid: honor a boot protocol request that arrives after startup Sep 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants