From 8861fe92ebee8783178bb4b8d23948a2923ba769 Mon Sep 17 00:00:00 2001 From: Mikey Sklar Date: Sun, 30 Aug 2026 20:41:51 -0700 Subject: [PATCH] usb_hid: honor a boot protocol request that arrives after startup 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 #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. --- shared-module/usb_hid/Device.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/shared-module/usb_hid/Device.c b/shared-module/usb_hid/Device.c index 42f5790123e..d9fea3d562e 100644 --- a/shared-module/usb_hid/Device.c +++ b/shared-module/usb_hid/Device.c @@ -119,6 +119,16 @@ void common_hal_usb_hid_device_send_report(usb_hid_device_obj_t *self, uint8_t * mp_arg_validate_length(len, self->in_report_lengths[id_idx], MP_QSTR_report); + // The host can enter boot protocol mid-run, after the setup-time swap. + const uint8_t boot_device = usb_hid_boot_device(); + if (report_id != 0 && + self->usage_page == HID_USAGE_PAGE_DESKTOP && + ((boot_device == 1 && self->usage == HID_USAGE_DESKTOP_KEYBOARD) || + (boot_device == 2 && self->usage == HID_USAGE_DESKTOP_MOUSE)) && + tud_hid_get_protocol() == HID_PROTOCOL_BOOT) { + report_id = 0; + } + // Wait until interface is ready, timeout = 2 seconds uint64_t end_ticks = supervisor_ticks_ms64() + 2000; while ((supervisor_ticks_ms64() < end_ticks) && !tud_hid_ready()) {