diff options
| author | HiFiPHile <[email protected]> | 2026-08-26 09:16:49 +0200 |
|---|---|---|
| committer | HiFiPHile <[email protected]> | 2026-08-26 09:16:49 +0200 |
| commit | 485d8a245dad8acc2fb6e91b66cfe86f4fa3a022 (patch) | |
| tree | 64b29b1239a9207cf088d39ca009f35606b311ad /test | |
| parent | e2c6dcf20a55b74c763411ba2162884a346f52a6 (diff) | |
implement explicit feeback support
Signed-off-by: HiFiPHile <[email protected]>
Diffstat (limited to 'test')
| -rw-r--r-- | test/unit-test/test/host/audio/test_audio_host.c | 196 |
1 files changed, 167 insertions, 29 deletions
diff --git a/test/unit-test/test/host/audio/test_audio_host.c b/test/unit-test/test/host/audio/test_audio_host.c index e5056bc2c..3e11e733c 100644 --- a/test/unit-test/test/host/audio/test_audio_host.c +++ b/test/unit-test/test/host/audio/test_audio_host.c @@ -43,8 +43,9 @@ static bool edpt_close_result; static uint8_t closed_ep[4]; static uint8_t edpt_close_count; -static bool edpt_busy; +static uint32_t edpt_busy_mask; static bool edpt_xfer_result; +static uint8_t edpt_xfer_ep[16]; static uint16_t edpt_xfer_bytes[16]; static uint8_t edpt_xfer_data[16][8]; static uint8_t *edpt_xfer_buffer[16]; @@ -55,6 +56,15 @@ static uint8_t err_cb_idx; static uint8_t err_cb_stream_idx; static uint16_t err_cb_xferred_bytes; +static uint32_t edpt_mask(uint8_t ep_addr) { + const uint8_t bit = tu_edpt_number(ep_addr) + (tu_edpt_dir(ep_addr) == TUSB_DIR_IN ? 16 : 0); + return 1u << bit; +} + +static void complete_edpt(uint8_t ep_addr) { + edpt_busy_mask &= ~edpt_mask(ep_addr); +} + void tuh_audio_err_cb(uint8_t idx, uint8_t stream_idx, uint16_t xferred_bytes) { err_cb_count++; err_cb_idx = idx; @@ -93,7 +103,7 @@ bool tuh_edpt_close(uint8_t daddr, uint8_t ep_addr) { closed_ep[edpt_close_count++] = ep_addr; } if (edpt_close_result) { - edpt_busy = false; + complete_edpt(ep_addr); } return edpt_close_result; } @@ -114,42 +124,40 @@ bool tuh_interface_set(uint8_t daddr, uint8_t itf_num, uint8_t itf_alt, tuh_xfer bool usbh_edpt_xfer_with_callback(uint8_t dev_addr, uint8_t ep_addr, uint8_t *buffer, uint16_t total_bytes, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { (void)dev_addr; - (void)ep_addr; (void)complete_cb; (void)user_data; if (edpt_xfer_count < TU_ARRAY_SIZE(edpt_xfer_bytes)) { + edpt_xfer_ep[edpt_xfer_count] = ep_addr; edpt_xfer_bytes[edpt_xfer_count] = total_bytes; edpt_xfer_buffer[edpt_xfer_count] = buffer; memcpy(edpt_xfer_data[edpt_xfer_count], buffer, TU_MIN(sizeof(edpt_xfer_data[0]), total_bytes)); edpt_xfer_count++; } if (!edpt_xfer_result) { - edpt_busy = false; // match usbh_edpt_xfer() cleanup after HCD rejection + complete_edpt(ep_addr); // match usbh_edpt_xfer() cleanup after HCD rejection } return edpt_xfer_result; } bool usbh_edpt_claim(uint8_t dev_addr, uint8_t ep_addr) { (void)dev_addr; - (void)ep_addr; - if (edpt_busy) { + const uint32_t mask = edpt_mask(ep_addr); + if (edpt_busy_mask & mask) { return false; } - edpt_busy = true; + edpt_busy_mask |= mask; return true; } bool usbh_edpt_release(uint8_t dev_addr, uint8_t ep_addr) { (void)dev_addr; - (void)ep_addr; - edpt_busy = false; + complete_edpt(ep_addr); return true; } bool usbh_edpt_busy(uint8_t dev_addr, uint8_t ep_addr) { (void)dev_addr; - (void)ep_addr; - return edpt_busy; + return (edpt_busy_mask & edpt_mask(ep_addr)) != 0; } void usbh_driver_set_config_complete(uint8_t dev_addr, uint8_t itf_num) { @@ -487,6 +495,30 @@ static const uint8_t playback_44100_max_packets_only[] = { TEST_UAC1_CS_DATA_EP_ATTR(AUDIO10_CS_AS_ISO_DATA_EP_ATT_MAX_PACKETS_ONLY), }; +static const uint8_t playback_44100_with_feedback_10_14[] = { + TEST_UAC1_AC_HEADER, + TEST_UAC1_INPUT_TERM(PLAYBACK_INPUT_TERM, AUDIO_TERM_TYPE_USB_STREAMING, 2), + TEST_UAC1_AS_ALT0, + TEST_UAC1_AS_INTERFACE(1, 2), + TEST_UAC1_AS_GENERAL(PLAYBACK_INPUT_TERM), + TEST_UAC1_FORMAT(2, 2, 16, 44100), + TEST_UAC1_DATA_EP(0x01, TUSB_ISO_EP_ATT_ADAPTIVE, 180, 1), + TEST_UAC1_CS_DATA_EP_ATTR(AUDIO10_CS_AS_ISO_DATA_EP_ATT_MAX_PACKETS_ONLY), + TEST_UAC1_DATA_EP(0x81, TUSB_ISO_EP_ATT_EXPLICIT_FB, 3, 1), +}; + +static const uint8_t playback_44100_with_feedback_16_16[] = { + TEST_UAC1_AC_HEADER, + TEST_UAC1_INPUT_TERM(PLAYBACK_INPUT_TERM, AUDIO_TERM_TYPE_USB_STREAMING, 2), + TEST_UAC1_AS_ALT0, + TEST_UAC1_AS_INTERFACE(1, 2), + TEST_UAC1_AS_GENERAL(PLAYBACK_INPUT_TERM), + TEST_UAC1_FORMAT(2, 2, 16, 44100), + TEST_UAC1_DATA_EP(0x01, TUSB_ISO_EP_ATT_ADAPTIVE, 180, 1), + TEST_UAC1_CS_DATA_EP_ATTR(AUDIO10_CS_AS_ISO_DATA_EP_ATT_MAX_PACKETS_ONLY), + TEST_UAC1_DATA_EP(0x81, TUSB_ISO_EP_ATT_EXPLICIT_FB, 4, 1), +}; + static const uint8_t playback_11025_interval4[] = { TEST_UAC1_AC_HEADER, TEST_UAC1_INPUT_TERM(PLAYBACK_INPUT_TERM, AUDIO_TERM_TYPE_USB_STREAMING, 2), @@ -529,6 +561,50 @@ static void complete_control_xfer_with_u16(uint16_t value) { complete_control_xfer(XFER_RESULT_SUCCESS); } +static uint16_t edpt_xfer_bytes_at(uint8_t ep_addr, uint8_t ep_xfer_idx) { + uint8_t found = 0; + for (uint8_t i = 0; i < edpt_xfer_count; i++) { + if (edpt_xfer_ep[i] == ep_addr && found++ == ep_xfer_idx) { + return edpt_xfer_bytes[i]; + } + } + TEST_FAIL_MESSAGE("Endpoint transfer not found"); + return 0; +} + +static uint8_t *edpt_xfer_buffer_at(uint8_t ep_addr, uint8_t ep_xfer_idx) { + uint8_t found = 0; + for (uint8_t i = 0; i < edpt_xfer_count; i++) { + if (edpt_xfer_ep[i] == ep_addr && found++ == ep_xfer_idx) { + return edpt_xfer_buffer[i]; + } + } + TEST_FAIL_MESSAGE("Endpoint transfer not found"); + return NULL; +} + +static void complete_playback_xfer(void) { + complete_edpt(0x01); + TEST_ASSERT_TRUE(audioh_xfer_cb(AUDIO_DEV_ADDR, 0x01, XFER_RESULT_SUCCESS, 0)); +} + +static void complete_feedback_xfer(uint32_t feedback_q16, uint8_t length) { + uint8_t *buffer = edpt_xfer_buffer_at(0x81, 0); + if (length == 3) { + const uint32_t feedback_q14 = feedback_q16 >> 2; + buffer[0] = (uint8_t)feedback_q14; + buffer[1] = (uint8_t)(feedback_q14 >> 8); + buffer[2] = (uint8_t)(feedback_q14 >> 16); + } else { + buffer[0] = (uint8_t)feedback_q16; + buffer[1] = (uint8_t)(feedback_q16 >> 8); + buffer[2] = (uint8_t)(feedback_q16 >> 16); + buffer[3] = (uint8_t)(feedback_q16 >> 24); + } + complete_edpt(0x81); + TEST_ASSERT_TRUE(audioh_xfer_cb(AUDIO_DEV_ADDR, 0x81, XFER_RESULT_SUCCESS, length)); +} + void setUp(void) { interface_set_result = true; memset(&interface_xfer, 0, sizeof(interface_xfer)); @@ -549,8 +625,9 @@ void setUp(void) { memset(closed_ep, 0, sizeof(closed_ep)); edpt_close_count = 0; - edpt_busy = false; + edpt_busy_mask = 0; edpt_xfer_result = true; + memset(edpt_xfer_ep, 0, sizeof(edpt_xfer_ep)); memset(edpt_xfer_bytes, 0, sizeof(edpt_xfer_bytes)); memset(edpt_xfer_data, 0, sizeof(edpt_xfer_data)); memset(edpt_xfer_buffer, 0, sizeof(edpt_xfer_buffer)); @@ -598,17 +675,22 @@ static void mount_descriptors(const uint8_t *desc, uint16_t desc_len) { control_xfer_count = 0; } -void test_audio_host_ignores_explicit_feedback_endpoint(void) { +void test_audio_host_saves_and_opens_explicit_feedback_endpoint(void) { // A MIDI-only AC collection must not consume an Audio Host instance. TEST_ASSERT_EQUAL_UINT16(0, audioh_open(0, AUDIO_DEV_ADDR, (const tusb_desc_interface_t *)midi_only_collection, sizeof(midi_only_collection))); TEST_ASSERT_EQUAL_UINT8(0, tuh_audio_get_dev_addr(0)); - open_descriptors(playback_with_explicit_feedback, sizeof(playback_with_explicit_feedback)); + mount_descriptors(playback_with_explicit_feedback, sizeof(playback_with_explicit_feedback)); TEST_ASSERT_EQUAL_UINT8(1, tuh_audio_stream_count(0)); TEST_ASSERT_EQUAL(TUH_AUDIO_STREAM_PLAYBACK, tuh_audio_stream_direction(0, 0)); TEST_ASSERT_EQUAL_UINT8(1, tuh_audio_config_count(0, 0)); + TEST_ASSERT_TRUE(tuh_audio_configure(0, 0, 0)); + TEST_ASSERT_EQUAL_UINT8(2, edpt_open_count); + TEST_ASSERT_EQUAL_HEX8(0x01, opened_ep[0].bEndpointAddress); + TEST_ASSERT_EQUAL_HEX8(0x81, opened_ep[1].bEndpointAddress); + TEST_ASSERT_EQUAL_UINT16(3, tu_edpt_packet_size(&opened_ep[1])); } void test_audio_host_rejects_uac2_interface_without_consuming_instance(void) { @@ -660,7 +742,7 @@ void test_audio_host_maps_capture_fu_declared_before_usb_output_terminal(void) { for (uint8_t packet = 0; packet < 11; packet++) { TEST_ASSERT_NOT_NULL(edpt_xfer_buffer[packet]); memset(edpt_xfer_buffer[packet], packet + 1, 96); - edpt_busy = false; + complete_edpt(0x81); TEST_ASSERT_TRUE(audioh_xfer_cb(AUDIO_DEV_ADDR, 0x81, XFER_RESULT_SUCCESS, 96)); TEST_ASSERT_EQUAL_UINT8(packet + 2, edpt_xfer_count); } @@ -716,7 +798,7 @@ void test_audio_host_sets_sampling_frequency_after_each_stream_activation(void) TEST_ASSERT_EQUAL_UINT8(0, interface_alt); TEST_ASSERT_EQUAL_UINT8(2, interface_set_count); complete_interface_set(XFER_RESULT_SUCCESS); - edpt_busy = false; + complete_edpt(0x81); TEST_ASSERT_TRUE(audioh_xfer_cb(AUDIO_DEV_ADDR, 0x81, XFER_RESULT_SUCCESS, 96)); TEST_ASSERT_TRUE(tuh_audio_start(0, 0)); @@ -798,7 +880,7 @@ void test_audio_host_keeps_running_when_stop_cannot_be_submitted(void) { interface_set_result = false; TEST_ASSERT_FALSE(tuh_audio_stop(0, 0)); - edpt_busy = false; + complete_edpt(0x81); TEST_ASSERT_TRUE(audioh_xfer_cb(AUDIO_DEV_ADDR, 0x81, XFER_RESULT_SUCCESS, 96)); TEST_ASSERT_EQUAL_UINT8(2, edpt_xfer_count); } @@ -1050,13 +1132,15 @@ void test_audio_host_schedules_44100_hz_fractional_packets_with_max_packets_only TEST_ASSERT_EQUAL_UINT8(1, edpt_xfer_count); while (edpt_xfer_count < 5) { - edpt_busy = false; - TEST_ASSERT_TRUE(audioh_xfer_cb(AUDIO_DEV_ADDR, 0x01, XFER_RESULT_SUCCESS, edpt_xfer_bytes[edpt_xfer_count - 1])); + complete_edpt(0x01); + TEST_ASSERT_TRUE(audioh_xfer_cb(AUDIO_DEV_ADDR, 0x01, XFER_RESULT_SUCCESS, + edpt_xfer_bytes[edpt_xfer_count - 1])); } TEST_ASSERT_EQUAL_UINT32(185, tuh_audio_write(0, 0, samples, 185)); while (edpt_xfer_count < 10) { - edpt_busy = false; - TEST_ASSERT_TRUE(audioh_xfer_cb(AUDIO_DEV_ADDR, 0x01, XFER_RESULT_SUCCESS, edpt_xfer_bytes[edpt_xfer_count - 1])); + complete_edpt(0x01); + TEST_ASSERT_TRUE(audioh_xfer_cb(AUDIO_DEV_ADDR, 0x01, XFER_RESULT_SUCCESS, + edpt_xfer_bytes[edpt_xfer_count - 1])); } for (uint8_t i = 0; i < 9; i++) { @@ -1065,6 +1149,62 @@ void test_audio_host_schedules_44100_hz_fractional_packets_with_max_packets_only TEST_ASSERT_EQUAL_UINT16(180, edpt_xfer_bytes[9]); } +void test_audio_host_applies_10_14_feedback_after_fractional_scheduling_loop(void) { + mount_descriptors(playback_44100_with_feedback_10_14, sizeof(playback_44100_with_feedback_10_14)); + + TEST_ASSERT_TRUE(tuh_audio_configure(0, 0, 0)); + TEST_ASSERT_TRUE(tuh_audio_start(0, 0)); + complete_interface_set(XFER_RESULT_SUCCESS); + TEST_ASSERT_EQUAL_UINT16(3, edpt_xfer_bytes_at(0x81, 0)); + TEST_ASSERT_EQUAL_UINT16(176, edpt_xfer_bytes_at(0x01, 0)); + + // Request 44 frames/ms after the first nominal 44.1-kHz packet. The old + // cycle must still emit its 45-frame correction packet before changing rate. + complete_feedback_xfer(44u << 16, 3); + for (uint8_t i = 0; i < 10; i++) { + complete_playback_xfer(); + } + + for (uint8_t i = 0; i < 9; i++) { + TEST_ASSERT_EQUAL_UINT16(176, edpt_xfer_bytes_at(0x01, i)); + } + TEST_ASSERT_EQUAL_UINT16(180, edpt_xfer_bytes_at(0x01, 9)); + TEST_ASSERT_EQUAL_UINT16(176, edpt_xfer_bytes_at(0x01, 10)); +} + +void test_audio_host_accepts_16_16_feedback(void) { + mount_descriptors(playback_44100_with_feedback_16_16, sizeof(playback_44100_with_feedback_16_16)); + + TEST_ASSERT_TRUE(tuh_audio_configure(0, 0, 0)); + TEST_ASSERT_TRUE(tuh_audio_start(0, 0)); + complete_interface_set(XFER_RESULT_SUCCESS); + TEST_ASSERT_EQUAL_UINT16(4, edpt_xfer_bytes_at(0x81, 0)); + + complete_feedback_xfer(45u << 16, 4); + for (uint8_t i = 0; i < 10; i++) { + complete_playback_xfer(); + } + + TEST_ASSERT_EQUAL_UINT16(180, edpt_xfer_bytes_at(0x01, 9)); + TEST_ASSERT_EQUAL_UINT16(180, edpt_xfer_bytes_at(0x01, 10)); +} + +void test_audio_host_ignores_out_of_range_feedback(void) { + mount_descriptors(playback_44100_with_feedback_16_16, sizeof(playback_44100_with_feedback_16_16)); + + TEST_ASSERT_TRUE(tuh_audio_configure(0, 0, 0)); + TEST_ASSERT_TRUE(tuh_audio_start(0, 0)); + complete_interface_set(XFER_RESULT_SUCCESS); + + complete_feedback_xfer(50u << 16, 4); + for (uint8_t i = 0; i < 10; i++) { + complete_playback_xfer(); + } + + TEST_ASSERT_EQUAL_UINT16(180, edpt_xfer_bytes_at(0x01, 9)); + TEST_ASSERT_EQUAL_UINT16(176, edpt_xfer_bytes_at(0x01, 10)); +} + void test_audio_host_uses_exponential_full_speed_iso_interval(void) { uint8_t samples[441 * 4] = {0}; mount_descriptors(playback_11025_interval4, sizeof(playback_11025_interval4)); @@ -1075,15 +1215,13 @@ void test_audio_host_uses_exponential_full_speed_iso_interval(void) { complete_interface_set(XFER_RESULT_SUCCESS); while (edpt_xfer_count < 5) { - edpt_busy = false; - TEST_ASSERT_TRUE(audioh_xfer_cb(AUDIO_DEV_ADDR, 0x01, XFER_RESULT_SUCCESS, - edpt_xfer_bytes[edpt_xfer_count - 1])); + complete_edpt(0x01); + TEST_ASSERT_TRUE(audioh_xfer_cb(AUDIO_DEV_ADDR, 0x01, XFER_RESULT_SUCCESS, edpt_xfer_bytes[edpt_xfer_count - 1])); } TEST_ASSERT_EQUAL_UINT32(185, tuh_audio_write(0, 0, samples, 185)); while (edpt_xfer_count < 10) { - edpt_busy = false; - TEST_ASSERT_TRUE(audioh_xfer_cb(AUDIO_DEV_ADDR, 0x01, XFER_RESULT_SUCCESS, - edpt_xfer_bytes[edpt_xfer_count - 1])); + complete_edpt(0x01); + TEST_ASSERT_TRUE(audioh_xfer_cb(AUDIO_DEV_ADDR, 0x01, XFER_RESULT_SUCCESS, edpt_xfer_bytes[edpt_xfer_count - 1])); } for (uint8_t i = 0; i < 9; i++) { @@ -1107,7 +1245,7 @@ void test_audio_host_sends_silence_when_playback_fifo_has_too_few_frames(void) { TEST_ASSERT_EACH_EQUAL_HEX8(0, edpt_xfer_data[0], sizeof(edpt_xfer_data[0])); TEST_ASSERT_EQUAL_UINT32(1, tuh_audio_write(0, 0, sample, 1)); - edpt_busy = false; + complete_edpt(0x01); TEST_ASSERT_TRUE(audioh_xfer_cb(AUDIO_DEV_ADDR, 0x01, XFER_RESULT_SUCCESS, edpt_xfer_bytes[0])); TEST_ASSERT_EQUAL_UINT8(2, edpt_xfer_count); @@ -1115,7 +1253,7 @@ void test_audio_host_sends_silence_when_playback_fifo_has_too_few_frames(void) { TEST_ASSERT_EACH_EQUAL_HEX8(0, edpt_xfer_data[1], sizeof(edpt_xfer_data[1])); TEST_ASSERT_EQUAL_UINT32(43, tuh_audio_write(0, 0, more_samples, 43)); - edpt_busy = false; + complete_edpt(0x01); TEST_ASSERT_TRUE(audioh_xfer_cb(AUDIO_DEV_ADDR, 0x01, XFER_RESULT_SUCCESS, edpt_xfer_bytes[1])); TEST_ASSERT_EQUAL_UINT8(3, edpt_xfer_count); |
