From e7c01d111642a8f6d8375c572d7c26c7be3fa9a9 Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Mon, 16 Mar 2026 18:32:45 -0600 Subject: [PATCH] fix(firmware): route 0x10/0x21 output reports to subcommand handler MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Switch sends subcommands (IMU enable, SPI reads, vibration enable, player lights, etc.) inside 0x10 and 0x21 output reports at byte 10. The firmware extracted rumble data from these reports but never routed the subcommand to handle_feature_report() — it fell through the if-else chain silently. This caused the handshake to stall: the Switch kept retrying early subcommands (0x00-0x0f cycling) because it never received ACK replies. It never progressed to sending 0x40 (Toggle IMU), 0x10 (SPI Read), 0x48 (Enable Vibration), or 0x30 (Set Player Lights). The IMU was technically sending data, but the Switch never enabled it via subcommand 0x40, so the Switch's IMU processing was undefined. Fix: after extracting rumble from 0x10/0x21 reports, also pass them to handle_feature_report() so the subcommand at buffer[10] gets processed and ACK'd. Same fix applied to both tud_hid_set_report_cb and tud_hid_report_received_cb. --- switch_pro_driver.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/switch_pro_driver.cpp b/switch_pro_driver.cpp index b84af59..b026473 100644 --- a/switch_pro_driver.cpp +++ b/switch_pro_driver.cpp @@ -794,13 +794,18 @@ void tud_hid_set_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_ if (switchReportID == REPORT_OUTPUT_00) { // No-op, just acknowledge to clear any stalls. return; + } else if (switchReportID == REPORT_OUTPUT_10 || switchReportID == REPORT_OUTPUT_21) { + // 0x10/0x21 output reports carry rumble (bytes 2-9) AND a subcommand + // at byte 10. The Switch sends IMU enable (0x40), SPI reads (0x10), + // vibration enable (0x48), player lights (0x30), etc. via these reports. + queued_report_id = report_id; + handle_feature_report(switchReportID, switchReportSubID, buffer, bufsize); } else if (switchReportID == REPORT_FEATURE) { queued_report_id = report_id; handle_feature_report(switchReportID, switchReportSubID, buffer, bufsize); } else if (switchReportID == REPORT_CONFIGURATION) { queued_report_id = report_id; handle_config_report(switchReportID, switchReportSubID, buffer, bufsize); - } else { } } @@ -817,6 +822,9 @@ void tud_hid_report_received_cb(uint8_t instance, uint8_t report_id, uint8_t con } if (switchReportID == REPORT_OUTPUT_00) { return; + } else if (switchReportID == REPORT_OUTPUT_10 || switchReportID == REPORT_OUTPUT_21) { + queued_report_id = report_id; + handle_feature_report(switchReportID, switchReportSubID, buffer, bufsize); } else if (switchReportID == REPORT_FEATURE) { queued_report_id = report_id; handle_feature_report(switchReportID, switchReportSubID, buffer, bufsize);