From 7414b303a02cfb0814329a0d8ef46ad0749e578b Mon Sep 17 00:00:00 2001 From: Felix Date: Wed, 29 Apr 2026 20:48:49 +0200 Subject: [PATCH 1/2] refactor: optimize USB polling rate and fix report race condition - Increased HID polling rate from 250Hz (4ms) to 1000Hz (1ms) in the configuration descriptor to match wired DualSense performance. - Fixed a potential race condition between Bluetooth data callbacks and the USB task using pico/critical_section. - Implemented a 'dirty flag' mechanism to prevent sending redundant duplicate reports, ensuring the dongle only transmits to the host when fresh Bluetooth data is received. - Added a fallback retry logic in interrupt_loop to ensure data integrity if a USB report fails to queue. --- src/main.cpp | 47 +++++++++++++++++++++++++++++++++++++++++-- src/usb_descriptors.c | 4 ++-- 2 files changed, 47 insertions(+), 4 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index a60b55e..1208b48 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -12,6 +12,9 @@ #include "hardware/vreg.h" #include "pico/cyw43_arch.h" +// Pico SDK speciifically for waiting on conditions +#include "pico/critical_section.h" + int reportSeqCounter = 0; uint8_t packetCounter = 0; @@ -26,10 +29,36 @@ uint8_t interrupt_in_data[63] = { 0x53, 0x9f, 0x28, 0x35, 0xa5, 0xa8, 0x0c, 0x8b }; +critical_section_t report_cs; +volatile bool report_dirty = false; + void interrupt_loop() { if (!tud_hid_ready()) return; - if (!tud_hid_report(0x01, interrupt_in_data, 63)) { - printf("[USBHID] tud_hid_report error\n"); + + bool should_send = false; + // Local buffer to hold the report data while we prepare it to send. + uint8_t safe_report[63]; + + + critical_section_enter_blocking(&report_cs); + if (report_dirty) { + memcpy(safe_report, interrupt_in_data, 63); + report_dirty = false; + should_send = true; + } + critical_section_exit(&report_cs); + + // Only send to TinyUSB if we actually grabbed fresh data + if (should_send) { + if (!tud_hid_report(0x01, safe_report, 63)) { + printf("[USBHID] tud_hid_report error\n"); + + // If the report failed to queue, restore the dirty flag + // so we try again on the next loop iteration. + critical_section_enter_blocking(&report_cs); + report_dirty = true; + critical_section_exit(&report_cs); + } } } @@ -39,7 +68,17 @@ void on_bt_data(CHANNEL_TYPE channel, uint8_t *data, uint16_t len) { if ((data[56] & 1) != (interrupt_in_data[53] & 1)) { set_headset(data[56] & 1); } + + // We add the critical section here to avoid any race conditions when writing to the interrupt_in_data buffer, + // which is shared between the main loop and this callback. + // The critical section ensures that only one thread can access the buffer at a time, + // preventing data corruption and ensuring thread safety. + // We also set the report_dirty flag to true to indicate that new data is available + // and needs to be sent in the next interrupt report. + critical_section_enter_blocking(&report_cs); memcpy(interrupt_in_data, data + 3, 63); + report_dirty = true; + critical_section_exit(&report_cs); } } @@ -99,6 +138,10 @@ int main() { vreg_set_voltage(VREG_VOLTAGE_1_20); sleep_ms(1000); set_sys_clock_khz(320000, true); + + // Initialize the critical section for the report buffer + critical_section_init(&report_cs); + board_init(); tusb_rhport_init_t dev_init = { diff --git a/src/usb_descriptors.c b/src/usb_descriptors.c index ab4380e..a0c8306 100644 --- a/src/usb_descriptors.c +++ b/src/usb_descriptors.c @@ -314,7 +314,7 @@ uint8_t const descriptor_configuration[] = { 0x84, // bEndpointAddress: IN EP4 0x03, // bmAttributes: Interrupt 0x40, 0x00, // wMaxPacketSize: 64 - 0x04, // bInterval: 4 (polling every 4ms) + 0x04, // bInterval: 1 (polling every 4ms -> 1ms) // Endpoint Descriptor (HID OUT: EP3) 0x07, // bLength @@ -322,7 +322,7 @@ uint8_t const descriptor_configuration[] = { 0x03, // bEndpointAddress: OUT EP3 0x03, // bmAttributes: Interrupt 0x40, 0x00, // wMaxPacketSize: 64 - 0x04, // bInterval: 4 + 0x01, // bInterval: 1 (polling every 4ms -> 1ms) }; // Invoked when received GET CONFIGURATION DESCRIPTOR From d391f031e4fee7efac126b67bcfd39ad04365135 Mon Sep 17 00:00:00 2001 From: awalol <61059886+awalol@users.noreply.github.com> Date: Thu, 30 Apr 2026 22:35:36 +0800 Subject: [PATCH 2/2] fix: set interrupt in binterval 1 --- src/usb_descriptors.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/usb_descriptors.c b/src/usb_descriptors.c index a0c8306..8283d40 100644 --- a/src/usb_descriptors.c +++ b/src/usb_descriptors.c @@ -314,7 +314,7 @@ uint8_t const descriptor_configuration[] = { 0x84, // bEndpointAddress: IN EP4 0x03, // bmAttributes: Interrupt 0x40, 0x00, // wMaxPacketSize: 64 - 0x04, // bInterval: 1 (polling every 4ms -> 1ms) + 0x01, // bInterval: 1 (polling every 4ms -> 1ms) // Endpoint Descriptor (HID OUT: EP3) 0x07, // bLength