summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHiFiPHile <[email protected]>2026-08-27 03:36:11 +0200
committerHiFiPHile <[email protected]>2026-08-27 10:34:34 +0200
commit544ba0453a02bbee3243f592a088eb6838614dfd (patch)
tree148b6a67245a23392e865d30670125a3ddb080a2
parent178426720cbe2f170872cc1469329570d37726a3 (diff)
audio: classify feedback endpoints by protocol
Use UAC1 synchronization attributes and UAC2 endpoint usage fields according to the active protocol. This prevents UAC2 audio-data IN endpoints from being mistaken for explicit feedback endpoints. Signed-off-by: HiFiPHile <[email protected]>
-rw-r--r--src/class/audio/audio_host.c31
-rw-r--r--test/unit-test/test/host/audio/test_audio_host.c29
2 files changed, 50 insertions, 10 deletions
diff --git a/src/class/audio/audio_host.c b/src/class/audio/audio_host.c
index c73e9f225..5c7ce5f8e 100644
--- a/src/class/audio/audio_host.c
+++ b/src/class/audio/audio_host.c
@@ -1297,15 +1297,28 @@ static const uint8_t *audioh_parse_as(audioh_interface_t *p_audio, const audioh_
break;
}
- const uint8_t usage = desc_endpoint->bmAttributes.usage;
- const bool implicit_feedback =
- usage == (TUSB_ISO_EP_ATT_IMPLICIT_FB >> 4) && tu_edpt_dir(desc_endpoint->bEndpointAddress) == TUSB_DIR_IN;
- const bool explicit_feedback =
- tu_edpt_dir(desc_endpoint->bEndpointAddress) == TUSB_DIR_IN &&
- (usage == (TUSB_ISO_EP_ATT_EXPLICIT_FB >> 4) ||
- (usage == (TUSB_ISO_EP_ATT_DATA >> 4) && desc_endpoint->bmAttributes.sync == TUSB_ISO_EP_ATT_NO_SYNC));
+ bool is_data_ep = false;
+ bool is_explicit_feedback = false;
+ switch (p_audio->protocol) {
+ #if CFG_TUH_AUDIO_PROTOCOLS & TUH_AUDIO_PROTOCOL_UAC1
+ case AUDIO_INT_PROTOCOL_CODE_V1:
+ is_data_ep = desc_endpoint->bmAttributes.sync != TUSB_ISO_EP_ATT_NO_SYNC;
+ is_explicit_feedback = tu_edpt_dir(desc_endpoint->bEndpointAddress) == TUSB_DIR_IN && !is_data_ep;
+ break;
+ #endif
+ #if CFG_TUH_AUDIO_PROTOCOLS & TUH_AUDIO_PROTOCOL_UAC2
+ case AUDIO_INT_PROTOCOL_CODE_V2:
+ is_data_ep = desc_endpoint->bmAttributes.usage == (TUSB_ISO_EP_ATT_DATA >> 4) ||
+ desc_endpoint->bmAttributes.usage == (TUSB_ISO_EP_ATT_IMPLICIT_FB >> 4);
+ is_explicit_feedback = tu_edpt_dir(desc_endpoint->bEndpointAddress) == TUSB_DIR_IN &&
+ desc_endpoint->bmAttributes.usage == (TUSB_ISO_EP_ATT_EXPLICIT_FB >> 4);
+ break;
+ #endif
+ default:
+ break;
+ }
- if (explicit_feedback) {
+ if (is_explicit_feedback) {
const uint16_t fb_ep_size = tu_edpt_packet_size(desc_endpoint);
if (has_feedback_ep || (fb_ep_size != 3 && fb_ep_size != 4)) {
TU_LOG_DRV(" AUDIO AS itf %u alt %u: invalid/extra feedback ep %02x ignored\r\n", itf_num, alt,
@@ -1325,7 +1338,7 @@ static const uint8_t *audioh_parse_as(audioh_interface_t *p_audio, const audioh_
break;
}
- if (usage == (TUSB_ISO_EP_ATT_DATA >> 4) || implicit_feedback) {
+ if (is_data_ep) {
if (has_data_ep) {
TU_LOG_DRV(" AUDIO AS itf %u alt %u: extra data ep %02x ignored\r\n", itf_num, alt,
desc_endpoint->bEndpointAddress);
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 aa2f70f9b..9f8957984 100644
--- a/test/unit-test/test/host/audio/test_audio_host.c
+++ b/test/unit-test/test/host/audio/test_audio_host.c
@@ -295,6 +295,9 @@ uint32_t tu_edpt_stream_read_xfer(tu_edpt_stream_t *s) {
#define TEST_UAC2_FORMAT(_bytes, _bits) \
6, TUSB_DESC_CS_INTERFACE, AUDIO20_CS_AS_INTERFACE_FORMAT_TYPE, AUDIO20_FORMAT_TYPE_I, _bytes, _bits
+#define TEST_UAC2_DATA_EP(_ep, _attr, _size, _interval) \
+ 7, TUSB_DESC_ENDPOINT, _ep, (TUSB_XFER_ISOCHRONOUS | (_attr)), U16_TO_U8S_LE(_size), _interval
+
#define TEST_UAC2_CS_DATA_EP \
8, TUSB_DESC_CS_ENDPOINT, AUDIO20_CS_EP_SUBTYPE_GENERAL, AUDIO20_CS_AS_ISO_DATA_EP_ATT_NON_MAX_PACKETS_OK, 0, \
AUDIO20_CS_AS_ISO_DATA_EP_LOCK_DELAY_UNIT_UNDEFINED, U16_TO_U8S_LE(0)
@@ -430,6 +433,19 @@ static const uint8_t uac2_duplex_shared_clock[] = {
TEST_UAC2_CS_DATA_EP,
};
+static const uint8_t uac2_capture_no_sync_data[] = {
+ TEST_UAC2_AC_HEADER(46),
+ TEST_UAC2_CLOCK_SOURCE(PLAYBACK_CLOCK, AUDIO20_CTRL_RW << AUDIO20_CLOCK_SOURCE_CTRL_CLK_FRQ_POS),
+ TEST_UAC2_INPUT_TERM(CAPTURE_INPUT_TERM, AUDIO_TERM_TYPE_IN_GENERIC_MIC, PLAYBACK_CLOCK, 1),
+ TEST_UAC2_OUTPUT_TERM(CAPTURE_OUTPUT_TERM, AUDIO_TERM_TYPE_USB_STREAMING, CAPTURE_INPUT_TERM, PLAYBACK_CLOCK),
+ TEST_UAC2_AS_ALT0,
+ TEST_UAC2_AS_INTERFACE(1, 1),
+ TEST_UAC2_AS_GENERAL(CAPTURE_OUTPUT_TERM, 1),
+ TEST_UAC2_FORMAT(2, 16),
+ TEST_UAC2_DATA_EP(0x81, TUSB_ISO_EP_ATT_DATA, 96, 1),
+ TEST_UAC2_CS_DATA_EP,
+};
+
static const uint8_t playback_with_implicit_feedback[] = {
TEST_UAC1_AC_HEADER_2,
TEST_UAC1_INPUT_TERM(PLAYBACK_INPUT_TERM, AUDIO_TERM_TYPE_USB_STREAMING, 2),
@@ -446,7 +462,7 @@ static const uint8_t playback_with_implicit_feedback[] = {
TEST_UAC1_AS_INTERFACE_NUM(AUDIO_AS_IN_ITF, 1, 1),
TEST_UAC1_AS_GENERAL(CAPTURE_OUTPUT_TERM),
TEST_UAC1_FORMAT(1, 2, 16, 48000),
- TEST_UAC1_DATA_EP(0x81, TUSB_ISO_EP_ATT_IMPLICIT_FB, 96, 1),
+ TEST_UAC1_DATA_EP(0x81, TUSB_ISO_EP_ATT_ASYNCHRONOUS | TUSB_ISO_EP_ATT_IMPLICIT_FB, 96, 1),
TEST_UAC1_CS_DATA_EP,
};
@@ -1101,6 +1117,17 @@ void test_audio_host_treats_implicit_feedback_as_audio_in_endpoint(void) {
TEST_ASSERT_EQUAL(TUH_AUDIO_STREAM_CAPTURE, tuh_audio_stream_direction(0, 1));
}
+void test_audio_host_uac2_treats_no_sync_data_usage_as_audio_in_endpoint(void) {
+ open_descriptors(uac2_capture_no_sync_data, sizeof(uac2_capture_no_sync_data));
+ TEST_ASSERT_TRUE(audioh_set_config(AUDIO_DEV_ADDR, AUDIO_AC_ITF));
+ complete_uac2_clock_range(44100, 48000);
+
+ TEST_ASSERT_TRUE(tuh_audio_mounted(0));
+ TEST_ASSERT_EQUAL_UINT8(1, tuh_audio_stream_count(0));
+ TEST_ASSERT_EQUAL(TUH_AUDIO_STREAM_CAPTURE, tuh_audio_stream_direction(0, 0));
+ TEST_ASSERT_EQUAL_UINT8(2, tuh_audio_config_count(0, 0));
+}
+
void test_audio_host_ignores_second_data_endpoint_in_same_as_interface(void) {
open_descriptors(playback_with_extra_data_endpoint, sizeof(playback_with_extra_data_endpoint));