summaryrefslogtreecommitdiff
path: root/src/class
diff options
context:
space:
mode:
authorHiFiPHile <[email protected]>2026-08-27 06:21:18 +0200
committerHiFiPHile <[email protected]>2026-08-27 10:34:34 +0200
commitb23e33a1ce07297df2f37219861d78d2a4cc9809 (patch)
tree0e4d46110d8e82ef28e9836e3a18e5c622b15430 /src/class
parentfa6283249362278b887364ae51f9d866fcb96ad3 (diff)
audio: expose generic Audio Control requests
Remove the narrow public Feature Unit request API while retaining managed mute and volume helpers. Expose validated Audio Control descriptors during enumeration and provide raw asynchronous and synchronous entity requests for advanced controls. Signed-off-by: HiFiPHile <[email protected]>
Diffstat (limited to 'src/class')
-rw-r--r--src/class/audio/audio_host.c203
-rw-r--r--src/class/audio/audio_host.h61
2 files changed, 113 insertions, 151 deletions
diff --git a/src/class/audio/audio_host.c b/src/class/audio/audio_host.c
index 370958aaf..432109022 100644
--- a/src/class/audio/audio_host.c
+++ b/src/class/audio/audio_host.c
@@ -229,6 +229,11 @@ CFG_TUH_MEM_SECTION static audioh_epbuf_t _audioh_epbuf[CFG_TUH_AUDIO_MAX];
// WEAK APPLICATION CALLBACKS
//--------------------------------------------------------------------+
+TU_ATTR_WEAK void tuh_audio_descriptor_cb(uint8_t idx, const tuh_audio_descriptor_cb_t *desc_cb_data) {
+ (void)idx;
+ (void)desc_cb_data;
+}
+
TU_ATTR_WEAK void tuh_audio_mount_cb(uint8_t idx) {
(void)idx;
}
@@ -417,25 +422,6 @@ static uint32_t audioh_nominal_frames_q16(uint32_t sample_rate, uint8_t ep_inter
return (uint32_t)((numerator + 500000u) / 1000000u);
}
-// Feature Unit controls with variable or unknown width return zero.
-static uint8_t audioh_fu_control_width(uint8_t control_selector) {
- switch (control_selector) {
- case AUDIO10_FU_CTRL_MUTE:
- case AUDIO10_FU_CTRL_BASS:
- case AUDIO10_FU_CTRL_MID:
- case AUDIO10_FU_CTRL_TREBLE:
- case AUDIO10_FU_CTRL_AGC:
- case AUDIO10_FU_CTRL_BASS_BOOST:
- case AUDIO10_FU_CTRL_LOUDNESS:
- return 1;
- case AUDIO10_FU_CTRL_VOLUME:
- case AUDIO10_FU_CTRL_DELAY:
- return 2;
- default:
- return 0;
- }
-}
-
// Preserve the stream identity and FIFO allocation while clearing device state.
static void audioh_stream_reset(tuh_audio_stream_t *s) {
s->daddr = 0;
@@ -1577,6 +1563,13 @@ uint16_t audioh_open(uint8_t rhport, uint8_t dev_addr, const tusb_desc_interface
}
p_audio->stream_count = stream_idx;
+ const tuh_audio_descriptor_cb_t desc_cb_data = {
+ .desc_audio_control = desc_itf,
+ .desc_cs_audio_control = ac_desc.desc_start,
+ .desc_cs_audio_control_len = (uint16_t)(ac_desc.desc_end - ac_desc.desc_start),
+ };
+ tuh_audio_descriptor_cb(idx, &desc_cb_data);
+
return (uint16_t)((uintptr_t)p_desc - (uintptr_t)desc_start);
open_failed:
@@ -1810,16 +1803,6 @@ uint8_t tuh_audio_get_dev_addr(uint8_t idx) {
return _audioh_itf[idx].daddr;
}
-uint8_t tuh_audio_get_feature_unit_id(uint8_t idx, uint8_t stream_idx) {
- TU_VERIFY(idx < CFG_TUH_AUDIO_MAX, 0);
- audioh_interface_t *p_audio = &_audioh_itf[idx];
- TU_VERIFY(p_audio->daddr != 0, 0);
-
- tuh_audio_stream_t *s = audioh_get_stream_by_idx(p_audio, stream_idx);
- TU_VERIFY(s, 0);
- return s->feature_unit_id;
-}
-
bool tuh_audio_mute_supported(uint8_t idx, uint8_t stream_idx) {
TU_VERIFY(idx < CFG_TUH_AUDIO_MAX, false);
audioh_interface_t *p_audio = &_audioh_itf[idx];
@@ -2176,7 +2159,7 @@ uint32_t tuh_audio_read_available(uint8_t dev_idx, uint8_t stream_idx) {
}
//--------------------------------------------------------------------+
-// FEATURE UNIT CONTROLS
+// AUDIO CONTROL REQUESTS
//--------------------------------------------------------------------+
// Release driver-owned request state before invoking the application callback,
@@ -2196,12 +2179,11 @@ static void audioh_fu_set_complete(tuh_xfer_t *xfer) {
}
enum {
- AUDIOH_FU_VALUE_U16,
AUDIOH_FU_VALUE_BOOL,
AUDIOH_FU_VALUE_I16
};
-static uint8_t audioh_fu_cur_request(uint8_t protocol, tusb_dir_t direction) {
+static uint8_t audioh_control_cur_request(uint8_t protocol, tusb_dir_t direction) {
#if !(CFG_TUH_AUDIO_PROTOCOLS & TUH_AUDIO_PROTOCOL_UAC1)
(void)direction;
#endif
@@ -2219,30 +2201,60 @@ static uint8_t audioh_fu_cur_request(uint8_t protocol, tusb_dir_t direction) {
}
}
-static bool audioh_fu_selector_supported(uint8_t protocol, uint8_t control_selector) {
- #if CFG_TUH_AUDIO_PROTOCOLS & TUH_AUDIO_PROTOCOL_UAC2
- if (protocol == AUDIO_INT_PROTOCOL_CODE_V2) {
- return control_selector == AUDIO20_FU_CTRL_MUTE || control_selector == AUDIO20_FU_CTRL_VOLUME;
+static bool audioh_control_submit(uint8_t idx, uint8_t entity_id, tusb_dir_t direction, uint8_t request,
+ uint8_t control_selector, uint8_t channel, void *buffer, uint16_t length,
+ tuh_xfer_t *xfer) {
+ TU_VERIFY(idx < CFG_TUH_AUDIO_MAX && entity_id != 0 && request != 0, false);
+ TU_VERIFY(direction == TUSB_DIR_OUT || direction == TUSB_DIR_IN, false);
+ TU_VERIFY(buffer != NULL || length == 0, false);
+ audioh_interface_t *p_audio = &_audioh_itf[idx];
+ TU_VERIFY(p_audio->mounted, false);
+
+ const tusb_control_request_t setup = {
+ .bmRequestType_bit = {.recipient = TUSB_REQ_RCPT_INTERFACE, .type = TUSB_REQ_TYPE_CLASS, .direction = direction},
+ .bRequest = request,
+ .wValue = tu_htole16(tu_u16(control_selector, channel)),
+ .wIndex = tu_htole16(tu_u16(entity_id, p_audio->ac_itf_num)),
+ .wLength = tu_htole16(length),
+ };
+ xfer->daddr = p_audio->daddr;
+ xfer->ep_addr = 0;
+ xfer->setup = &setup;
+ xfer->buffer = buffer;
+ return tuh_control_xfer(xfer);
+}
+
+bool tuh_audio_control_xfer(uint8_t idx, uint8_t entity_id, tusb_dir_t direction, uint8_t request,
+ uint8_t control_selector, uint8_t channel, void *buffer, uint16_t length,
+ tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+ tuh_xfer_t xfer = {.complete_cb = complete_cb, .user_data = user_data};
+ return audioh_control_submit(idx, entity_id, direction, request, control_selector, channel, buffer, length, &xfer);
+}
+
+tusb_xfer_result_t tuh_audio_control_xfer_sync(uint8_t idx, uint8_t entity_id, tusb_dir_t direction, uint8_t request,
+ uint8_t control_selector, uint8_t channel, void *buffer, uint16_t length,
+ uint32_t *actual_len) {
+ if (actual_len != NULL) {
+ *actual_len = 0;
}
- #else
- (void)protocol;
- (void)control_selector;
- #endif
- return true;
+
+ tuh_xfer_t xfer = {0};
+ if (!audioh_control_submit(idx, entity_id, direction, request, control_selector, channel, buffer, length, &xfer)) {
+ return XFER_RESULT_TIMEOUT;
+ }
+
+ if (actual_len != NULL) {
+ *actual_len = xfer.actual_len;
+ }
+ return xfer.result;
}
static void audioh_fu_value_store(audioh_ctrl_state_t *ctrl, audioh_epbuf_t *epbuf) {
if (ctrl->fu.control.value_type == AUDIOH_FU_VALUE_BOOL) {
*((bool *)ctrl->value) = audioh_fu_ctrl(epbuf)[0] != 0;
- } else if (ctrl->fu.control.width == 1) {
- *((uint16_t *)ctrl->value) = audioh_fu_ctrl(epbuf)[0];
} else {
- const uint16_t value = tu_le16toh(tu_unaligned_read16(audioh_fu_ctrl(epbuf)));
- if (ctrl->fu.control.value_type == AUDIOH_FU_VALUE_I16) {
- *((int16_t *)ctrl->value) = (int16_t)value;
- } else {
- *((uint16_t *)ctrl->value) = value;
- }
+ const uint16_t value = tu_le16toh(tu_unaligned_read16(audioh_fu_ctrl(epbuf)));
+ *((int16_t *)ctrl->value) = (int16_t)value;
}
}
@@ -2317,11 +2329,12 @@ static bool audioh_mount_feature_unit_submit(uint8_t idx) {
tuh_audio_stream_t *s = audioh_get_stream_by_idx(p_audio, ctrl->fu.mount.stream_idx);
TU_ASSERT(s != NULL);
- const bool uac2 = p_audio->protocol == AUDIO_INT_PROTOCOL_CODE_V2;
- const tusb_control_request_t request = {
+ const bool uac2 = p_audio->protocol == AUDIO_INT_PROTOCOL_CODE_V2;
+ const uint8_t selector = uac2 ? AUDIO20_FU_CTRL_VOLUME : AUDIO10_FU_CTRL_VOLUME;
+ const tusb_control_request_t request = {
.bmRequestType_bit = {.recipient = TUSB_REQ_RCPT_INTERFACE, .type = TUSB_REQ_TYPE_CLASS, .direction = TUSB_DIR_IN},
.bRequest = uac2 ? AUDIO20_CS_REQ_RANGE : audioh_fu_volume_range_request(ctrl->fu.mount.range_step),
- .wValue = tu_htole16(tu_u16(AUDIO10_FU_CTRL_VOLUME, 0)),
+ .wValue = tu_htole16(tu_u16(selector, 0)),
.wIndex = tu_htole16(tu_u16(s->feature_unit_id, p_audio->ac_itf_num)),
.wLength = tu_htole16(uac2 ? 8u : 2u),
};
@@ -2411,24 +2424,20 @@ static void audioh_mount_feature_unit_complete(tuh_xfer_t *xfer) {
audioh_mount_feature_unit_next(idx);
}
-bool tuh_audio_feature_unit_set(uint8_t idx, uint8_t stream_idx, uint8_t control_selector, uint8_t channel,
- uint16_t value, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+static bool audioh_fu_set(uint8_t idx, uint8_t stream_idx, uint8_t control_selector, uint16_t value, uint8_t width,
+ tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
TU_VERIFY(idx < CFG_TUH_AUDIO_MAX, false);
audioh_interface_t *p_audio = &_audioh_itf[idx];
TU_VERIFY(p_audio->mounted, false);
tuh_audio_stream_t *s = audioh_get_stream_by_idx(p_audio, stream_idx);
TU_VERIFY(s && s->feature_unit_id != 0, false);
- TU_VERIFY(audioh_fu_selector_supported(p_audio->protocol, control_selector), false);
-
- const uint8_t width = audioh_fu_control_width(control_selector);
- TU_VERIFY(width != 0, false);
if (control_selector == AUDIO10_FU_CTRL_MUTE) {
TU_VERIFY(s->mute_access == AUDIOH_CTRL_READ_WRITE, false);
} else if (control_selector == AUDIO10_FU_CTRL_VOLUME) {
TU_VERIFY(s->volume_access == AUDIOH_CTRL_READ_WRITE, false);
}
- const uint8_t request_code = audioh_fu_cur_request(p_audio->protocol, TUSB_DIR_OUT);
+ const uint8_t request_code = audioh_control_cur_request(p_audio->protocol, TUSB_DIR_OUT);
TU_VERIFY(request_code != 0, false);
audioh_ctrl_state_t *ctrl = &p_audio->ctrl;
audioh_epbuf_t *epbuf = &_audioh_epbuf[idx];
@@ -2436,29 +2445,17 @@ bool tuh_audio_feature_unit_set(uint8_t idx, uint8_t stream_idx, uint8_t control
// Reserve both bookkeeping and payload storage before populating the request.
ctrl->fu_busy = true;
- const tusb_control_request_t request = {.bmRequestType_bit = {.recipient = TUSB_REQ_RCPT_INTERFACE,
- .type = TUSB_REQ_TYPE_CLASS,
- .direction = TUSB_DIR_OUT},
- .bRequest = request_code,
- .wValue = tu_htole16(tu_u16(control_selector, channel)),
- .wIndex = tu_htole16(tu_u16(s->feature_unit_id, p_audio->ac_itf_num)),
- .wLength = width};
-
uint8_t *val_buf = audioh_fu_ctrl(epbuf);
val_buf[0] = (uint8_t)(value & 0xFF);
if (width == 2) {
val_buf[1] = (uint8_t)((value >> 8) & 0xFF);
}
- tuh_xfer_t xfer = {.daddr = p_audio->daddr,
- .ep_addr = 0,
- .setup = &request,
- .buffer = val_buf,
- .complete_cb = complete_cb,
- .user_data = user_data};
+ tuh_xfer_t xfer = {.complete_cb = complete_cb, .user_data = user_data};
if (complete_cb == NULL) {
- const bool result = tuh_control_xfer(&xfer);
+ const bool result = audioh_control_submit(idx, s->feature_unit_id, TUSB_DIR_OUT, request_code, control_selector, 0,
+ val_buf, width, &xfer);
ctrl->fu_busy = false;
return result;
}
@@ -2468,7 +2465,8 @@ bool tuh_audio_feature_unit_set(uint8_t idx, uint8_t stream_idx, uint8_t control
xfer.complete_cb = audioh_fu_set_complete;
xfer.user_data = (uintptr_t)idx;
- if (!tuh_control_xfer(&xfer)) {
+ if (!audioh_control_submit(idx, s->feature_unit_id, TUSB_DIR_OUT, request_code, control_selector, 0, val_buf, width,
+ &xfer)) {
ctrl->complete_cb = NULL;
ctrl->fu_busy = false;
return false;
@@ -2476,24 +2474,20 @@ bool tuh_audio_feature_unit_set(uint8_t idx, uint8_t stream_idx, uint8_t control
return true;
}
-static bool audioh_feature_unit_get(uint8_t idx, uint8_t stream_idx, uint8_t control_selector, uint8_t channel,
- void *value, uint8_t value_type, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+static bool audioh_fu_get(uint8_t idx, uint8_t stream_idx, uint8_t control_selector, void *value, uint8_t width,
+ uint8_t value_type, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
TU_VERIFY(idx < CFG_TUH_AUDIO_MAX, false);
audioh_interface_t *p_audio = &_audioh_itf[idx];
TU_VERIFY(p_audio->mounted && value, false);
tuh_audio_stream_t *s = audioh_get_stream_by_idx(p_audio, stream_idx);
TU_VERIFY(s && s->feature_unit_id != 0, false);
- TU_VERIFY(audioh_fu_selector_supported(p_audio->protocol, control_selector), false);
-
- const uint8_t width = audioh_fu_control_width(control_selector);
- TU_VERIFY(width != 0, false);
if (control_selector == AUDIO10_FU_CTRL_MUTE) {
TU_VERIFY(s->mute_access != AUDIOH_CTRL_NONE, false);
} else if (control_selector == AUDIO10_FU_CTRL_VOLUME) {
TU_VERIFY(s->volume_access != AUDIOH_CTRL_NONE, false);
}
- const uint8_t request_code = audioh_fu_cur_request(p_audio->protocol, TUSB_DIR_IN);
+ const uint8_t request_code = audioh_control_cur_request(p_audio->protocol, TUSB_DIR_IN);
TU_VERIFY(request_code != 0, false);
audioh_ctrl_state_t *ctrl = &p_audio->ctrl;
audioh_epbuf_t *epbuf = &_audioh_epbuf[idx];
@@ -2503,24 +2497,12 @@ static bool audioh_feature_unit_get(uint8_t idx, uint8_t stream_idx, uint8_t con
ctrl->fu.control.width = width;
ctrl->fu.control.value_type = value_type;
- const tusb_control_request_t request = {.bmRequestType_bit = {.recipient = TUSB_REQ_RCPT_INTERFACE,
- .type = TUSB_REQ_TYPE_CLASS,
- .direction = TUSB_DIR_IN},
- .bRequest = request_code,
- .wValue = tu_htole16(tu_u16(control_selector, channel)),
- .wIndex = tu_htole16(tu_u16(s->feature_unit_id, p_audio->ac_itf_num)),
- .wLength = width};
-
if (complete_cb == NULL) {
// The synchronous transfer completes before its driver-owned response is
// converted to host order.
- tuh_xfer_t xfer = {.daddr = p_audio->daddr,
- .ep_addr = 0,
- .setup = &request,
- .buffer = audioh_fu_ctrl(epbuf),
- .complete_cb = NULL,
- .user_data = user_data};
- if (!tuh_control_xfer(&xfer)) {
+ tuh_xfer_t xfer = {.complete_cb = NULL, .user_data = user_data};
+ if (!audioh_control_submit(idx, s->feature_unit_id, TUSB_DIR_IN, request_code, control_selector, 0,
+ audioh_fu_ctrl(epbuf), width, &xfer)) {
ctrl->fu_busy = false;
return false;
}
@@ -2536,14 +2518,10 @@ static bool audioh_feature_unit_get(uint8_t idx, uint8_t stream_idx, uint8_t con
// The asynchronous wrapper converts the response before calling the application.
ctrl->complete_cb = complete_cb;
ctrl->user_data = user_data;
- tuh_xfer_t xfer = {.daddr = p_audio->daddr,
- .ep_addr = 0,
- .setup = &request,
- .buffer = audioh_fu_ctrl(epbuf),
- .complete_cb = audioh_fu_get_complete,
- .user_data = (uintptr_t)idx};
+ tuh_xfer_t xfer = {.complete_cb = audioh_fu_get_complete, .user_data = (uintptr_t)idx};
- if (!tuh_control_xfer(&xfer)) {
+ if (!audioh_control_submit(idx, s->feature_unit_id, TUSB_DIR_IN, request_code, control_selector, 0,
+ audioh_fu_ctrl(epbuf), width, &xfer)) {
ctrl->complete_cb = NULL;
ctrl->fu_busy = false;
return false;
@@ -2551,21 +2529,14 @@ static bool audioh_feature_unit_get(uint8_t idx, uint8_t stream_idx, uint8_t con
return true;
}
-bool tuh_audio_feature_unit_get(uint8_t idx, uint8_t stream_idx, uint8_t control_selector, uint8_t channel,
- uint16_t *value, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
- return audioh_feature_unit_get(idx, stream_idx, control_selector, channel, value, AUDIOH_FU_VALUE_U16, complete_cb,
- user_data);
-}
-
bool tuh_audio_mute_set(uint8_t idx, uint8_t stream_idx, bool mute, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
TU_VERIFY(tuh_audio_mute_supported(idx, stream_idx), false);
- return tuh_audio_feature_unit_set(idx, stream_idx, AUDIO10_FU_CTRL_MUTE, 0, mute ? 1 : 0, complete_cb, user_data);
+ return audioh_fu_set(idx, stream_idx, AUDIO10_FU_CTRL_MUTE, mute ? 1 : 0, 1, complete_cb, user_data);
}
bool tuh_audio_mute_get(uint8_t idx, uint8_t stream_idx, bool *mute, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
TU_VERIFY(mute != NULL && tuh_audio_mute_supported(idx, stream_idx), false);
- return audioh_feature_unit_get(idx, stream_idx, AUDIO10_FU_CTRL_MUTE, 0, mute, AUDIOH_FU_VALUE_BOOL, complete_cb,
- user_data);
+ return audioh_fu_get(idx, stream_idx, AUDIO10_FU_CTRL_MUTE, mute, 1, AUDIOH_FU_VALUE_BOOL, complete_cb, user_data);
}
bool tuh_audio_volume_set(uint8_t idx, uint8_t stream_idx, int16_t volume, tuh_xfer_cb_t complete_cb,
@@ -2582,16 +2553,14 @@ bool tuh_audio_volume_set(uint8_t idx, uint8_t stream_idx, int16_t volume, tuh_x
}
volume = (int16_t)rounded;
}
- return tuh_audio_feature_unit_set(idx, stream_idx, AUDIO10_FU_CTRL_VOLUME, 0, (uint16_t)volume, complete_cb,
- user_data);
+ return audioh_fu_set(idx, stream_idx, AUDIO10_FU_CTRL_VOLUME, (uint16_t)volume, 2, complete_cb, user_data);
}
bool tuh_audio_volume_get(uint8_t idx, uint8_t stream_idx, int16_t *volume, tuh_xfer_cb_t complete_cb,
uintptr_t user_data) {
tuh_audio_volume_range_t range;
TU_VERIFY(volume != NULL && tuh_audio_volume_range_get(idx, stream_idx, &range), false);
- return audioh_feature_unit_get(idx, stream_idx, AUDIO10_FU_CTRL_VOLUME, 0, volume, AUDIOH_FU_VALUE_I16, complete_cb,
- user_data);
+ return audioh_fu_get(idx, stream_idx, AUDIO10_FU_CTRL_VOLUME, volume, 2, AUDIOH_FU_VALUE_I16, complete_cb, user_data);
}
#endif
diff --git a/src/class/audio/audio_host.h b/src/class/audio/audio_host.h
index 4807d376a..2e778c184 100644
--- a/src/class/audio/audio_host.h
+++ b/src/class/audio/audio_host.h
@@ -115,6 +115,14 @@ typedef struct {
uint16_t res;
} tuh_audio_volume_range_t;
+// Audio Control descriptors reported during enumeration. Descriptor pointers
+// are valid only for the duration of tuh_audio_descriptor_cb().
+typedef struct {
+ const tusb_desc_interface_t *desc_audio_control;
+ const uint8_t *desc_cs_audio_control;
+ uint16_t desc_cs_audio_control_len;
+} tuh_audio_descriptor_cb_t;
+
//--------------------------------------------------------------------+
// Stream Enumeration
//--------------------------------------------------------------------+
@@ -212,8 +220,6 @@ static inline uint32_t tuh_audio_config_frame_size(const tuh_audio_stream_config
bool tuh_audio_mounted(uint8_t idx);
// Get device address of Audio device
uint8_t tuh_audio_get_dev_addr(uint8_t idx);
-// Get the Feature Unit ID associated with a stream (0 = none)
-uint8_t tuh_audio_get_feature_unit_id(uint8_t idx, uint8_t stream_idx);
// True when the stream's Feature Unit supports master mute control.
bool tuh_audio_mute_supported(uint8_t idx, uint8_t stream_idx);
// Get the cached master volume range. Returns false when volume is unsupported.
@@ -223,22 +229,13 @@ bool tuh_audio_volume_range_get(uint8_t idx, uint8_t stream_idx, tuh_audio_volum
// Control Request API
//--------------------------------------------------------------------+
-// Set a Feature Unit control associated with an Audio stream. UAC2 supports
-// mute and volume through this low-level API; the other fixed-width selectors
-// below are UAC1-only.
-// Mute/bass/mid/treble/AGC/bass boost/loudness use one byte; volume/delay use two.
-// Graphic EQ and unknown selectors are unsupported.
-bool tuh_audio_feature_unit_set(uint8_t idx, uint8_t stream_idx, uint8_t control_selector, uint8_t channel,
- uint16_t value, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
-
-// Get a Feature Unit control associated with an Audio stream. UAC2 supports
-// mute and volume through this low-level API; the other fixed-width selectors
-// below are UAC1-only.
-// The value is converted to host byte order before complete_cb is invoked.
-// Graphic EQ and unknown selectors are unsupported.
-// Only one Feature Unit operation may be in flight per device.
-bool tuh_audio_feature_unit_get(uint8_t idx, uint8_t stream_idx, uint8_t control_selector, uint8_t channel,
- uint16_t *value, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
+// Submit a class-specific request to an entity on the Audio Control interface.
+// request is the protocol-specific UAC request code. buffer contains the raw
+// little-endian control payload. For an asynchronous transfer, buffer must
+// remain valid until complete_cb is invoked.
+bool tuh_audio_control_xfer(uint8_t idx, uint8_t entity_id, tusb_dir_t direction, uint8_t request,
+ uint8_t control_selector, uint8_t channel, void *buffer, uint16_t length,
+ tuh_xfer_cb_t complete_cb, uintptr_t user_data);
// Master mute and volume controls. Capability and range information is cached
// before tuh_audio_mount_cb() is invoked. Volume SET accepts
@@ -252,23 +249,12 @@ bool tuh_audio_volume_get(uint8_t idx, uint8_t stream_idx, int16_t *volume, tuh_
uintptr_t user_data);
//--------------------------------------------------------------------+
-// Control Request Sync API
-// Each Function will make a USB control transfer request to/from device the function will block until request is
-// complete. The function will return the transfer request result
+// Synchronous control requests block until the transfer completes and return
+// its result. actual_len may be NULL when the received length is not needed.
//--------------------------------------------------------------------+
-TU_ATTR_ALWAYS_INLINE static inline tusb_xfer_result_t tuh_audio_feature_unit_set_sync(uint8_t idx, uint8_t stream_idx,
- uint8_t control_selector,
- uint8_t channel,
- uint16_t value) {
- TU_API_SYNC(tuh_audio_feature_unit_set, idx, stream_idx, control_selector, channel, value);
-}
-
-TU_ATTR_ALWAYS_INLINE static inline tusb_xfer_result_t tuh_audio_feature_unit_get_sync(uint8_t idx, uint8_t stream_idx,
- uint8_t control_selector,
- uint8_t channel,
- uint16_t *value) {
- TU_API_SYNC(tuh_audio_feature_unit_get, idx, stream_idx, control_selector, channel, value);
-}
+tusb_xfer_result_t tuh_audio_control_xfer_sync(uint8_t idx, uint8_t entity_id, tusb_dir_t direction, uint8_t request,
+ uint8_t control_selector, uint8_t channel, void *buffer, uint16_t length,
+ uint32_t *actual_len);
TU_ATTR_ALWAYS_INLINE static inline tusb_xfer_result_t tuh_audio_mute_set_sync(uint8_t idx, uint8_t stream_idx,
bool mute) {
@@ -294,6 +280,13 @@ TU_ATTR_ALWAYS_INLINE static inline tusb_xfer_result_t tuh_audio_volume_get_sync
// Callbacks (Weak is optional)
//--------------------------------------------------------------------+
+// Invoked after the Audio Control and Streaming descriptors have been
+// validated during enumeration, before tuh_audio_mount_cb(). The interface is
+// not mounted yet and control requests must not be submitted from this
+// callback. Applications may inspect or copy descriptors needed for later raw
+// entity control requests.
+void tuh_audio_descriptor_cb(uint8_t idx, const tuh_audio_descriptor_cb_t *desc_cb_data);
+
// Invoked when device with Audio interface is mounted
void tuh_audio_mount_cb(uint8_t idx);