// // Created by awalol on 2026/3/4. // #include #include "bsp/board_api.h" #include "bt.h" #include "utils.h" #include "resample.h" #include "audio.h" #include "hardware/clocks.h" #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; uint8_t interrupt_in_data[63] = { 0x7f, 0x7d, 0x7f, 0x7e, 0x00, 0x00, 0xa7, 0x08, 0x00, 0x00, 0x00, 0x52, 0x43, 0x30, 0x41, 0x01, 0x00, 0x0e, 0x00, 0xef, 0xff, 0x03, 0x03, 0x7b, 0x1b, 0x18, 0xf0, 0xcc, 0x9c, 0x60, 0x00, 0xfc, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x09, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa7, 0xad, 0x60, 0x00, 0x29, 0x18, 0x00, 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; 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); } } } void on_bt_data(CHANNEL_TYPE channel, uint8_t *data, uint16_t len) { // printf("[Main] BT data callback: channel=%u len=%u\n", channel, len); if (channel == INTERRUPT && data[1] == 0x31) { 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); } } // Invoked when received GET_REPORT control request // Application must fill buffer report's content and return its length. // Return zero will cause the stack to STALL request uint16_t tud_hid_get_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t *buffer, uint16_t reqlen) { (void) itf; (void) report_id; (void) report_type; (void) buffer; (void) reqlen; std::vector feature_data = get_feature_data(report_id, reqlen); if (!feature_data.empty()) { memcpy(buffer, feature_data.data() + 1, feature_data.size() - 1); } return feature_data.empty() ? 0 : feature_data.size() - 1; } // Invoked when received SET_REPORT control request or // received data on OUT endpoint ( Report ID = 0, Type = 0 ) void tud_hid_set_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t const *buffer, uint16_t bufsize) { (void) itf; (void) report_id; (void) report_type; (void) buffer; (void) bufsize; // INTERRUPT OUT if (report_id == 0) { switch (buffer[0]) { case 0x02: { uint8_t outputData[78]; outputData[0] = 0x31; outputData[1] = reportSeqCounter << 4; if (++reportSeqCounter == 256) { reportSeqCounter = 0; } outputData[2] = 0x10; memcpy(outputData + 3, buffer + 1, bufsize - 1); bt_write(outputData, sizeof(outputData)); break; } } } if (report_id == 0x80) { set_feature_data(report_id,const_cast(buffer),bufsize); return; } } 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 = { .role = TUSB_ROLE_DEVICE, .speed = TUSB_SPEED_AUTO }; tusb_init(BOARD_TUD_RHPORT, &dev_init); tud_disconnect(); board_init_after_tusb(); bt_init(); bt_register_data_callback(on_bt_data); audio_init(); while (1) { cyw43_arch_poll(); tud_task(); audio_loop(); interrupt_loop(); } }