-- cgit v1.3.1 From 4c5c346582e17c61986c6ee61e962d9738cd33fd Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 25 Apr 2026 18:09:55 +0000 Subject: fix: add BE bitfield guards for audio and fix UAC2 example endian-safe field extraction - Add TU_BITFIELD_ORDER guards for audio10_desc_as_iso_data_ep_t.bmAttributes in audio.h - Add TU_BITFIELD_ORDER guards for audio20_control_request_t.bmRequestType_bit in audio.h - Fix cdc_uac2/src/uac2_app.c: replace alias cast with TU_U16_LOW/HIGH field extraction - Fix uac2_headset/src/main.c: replace alias cast with TU_U16_LOW/HIGH field extraction - Fix uac2_speaker_fb/src/main.c: replace alias cast with TU_U16_LOW/HIGH field extraction Addresses review comment: https://github.com/hathach/tinyusb/pull/3597#issuecomment-4320042007 Agent-Logs-Url: https://github.com/hathach/tinyusb/sessions/8b695271-74b3-4a26-b3d8-c48ecdf2e481 Co-authored-by: HiFiPhile <4375114+HiFiPhile@users.noreply.github.com> --- examples/device/cdc_uac2/src/uac2_app.c | 126 +++++++++++++++------------- examples/device/uac2_headset/src/main.c | 128 ++++++++++++++++------------- examples/device/uac2_speaker_fb/src/main.c | 126 +++++++++++++++------------- hw/mcu/raspberry_pi/FreeRTOS-Kernel | 1 + hw/mcu/raspberry_pi/Pico-PIO-USB | 1 + hw/mcu/st/cmsis_device_f4 | 1 + hw/mcu/st/stm32f4xx_hal_driver | 1 + lib/CMSIS_5 | 1 + lib/FreeRTOS-Kernel | 1 + lib/fatfs | 1 + lib/lwip | 1 + lib/threadx | 1 + src/class/audio/audio.h | 17 ++++ tools/linkermap | 1 + tools/uf2 | 1 + 15 files changed, 239 insertions(+), 169 deletions(-) create mode 160000 hw/mcu/raspberry_pi/FreeRTOS-Kernel create mode 160000 hw/mcu/raspberry_pi/Pico-PIO-USB create mode 160000 hw/mcu/st/cmsis_device_f4 create mode 160000 hw/mcu/st/stm32f4xx_hal_driver create mode 160000 lib/CMSIS_5 create mode 160000 lib/FreeRTOS-Kernel create mode 160000 lib/fatfs create mode 160000 lib/lwip create mode 160000 lib/threadx create mode 160000 tools/linkermap create mode 160000 tools/uf2 diff --git a/examples/device/cdc_uac2/src/uac2_app.c b/examples/device/cdc_uac2/src/uac2_app.c index 6e9d1d9e3..59c695514 100644 --- a/examples/device/cdc_uac2/src/uac2_app.c +++ b/examples/device/cdc_uac2/src/uac2_app.c @@ -82,20 +82,23 @@ void audio_task(void) { } // Helper for clock get requests -static bool tud_audio_clock_get_request(uint8_t rhport, audio20_control_request_t const *request) +static bool tud_audio_clock_get_request(uint8_t rhport, tusb_control_request_t const *p_request) { - TU_ASSERT(request->bEntityID == UAC2_ENTITY_CLOCK); + uint8_t const entity_id = TU_U16_HIGH(p_request->wIndex); + uint8_t const ctrl_sel = TU_U16_HIGH(p_request->wValue); - if (request->bControlSelector == AUDIO20_CS_CTRL_SAM_FREQ) + TU_ASSERT(entity_id == UAC2_ENTITY_CLOCK); + + if (ctrl_sel == AUDIO20_CS_CTRL_SAM_FREQ) { - if (request->bRequest == AUDIO20_CS_REQ_CUR) + if (p_request->bRequest == AUDIO20_CS_REQ_CUR) { TU_LOG1("Clock get current freq %" PRIu32 "\r\n", current_sample_rate); audio20_control_cur_4_t curf = { (int32_t) tu_htole32(current_sample_rate) }; - return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *)request, &curf, sizeof(curf)); + return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &curf, sizeof(curf)); } - else if (request->bRequest == AUDIO20_CS_REQ_RANGE) + else if (p_request->bRequest == AUDIO20_CS_REQ_RANGE) { audio20_control_range_4_n_t(N_SAMPLE_RATES) rangef = { @@ -110,32 +113,35 @@ static bool tud_audio_clock_get_request(uint8_t rhport, audio20_control_request_ TU_LOG1("Range %d (%d, %d, %d)\r\n", i, (int)rangef.subrange[i].bMin, (int)rangef.subrange[i].bMax, (int)rangef.subrange[i].bRes); } - return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *)request, &rangef, sizeof(rangef)); + return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &rangef, sizeof(rangef)); } } - else if (request->bControlSelector == AUDIO20_CS_CTRL_CLK_VALID && - request->bRequest == AUDIO20_CS_REQ_CUR) + else if (ctrl_sel == AUDIO20_CS_CTRL_CLK_VALID && + p_request->bRequest == AUDIO20_CS_REQ_CUR) { audio20_control_cur_1_t cur_valid = { .bCur = 1 }; TU_LOG1("Clock get is valid %u\r\n", cur_valid.bCur); - return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *)request, &cur_valid, sizeof(cur_valid)); + return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &cur_valid, sizeof(cur_valid)); } TU_LOG1("Clock get request not supported, entity = %u, selector = %u, request = %u\r\n", - request->bEntityID, request->bControlSelector, request->bRequest); + entity_id, ctrl_sel, p_request->bRequest); return false; } // Helper for clock set requests -static bool tud_audio_clock_set_request(uint8_t rhport, audio20_control_request_t const *request, uint8_t const *buf) +static bool tud_audio_clock_set_request(uint8_t rhport, tusb_control_request_t const *p_request, uint8_t const *buf) { (void)rhport; - TU_ASSERT(request->bEntityID == UAC2_ENTITY_CLOCK); - TU_VERIFY(request->bRequest == AUDIO20_CS_REQ_CUR); + uint8_t const entity_id = TU_U16_HIGH(p_request->wIndex); + uint8_t const ctrl_sel = TU_U16_HIGH(p_request->wValue); + + TU_ASSERT(entity_id == UAC2_ENTITY_CLOCK); + TU_VERIFY(p_request->bRequest == AUDIO20_CS_REQ_CUR); - if (request->bControlSelector == AUDIO20_CS_CTRL_SAM_FREQ) + if (ctrl_sel == AUDIO20_CS_CTRL_SAM_FREQ) { - TU_VERIFY(request->wLength == sizeof(audio20_control_cur_4_t)); + TU_VERIFY(p_request->wLength == sizeof(audio20_control_cur_4_t)); current_sample_rate = (uint32_t) ((audio20_control_cur_4_t const *)buf)->bCur; @@ -146,79 +152,87 @@ static bool tud_audio_clock_set_request(uint8_t rhport, audio20_control_request_ else { TU_LOG1("Clock set request not supported, entity = %u, selector = %u, request = %u\r\n", - request->bEntityID, request->bControlSelector, request->bRequest); + entity_id, ctrl_sel, p_request->bRequest); return false; } } // Helper for feature unit get requests -static bool tud_audio_feature_unit_get_request(uint8_t rhport, audio20_control_request_t const *request) +static bool tud_audio_feature_unit_get_request(uint8_t rhport, tusb_control_request_t const *p_request) { - TU_ASSERT(request->bEntityID == UAC2_ENTITY_SPK_FEATURE_UNIT); + uint8_t const entity_id = TU_U16_HIGH(p_request->wIndex); + uint8_t const ctrl_sel = TU_U16_HIGH(p_request->wValue); + uint8_t const channel_num = TU_U16_LOW(p_request->wValue); - if (request->bControlSelector == AUDIO20_FU_CTRL_MUTE && request->bRequest == AUDIO20_CS_REQ_CUR) + TU_ASSERT(entity_id == UAC2_ENTITY_SPK_FEATURE_UNIT); + + if (ctrl_sel == AUDIO20_FU_CTRL_MUTE && p_request->bRequest == AUDIO20_CS_REQ_CUR) { - audio20_control_cur_1_t mute1 = { .bCur = mute[request->bChannelNumber] }; - TU_LOG1("Get channel %u mute %d\r\n", request->bChannelNumber, mute1.bCur); - return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *)request, &mute1, sizeof(mute1)); + audio20_control_cur_1_t mute1 = { .bCur = mute[channel_num] }; + TU_LOG1("Get channel %u mute %d\r\n", channel_num, mute1.bCur); + return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &mute1, sizeof(mute1)); } - else if (request->bControlSelector == AUDIO20_FU_CTRL_VOLUME) + else if (ctrl_sel == AUDIO20_FU_CTRL_VOLUME) { - if (request->bRequest == AUDIO20_CS_REQ_RANGE) + if (p_request->bRequest == AUDIO20_CS_REQ_RANGE) { audio20_control_range_2_n_t(1) range_vol = { .wNumSubRanges = tu_htole16(1), .subrange[0] = { .bMin = tu_htole16(-VOLUME_CTRL_50_DB), tu_htole16(VOLUME_CTRL_0_DB), tu_htole16(256) } }; - TU_LOG1("Get channel %u volume range (%d, %d, %u) dB\r\n", request->bChannelNumber, + TU_LOG1("Get channel %u volume range (%d, %d, %u) dB\r\n", channel_num, range_vol.subrange[0].bMin / 256, range_vol.subrange[0].bMax / 256, range_vol.subrange[0].bRes / 256); - return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *)request, &range_vol, sizeof(range_vol)); + return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &range_vol, sizeof(range_vol)); } - else if (request->bRequest == AUDIO20_CS_REQ_CUR) + else if (p_request->bRequest == AUDIO20_CS_REQ_CUR) { - audio20_control_cur_2_t cur_vol = { .bCur = tu_htole16(volume[request->bChannelNumber]) }; - TU_LOG1("Get channel %u volume %d dB\r\n", request->bChannelNumber, cur_vol.bCur / 256); - return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *)request, &cur_vol, sizeof(cur_vol)); + audio20_control_cur_2_t cur_vol = { .bCur = tu_htole16(volume[channel_num]) }; + TU_LOG1("Get channel %u volume %d dB\r\n", channel_num, cur_vol.bCur / 256); + return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &cur_vol, sizeof(cur_vol)); } } TU_LOG1("Feature unit get request not supported, entity = %u, selector = %u, request = %u\r\n", - request->bEntityID, request->bControlSelector, request->bRequest); + entity_id, ctrl_sel, p_request->bRequest); return false; } // Helper for feature unit set requests -static bool tud_audio_feature_unit_set_request(uint8_t rhport, audio20_control_request_t const *request, uint8_t const *buf) +static bool tud_audio_feature_unit_set_request(uint8_t rhport, tusb_control_request_t const *p_request, uint8_t const *buf) { (void)rhport; - TU_ASSERT(request->bEntityID == UAC2_ENTITY_SPK_FEATURE_UNIT); - TU_VERIFY(request->bRequest == AUDIO20_CS_REQ_CUR); + uint8_t const entity_id = TU_U16_HIGH(p_request->wIndex); + uint8_t const ctrl_sel = TU_U16_HIGH(p_request->wValue); + uint8_t const channel_num = TU_U16_LOW(p_request->wValue); + + TU_ASSERT(entity_id == UAC2_ENTITY_SPK_FEATURE_UNIT); + TU_VERIFY(p_request->bRequest == AUDIO20_CS_REQ_CUR); - if (request->bControlSelector == AUDIO20_FU_CTRL_MUTE) + if (ctrl_sel == AUDIO20_FU_CTRL_MUTE) { - TU_VERIFY(request->wLength == sizeof(audio20_control_cur_1_t)); + TU_VERIFY(p_request->wLength == sizeof(audio20_control_cur_1_t)); - mute[request->bChannelNumber] = ((audio20_control_cur_1_t const *)buf)->bCur; + mute[channel_num] = ((audio20_control_cur_1_t const *)buf)->bCur; - TU_LOG1("Set channel %d Mute: %d\r\n", request->bChannelNumber, mute[request->bChannelNumber]); + TU_LOG1("Set channel %d Mute: %d\r\n", channel_num, mute[channel_num]); return true; } - else if (request->bControlSelector == AUDIO20_FU_CTRL_VOLUME) + else if (ctrl_sel == AUDIO20_FU_CTRL_VOLUME) { - TU_VERIFY(request->wLength == sizeof(audio20_control_cur_2_t)); + TU_VERIFY(p_request->wLength == sizeof(audio20_control_cur_2_t)); - volume[request->bChannelNumber] = ((audio20_control_cur_2_t const *)buf)->bCur; + volume[channel_num] = ((audio20_control_cur_2_t const *)buf)->bCur; - TU_LOG1("Set channel %d volume: %d dB\r\n", request->bChannelNumber, volume[request->bChannelNumber] / 256); + TU_LOG1("Set channel %d volume: %d dB\r\n", channel_num, volume[channel_num] / 256); return true; } else { TU_LOG1("Feature unit set request not supported, entity = %u, selector = %u, request = %u\r\n", - request->bEntityID, request->bControlSelector, request->bRequest); + entity_id, ctrl_sel, p_request->bRequest); return false; } } @@ -229,32 +243,32 @@ static bool tud_audio_feature_unit_set_request(uint8_t rhport, audio20_control_r // Invoked when audio class specific get request received for an entity bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const *p_request) { - audio20_control_request_t const *request = (audio20_control_request_t const *)p_request; + uint8_t const entity_id = TU_U16_HIGH(p_request->wIndex); - if (request->bEntityID == UAC2_ENTITY_CLOCK) { - return tud_audio_clock_get_request(rhport, request); + if (entity_id == UAC2_ENTITY_CLOCK) { + return tud_audio_clock_get_request(rhport, p_request); } - if (request->bEntityID == UAC2_ENTITY_SPK_FEATURE_UNIT) { - return tud_audio_feature_unit_get_request(rhport, request); + if (entity_id == UAC2_ENTITY_SPK_FEATURE_UNIT) { + return tud_audio_feature_unit_get_request(rhport, p_request); } else { TU_LOG1("Get request not handled, entity = %d, selector = %d, request = %d\r\n", - request->bEntityID, request->bControlSelector, request->bRequest); + entity_id, TU_U16_HIGH(p_request->wValue), p_request->bRequest); } return false; } // Invoked when audio class specific set request received for an entity bool tud_audio_set_req_entity_cb(uint8_t rhport, tusb_control_request_t const *p_request, uint8_t *buf) { - audio20_control_request_t const *request = (audio20_control_request_t const *)p_request; + uint8_t const entity_id = TU_U16_HIGH(p_request->wIndex); - if (request->bEntityID == UAC2_ENTITY_SPK_FEATURE_UNIT) { - return tud_audio_feature_unit_set_request(rhport, request, buf); + if (entity_id == UAC2_ENTITY_SPK_FEATURE_UNIT) { + return tud_audio_feature_unit_set_request(rhport, p_request, buf); } - if (request->bEntityID == UAC2_ENTITY_CLOCK) { - return tud_audio_clock_set_request(rhport, request, buf); + if (entity_id == UAC2_ENTITY_CLOCK) { + return tud_audio_clock_set_request(rhport, p_request, buf); } TU_LOG1("Set request not handled, entity = %d, selector = %d, request = %d\r\n", - request->bEntityID, request->bControlSelector, request->bRequest); + entity_id, TU_U16_HIGH(p_request->wValue), p_request->bRequest); return false; } diff --git a/examples/device/uac2_headset/src/main.c b/examples/device/uac2_headset/src/main.c index 779e927bc..10ffa00f8 100644 --- a/examples/device/uac2_headset/src/main.c +++ b/examples/device/uac2_headset/src/main.c @@ -319,16 +319,19 @@ static bool audio10_get_req_entity(uint8_t rhport, tusb_control_request_t const #if TUD_OPT_HIGH_SPEED // Helper for clock get requests -static bool audio20_clock_get_request(uint8_t rhport, audio20_control_request_t const *request) { - TU_ASSERT(request->bEntityID == UAC2_ENTITY_CLOCK); +static bool audio20_clock_get_request(uint8_t rhport, tusb_control_request_t const *p_request) { + uint8_t const entity_id = TU_U16_HIGH(p_request->wIndex); + uint8_t const ctrl_sel = TU_U16_HIGH(p_request->wValue); - if (request->bControlSelector == AUDIO20_CS_CTRL_SAM_FREQ) { - if (request->bRequest == AUDIO20_CS_REQ_CUR) { + TU_ASSERT(entity_id == UAC2_ENTITY_CLOCK); + + if (ctrl_sel == AUDIO20_CS_CTRL_SAM_FREQ) { + if (p_request->bRequest == AUDIO20_CS_REQ_CUR) { TU_LOG1("Clock get current freq %" PRIu32 "\r\n", current_sample_rate); audio20_control_cur_4_t curf = {(int32_t) tu_htole32(current_sample_rate)}; - return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *) request, &curf, sizeof(curf)); - } else if (request->bRequest == AUDIO20_CS_REQ_RANGE) { + return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &curf, sizeof(curf)); + } else if (p_request->bRequest == AUDIO20_CS_REQ_RANGE) { audio20_control_range_4_n_t(N_SAMPLE_RATES) rangef = { .wNumSubRanges = tu_htole16(N_SAMPLE_RATES)}; @@ -340,28 +343,31 @@ static bool audio20_clock_get_request(uint8_t rhport, audio20_control_request_t TU_LOG1("Range %d (%d, %d, %d)\r\n", i, (int) rangef.subrange[i].bMin, (int) rangef.subrange[i].bMax, (int) rangef.subrange[i].bRes); } - return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *) request, &rangef, sizeof(rangef)); + return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &rangef, sizeof(rangef)); } - } else if (request->bControlSelector == AUDIO20_CS_CTRL_CLK_VALID && - request->bRequest == AUDIO20_CS_REQ_CUR) { + } else if (ctrl_sel == AUDIO20_CS_CTRL_CLK_VALID && + p_request->bRequest == AUDIO20_CS_REQ_CUR) { audio20_control_cur_1_t cur_valid = {.bCur = 1}; TU_LOG1("Clock get is valid %u\r\n", cur_valid.bCur); - return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *) request, &cur_valid, sizeof(cur_valid)); + return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &cur_valid, sizeof(cur_valid)); } TU_LOG1("Clock get request not supported, entity = %u, selector = %u, request = %u\r\n", - request->bEntityID, request->bControlSelector, request->bRequest); + entity_id, ctrl_sel, p_request->bRequest); return false; } // Helper for clock set requests -static bool audio20_clock_set_request(uint8_t rhport, audio20_control_request_t const *request, uint8_t const *buf) { +static bool audio20_clock_set_request(uint8_t rhport, tusb_control_request_t const *p_request, uint8_t const *buf) { (void) rhport; - TU_ASSERT(request->bEntityID == UAC2_ENTITY_CLOCK); - TU_VERIFY(request->bRequest == AUDIO20_CS_REQ_CUR); + uint8_t const entity_id = TU_U16_HIGH(p_request->wIndex); + uint8_t const ctrl_sel = TU_U16_HIGH(p_request->wValue); + + TU_ASSERT(entity_id == UAC2_ENTITY_CLOCK); + TU_VERIFY(p_request->bRequest == AUDIO20_CS_REQ_CUR); - if (request->bControlSelector == AUDIO20_CS_CTRL_SAM_FREQ) { - TU_VERIFY(request->wLength == sizeof(audio20_control_cur_4_t)); + if (ctrl_sel == AUDIO20_CS_CTRL_SAM_FREQ) { + TU_VERIFY(p_request->wLength == sizeof(audio20_control_cur_4_t)); current_sample_rate = (uint32_t) ((audio20_control_cur_4_t const *) buf)->bCur; @@ -370,92 +376,100 @@ static bool audio20_clock_set_request(uint8_t rhport, audio20_control_request_t return true; } else { TU_LOG1("Clock set request not supported, entity = %u, selector = %u, request = %u\r\n", - request->bEntityID, request->bControlSelector, request->bRequest); + entity_id, ctrl_sel, p_request->bRequest); return false; } } // Helper for feature unit get requests -static bool audio20_feature_unit_get_request(uint8_t rhport, audio20_control_request_t const *request) { - TU_ASSERT(request->bEntityID == UAC2_ENTITY_SPK_FEATURE_UNIT); - - if (request->bControlSelector == AUDIO20_FU_CTRL_MUTE && request->bRequest == AUDIO20_CS_REQ_CUR) { - audio20_control_cur_1_t mute1 = {.bCur = mute[request->bChannelNumber]}; - TU_LOG1("Get channel %u mute %d\r\n", request->bChannelNumber, mute1.bCur); - return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *) request, &mute1, sizeof(mute1)); - } else if (request->bControlSelector == AUDIO20_FU_CTRL_VOLUME) { - if (request->bRequest == AUDIO20_CS_REQ_RANGE) { +static bool audio20_feature_unit_get_request(uint8_t rhport, tusb_control_request_t const *p_request) { + uint8_t const entity_id = TU_U16_HIGH(p_request->wIndex); + uint8_t const ctrl_sel = TU_U16_HIGH(p_request->wValue); + uint8_t const channel_num = TU_U16_LOW(p_request->wValue); + + TU_ASSERT(entity_id == UAC2_ENTITY_SPK_FEATURE_UNIT); + + if (ctrl_sel == AUDIO20_FU_CTRL_MUTE && p_request->bRequest == AUDIO20_CS_REQ_CUR) { + audio20_control_cur_1_t mute1 = {.bCur = mute[channel_num]}; + TU_LOG1("Get channel %u mute %d\r\n", channel_num, mute1.bCur); + return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &mute1, sizeof(mute1)); + } else if (ctrl_sel == AUDIO20_FU_CTRL_VOLUME) { + if (p_request->bRequest == AUDIO20_CS_REQ_RANGE) { audio20_control_range_2_n_t(1) range_vol = { .wNumSubRanges = tu_htole16(1), .subrange[0] = {.bMin = tu_htole16(-VOLUME_CTRL_50_DB), tu_htole16(VOLUME_CTRL_0_DB), tu_htole16(256)}}; - TU_LOG1("Get channel %u volume range (%d, %d, %u) dB\r\n", request->bChannelNumber, + TU_LOG1("Get channel %u volume range (%d, %d, %u) dB\r\n", channel_num, range_vol.subrange[0].bMin / 256, range_vol.subrange[0].bMax / 256, range_vol.subrange[0].bRes / 256); - return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *) request, &range_vol, sizeof(range_vol)); - } else if (request->bRequest == AUDIO20_CS_REQ_CUR) { - audio20_control_cur_2_t cur_vol = {.bCur = tu_htole16(volume[request->bChannelNumber])}; - TU_LOG1("Get channel %u volume %d dB\r\n", request->bChannelNumber, cur_vol.bCur / 256); - return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *) request, &cur_vol, sizeof(cur_vol)); + return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &range_vol, sizeof(range_vol)); + } else if (p_request->bRequest == AUDIO20_CS_REQ_CUR) { + audio20_control_cur_2_t cur_vol = {.bCur = tu_htole16(volume[channel_num])}; + TU_LOG1("Get channel %u volume %d dB\r\n", channel_num, cur_vol.bCur / 256); + return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &cur_vol, sizeof(cur_vol)); } } TU_LOG1("Feature unit get request not supported, entity = %u, selector = %u, request = %u\r\n", - request->bEntityID, request->bControlSelector, request->bRequest); + entity_id, ctrl_sel, p_request->bRequest); return false; } // Helper for feature unit set requests -static bool audio20_feature_unit_set_request(uint8_t rhport, audio20_control_request_t const *request, uint8_t const *buf) { +static bool audio20_feature_unit_set_request(uint8_t rhport, tusb_control_request_t const *p_request, uint8_t const *buf) { (void) rhport; - TU_ASSERT(request->bEntityID == UAC2_ENTITY_SPK_FEATURE_UNIT); - TU_VERIFY(request->bRequest == AUDIO20_CS_REQ_CUR); + uint8_t const entity_id = TU_U16_HIGH(p_request->wIndex); + uint8_t const ctrl_sel = TU_U16_HIGH(p_request->wValue); + uint8_t const channel_num = TU_U16_LOW(p_request->wValue); + + TU_ASSERT(entity_id == UAC2_ENTITY_SPK_FEATURE_UNIT); + TU_VERIFY(p_request->bRequest == AUDIO20_CS_REQ_CUR); - if (request->bControlSelector == AUDIO20_FU_CTRL_MUTE) { - TU_VERIFY(request->wLength == sizeof(audio20_control_cur_1_t)); + if (ctrl_sel == AUDIO20_FU_CTRL_MUTE) { + TU_VERIFY(p_request->wLength == sizeof(audio20_control_cur_1_t)); - mute[request->bChannelNumber] = ((audio20_control_cur_1_t const *) buf)->bCur; + mute[channel_num] = ((audio20_control_cur_1_t const *) buf)->bCur; - TU_LOG1("Set channel %d Mute: %d\r\n", request->bChannelNumber, mute[request->bChannelNumber]); + TU_LOG1("Set channel %d Mute: %d\r\n", channel_num, mute[channel_num]); return true; - } else if (request->bControlSelector == AUDIO20_FU_CTRL_VOLUME) { - TU_VERIFY(request->wLength == sizeof(audio20_control_cur_2_t)); + } else if (ctrl_sel == AUDIO20_FU_CTRL_VOLUME) { + TU_VERIFY(p_request->wLength == sizeof(audio20_control_cur_2_t)); - volume[request->bChannelNumber] = ((audio20_control_cur_2_t const *) buf)->bCur; + volume[channel_num] = ((audio20_control_cur_2_t const *) buf)->bCur; - TU_LOG1("Set channel %d volume: %d dB\r\n", request->bChannelNumber, volume[request->bChannelNumber] / 256); + TU_LOG1("Set channel %d volume: %d dB\r\n", channel_num, volume[channel_num] / 256); return true; } else { TU_LOG1("Feature unit set request not supported, entity = %u, selector = %u, request = %u\r\n", - request->bEntityID, request->bControlSelector, request->bRequest); + entity_id, ctrl_sel, p_request->bRequest); return false; } } static bool audio20_get_req_entity(uint8_t rhport, tusb_control_request_t const *p_request) { - audio20_control_request_t const *request = (audio20_control_request_t const *) p_request; + uint8_t const entity_id = TU_U16_HIGH(p_request->wIndex); - if (request->bEntityID == UAC2_ENTITY_CLOCK) - return audio20_clock_get_request(rhport, request); - if (request->bEntityID == UAC2_ENTITY_SPK_FEATURE_UNIT) - return audio20_feature_unit_get_request(rhport, request); + if (entity_id == UAC2_ENTITY_CLOCK) + return audio20_clock_get_request(rhport, p_request); + if (entity_id == UAC2_ENTITY_SPK_FEATURE_UNIT) + return audio20_feature_unit_get_request(rhport, p_request); else { TU_LOG1("Get request not handled, entity = %d, selector = %d, request = %d\r\n", - request->bEntityID, request->bControlSelector, request->bRequest); + entity_id, TU_U16_HIGH(p_request->wValue), p_request->bRequest); } return false; } static bool audio20_set_req_entity(uint8_t rhport, tusb_control_request_t const *p_request, uint8_t *buf) { - audio20_control_request_t const *request = (audio20_control_request_t const *) p_request; + uint8_t const entity_id = TU_U16_HIGH(p_request->wIndex); - if (request->bEntityID == UAC2_ENTITY_SPK_FEATURE_UNIT) - return audio20_feature_unit_set_request(rhport, request, buf); - if (request->bEntityID == UAC2_ENTITY_CLOCK) - return audio20_clock_set_request(rhport, request, buf); + if (entity_id == UAC2_ENTITY_SPK_FEATURE_UNIT) + return audio20_feature_unit_set_request(rhport, p_request, buf); + if (entity_id == UAC2_ENTITY_CLOCK) + return audio20_clock_set_request(rhport, p_request, buf); TU_LOG1("Set request not handled, entity = %d, selector = %d, request = %d\r\n", - request->bEntityID, request->bControlSelector, request->bRequest); + entity_id, TU_U16_HIGH(p_request->wValue), p_request->bRequest); return false; } diff --git a/examples/device/uac2_speaker_fb/src/main.c b/examples/device/uac2_speaker_fb/src/main.c index 402642162..1680807e5 100644 --- a/examples/device/uac2_speaker_fb/src/main.c +++ b/examples/device/uac2_speaker_fb/src/main.c @@ -315,16 +315,19 @@ const uint32_t sample_rates[] = {44100, 48000, 88200, 96000}; #define N_SAMPLE_RATES TU_ARRAY_SIZE(sample_rates) -static bool audio20_clock_get_request(uint8_t rhport, audio20_control_request_t const *request) { - TU_ASSERT(request->bEntityID == UAC2_ENTITY_CLOCK); +static bool audio20_clock_get_request(uint8_t rhport, tusb_control_request_t const *p_request) { + uint8_t const entity_id = TU_U16_HIGH(p_request->wIndex); + uint8_t const ctrl_sel = TU_U16_HIGH(p_request->wValue); - if (request->bControlSelector == AUDIO20_CS_CTRL_SAM_FREQ) { - if (request->bRequest == AUDIO20_CS_REQ_CUR) { + TU_ASSERT(entity_id == UAC2_ENTITY_CLOCK); + + if (ctrl_sel == AUDIO20_CS_CTRL_SAM_FREQ) { + if (p_request->bRequest == AUDIO20_CS_REQ_CUR) { TU_LOG1("Clock get current freq %" PRIu32 "\r\n", current_sample_rate); audio20_control_cur_4_t curf = {(int32_t) tu_htole32(current_sample_rate)}; - return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *) request, &curf, sizeof(curf)); - } else if (request->bRequest == AUDIO20_CS_REQ_RANGE) { + return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &curf, sizeof(curf)); + } else if (p_request->bRequest == AUDIO20_CS_REQ_RANGE) { audio20_control_range_4_n_t(N_SAMPLE_RATES) rangef = { .wNumSubRanges = tu_htole16(N_SAMPLE_RATES)}; @@ -336,25 +339,28 @@ static bool audio20_clock_get_request(uint8_t rhport, audio20_control_request_t TU_LOG1("Range %d (%d, %d, %d)\r\n", i, (int) rangef.subrange[i].bMin, (int) rangef.subrange[i].bMax, (int) rangef.subrange[i].bRes); } - return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *) request, &rangef, sizeof(rangef)); + return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &rangef, sizeof(rangef)); } - } else if (request->bControlSelector == AUDIO20_CS_CTRL_CLK_VALID && - request->bRequest == AUDIO20_CS_REQ_CUR) { + } else if (ctrl_sel == AUDIO20_CS_CTRL_CLK_VALID && + p_request->bRequest == AUDIO20_CS_REQ_CUR) { audio20_control_cur_1_t cur_valid = {.bCur = 1}; TU_LOG1("Clock get is valid %u\r\n", cur_valid.bCur); - return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *) request, &cur_valid, sizeof(cur_valid)); + return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &cur_valid, sizeof(cur_valid)); } TU_LOG1("Clock get request not supported, entity = %u, selector = %u, request = %u\r\n", - request->bEntityID, request->bControlSelector, request->bRequest); + entity_id, ctrl_sel, p_request->bRequest); return false; } -static bool audio20_clock_set_request(audio20_control_request_t const *request, uint8_t const *buf) { - TU_ASSERT(request->bEntityID == UAC2_ENTITY_CLOCK); - TU_VERIFY(request->bRequest == AUDIO20_CS_REQ_CUR); +static bool audio20_clock_set_request(tusb_control_request_t const *p_request, uint8_t const *buf) { + uint8_t const entity_id = TU_U16_HIGH(p_request->wIndex); + uint8_t const ctrl_sel = TU_U16_HIGH(p_request->wValue); + + TU_ASSERT(entity_id == UAC2_ENTITY_CLOCK); + TU_VERIFY(p_request->bRequest == AUDIO20_CS_REQ_CUR); - if (request->bControlSelector == AUDIO20_CS_CTRL_SAM_FREQ) { - TU_VERIFY(request->wLength == sizeof(audio20_control_cur_4_t)); + if (ctrl_sel == AUDIO20_CS_CTRL_SAM_FREQ) { + TU_VERIFY(p_request->wLength == sizeof(audio20_control_cur_4_t)); current_sample_rate = (uint32_t) ((audio20_control_cur_4_t const *) buf)->bCur; @@ -363,88 +369,96 @@ static bool audio20_clock_set_request(audio20_control_request_t const *request, return true; } else { TU_LOG1("Clock set request not supported, entity = %u, selector = %u, request = %u\r\n", - request->bEntityID, request->bControlSelector, request->bRequest); + entity_id, ctrl_sel, p_request->bRequest); return false; } } -static bool audio20_feature_unit_get_request(uint8_t rhport, audio20_control_request_t const *request) { - TU_ASSERT(request->bEntityID == UAC2_ENTITY_FEATURE_UNIT); +static bool audio20_feature_unit_get_request(uint8_t rhport, tusb_control_request_t const *p_request) { + uint8_t const entity_id = TU_U16_HIGH(p_request->wIndex); + uint8_t const ctrl_sel = TU_U16_HIGH(p_request->wValue); + uint8_t const channel_num = TU_U16_LOW(p_request->wValue); - if (request->bControlSelector == AUDIO20_FU_CTRL_MUTE && request->bRequest == AUDIO20_CS_REQ_CUR) { - audio20_control_cur_1_t mute1 = {.bCur = mute[request->bChannelNumber]}; - TU_LOG1("Get channel %u mute %d\r\n", request->bChannelNumber, mute1.bCur); - return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *) request, &mute1, sizeof(mute1)); - } else if (request->bControlSelector == AUDIO20_FU_CTRL_VOLUME) { - if (request->bRequest == AUDIO20_CS_REQ_RANGE) { + TU_ASSERT(entity_id == UAC2_ENTITY_FEATURE_UNIT); + + if (ctrl_sel == AUDIO20_FU_CTRL_MUTE && p_request->bRequest == AUDIO20_CS_REQ_CUR) { + audio20_control_cur_1_t mute1 = {.bCur = mute[channel_num]}; + TU_LOG1("Get channel %u mute %d\r\n", channel_num, mute1.bCur); + return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &mute1, sizeof(mute1)); + } else if (ctrl_sel == AUDIO20_FU_CTRL_VOLUME) { + if (p_request->bRequest == AUDIO20_CS_REQ_RANGE) { audio20_control_range_2_n_t(1) range_vol = { .wNumSubRanges = tu_htole16(1), .subrange[0] = {.bMin = tu_htole16(-VOLUME_CTRL_50_DB), tu_htole16(VOLUME_CTRL_0_DB), tu_htole16(256)}}; - TU_LOG1("Get channel %u volume range (%d, %d, %u) dB\r\n", request->bChannelNumber, + TU_LOG1("Get channel %u volume range (%d, %d, %u) dB\r\n", channel_num, range_vol.subrange[0].bMin / 256, range_vol.subrange[0].bMax / 256, range_vol.subrange[0].bRes / 256); - return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *) request, &range_vol, sizeof(range_vol)); - } else if (request->bRequest == AUDIO20_CS_REQ_CUR) { - audio20_control_cur_2_t cur_vol = {.bCur = tu_htole16(volume[request->bChannelNumber])}; - TU_LOG1("Get channel %u volume %d dB\r\n", request->bChannelNumber, cur_vol.bCur / 256); - return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *) request, &cur_vol, sizeof(cur_vol)); + return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &range_vol, sizeof(range_vol)); + } else if (p_request->bRequest == AUDIO20_CS_REQ_CUR) { + audio20_control_cur_2_t cur_vol = {.bCur = tu_htole16(volume[channel_num])}; + TU_LOG1("Get channel %u volume %d dB\r\n", channel_num, cur_vol.bCur / 256); + return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &cur_vol, sizeof(cur_vol)); } } TU_LOG1("Feature unit get request not supported, entity = %u, selector = %u, request = %u\r\n", - request->bEntityID, request->bControlSelector, request->bRequest); + entity_id, ctrl_sel, p_request->bRequest); return false; } -static bool audio20_feature_unit_set_request(audio20_control_request_t const *request, uint8_t const *buf) { - TU_ASSERT(request->bEntityID == UAC2_ENTITY_FEATURE_UNIT); - TU_VERIFY(request->bRequest == AUDIO20_CS_REQ_CUR); +static bool audio20_feature_unit_set_request(tusb_control_request_t const *p_request, uint8_t const *buf) { + uint8_t const entity_id = TU_U16_HIGH(p_request->wIndex); + uint8_t const ctrl_sel = TU_U16_HIGH(p_request->wValue); + uint8_t const channel_num = TU_U16_LOW(p_request->wValue); + + TU_ASSERT(entity_id == UAC2_ENTITY_FEATURE_UNIT); + TU_VERIFY(p_request->bRequest == AUDIO20_CS_REQ_CUR); - if (request->bControlSelector == AUDIO20_FU_CTRL_MUTE) { - TU_VERIFY(request->wLength == sizeof(audio20_control_cur_1_t)); + if (ctrl_sel == AUDIO20_FU_CTRL_MUTE) { + TU_VERIFY(p_request->wLength == sizeof(audio20_control_cur_1_t)); - mute[request->bChannelNumber] = ((audio20_control_cur_1_t const *) buf)->bCur; + mute[channel_num] = ((audio20_control_cur_1_t const *) buf)->bCur; - TU_LOG1("Set channel %d Mute: %d\r\n", request->bChannelNumber, mute[request->bChannelNumber]); + TU_LOG1("Set channel %d Mute: %d\r\n", channel_num, mute[channel_num]); return true; - } else if (request->bControlSelector == AUDIO20_FU_CTRL_VOLUME) { - TU_VERIFY(request->wLength == sizeof(audio20_control_cur_2_t)); + } else if (ctrl_sel == AUDIO20_FU_CTRL_VOLUME) { + TU_VERIFY(p_request->wLength == sizeof(audio20_control_cur_2_t)); - volume[request->bChannelNumber] = ((audio20_control_cur_2_t const *) buf)->bCur; + volume[channel_num] = ((audio20_control_cur_2_t const *) buf)->bCur; - TU_LOG1("Set channel %d volume: %d dB\r\n", request->bChannelNumber, volume[request->bChannelNumber] / 256); + TU_LOG1("Set channel %d volume: %d dB\r\n", channel_num, volume[channel_num] / 256); return true; } else { TU_LOG1("Feature unit set request not supported, entity = %u, selector = %u, request = %u\r\n", - request->bEntityID, request->bControlSelector, request->bRequest); + entity_id, ctrl_sel, p_request->bRequest); return false; } } static bool audio20_get_req_entity(uint8_t rhport, tusb_control_request_t const *p_request) { - audio20_control_request_t const *request = (audio20_control_request_t const *) p_request; + uint8_t const entity_id = TU_U16_HIGH(p_request->wIndex); - if (request->bEntityID == UAC2_ENTITY_CLOCK) - return audio20_clock_get_request(rhport, request); - if (request->bEntityID == UAC2_ENTITY_FEATURE_UNIT) - return audio20_feature_unit_get_request(rhport, request); + if (entity_id == UAC2_ENTITY_CLOCK) + return audio20_clock_get_request(rhport, p_request); + if (entity_id == UAC2_ENTITY_FEATURE_UNIT) + return audio20_feature_unit_get_request(rhport, p_request); else { TU_LOG1("Get request not handled, entity = %d, selector = %d, request = %d\r\n", - request->bEntityID, request->bControlSelector, request->bRequest); + entity_id, TU_U16_HIGH(p_request->wValue), p_request->bRequest); } return false; } static bool audio20_set_req_entity(tusb_control_request_t const *p_request, uint8_t *buf) { - audio20_control_request_t const *request = (audio20_control_request_t const *) p_request; + uint8_t const entity_id = TU_U16_HIGH(p_request->wIndex); - if (request->bEntityID == UAC2_ENTITY_FEATURE_UNIT) - return audio20_feature_unit_set_request(request, buf); - if (request->bEntityID == UAC2_ENTITY_CLOCK) - return audio20_clock_set_request(request, buf); + if (entity_id == UAC2_ENTITY_FEATURE_UNIT) + return audio20_feature_unit_set_request(p_request, buf); + if (entity_id == UAC2_ENTITY_CLOCK) + return audio20_clock_set_request(p_request, buf); TU_LOG1("Set request not handled, entity = %d, selector = %d, request = %d\r\n", - request->bEntityID, request->bControlSelector, request->bRequest); + entity_id, TU_U16_HIGH(p_request->wValue), p_request->bRequest); return false; } diff --git a/hw/mcu/raspberry_pi/FreeRTOS-Kernel b/hw/mcu/raspberry_pi/FreeRTOS-Kernel new file mode 160000 index 000000000..4f7299d6e --- /dev/null +++ b/hw/mcu/raspberry_pi/FreeRTOS-Kernel @@ -0,0 +1 @@ +Subproject commit 4f7299d6ea746b27a9dd19e87af568e34bd65b15 diff --git a/hw/mcu/raspberry_pi/Pico-PIO-USB b/hw/mcu/raspberry_pi/Pico-PIO-USB new file mode 160000 index 000000000..675543bcc --- /dev/null +++ b/hw/mcu/raspberry_pi/Pico-PIO-USB @@ -0,0 +1 @@ +Subproject commit 675543bcc9baa8170f868ab7ba316d418dbcf41f diff --git a/hw/mcu/st/cmsis_device_f4 b/hw/mcu/st/cmsis_device_f4 new file mode 160000 index 000000000..3c77349ce --- /dev/null +++ b/hw/mcu/st/cmsis_device_f4 @@ -0,0 +1 @@ +Subproject commit 3c77349ce04c8af401454cc51f85ea9a50e34fc1 diff --git a/hw/mcu/st/stm32f4xx_hal_driver b/hw/mcu/st/stm32f4xx_hal_driver new file mode 160000 index 000000000..b6f0ed382 --- /dev/null +++ b/hw/mcu/st/stm32f4xx_hal_driver @@ -0,0 +1 @@ +Subproject commit b6f0ed3829f3829eb358a2e7417d80bba1a42db7 diff --git a/lib/CMSIS_5 b/lib/CMSIS_5 new file mode 160000 index 000000000..2b7495b85 --- /dev/null +++ b/lib/CMSIS_5 @@ -0,0 +1 @@ +Subproject commit 2b7495b8535bdcb306dac29b9ded4cfb679d7e5c diff --git a/lib/FreeRTOS-Kernel b/lib/FreeRTOS-Kernel new file mode 160000 index 000000000..9b777ae5c --- /dev/null +++ b/lib/FreeRTOS-Kernel @@ -0,0 +1 @@ +Subproject commit 9b777ae5c5b8e9e456065a00294d1e5f5f9facf5 diff --git a/lib/fatfs b/lib/fatfs new file mode 160000 index 000000000..30ca13c62 --- /dev/null +++ b/lib/fatfs @@ -0,0 +1 @@ +Subproject commit 30ca13c62615df0d2e9104ab41256985b96590c1 diff --git a/lib/lwip b/lib/lwip new file mode 160000 index 000000000..159e31b68 --- /dev/null +++ b/lib/lwip @@ -0,0 +1 @@ +Subproject commit 159e31b689577dbf69cf0683bbaffbd71fa5ee10 diff --git a/lib/threadx b/lib/threadx new file mode 160000 index 000000000..4b6e8100d --- /dev/null +++ b/lib/threadx @@ -0,0 +1 @@ +Subproject commit 4b6e8100d932a3a67b34c6eb17f84f3bffb9e2ae diff --git a/src/class/audio/audio.h b/src/class/audio/audio.h index cff38cc22..d0d50cf5b 100644 --- a/src/class/audio/audio.h +++ b/src/class/audio/audio.h @@ -490,10 +490,19 @@ typedef struct TU_ATTR_PACKED { uint8_t bDescriptorType; ///< Descriptor Type. Value: TUSB_DESC_ENDPOINT. uint8_t bEndpointAddress;///< The address of the endpoint on the USB device described by this descriptor. struct TU_ATTR_PACKED { +#if (TU_BITFIELD_ORDER == TU_BITFIELD_LE) uint8_t xfer : 2; // Control, ISO, Bulk, Interrupt uint8_t sync : 2; // None, Asynchronous, Adaptive, Synchronous uint8_t usage : 2; // Data, Feedback, Implicit feedback uint8_t : 2; +#elif (TU_BITFIELD_ORDER == TU_BITFIELD_BE) + uint8_t : 2; + uint8_t usage : 2; // Data, Feedback, Implicit feedback + uint8_t sync : 2; // None, Asynchronous, Adaptive, Synchronous + uint8_t xfer : 2; // Control, ISO, Bulk, Interrupt +#else + #error "Please define TU_BITFIELD_ORDER as TU_BITFIELD_LE or TU_BITFIELD_BE" +#endif } bmAttributes; uint16_t wMaxPacketSize; ///< Maximum packet size this endpoint is capable of sending or receiving when this configuration is selected. uint8_t bInterval; ///< Interval for polling endpoint for data transfers. @@ -1181,9 +1190,17 @@ typedef struct TU_ATTR_PACKED { typedef struct TU_ATTR_PACKED { union { struct TU_ATTR_PACKED { +#if (TU_BITFIELD_ORDER == TU_BITFIELD_LE) uint8_t recipient : 5;///< Recipient type tusb_request_recipient_t. uint8_t type : 2; ///< Request type tusb_request_type_t. uint8_t direction : 1;///< Direction type. tusb_dir_t +#elif (TU_BITFIELD_ORDER == TU_BITFIELD_BE) + uint8_t direction : 1;///< Direction type. tusb_dir_t + uint8_t type : 2; ///< Request type tusb_request_type_t. + uint8_t recipient : 5;///< Recipient type tusb_request_recipient_t. +#else + #error "Please define TU_BITFIELD_ORDER as TU_BITFIELD_LE or TU_BITFIELD_BE" +#endif } bmRequestType_bit; uint8_t bmRequestType; diff --git a/tools/linkermap b/tools/linkermap new file mode 160000 index 000000000..8e1f440fa --- /dev/null +++ b/tools/linkermap @@ -0,0 +1 @@ +Subproject commit 8e1f440fa15c567aceb5aa0d14f6d18c329cc67f diff --git a/tools/uf2 b/tools/uf2 new file mode 160000 index 000000000..c594542b2 --- /dev/null +++ b/tools/uf2 @@ -0,0 +1 @@ +Subproject commit c594542b2faa01cc33a2b97c9fbebc38549df80a -- cgit v1.3.1 From 5e85ff83af68c6e4d4ff5ad8ffd8129fe7ea0271 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 25 Apr 2026 18:12:18 +0000 Subject: chore: remove accidentally tracked dependency gitlinks Co-authored-by: HiFiPhile <4375114+HiFiPhile@users.noreply.github.com> --- hw/mcu/raspberry_pi/FreeRTOS-Kernel | 1 - hw/mcu/raspberry_pi/Pico-PIO-USB | 1 - hw/mcu/st/cmsis_device_f4 | 1 - hw/mcu/st/stm32f4xx_hal_driver | 1 - lib/CMSIS_5 | 1 - lib/FreeRTOS-Kernel | 1 - lib/fatfs | 1 - lib/lwip | 1 - lib/threadx | 1 - tools/linkermap | 1 - tools/uf2 | 1 - 11 files changed, 11 deletions(-) delete mode 160000 hw/mcu/raspberry_pi/FreeRTOS-Kernel delete mode 160000 hw/mcu/raspberry_pi/Pico-PIO-USB delete mode 160000 hw/mcu/st/cmsis_device_f4 delete mode 160000 hw/mcu/st/stm32f4xx_hal_driver delete mode 160000 lib/CMSIS_5 delete mode 160000 lib/FreeRTOS-Kernel delete mode 160000 lib/fatfs delete mode 160000 lib/lwip delete mode 160000 lib/threadx delete mode 160000 tools/linkermap delete mode 160000 tools/uf2 diff --git a/hw/mcu/raspberry_pi/FreeRTOS-Kernel b/hw/mcu/raspberry_pi/FreeRTOS-Kernel deleted file mode 160000 index 4f7299d6e..000000000 --- a/hw/mcu/raspberry_pi/FreeRTOS-Kernel +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 4f7299d6ea746b27a9dd19e87af568e34bd65b15 diff --git a/hw/mcu/raspberry_pi/Pico-PIO-USB b/hw/mcu/raspberry_pi/Pico-PIO-USB deleted file mode 160000 index 675543bcc..000000000 --- a/hw/mcu/raspberry_pi/Pico-PIO-USB +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 675543bcc9baa8170f868ab7ba316d418dbcf41f diff --git a/hw/mcu/st/cmsis_device_f4 b/hw/mcu/st/cmsis_device_f4 deleted file mode 160000 index 3c77349ce..000000000 --- a/hw/mcu/st/cmsis_device_f4 +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 3c77349ce04c8af401454cc51f85ea9a50e34fc1 diff --git a/hw/mcu/st/stm32f4xx_hal_driver b/hw/mcu/st/stm32f4xx_hal_driver deleted file mode 160000 index b6f0ed382..000000000 --- a/hw/mcu/st/stm32f4xx_hal_driver +++ /dev/null @@ -1 +0,0 @@ -Subproject commit b6f0ed3829f3829eb358a2e7417d80bba1a42db7 diff --git a/lib/CMSIS_5 b/lib/CMSIS_5 deleted file mode 160000 index 2b7495b85..000000000 --- a/lib/CMSIS_5 +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 2b7495b8535bdcb306dac29b9ded4cfb679d7e5c diff --git a/lib/FreeRTOS-Kernel b/lib/FreeRTOS-Kernel deleted file mode 160000 index 9b777ae5c..000000000 --- a/lib/FreeRTOS-Kernel +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 9b777ae5c5b8e9e456065a00294d1e5f5f9facf5 diff --git a/lib/fatfs b/lib/fatfs deleted file mode 160000 index 30ca13c62..000000000 --- a/lib/fatfs +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 30ca13c62615df0d2e9104ab41256985b96590c1 diff --git a/lib/lwip b/lib/lwip deleted file mode 160000 index 159e31b68..000000000 --- a/lib/lwip +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 159e31b689577dbf69cf0683bbaffbd71fa5ee10 diff --git a/lib/threadx b/lib/threadx deleted file mode 160000 index 4b6e8100d..000000000 --- a/lib/threadx +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 4b6e8100d932a3a67b34c6eb17f84f3bffb9e2ae diff --git a/tools/linkermap b/tools/linkermap deleted file mode 160000 index 8e1f440fa..000000000 --- a/tools/linkermap +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 8e1f440fa15c567aceb5aa0d14f6d18c329cc67f diff --git a/tools/uf2 b/tools/uf2 deleted file mode 160000 index c594542b2..000000000 --- a/tools/uf2 +++ /dev/null @@ -1 +0,0 @@ -Subproject commit c594542b2faa01cc33a2b97c9fbebc38549df80a -- cgit v1.3.1 From 8a21bb3dfbaaa6553f0c010f9f6abf37418ebf08 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 25 Apr 2026 18:12:42 +0000 Subject: chore: remove accidentally tracked dependency gitlinks from prior commit Agent-Logs-Url: https://github.com/hathach/tinyusb/sessions/8b695271-74b3-4a26-b3d8-c48ecdf2e481 Co-authored-by: HiFiPhile <4375114+HiFiPhile@users.noreply.github.com> --- hw/mcu/raspberry_pi/FreeRTOS-Kernel | 1 + hw/mcu/raspberry_pi/Pico-PIO-USB | 1 + hw/mcu/st/cmsis_device_f4 | 1 + hw/mcu/st/stm32f4xx_hal_driver | 1 + lib/CMSIS_5 | 1 + lib/FreeRTOS-Kernel | 1 + lib/fatfs | 1 + lib/lwip | 1 + lib/threadx | 1 + tools/linkermap | 1 + tools/uf2 | 1 + 11 files changed, 11 insertions(+) create mode 160000 hw/mcu/raspberry_pi/FreeRTOS-Kernel create mode 160000 hw/mcu/raspberry_pi/Pico-PIO-USB create mode 160000 hw/mcu/st/cmsis_device_f4 create mode 160000 hw/mcu/st/stm32f4xx_hal_driver create mode 160000 lib/CMSIS_5 create mode 160000 lib/FreeRTOS-Kernel create mode 160000 lib/fatfs create mode 160000 lib/lwip create mode 160000 lib/threadx create mode 160000 tools/linkermap create mode 160000 tools/uf2 diff --git a/hw/mcu/raspberry_pi/FreeRTOS-Kernel b/hw/mcu/raspberry_pi/FreeRTOS-Kernel new file mode 160000 index 000000000..4f7299d6e --- /dev/null +++ b/hw/mcu/raspberry_pi/FreeRTOS-Kernel @@ -0,0 +1 @@ +Subproject commit 4f7299d6ea746b27a9dd19e87af568e34bd65b15 diff --git a/hw/mcu/raspberry_pi/Pico-PIO-USB b/hw/mcu/raspberry_pi/Pico-PIO-USB new file mode 160000 index 000000000..675543bcc --- /dev/null +++ b/hw/mcu/raspberry_pi/Pico-PIO-USB @@ -0,0 +1 @@ +Subproject commit 675543bcc9baa8170f868ab7ba316d418dbcf41f diff --git a/hw/mcu/st/cmsis_device_f4 b/hw/mcu/st/cmsis_device_f4 new file mode 160000 index 000000000..3c77349ce --- /dev/null +++ b/hw/mcu/st/cmsis_device_f4 @@ -0,0 +1 @@ +Subproject commit 3c77349ce04c8af401454cc51f85ea9a50e34fc1 diff --git a/hw/mcu/st/stm32f4xx_hal_driver b/hw/mcu/st/stm32f4xx_hal_driver new file mode 160000 index 000000000..b6f0ed382 --- /dev/null +++ b/hw/mcu/st/stm32f4xx_hal_driver @@ -0,0 +1 @@ +Subproject commit b6f0ed3829f3829eb358a2e7417d80bba1a42db7 diff --git a/lib/CMSIS_5 b/lib/CMSIS_5 new file mode 160000 index 000000000..2b7495b85 --- /dev/null +++ b/lib/CMSIS_5 @@ -0,0 +1 @@ +Subproject commit 2b7495b8535bdcb306dac29b9ded4cfb679d7e5c diff --git a/lib/FreeRTOS-Kernel b/lib/FreeRTOS-Kernel new file mode 160000 index 000000000..9b777ae5c --- /dev/null +++ b/lib/FreeRTOS-Kernel @@ -0,0 +1 @@ +Subproject commit 9b777ae5c5b8e9e456065a00294d1e5f5f9facf5 diff --git a/lib/fatfs b/lib/fatfs new file mode 160000 index 000000000..30ca13c62 --- /dev/null +++ b/lib/fatfs @@ -0,0 +1 @@ +Subproject commit 30ca13c62615df0d2e9104ab41256985b96590c1 diff --git a/lib/lwip b/lib/lwip new file mode 160000 index 000000000..159e31b68 --- /dev/null +++ b/lib/lwip @@ -0,0 +1 @@ +Subproject commit 159e31b689577dbf69cf0683bbaffbd71fa5ee10 diff --git a/lib/threadx b/lib/threadx new file mode 160000 index 000000000..4b6e8100d --- /dev/null +++ b/lib/threadx @@ -0,0 +1 @@ +Subproject commit 4b6e8100d932a3a67b34c6eb17f84f3bffb9e2ae diff --git a/tools/linkermap b/tools/linkermap new file mode 160000 index 000000000..8e1f440fa --- /dev/null +++ b/tools/linkermap @@ -0,0 +1 @@ +Subproject commit 8e1f440fa15c567aceb5aa0d14f6d18c329cc67f diff --git a/tools/uf2 b/tools/uf2 new file mode 160000 index 000000000..c594542b2 --- /dev/null +++ b/tools/uf2 @@ -0,0 +1 @@ +Subproject commit c594542b2faa01cc33a2b97c9fbebc38549df80a -- cgit v1.3.1 From e8baf2e0102660d51eb515d1b0651d8298178dc3 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 25 Apr 2026 18:13:57 +0000 Subject: chore: gitignore fetched dependency directories to prevent accidental tracking Co-authored-by: HiFiPhile <4375114+HiFiPhile@users.noreply.github.com> --- .gitignore | 9 +++++++++ hw/mcu/raspberry_pi/FreeRTOS-Kernel | 1 - hw/mcu/raspberry_pi/Pico-PIO-USB | 1 - hw/mcu/st/cmsis_device_f4 | 1 - hw/mcu/st/stm32f4xx_hal_driver | 1 - lib/CMSIS_5 | 1 - lib/FreeRTOS-Kernel | 1 - lib/fatfs | 1 - lib/lwip | 1 - lib/threadx | 1 - tools/linkermap | 1 - tools/uf2 | 1 - 12 files changed, 9 insertions(+), 11 deletions(-) delete mode 160000 hw/mcu/raspberry_pi/FreeRTOS-Kernel delete mode 160000 hw/mcu/raspberry_pi/Pico-PIO-USB delete mode 160000 hw/mcu/st/cmsis_device_f4 delete mode 160000 hw/mcu/st/stm32f4xx_hal_driver delete mode 160000 lib/CMSIS_5 delete mode 160000 lib/FreeRTOS-Kernel delete mode 160000 lib/fatfs delete mode 160000 lib/lwip delete mode 160000 lib/threadx delete mode 160000 tools/linkermap delete mode 160000 tools/uf2 diff --git a/.gitignore b/.gitignore index b833191f8..641283476 100644 --- a/.gitignore +++ b/.gitignore @@ -56,3 +56,12 @@ BrowseInfo .cmake_build README_processed.rst .worktrees +# Fetched dependencies (get_deps.py) +hw/mcu/*/ +lib/CMSIS_5/ +lib/FreeRTOS-Kernel/ +lib/fatfs/ +lib/lwip/ +lib/threadx/ +tools/linkermap/ +tools/uf2/ diff --git a/hw/mcu/raspberry_pi/FreeRTOS-Kernel b/hw/mcu/raspberry_pi/FreeRTOS-Kernel deleted file mode 160000 index 4f7299d6e..000000000 --- a/hw/mcu/raspberry_pi/FreeRTOS-Kernel +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 4f7299d6ea746b27a9dd19e87af568e34bd65b15 diff --git a/hw/mcu/raspberry_pi/Pico-PIO-USB b/hw/mcu/raspberry_pi/Pico-PIO-USB deleted file mode 160000 index 675543bcc..000000000 --- a/hw/mcu/raspberry_pi/Pico-PIO-USB +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 675543bcc9baa8170f868ab7ba316d418dbcf41f diff --git a/hw/mcu/st/cmsis_device_f4 b/hw/mcu/st/cmsis_device_f4 deleted file mode 160000 index 3c77349ce..000000000 --- a/hw/mcu/st/cmsis_device_f4 +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 3c77349ce04c8af401454cc51f85ea9a50e34fc1 diff --git a/hw/mcu/st/stm32f4xx_hal_driver b/hw/mcu/st/stm32f4xx_hal_driver deleted file mode 160000 index b6f0ed382..000000000 --- a/hw/mcu/st/stm32f4xx_hal_driver +++ /dev/null @@ -1 +0,0 @@ -Subproject commit b6f0ed3829f3829eb358a2e7417d80bba1a42db7 diff --git a/lib/CMSIS_5 b/lib/CMSIS_5 deleted file mode 160000 index 2b7495b85..000000000 --- a/lib/CMSIS_5 +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 2b7495b8535bdcb306dac29b9ded4cfb679d7e5c diff --git a/lib/FreeRTOS-Kernel b/lib/FreeRTOS-Kernel deleted file mode 160000 index 9b777ae5c..000000000 --- a/lib/FreeRTOS-Kernel +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 9b777ae5c5b8e9e456065a00294d1e5f5f9facf5 diff --git a/lib/fatfs b/lib/fatfs deleted file mode 160000 index 30ca13c62..000000000 --- a/lib/fatfs +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 30ca13c62615df0d2e9104ab41256985b96590c1 diff --git a/lib/lwip b/lib/lwip deleted file mode 160000 index 159e31b68..000000000 --- a/lib/lwip +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 159e31b689577dbf69cf0683bbaffbd71fa5ee10 diff --git a/lib/threadx b/lib/threadx deleted file mode 160000 index 4b6e8100d..000000000 --- a/lib/threadx +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 4b6e8100d932a3a67b34c6eb17f84f3bffb9e2ae diff --git a/tools/linkermap b/tools/linkermap deleted file mode 160000 index 8e1f440fa..000000000 --- a/tools/linkermap +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 8e1f440fa15c567aceb5aa0d14f6d18c329cc67f diff --git a/tools/uf2 b/tools/uf2 deleted file mode 160000 index c594542b2..000000000 --- a/tools/uf2 +++ /dev/null @@ -1 +0,0 @@ -Subproject commit c594542b2faa01cc33a2b97c9fbebc38549df80a -- cgit v1.3.1 From 8c990885e304079e9e381f3899a53105c824624b Mon Sep 17 00:00:00 2001 From: Jie Feng Date: Sun, 14 Jun 2026 20:25:02 +0800 Subject: Fix one direction endpoint examples --- examples/device/midi2_device/src/usb_descriptors.c | 4 ++++ examples/device/usbtmc/src/usb_descriptors.c | 14 ++++++++++++-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/examples/device/midi2_device/src/usb_descriptors.c b/examples/device/midi2_device/src/usb_descriptors.c index 19a43f2d8..f035f9c48 100644 --- a/examples/device/midi2_device/src/usb_descriptors.c +++ b/examples/device/midi2_device/src/usb_descriptors.c @@ -70,7 +70,11 @@ enum { // Endpoint addresses #define EPNUM_MIDI2_OUT 0x01 +#if CFG_TUD_ENDPOINT_ONE_DIRECTION_ONLY +#define EPNUM_MIDI2_IN 0x82 +#else #define EPNUM_MIDI2_IN 0x81 +#endif static uint8_t const desc_fs_configuration[] = { // Config number, interface count, string index, total length, attribute, power in mA diff --git a/examples/device/usbtmc/src/usb_descriptors.c b/examples/device/usbtmc/src/usb_descriptors.c index 16bd176f8..ecdcef834 100644 --- a/examples/device/usbtmc/src/usb_descriptors.c +++ b/examples/device/usbtmc/src/usb_descriptors.c @@ -79,16 +79,26 @@ uint8_t const * tud_descriptor_device_cb(void) #if defined(CFG_TUD_USBTMC) +#if CFG_TUD_ENDPOINT_ONE_DIRECTION_ONLY +# define EPNUM_USBTMC_OUT 0x01 +# define EPNUM_USBTMC_IN 0x82 +# define EPNUM_USBTMC_INT 0x83 +#else +# define EPNUM_USBTMC_OUT 0x01 +# define EPNUM_USBTMC_IN 0x81 +# define EPNUM_USBTMC_INT 0x82 +#endif + # define TUD_USBTMC_DESC_MAIN(_itfnum,_bNumEndpoints, _bulkMaxPacketLength) \ TUD_USBTMC_IF_DESCRIPTOR(_itfnum, _bNumEndpoints, /*_stridx = */ 4u, TUD_USBTMC_PROTOCOL_USB488), \ - TUD_USBTMC_BULK_DESCRIPTORS(/* OUT = */0x01, /* IN = */ 0x81, /* packet size = */_bulkMaxPacketLength) + TUD_USBTMC_BULK_DESCRIPTORS(EPNUM_USBTMC_OUT, EPNUM_USBTMC_IN, /* packet size = */_bulkMaxPacketLength) #if CFG_TUD_USBTMC_ENABLE_INT_EP // USBTMC Interrupt xfer always has length of 2, but we use epMaxSize=8 for // compatibility with mcus that only allow 8, 16, 32 or 64 for FS endpoints # define TUD_USBTMC_DESC(_itfnum, _bulkMaxPacketLength) \ TUD_USBTMC_DESC_MAIN(_itfnum, /* _epCount = */ 3, _bulkMaxPacketLength), \ - TUD_USBTMC_INT_DESCRIPTOR(/* INT ep # */ 0x82, /* epMaxSize = */ 8, /* bInterval = */16u ) + TUD_USBTMC_INT_DESCRIPTOR(EPNUM_USBTMC_INT, /* epMaxSize = */ 8, /* bInterval = */16u ) # define TUD_USBTMC_DESC_LEN (TUD_USBTMC_IF_DESCRIPTOR_LEN + TUD_USBTMC_BULK_DESCRIPTORS_LEN + TUD_USBTMC_INT_DESCRIPTOR_LEN) #else -- cgit v1.3.1 From 6d3d33d997cd73a565e8355c84b67f0366d752ce Mon Sep 17 00:00:00 2001 From: Cedric Van den Bergh Date: Tue, 16 Jun 2026 23:28:07 +0100 Subject: ncm: add weak callback for initial link state netd_init resets link_is_up to a compile-time default, which is incorrect when the host reboots without power-cycling the device. Add tud_network_default_link_state_cb() so applications can return the actual physical link state. The weak default preserves existing CFG_TUD_NCM_DEFAULT_LINK_UP behaviour. --- src/class/net/ncm_device.c | 15 +++++++++------ src/class/net/net_device.h | 4 ++++ 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/src/class/net/ncm_device.c b/src/class/net/ncm_device.c index a78e472c2..e5f441300 100644 --- a/src/class/net/ncm_device.c +++ b/src/class/net/ncm_device.c @@ -152,6 +152,14 @@ TU_ATTR_WEAK void tud_network_set_packet_filter_cb(uint16_t packet_filter) { (void) packet_filter; } +TU_ATTR_WEAK bool tud_network_default_link_state_cb(void) { + #ifdef CFG_TUD_NCM_DEFAULT_LINK_UP + return CFG_TUD_NCM_DEFAULT_LINK_UP; + #else + return true; + #endif +} + /** * This is the NTB parameter structure * @@ -852,12 +860,7 @@ void netd_init(void) { for (int i = 0; i < RECV_NTB_N; ++i) { ncm_interface.recv_free_ntb[i] = &ncm_epbuf.recv[i].ntb; } - // Default link state - can be configured via CFG_TUD_NCM_DEFAULT_LINK_UP - #ifdef CFG_TUD_NCM_DEFAULT_LINK_UP - ncm_interface.link_is_up = CFG_TUD_NCM_DEFAULT_LINK_UP; - #else - ncm_interface.link_is_up = true; // Default to link up if not set. - #endif + ncm_interface.link_is_up = tud_network_default_link_state_cb(); } // netd_init /** diff --git a/src/class/net/net_device.h b/src/class/net/net_device.h index 332df09b3..1ad069d92 100644 --- a/src/class/net/net_device.h +++ b/src/class/net/net_device.h @@ -106,6 +106,10 @@ extern uint8_t tud_network_mac_address[6]; // Optional callback: informs the application about host requested packet filter bits void tud_network_set_packet_filter_cb(uint16_t packet_filter); +// Optional callback: called during netd_init() to get the initial link state. +// Override to return the actual physical link state instead of the compile-time default. +bool tud_network_default_link_state_cb(void); + // Set the network link state (up/down) and notify the host void tud_network_link_state(uint8_t rhport, bool is_up); -- cgit v1.3.1 From edf675f468a9ff7ac8c5e14d41d1060f3296fb2b Mon Sep 17 00:00:00 2001 From: hathach Date: Thu, 18 Jun 2026 16:36:05 +0700 Subject: class/audio: remove unused audio20_control_request_t After the UAC2 examples switched to tusb_control_request_t with TU_U16_HIGH/LOW() extraction, audio20_control_request_t is no longer referenced anywhere in the tree. It is a byte-overlay of the setup packet whose bChannelNumber/bControlSelector/bInterface/bEntityID sub-byte fields silently misread on big-endian once wValue/wIndex are converted to host order (tu_le16toh in dcd.h), so leaving it in the public header is a latent BE trap; the BE bitfield guard previously added to its bmRequestType_bit only masked that by guarding byte 0. Drop the struct entirely. Callers should use tusb_control_request_t and TU_U16_LOW/HIGH(wValue|wIndex), matching audio_device.c and the examples. Co-Authored-By: Claude Opus 4.8 (1M context) --- src/class/audio/audio.h | 31 ------------------------------- 1 file changed, 31 deletions(-) diff --git a/src/class/audio/audio.h b/src/class/audio/audio.h index d0d50cf5b..428391bb2 100644 --- a/src/class/audio/audio.h +++ b/src/class/audio/audio.h @@ -1186,37 +1186,6 @@ typedef struct TU_ATTR_PACKED { uint16_t wLockDelay; ///< Indicates the time it takes this endpoint to reliably lock its internal clock recovery circuitry. Units used depend on the value of the bLockDelayUnits field. } audio20_desc_cs_as_iso_data_ep_t; -// 5.2.2 Control Request Layout -typedef struct TU_ATTR_PACKED { - union { - struct TU_ATTR_PACKED { -#if (TU_BITFIELD_ORDER == TU_BITFIELD_LE) - uint8_t recipient : 5;///< Recipient type tusb_request_recipient_t. - uint8_t type : 2; ///< Request type tusb_request_type_t. - uint8_t direction : 1;///< Direction type. tusb_dir_t -#elif (TU_BITFIELD_ORDER == TU_BITFIELD_BE) - uint8_t direction : 1;///< Direction type. tusb_dir_t - uint8_t type : 2; ///< Request type tusb_request_type_t. - uint8_t recipient : 5;///< Recipient type tusb_request_recipient_t. -#else - #error "Please define TU_BITFIELD_ORDER as TU_BITFIELD_LE or TU_BITFIELD_BE" -#endif - } bmRequestType_bit; - - uint8_t bmRequestType; - }; - - uint8_t bRequest;///< Request type audio_cs_req_t - uint8_t bChannelNumber; - uint8_t bControlSelector; - union { - uint8_t bInterface; - uint8_t bEndpoint; - }; - uint8_t bEntityID; - uint16_t wLength; -} audio20_control_request_t; - //// 5.2.3 Control Request Parameter Block Layout // 5.2.3.1 1-byte Control CUR Parameter Block -- cgit v1.3.1 From 8d6e3c2dcd6902f834df9eed4d8e2d582dace673 Mon Sep 17 00:00:00 2001 From: hathach Date: Thu, 18 Jun 2026 17:09:12 +0700 Subject: test/hil: fail audio test on missing alsa-utils instead of skipping MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit arecord (alsa-utils) is a documented HIL host requirement, like mtools/libmtp9/iperf — none of which have a skip-if-missing guard. The audio test was the exception: it silently returned 'skipped' when arecord was absent, masking host misconfiguration. The ci.lan rig had been silently skipping device/audio_test_freertos on every board because alsa-utils was never installed. Remove the shutil.which('arecord') guard so a missing package surfaces as a failure, consistent with the other tool-dependent tests, and drop the now-unused shutil import. Note in the host-setup comment that these packages are required (a missing tool fails its test rather than skipping). Co-Authored-By: Claude Opus 4.8 (1M context) --- test/hil/hil_test.py | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/test/hil/hil_test.py b/test/hil/hil_test.py index 8ae9ee0dc..055618e3c 100755 --- a/test/hil/hil_test.py +++ b/test/hil/hil_test.py @@ -22,7 +22,7 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. -# Host setup: +# Host setup (required: a missing tool fails its test rather than skipping it): # - System packages: sudo apt install mtools libmtp9 alsa-utils iperf # mtools - read_disk_file (device/cdc_msc, device/msc_dual_lun) # libmtp9 - pymtp ctypes load (device/mtp); Debian 13 uses libmtp9t64 @@ -51,7 +51,6 @@ import serial import subprocess import json import glob -import shutil from multiprocessing import Pool, Lock from multiprocessing import TimeoutError as MpTimeoutError import hashlib @@ -1380,10 +1379,6 @@ def test_device_audio_test_freertos(board): if os.name == 'nt': return 'skipped' - arecord = shutil.which('arecord') - if arecord is None: - return 'skipped' - pcm = None timeout = ENUM_TIMEOUT while timeout > 0: @@ -1397,7 +1392,7 @@ def test_device_audio_test_freertos(board): raw_path = f'/tmp/tinyusb_audio_{uid}.raw' cmd = [ - arecord, + 'arecord', '-D', pcm, '-q', '-f', 'S16_LE', -- cgit v1.3.1 From 202746d34d3340d769e795f2e6ac6fcd8c666607 Mon Sep 17 00:00:00 2001 From: hathach Date: Thu, 18 Jun 2026 17:19:03 +0700 Subject: ci(metrics): collapse Average Code Size Metrics table when no base When no base metrics are available to compare against, the PR comment falls back to the combined "TinyUSB Average Code Size Metrics" report (build.yml copies metrics.md to metrics_compare.md). That posted the full per-example size table inline, cluttering the comment. Wrap the table in a
Size table block so the heading stays visible but the detail is collapsed by default, matching the Size Difference Report's collapsible sections. Co-Authored-By: Claude Fable 5 --- tools/metrics.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/metrics.py b/tools/metrics.py index d716cb2a5..0e29fc1ab 100644 --- a/tools/metrics.py +++ b/tools/metrics.py @@ -384,9 +384,9 @@ def render_combine_table(json_data, sort_order='name+'): def write_combine_markdown(json_data, path, sort_order='name+', title="TinyUSB Average Code Size Metrics"): """Write averaged size data to a markdown file.""" - md_lines = [f"## {title}", ""] + md_lines = [f"## {title}", "", "
Size table", ""] md_lines.extend(render_combine_table(json_data, sort_order)) - md_lines.append("") + md_lines.extend(["", "
", ""]) if json_data.get("file_list"): md_lines.extend(["
", "Input files", ""]) -- cgit v1.3.1 From 3710e6e5a580fadb53ec2e6cbf12dd6fb65c39c2 Mon Sep 17 00:00:00 2001 From: hathach Date: Thu, 18 Jun 2026 17:42:51 +0700 Subject: examples/uac2: drop redundant entity_id check in request helpers The audio20 get/set entity dispatchers already extract entity_id from wIndex and route to the matching clock / feature-unit helper, so each helper's own entity_id re-derivation and TU_ASSERT(entity_id == ...) was dead: the helper is only ever reached for its one entity. Unknown entities are still rejected by the dispatcher's "not handled" path. Remove the redundant local, the dead assert, and the constant "entity" field from each helper's not-supported log (the message text already identifies the entity). The local is dropped entirely rather than kept for the log, since TU_LOG1 compiles out in release and would leave it unused. Co-Authored-By: Claude Opus 4.8 (1M context) --- examples/device/cdc_uac2/src/uac2_app.c | 32 ++++++++++-------------------- examples/device/uac2_headset/src/main.c | 30 ++++++++++------------------ examples/device/uac2_speaker_fb/src/main.c | 30 ++++++++++------------------ 3 files changed, 31 insertions(+), 61 deletions(-) diff --git a/examples/device/cdc_uac2/src/uac2_app.c b/examples/device/cdc_uac2/src/uac2_app.c index 59c695514..a504c3b57 100644 --- a/examples/device/cdc_uac2/src/uac2_app.c +++ b/examples/device/cdc_uac2/src/uac2_app.c @@ -84,10 +84,7 @@ void audio_task(void) { // Helper for clock get requests static bool tud_audio_clock_get_request(uint8_t rhport, tusb_control_request_t const *p_request) { - uint8_t const entity_id = TU_U16_HIGH(p_request->wIndex); - uint8_t const ctrl_sel = TU_U16_HIGH(p_request->wValue); - - TU_ASSERT(entity_id == UAC2_ENTITY_CLOCK); + uint8_t const ctrl_sel = TU_U16_HIGH(p_request->wValue); if (ctrl_sel == AUDIO20_CS_CTRL_SAM_FREQ) { @@ -123,8 +120,8 @@ static bool tud_audio_clock_get_request(uint8_t rhport, tusb_control_request_t c TU_LOG1("Clock get is valid %u\r\n", cur_valid.bCur); return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &cur_valid, sizeof(cur_valid)); } - TU_LOG1("Clock get request not supported, entity = %u, selector = %u, request = %u\r\n", - entity_id, ctrl_sel, p_request->bRequest); + TU_LOG1("Clock get request not supported, selector = %u, request = %u\r\n", + ctrl_sel, p_request->bRequest); return false; } @@ -133,10 +130,8 @@ static bool tud_audio_clock_set_request(uint8_t rhport, tusb_control_request_t c { (void)rhport; - uint8_t const entity_id = TU_U16_HIGH(p_request->wIndex); - uint8_t const ctrl_sel = TU_U16_HIGH(p_request->wValue); + uint8_t const ctrl_sel = TU_U16_HIGH(p_request->wValue); - TU_ASSERT(entity_id == UAC2_ENTITY_CLOCK); TU_VERIFY(p_request->bRequest == AUDIO20_CS_REQ_CUR); if (ctrl_sel == AUDIO20_CS_CTRL_SAM_FREQ) @@ -151,8 +146,8 @@ static bool tud_audio_clock_set_request(uint8_t rhport, tusb_control_request_t c } else { - TU_LOG1("Clock set request not supported, entity = %u, selector = %u, request = %u\r\n", - entity_id, ctrl_sel, p_request->bRequest); + TU_LOG1("Clock set request not supported, selector = %u, request = %u\r\n", + ctrl_sel, p_request->bRequest); return false; } } @@ -160,12 +155,9 @@ static bool tud_audio_clock_set_request(uint8_t rhport, tusb_control_request_t c // Helper for feature unit get requests static bool tud_audio_feature_unit_get_request(uint8_t rhport, tusb_control_request_t const *p_request) { - uint8_t const entity_id = TU_U16_HIGH(p_request->wIndex); - uint8_t const ctrl_sel = TU_U16_HIGH(p_request->wValue); + uint8_t const ctrl_sel = TU_U16_HIGH(p_request->wValue); uint8_t const channel_num = TU_U16_LOW(p_request->wValue); - TU_ASSERT(entity_id == UAC2_ENTITY_SPK_FEATURE_UNIT); - if (ctrl_sel == AUDIO20_FU_CTRL_MUTE && p_request->bRequest == AUDIO20_CS_REQ_CUR) { audio20_control_cur_1_t mute1 = { .bCur = mute[channel_num] }; @@ -191,8 +183,8 @@ static bool tud_audio_feature_unit_get_request(uint8_t rhport, tusb_control_requ return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &cur_vol, sizeof(cur_vol)); } } - TU_LOG1("Feature unit get request not supported, entity = %u, selector = %u, request = %u\r\n", - entity_id, ctrl_sel, p_request->bRequest); + TU_LOG1("Feature unit get request not supported, selector = %u, request = %u\r\n", + ctrl_sel, p_request->bRequest); return false; } @@ -202,11 +194,9 @@ static bool tud_audio_feature_unit_set_request(uint8_t rhport, tusb_control_requ { (void)rhport; - uint8_t const entity_id = TU_U16_HIGH(p_request->wIndex); uint8_t const ctrl_sel = TU_U16_HIGH(p_request->wValue); uint8_t const channel_num = TU_U16_LOW(p_request->wValue); - TU_ASSERT(entity_id == UAC2_ENTITY_SPK_FEATURE_UNIT); TU_VERIFY(p_request->bRequest == AUDIO20_CS_REQ_CUR); if (ctrl_sel == AUDIO20_FU_CTRL_MUTE) @@ -231,8 +221,8 @@ static bool tud_audio_feature_unit_set_request(uint8_t rhport, tusb_control_requ } else { - TU_LOG1("Feature unit set request not supported, entity = %u, selector = %u, request = %u\r\n", - entity_id, ctrl_sel, p_request->bRequest); + TU_LOG1("Feature unit set request not supported, selector = %u, request = %u\r\n", + ctrl_sel, p_request->bRequest); return false; } } diff --git a/examples/device/uac2_headset/src/main.c b/examples/device/uac2_headset/src/main.c index 10ffa00f8..c30b31f67 100644 --- a/examples/device/uac2_headset/src/main.c +++ b/examples/device/uac2_headset/src/main.c @@ -320,10 +320,7 @@ static bool audio10_get_req_entity(uint8_t rhport, tusb_control_request_t const // Helper for clock get requests static bool audio20_clock_get_request(uint8_t rhport, tusb_control_request_t const *p_request) { - uint8_t const entity_id = TU_U16_HIGH(p_request->wIndex); - uint8_t const ctrl_sel = TU_U16_HIGH(p_request->wValue); - - TU_ASSERT(entity_id == UAC2_ENTITY_CLOCK); + uint8_t const ctrl_sel = TU_U16_HIGH(p_request->wValue); if (ctrl_sel == AUDIO20_CS_CTRL_SAM_FREQ) { if (p_request->bRequest == AUDIO20_CS_REQ_CUR) { @@ -351,8 +348,8 @@ static bool audio20_clock_get_request(uint8_t rhport, tusb_control_request_t con TU_LOG1("Clock get is valid %u\r\n", cur_valid.bCur); return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &cur_valid, sizeof(cur_valid)); } - TU_LOG1("Clock get request not supported, entity = %u, selector = %u, request = %u\r\n", - entity_id, ctrl_sel, p_request->bRequest); + TU_LOG1("Clock get request not supported, selector = %u, request = %u\r\n", + ctrl_sel, p_request->bRequest); return false; } @@ -360,10 +357,8 @@ static bool audio20_clock_get_request(uint8_t rhport, tusb_control_request_t con static bool audio20_clock_set_request(uint8_t rhport, tusb_control_request_t const *p_request, uint8_t const *buf) { (void) rhport; - uint8_t const entity_id = TU_U16_HIGH(p_request->wIndex); - uint8_t const ctrl_sel = TU_U16_HIGH(p_request->wValue); + uint8_t const ctrl_sel = TU_U16_HIGH(p_request->wValue); - TU_ASSERT(entity_id == UAC2_ENTITY_CLOCK); TU_VERIFY(p_request->bRequest == AUDIO20_CS_REQ_CUR); if (ctrl_sel == AUDIO20_CS_CTRL_SAM_FREQ) { @@ -375,20 +370,17 @@ static bool audio20_clock_set_request(uint8_t rhport, tusb_control_request_t con return true; } else { - TU_LOG1("Clock set request not supported, entity = %u, selector = %u, request = %u\r\n", - entity_id, ctrl_sel, p_request->bRequest); + TU_LOG1("Clock set request not supported, selector = %u, request = %u\r\n", + ctrl_sel, p_request->bRequest); return false; } } // Helper for feature unit get requests static bool audio20_feature_unit_get_request(uint8_t rhport, tusb_control_request_t const *p_request) { - uint8_t const entity_id = TU_U16_HIGH(p_request->wIndex); uint8_t const ctrl_sel = TU_U16_HIGH(p_request->wValue); uint8_t const channel_num = TU_U16_LOW(p_request->wValue); - TU_ASSERT(entity_id == UAC2_ENTITY_SPK_FEATURE_UNIT); - if (ctrl_sel == AUDIO20_FU_CTRL_MUTE && p_request->bRequest == AUDIO20_CS_REQ_CUR) { audio20_control_cur_1_t mute1 = {.bCur = mute[channel_num]}; TU_LOG1("Get channel %u mute %d\r\n", channel_num, mute1.bCur); @@ -407,8 +399,8 @@ static bool audio20_feature_unit_get_request(uint8_t rhport, tusb_control_reques return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &cur_vol, sizeof(cur_vol)); } } - TU_LOG1("Feature unit get request not supported, entity = %u, selector = %u, request = %u\r\n", - entity_id, ctrl_sel, p_request->bRequest); + TU_LOG1("Feature unit get request not supported, selector = %u, request = %u\r\n", + ctrl_sel, p_request->bRequest); return false; } @@ -417,11 +409,9 @@ static bool audio20_feature_unit_get_request(uint8_t rhport, tusb_control_reques static bool audio20_feature_unit_set_request(uint8_t rhport, tusb_control_request_t const *p_request, uint8_t const *buf) { (void) rhport; - uint8_t const entity_id = TU_U16_HIGH(p_request->wIndex); uint8_t const ctrl_sel = TU_U16_HIGH(p_request->wValue); uint8_t const channel_num = TU_U16_LOW(p_request->wValue); - TU_ASSERT(entity_id == UAC2_ENTITY_SPK_FEATURE_UNIT); TU_VERIFY(p_request->bRequest == AUDIO20_CS_REQ_CUR); if (ctrl_sel == AUDIO20_FU_CTRL_MUTE) { @@ -441,8 +431,8 @@ static bool audio20_feature_unit_set_request(uint8_t rhport, tusb_control_reques return true; } else { - TU_LOG1("Feature unit set request not supported, entity = %u, selector = %u, request = %u\r\n", - entity_id, ctrl_sel, p_request->bRequest); + TU_LOG1("Feature unit set request not supported, selector = %u, request = %u\r\n", + ctrl_sel, p_request->bRequest); return false; } } diff --git a/examples/device/uac2_speaker_fb/src/main.c b/examples/device/uac2_speaker_fb/src/main.c index 1680807e5..7e4d27f1c 100644 --- a/examples/device/uac2_speaker_fb/src/main.c +++ b/examples/device/uac2_speaker_fb/src/main.c @@ -316,10 +316,7 @@ const uint32_t sample_rates[] = {44100, 48000, 88200, 96000}; #define N_SAMPLE_RATES TU_ARRAY_SIZE(sample_rates) static bool audio20_clock_get_request(uint8_t rhport, tusb_control_request_t const *p_request) { - uint8_t const entity_id = TU_U16_HIGH(p_request->wIndex); - uint8_t const ctrl_sel = TU_U16_HIGH(p_request->wValue); - - TU_ASSERT(entity_id == UAC2_ENTITY_CLOCK); + uint8_t const ctrl_sel = TU_U16_HIGH(p_request->wValue); if (ctrl_sel == AUDIO20_CS_CTRL_SAM_FREQ) { if (p_request->bRequest == AUDIO20_CS_REQ_CUR) { @@ -347,16 +344,14 @@ static bool audio20_clock_get_request(uint8_t rhport, tusb_control_request_t con TU_LOG1("Clock get is valid %u\r\n", cur_valid.bCur); return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &cur_valid, sizeof(cur_valid)); } - TU_LOG1("Clock get request not supported, entity = %u, selector = %u, request = %u\r\n", - entity_id, ctrl_sel, p_request->bRequest); + TU_LOG1("Clock get request not supported, selector = %u, request = %u\r\n", + ctrl_sel, p_request->bRequest); return false; } static bool audio20_clock_set_request(tusb_control_request_t const *p_request, uint8_t const *buf) { - uint8_t const entity_id = TU_U16_HIGH(p_request->wIndex); - uint8_t const ctrl_sel = TU_U16_HIGH(p_request->wValue); + uint8_t const ctrl_sel = TU_U16_HIGH(p_request->wValue); - TU_ASSERT(entity_id == UAC2_ENTITY_CLOCK); TU_VERIFY(p_request->bRequest == AUDIO20_CS_REQ_CUR); if (ctrl_sel == AUDIO20_CS_CTRL_SAM_FREQ) { @@ -368,19 +363,16 @@ static bool audio20_clock_set_request(tusb_control_request_t const *p_request, u return true; } else { - TU_LOG1("Clock set request not supported, entity = %u, selector = %u, request = %u\r\n", - entity_id, ctrl_sel, p_request->bRequest); + TU_LOG1("Clock set request not supported, selector = %u, request = %u\r\n", + ctrl_sel, p_request->bRequest); return false; } } static bool audio20_feature_unit_get_request(uint8_t rhport, tusb_control_request_t const *p_request) { - uint8_t const entity_id = TU_U16_HIGH(p_request->wIndex); uint8_t const ctrl_sel = TU_U16_HIGH(p_request->wValue); uint8_t const channel_num = TU_U16_LOW(p_request->wValue); - TU_ASSERT(entity_id == UAC2_ENTITY_FEATURE_UNIT); - if (ctrl_sel == AUDIO20_FU_CTRL_MUTE && p_request->bRequest == AUDIO20_CS_REQ_CUR) { audio20_control_cur_1_t mute1 = {.bCur = mute[channel_num]}; TU_LOG1("Get channel %u mute %d\r\n", channel_num, mute1.bCur); @@ -399,18 +391,16 @@ static bool audio20_feature_unit_get_request(uint8_t rhport, tusb_control_reques return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &cur_vol, sizeof(cur_vol)); } } - TU_LOG1("Feature unit get request not supported, entity = %u, selector = %u, request = %u\r\n", - entity_id, ctrl_sel, p_request->bRequest); + TU_LOG1("Feature unit get request not supported, selector = %u, request = %u\r\n", + ctrl_sel, p_request->bRequest); return false; } static bool audio20_feature_unit_set_request(tusb_control_request_t const *p_request, uint8_t const *buf) { - uint8_t const entity_id = TU_U16_HIGH(p_request->wIndex); uint8_t const ctrl_sel = TU_U16_HIGH(p_request->wValue); uint8_t const channel_num = TU_U16_LOW(p_request->wValue); - TU_ASSERT(entity_id == UAC2_ENTITY_FEATURE_UNIT); TU_VERIFY(p_request->bRequest == AUDIO20_CS_REQ_CUR); if (ctrl_sel == AUDIO20_FU_CTRL_MUTE) { @@ -430,8 +420,8 @@ static bool audio20_feature_unit_set_request(tusb_control_request_t const *p_req return true; } else { - TU_LOG1("Feature unit set request not supported, entity = %u, selector = %u, request = %u\r\n", - entity_id, ctrl_sel, p_request->bRequest); + TU_LOG1("Feature unit set request not supported, selector = %u, request = %u\r\n", + ctrl_sel, p_request->bRequest); return false; } } -- cgit v1.3.1 From d0e51346cdc691624bc10da89a775a82ef86d92a Mon Sep 17 00:00:00 2001 From: hathach Date: Thu, 18 Jun 2026 17:44:16 +0700 Subject: fix(stm32_fsdev): don't enable the unused USB wakeup EXTI IRQ (F1/F3/G4/L1) The classic-USB STM32 fsdev driver enabled the EXTI-line USB wakeup interrupt (USBWakeUp_IRQn, and USBWakeUp_RMP_IRQn on the F3 remap path) in the NVIC, but never uses it: resume is serviced in-band via ISTR.WKUP in the USB_LP/HP ISR, and the driver never arms or clears that EXTI line. The wakeup EXTI interrupt is only needed to wake the core from STOP mode, which TinyUSB does not implement. Leaving its NVIC vector enabled lets it fire spuriously into an unhandled or looping vector -- the freeze reported in #3696 on STM32G473. USBWakeUp_IRQn is a valid, dedicated USB-wakeup-via-EXTI interrupt (e.g. stm32g473xx.h: =42 "USB Wakeup through EXTI line"), not an "unrelated interrupt"; it is simply unused here. - Comment out USBWakeUp_IRQn for F1/F3/G4/L1 and USBWakeUp_RMP_IRQn on the F3 remap path, kept in place so STOP-mode wakeup is a one-line re-enable. - Keep the STM32L1 USBWakeUp_IRQn -> USB_FS_WKUP_IRQn alias for that re-enable. - Document the rationale in fsdev_stm32.h with a TODO. - Comment out the matching USBWakeUp(_RMP)_IRQHandler in the F1/F3/G4 BSPs, and the FreeRTOS NVIC_SetPriority(USBWakeUp_IRQn) on F1/G4. Fixes #3696 Co-Authored-By: Claude Opus 4.8 (1M context) --- hw/bsp/stm32f1/family.c | 10 ++++++---- hw/bsp/stm32f3/family.c | 7 ++++--- hw/bsp/stm32g4/family.c | 10 ++++++---- src/portable/st/stm32_fsdev/fsdev_stm32.h | 22 +++++++++++++--------- 4 files changed, 29 insertions(+), 20 deletions(-) diff --git a/hw/bsp/stm32f1/family.c b/hw/bsp/stm32f1/family.c index abde44d21..67427da1f 100644 --- a/hw/bsp/stm32f1/family.c +++ b/hw/bsp/stm32f1/family.c @@ -63,9 +63,11 @@ void USB_LP_IRQHandler(void) { tud_int_handler(0); } -void USBWakeUp_IRQHandler(void) { - tud_int_handler(0); -} +// USB wakeup EXTI IRQ is not enabled by the fsdev driver (see fsdev_stm32.h); +// restore when STOP-mode wakeup is implemented. +//void USBWakeUp_IRQHandler(void) { +// tud_int_handler(0); +//} //--------------------------------------------------------------------+ // MACRO TYPEDEF CONSTANT ENUM @@ -128,7 +130,7 @@ void board_init(void) { // If freeRTOS is used, IRQ priority is limit by max syscall ( smaller is higher ) NVIC_SetPriority(USB_HP_CAN1_TX_IRQn, configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY); NVIC_SetPriority(USB_LP_CAN1_RX0_IRQn, configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY); - NVIC_SetPriority(USBWakeUp_IRQn, configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY); + //NVIC_SetPriority(USBWakeUp_IRQn, configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY); #endif // LED diff --git a/hw/bsp/stm32f3/family.c b/hw/bsp/stm32f3/family.c index 35e1852e8..bddf224d2 100644 --- a/hw/bsp/stm32f3/family.c +++ b/hw/bsp/stm32f3/family.c @@ -76,9 +76,10 @@ void USB_LP_IRQHandler(void) { // USB wakeup interrupt (Channel 76): Triggered by the wakeup event from the USB // Suspend mode. -void USBWakeUp_RMP_IRQHandler(void) { - tud_int_handler(0); -} +// Not enabled by the fsdev driver (see fsdev_stm32.h); restore for STOP-mode wakeup. +//void USBWakeUp_RMP_IRQHandler(void) { +// tud_int_handler(0); +//} //--------------------------------------------------------------------+ // MACRO TYPEDEF CONSTANT ENUM diff --git a/hw/bsp/stm32g4/family.c b/hw/bsp/stm32g4/family.c index 433f74e2a..cf7d4329b 100644 --- a/hw/bsp/stm32g4/family.c +++ b/hw/bsp/stm32g4/family.c @@ -61,9 +61,11 @@ void USB_LP_IRQHandler(void) { tud_int_handler(0); } -void USBWakeUp_IRQHandler(void) { - tud_int_handler(0); -} +// USB wakeup EXTI IRQ is not enabled by the fsdev driver (see fsdev_stm32.h); +// restore when STOP-mode wakeup is implemented. +//void USBWakeUp_IRQHandler(void) { +// tud_int_handler(0); +//} // USB PD void UCPD1_IRQHandler(void) { @@ -99,7 +101,7 @@ void board_init(void) { // If freeRTOS is used, IRQ priority is limit by max syscall ( smaller is higher ) NVIC_SetPriority(USB_HP_IRQn, configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY); NVIC_SetPriority(USB_LP_IRQn, configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY); - NVIC_SetPriority(USBWakeUp_IRQn, configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY); + //NVIC_SetPriority(USBWakeUp_IRQn, configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY); #endif GPIO_InitTypeDef GPIO_InitStruct; diff --git a/src/portable/st/stm32_fsdev/fsdev_stm32.h b/src/portable/st/stm32_fsdev/fsdev_stm32.h index b15c95302..93cdac808 100644 --- a/src/portable/st/stm32_fsdev/fsdev_stm32.h +++ b/src/portable/st/stm32_fsdev/fsdev_stm32.h @@ -168,14 +168,18 @@ #define FSDEV_USE_SBUF_ISO 0 #endif -//--------------------------------------------------------------------+ -// -//--------------------------------------------------------------------+ - +// STM32L1 calls it USB_FS_WKUP_IRQn; alias so the commented USBWakeUp_IRQn below +// can be uncommented as-is. #if TU_CHECK_MCU(OPT_MCU_STM32L1) && !defined(USBWakeUp_IRQn) #define USBWakeUp_IRQn USB_FS_WKUP_IRQn #endif +// USB interrupt vectors to enable in NVIC. The EXTI-line USB wakeup interrupt +// (USBWakeUp_IRQn, and USBWakeUp_RMP_IRQn on F3) is left commented out: resume is +// handled in-band via ISTR.WKUP in the USB_LP/HP ISR; the EXTI line is only needed to +// wake the core from STOP mode, which this driver does not implement (it never arms or +// clears that EXTI line, so enabling its NVIC vector can only spuriously fire/freeze). +// TODO: uncomment USBWakeUp_IRQn (+ arm/clear its EXTI line) when adding STOP-mode wakeup. static const IRQn_Type fsdev_irq[] = { #if TU_CHECK_MCU(OPT_MCU_STM32F0, OPT_MCU_STM32L0, OPT_MCU_STM32L4, OPT_MCU_STM32U5) USB_IRQn, @@ -192,15 +196,15 @@ static const IRQn_Type fsdev_irq[] = { #elif CFG_TUSB_MCU == OPT_MCU_STM32F1 USB_HP_CAN1_TX_IRQn, USB_LP_CAN1_RX0_IRQn, - USBWakeUp_IRQn, + //USBWakeUp_IRQn, #elif CFG_TUSB_MCU == OPT_MCU_STM32F3 USB_HP_CAN_TX_IRQn, USB_LP_CAN_RX0_IRQn, - USBWakeUp_IRQn, + //USBWakeUp_IRQn, #elif TU_CHECK_MCU(OPT_MCU_STM32G4, OPT_MCU_STM32L1) USB_HP_IRQn, USB_LP_IRQn, - USBWakeUp_IRQn, + //USBWakeUp_IRQn, #elif CFG_TUSB_MCU == OPT_MCU_STM32WB USB_HP_IRQn, USB_LP_IRQn, @@ -223,7 +227,7 @@ TU_ATTR_ALWAYS_INLINE static inline void fsdev_int_enable(uint8_t rhport) { if (SYSCFG->CFGR1 & SYSCFG_CFGR1_USB_IT_RMP) { NVIC_EnableIRQ(USB_HP_IRQn); NVIC_EnableIRQ(USB_LP_IRQn); - NVIC_EnableIRQ(USBWakeUp_RMP_IRQn); + //NVIC_EnableIRQ(USBWakeUp_RMP_IRQn); } else #endif { @@ -243,7 +247,7 @@ TU_ATTR_ALWAYS_INLINE static inline void fsdev_int_disable(uint8_t rhport) { if (SYSCFG->CFGR1 & SYSCFG_CFGR1_USB_IT_RMP) { NVIC_DisableIRQ(USB_HP_IRQn); NVIC_DisableIRQ(USB_LP_IRQn); - NVIC_DisableIRQ(USBWakeUp_RMP_IRQn); + //NVIC_DisableIRQ(USBWakeUp_RMP_IRQn); } else #endif { -- cgit v1.3.1 From d9dc891ee2aaedb733da4c0f2fee5a68be001ee7 Mon Sep 17 00:00:00 2001 From: hathach Date: Thu, 18 Jun 2026 18:08:25 +0700 Subject: test/hil: fix esp32 audio_test_freertos (FreeRTOS tick), skip metro_m4 Enabling the audio test fleet-wide surfaced failures on esp32-p4/s3 and metro_m4_express: the UAC mic enumerates but arecord fails the iso IN read with EIO, while 18 other boards pass strict=1.000. esp32: root cause is the FreeRTOS tick rate. ESP-IDF defaults CONFIG_FREERTOS_HZ to 100, so the audio task wakes only every 10 ms and can't service the 1 ms UAC iso frames -> underrun -> arecord EIO. (The same dwc2 driver passes on STM32, whose FreeRTOSConfig is 1000 Hz.) Set CONFIG_FREERTOS_HZ=1000 in the example sdkconfig.defaults; the example defaults are honored in the generated sdkconfig alongside the BSP's, so this takes effect. metro_m4_express (samd51): not tick-rate -- its FreeRTOSConfig is already 1000 Hz like the passing boards -- so it's a separate iso-IN issue, skipped for now. Co-Authored-By: Claude Opus 4.8 (1M context) --- examples/device/audio_test_freertos/sdkconfig.defaults | 3 +++ test/hil/tinyusb.json | 3 ++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/examples/device/audio_test_freertos/sdkconfig.defaults b/examples/device/audio_test_freertos/sdkconfig.defaults index 83871619e..6e7a1cf52 100644 --- a/examples/device/audio_test_freertos/sdkconfig.defaults +++ b/examples/device/audio_test_freertos/sdkconfig.defaults @@ -1,3 +1,6 @@ CONFIG_IDF_CMAKE=y +# 1000 Hz tick: the UAC iso IN endpoint must be serviced every 1 ms frame; +# ESP-IDF's default 100 Hz starves the audio task -> host capture fails (arecord EIO). +CONFIG_FREERTOS_HZ=1000 CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK=y CONFIG_FREERTOS_SUPPORT_STATIC_ALLOCATION=y diff --git a/test/hil/tinyusb.json b/test/hil/tinyusb.json index 41d94bc00..a534f2601 100644 --- a/test/hil/tinyusb.json +++ b/test/hil/tinyusb.json @@ -132,6 +132,7 @@ "device": true, "host": false, "dual": true, + "skip": ["device/audio_test_freertos"], "dev_attached": [ { "vid_pid": "067b_2303", @@ -139,7 +140,7 @@ "is_cdc": true } ], - "comment": "pl23x" + "comment": "pl23x; audio_test_freertos skipped: samd51 iso-IN capture fails (arecord EIO)" }, "flasher": { "name": "jlink", -- cgit v1.3.1 From e7b373ede2dc1ab7322d9687bc5ca87dd156e355 Mon Sep 17 00:00:00 2001 From: hathach Date: Thu, 18 Jun 2026 19:37:12 +0700 Subject: ci(review): run Claude PR review at max effort Pass --effort max to the claude CLI in the auto-review workflow so PR reviews run at maximum reasoning effort. Switch claude_args to a multi-line block scalar for readability, keeping --max-turns 50 and --model claude-opus-4-8 unchanged. Co-Authored-By: Claude Fable 5 --- .github/workflows/claude-code-review.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/claude-code-review.yml b/.github/workflows/claude-code-review.yml index 60cc4db4b..59019616f 100644 --- a/.github/workflows/claude-code-review.yml +++ b/.github/workflows/claude-code-review.yml @@ -53,5 +53,8 @@ jobs: # TEMPORARY: expose the full Claude transcript in the Actions log for # debugging. Revert to remove once done. show_full_output: true - claude_args: '--max-turns 50 --model claude-opus-4-8' + claude_args: | + --max-turns 50 + --model claude-opus-4-8 + --effort max # See https://github.com/anthropics/claude-code-action/blob/main/docs/usage.md -- cgit v1.3.1 From ab7888bc8f02226afbba662c7b7fcf265faa17a3 Mon Sep 17 00:00:00 2001 From: hathach Date: Fri, 19 Jun 2026 21:00:01 +0700 Subject: video: assert usbd_edpt_iso_activate() result in _open_vs_itf() The isochronous streaming endpoint was activated with the usbd_edpt_iso_activate() return value ignored, unlike every neighbouring open in the same function (usbd_edpt_open() is wrapped in TU_ASSERT on both the non-ISO-alloc fallback and the bulk branch). When a DCD refuses the iso endpoint -- e.g. it has no isochronous support, or the requested packet size does not fit its endpoint buffers -- that failure was silently swallowed and the alternate setting was reported as opened, leaving the host streaming to an endpoint the device never armed. Wrap it in TU_ASSERT so the open fails cleanly and the refusal propagates, matching the adjacent endpoint-open calls. Co-Authored-By: Claude Opus 4.8 (1M context) --- src/class/video/video_device.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/class/video/video_device.c b/src/class/video/video_device.c index bbcfe45d5..e31ab4194 100644 --- a/src/class/video/video_device.c +++ b/src/class/video/video_device.c @@ -865,7 +865,7 @@ static bool _open_vs_itf(uint8_t rhport, videod_streaming_interface_t *stm, uint /* FS must be less than or equal to max packet size */ TU_VERIFY (tu_edpt_packet_size(ep) >= max_size); #ifdef TUP_DCD_EDPT_ISO_ALLOC - usbd_edpt_iso_activate(rhport, ep); + TU_ASSERT(usbd_edpt_iso_activate(rhport, ep)); #else TU_ASSERT(usbd_edpt_open(rhport, ep)); #endif -- cgit v1.3.1 From 299c0a55629691f6bbded895a4be377633e815ab Mon Sep 17 00:00:00 2001 From: Ha Thach Date: Mon, 22 Jun 2026 15:33:03 +0700 Subject: ci: post HIL report comment from workflow_run so it works on forked PRs (#3723) * ci: post HIL report comment from workflow_run so it works on forked PRs --- .github/workflows/build.yml | 47 ------------ .github/workflows/metrics_comment.yml | 39 ---------- .github/workflows/pr_comment.yml | 131 ++++++++++++++++++++++++++++++++++ 3 files changed, 131 insertions(+), 86 deletions(-) delete mode 100644 .github/workflows/metrics_comment.yml create mode 100644 .github/workflows/pr_comment.yml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 3f6458285..c8c597e50 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -171,10 +171,6 @@ jobs: mv metrics_compare.md $COMPARE_FILE gh release upload $CURR_TAG metrics.json $COMPARE_FILE - - name: Save PR number - if: github.event_name == 'pull_request' - run: echo ${{ github.event.number }} > pr_number.txt - - name: Upload Metrics Comment Artifact if: github.event_name == 'pull_request' uses: actions/upload-artifact@v7 @@ -183,7 +179,6 @@ jobs: path: | metrics_compare.md metrics.json - pr_number.txt - name: Post Code Metrics as PR Comment if: (github.event_name == 'workflow_dispatch') || (github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork == false) @@ -420,45 +415,3 @@ jobs: path: hil_report.md if-no-files-found: ignore overwrite: true - - # --------------------------------------- - # Combine HIL results from the rigs into a single sticky PR comment (one table per rig) - # --------------------------------------- - hil-report: - needs: [ hil-tinyusb, hil-hfp-iar ] - if: | - always() && - (needs.hil-tinyusb.result != 'skipped' || needs.hil-hfp-iar.result != 'skipped') && - github.event_name == 'pull_request' && - github.repository_owner == 'hathach' && - github.event.pull_request.head.repo.fork == false - runs-on: ubuntu-latest - permissions: - pull-requests: write - steps: - - name: Download HIL reports - uses: actions/download-artifact@v5 - with: - pattern: hil-report-* - path: hil-reports - - - name: Combine rig reports (one table per rig) - run: | - { - echo "## Hardware-in-the-loop (HIL) Test Report" - echo - for d in hil-reports/hil-report-*; do - [ -d "$d" ] || continue - echo "### ${d#hil-reports/hil-report-}" - echo - cat "$d/hil_report.md" 2>/dev/null || echo "_no report produced_" - echo - done - } > hil_combined.md - cat hil_combined.md - - - name: Post HIL report as sticky PR comment - uses: marocchino/sticky-pull-request-comment@v2 - with: - header: hil-report - path: hil_combined.md diff --git a/.github/workflows/metrics_comment.yml b/.github/workflows/metrics_comment.yml deleted file mode 100644 index 5d250211f..000000000 --- a/.github/workflows/metrics_comment.yml +++ /dev/null @@ -1,39 +0,0 @@ -name: Metrics Comment - -on: - workflow_run: - workflows: ["Build"] - types: - - completed - -jobs: - post-comment: - runs-on: ubuntu-latest - if: > - github.event.workflow_run.event == 'pull_request' && - github.event.workflow_run.conclusion == 'success' - permissions: - actions: read - pull-requests: write - steps: - - name: Download Artifacts - uses: actions/download-artifact@v5 - with: - run-id: ${{ github.event.workflow_run.id }} - github-token: ${{ secrets.GITHUB_TOKEN }} - name: metrics-comment - - - name: Read PR Number - id: pr_number - run: | - if [ -f pr_number.txt ]; then - echo "number=$(cat pr_number.txt)" >> $GITHUB_OUTPUT - fi - - - name: Post Code Metrics as PR Comment - if: steps.pr_number.outputs.number != '' - uses: marocchino/sticky-pull-request-comment@v2 - with: - header: code-metrics - path: metrics_compare.md - number: ${{ steps.pr_number.outputs.number }} diff --git a/.github/workflows/pr_comment.yml b/.github/workflows/pr_comment.yml new file mode 100644 index 000000000..4d50817b4 --- /dev/null +++ b/.github/workflows/pr_comment.yml @@ -0,0 +1,131 @@ +name: PR Comment + +on: + workflow_run: + workflows: ["Build"] + types: + - completed + +jobs: + # Resolve the PR number from trusted workflow_run metadata, NOT from build artifacts: a forked PR + # controls its own Build run and could plant any number, which the privileged jobs below would + # then post to. Same-repo PRs populate workflow_run.pull_requests; for forks it is empty, so look + # the PR up by the trusted head SHA. + pr_number: + if: github.event.workflow_run.event == 'pull_request' + runs-on: ubuntu-latest + permissions: + contents: read + pull-requests: read + outputs: + number: ${{ steps.resolve.outputs.number }} + steps: + - name: Resolve PR number + id: resolve + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + REPO: ${{ github.repository }} + HEAD_SHA: ${{ github.event.workflow_run.head_sha }} + HEAD_BRANCH: ${{ github.event.workflow_run.head_branch }} + HEAD_REPO: ${{ github.event.workflow_run.head_repository.full_name }} + PRS_JSON: ${{ toJSON(github.event.workflow_run.pull_requests) }} + run: | + # Every lookup is best-effort: on any miss the number stays empty and the comment jobs + # below simply skip (never a failed check). + # Same-repo PRs: workflow_run.pull_requests is populated. + num=$(printf '%s' "$PRS_JSON" | jq -r '.[0].number // empty') + # Fork PRs: pull_requests is empty. Find the open PR by its trusted head ref and confirm + # its head SHA matches the built commit. + if [ -z "$num" ] && [ -n "$HEAD_BRANCH" ] && [ -n "$HEAD_REPO" ]; then + num=$(gh api --method GET "repos/$REPO/pulls" \ + -f state=open -f head="${HEAD_REPO%%/*}:$HEAD_BRANCH" \ + --jq '[.[] | select(.head.sha == env.HEAD_SHA)][0].number // empty' 2>/dev/null || true) + fi + echo "number=$num" >> "$GITHUB_OUTPUT" + + metrics-comment: + needs: pr_number + if: > + github.event.workflow_run.conclusion == 'success' && + needs.pr_number.outputs.number != '' + runs-on: ubuntu-latest + permissions: + actions: read + pull-requests: write + steps: + - name: Download Artifacts + uses: actions/download-artifact@v5 + with: + run-id: ${{ github.event.workflow_run.id }} + github-token: ${{ secrets.GITHUB_TOKEN }} + name: metrics-comment + # Best-effort: docs-only PRs skip code-metrics, so the artifact may be absent. + continue-on-error: true + + - name: Post Code Metrics as PR Comment + if: hashFiles('metrics_compare.md') != '' + uses: marocchino/sticky-pull-request-comment@v2 + with: + header: code-metrics + path: metrics_compare.md + number: ${{ needs.pr_number.outputs.number }} + + # --------------------------------------- + # Combine the rigs' HIL reports into one sticky PR comment (one table per rig). + # Runs here (workflow_run / base-repo context) rather than in build.yml so it also works on + # forked PRs, whose build-side GITHUB_TOKEN is read-only and cannot post comments. Posts even + # on build/HIL failure (when the report matters most); skips only on cancellation. + # --------------------------------------- + hil-comment: + needs: pr_number + if: > + github.event.workflow_run.conclusion != 'cancelled' && + needs.pr_number.outputs.number != '' + runs-on: ubuntu-latest + permissions: + actions: read + pull-requests: write + steps: + - name: Download HIL reports + uses: actions/download-artifact@v5 + with: + run-id: ${{ github.event.workflow_run.id }} + github-token: ${{ secrets.GITHUB_TOKEN }} + pattern: hil-report-* + path: hil-reports + continue-on-error: true + + - name: Combine rig reports (one table per rig) + id: combine + run: | + shopt -s nullglob + dirs=(hil-reports/hil-report-*) + if [ ${#dirs[@]} -eq 0 ]; then + echo "No HIL reports found" + exit 0 + fi + { + echo "## Hardware-in-the-loop (HIL) Test Report" + echo + for d in "${dirs[@]}"; do + [ -d "$d" ] || continue + echo "### ${d#hil-reports/hil-report-}" + echo + cat "$d/hil_report.md" 2>/dev/null || echo "_no report produced_" + echo + done + } > hil_combined.md + # Fork PRs can influence report content and this job posts in base-repo context, so + # neutralize @-mentions (insert a zero-width space) to prevent notification abuse. + zwsp=$(printf '\342\200\213') + sed -i -E "s/@([A-Za-z0-9_-])/@${zwsp}\1/g" hil_combined.md + cat hil_combined.md + echo "found=true" >> "$GITHUB_OUTPUT" + + - name: Post HIL report as sticky PR comment + if: steps.combine.outputs.found == 'true' + uses: marocchino/sticky-pull-request-comment@v2 + with: + header: hil-report + path: hil_combined.md + number: ${{ needs.pr_number.outputs.number }} -- cgit v1.3.1