#363·deskhop

Reports are misrouted when boot protocol is forced

Author: mglushkoCreated Aug 19, 2026Updated Aug 19, 2026

With Force Mouse Boot Mode enabled my mouse stops working. The pointer only moves while the left button is held down.

Repro:

  1. Plug in a mouse whose descriptor declares a report ID. Mine is a Keychron Ultra-Link 8K dongle (3434:d028), interface 0, mouse on report ID 1.
  2. Tick Force Mouse Boot Mode, save, replug the dongle.
  3. Pointer is dead. Hold the left button and it moves normally.

The cause is in tuh_hid_report_received_cb:

c
if (iface->uses_report_id || itf_protocol == HID_ITF_PROTOCOL_NONE) {
    uint8_t report_id = 0;
    if (iface->uses_report_id)
        report_id = report[0];

uses_report_id is set from the report descriptor at enumeration and never revised, and nothing here looks at iface->protocol. Once the device is switched to boot protocol it stops sending a report ID, so report[0] is data. On a mouse that byte is the buttons, so the report goes to report_handler[buttons]. Button 1 sets bit 0, which matches report ID 1, which is why holding it works.

Force KBD Boot Protocol has the same problem and is worse, because report[0] is then the modifier byte. On a keyboard that also declares consumer or system collections on low report IDs, keystrokes reach the wrong receiver. A Logitech G Pro Superlight 2 receiver binds handlers at 1, 3 and 4, so Ctrl+Shift would route to process_consumer_report and Alt to process_system_report.

One consequence worth noting: the HID_PROTOCOL_BOOT branch in extract_kbd_data is unreachable for any keyboard that declares a report ID, which is exactly the set of devices it is there for.

This looks like it has been present since v0.61, which is where the dispatch and both flags were introduced.

The fix I am running:

c
static inline bool report_carries_id(const hid_interface_t *iface) {
    return iface->uses_report_id && iface->protocol != HID_PROTOCOL_BOOT;
}

used in place of iface->uses_report_id in both spots. extract_kbd_data and extract_report_values already check HID_PROTOCOL_BOOT before they consult uses_report_id, so this just applies the same rule at routing. Nothing changes outside boot protocol. Confirmed on hardware: pointer behaves normally with the box still ticked.

Note that the scroll wheel does stop working in boot mode, but that is the boot report being three bytes with no wheel in it, not related to this.

Happy to open a PR if useful.