summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-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);