diff options
Diffstat (limited to 'src/class')
| -rw-r--r-- | src/class/audio/audio_host.c | 315 | ||||
| -rw-r--r-- | src/class/audio/audio_host.h | 46 |
2 files changed, 315 insertions, 46 deletions
diff --git a/src/class/audio/audio_host.c b/src/class/audio/audio_host.c index c12493a5c..2a8df9075 100644 --- a/src/class/audio/audio_host.c +++ b/src/class/audio/audio_host.c @@ -135,7 +135,10 @@ typedef struct { bool running; // tuh_audio_start() called, transfers may be submitted // One Feature Unit associated with this logical stream (0 = none) - uint8_t feature_unit_id; + uint8_t feature_unit_id; + bool mute_supported; + bool volume_supported; + tuh_audio_volume_range_t volume_range; // Size in bytes of one frame (all channels) of the active configuration uint16_t frame_bytes; @@ -173,12 +176,16 @@ typedef struct { TUH_EPBUF_DEF(fu_ctrl, 8); // feature-unit SET data TUH_EPBUF_DEF(epin, CFG_TUH_AUDIO_EPIN_BUFSIZE); // capture transfer buffer TUH_EPBUF_DEF(epout, CFG_TUH_AUDIO_EPOUT_BUFSIZE); // playback transfer buffer - // Feature-unit request state: only one GET or SET in flight per device - tuh_xfer_cb_t complete_cb; - uintptr_t user_data; - uint16_t *value; - uint8_t width; - bool fu_busy; + // Feature-unit request state: only one operation in flight per device + tuh_xfer_cb_t complete_cb; + uintptr_t user_data; + void *value; + tuh_audio_volume_range_t volume_range_temp; + uint8_t width; + uint8_t value_type; + uint8_t volume_stream_idx; + uint8_t volume_range_step; + bool fu_busy; } audioh_epbuf_t; static audioh_interface_t _audioh_itf[CFG_TUH_AUDIO_MAX]; @@ -229,6 +236,12 @@ static tuh_audio_stream_t *audioh_get_stream_by_idx(audioh_interface_t *p_audio, return NULL; } +static void audioh_stream_set_feature_unit(tuh_audio_stream_t *s, uint8_t unit_id, uint8_t controls) { + s->feature_unit_id = unit_id; + s->mute_supported = (controls & AUDIO10_FU_CONTROL_BM_MUTE) != 0; + s->volume_supported = (controls & AUDIO10_FU_CONTROL_BM_VOLUME) != 0; +} + // Map a UAC 1.0 (subframe size, bit resolution) pair to a supported format static bool audioh_format_from_uac1(uint8_t subframe_size, uint8_t bit_resolution, tuh_audio_format_t *format) { if (subframe_size == 1 && bit_resolution == 8) { @@ -282,6 +295,9 @@ static void audioh_stream_reset(tuh_audio_stream_t *s) { s->state = STREAM_STATE_IDLE; s->running = false; s->feature_unit_id = 0; + s->mute_supported = false; + s->volume_supported = false; + s->volume_range = (tuh_audio_volume_range_t){0}; s->frame_bytes = 0; s->frames_per_interval = 0; s->frames_rem = 0; @@ -811,6 +827,7 @@ uint16_t audioh_open(uint8_t rhport, uint8_t dev_addr, const tusb_desc_interface typedef struct { uint8_t id; uint8_t source_id; + uint8_t controls; } audioh_fu_info_t; audioh_fu_info_t pending_fu[TUH_AUDIO_STREAM_DIRECTION_COUNT] = {0}; @@ -837,9 +854,8 @@ uint16_t audioh_open(uint8_t rhport, uint8_t dev_addr, const tusb_desc_interface usb_input_terminal_id = terminal->bTerminalID; for (uint8_t i = 0; i < TU_ARRAY_SIZE(pending_fu); i++) { if (pending_fu[i].source_id == usb_input_terminal_id) { - p_audio->out_stream.feature_unit_id = pending_fu[i].id; - pending_fu[i].id = 0; - pending_fu[i].source_id = 0; + audioh_stream_set_feature_unit(&p_audio->out_stream, pending_fu[i].id, pending_fu[i].controls); + pending_fu[i] = (audioh_fu_info_t){0}; break; } } @@ -855,9 +871,8 @@ uint16_t audioh_open(uint8_t rhport, uint8_t dev_addr, const tusb_desc_interface usb_output_source_id = terminal->bSourceID; for (uint8_t i = 0; i < TU_ARRAY_SIZE(pending_fu); i++) { if (pending_fu[i].id == usb_output_source_id) { - p_audio->in_stream.feature_unit_id = pending_fu[i].id; - pending_fu[i].id = 0; - pending_fu[i].source_id = 0; + audioh_stream_set_feature_unit(&p_audio->in_stream, pending_fu[i].id, pending_fu[i].controls); + pending_fu[i] = (audioh_fu_info_t){0}; break; } } @@ -865,18 +880,28 @@ uint16_t audioh_open(uint8_t rhport, uint8_t dev_addr, const tusb_desc_interface break; } case AUDIO10_CS_AC_INTERFACE_FEATURE_UNIT: { - if (!TUH_VALIDATE_BASIC(tu_desc_len(p_desc) >= 5)) { + if (!TUH_VALIDATE_BASIC(tu_desc_len(p_desc) >= 7)) { + goto open_failed; + } + const uint8_t control_size = p_desc[5]; + if (!TUH_VALIDATE_BASIC(control_size > 0) || + !TUH_VALIDATE_BASIC(control_size <= (uint8_t)(tu_desc_len(p_desc) - 7))) { goto open_failed; } + const uint8_t controls = p_desc[6] & (AUDIO10_FU_CONTROL_BM_MUTE | AUDIO10_FU_CONTROL_BM_VOLUME); + if (controls == 0) { + break; + } + bool mapped = false; if (usb_input_terminal_id != 0 && p_audio->out_stream.feature_unit_id == 0 && p_desc[4] == usb_input_terminal_id) { - p_audio->out_stream.feature_unit_id = p_desc[3]; + audioh_stream_set_feature_unit(&p_audio->out_stream, p_desc[3], controls); mapped = true; } if (usb_output_source_id != 0 && p_audio->in_stream.feature_unit_id == 0 && p_desc[3] == usb_output_source_id) { - p_audio->in_stream.feature_unit_id = p_desc[3]; + audioh_stream_set_feature_unit(&p_audio->in_stream, p_desc[3], controls); mapped = true; } if (!mapped) { @@ -884,6 +909,7 @@ uint16_t audioh_open(uint8_t rhport, uint8_t dev_addr, const tusb_desc_interface if (pending_fu[i].id == 0) { pending_fu[i].id = p_desc[3]; pending_fu[i].source_id = p_desc[4]; + pending_fu[i].controls = controls; break; } } @@ -956,6 +982,8 @@ open_failed: //--------------------------------------------------------------------+ // Set Configuration //--------------------------------------------------------------------+ +static void audioh_mount_feature_unit_next(uint8_t idx); + bool audioh_set_config(uint8_t dev_addr, uint8_t itf_num) { uint8_t idx = TUSB_INDEX_INVALID_8; for (uint8_t i = 0; i < CFG_TUH_AUDIO_MAX; i++) { @@ -972,13 +1000,9 @@ bool audioh_set_config(uint8_t dev_addr, uint8_t itf_num) { return true; } - audioh_interface_t *p_audio = &_audioh_itf[idx]; - p_audio->mounted = true; - TU_LOG_DRV(" AUDIO mounted: addr = %u index = %u\r\n", dev_addr, idx); - - tuh_audio_mount_cb(idx); - - usbh_driver_set_config_complete(dev_addr, itf_num); + audioh_epbuf_t *epbuf = &_audioh_epbuf[idx]; + epbuf->volume_stream_idx = 0; + audioh_mount_feature_unit_next(idx); return true; } @@ -1005,6 +1029,24 @@ uint8_t tuh_audio_get_feature_unit_id(uint8_t idx, uint8_t stream_idx) { 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]; + TU_VERIFY(p_audio->mounted, false); + tuh_audio_stream_t *s = audioh_get_stream_by_idx(p_audio, stream_idx); + return s != NULL && s->mute_supported; +} + +bool tuh_audio_volume_range_get(uint8_t idx, uint8_t stream_idx, tuh_audio_volume_range_t *range) { + TU_VERIFY(idx < CFG_TUH_AUDIO_MAX && range != NULL, 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 != NULL && s->volume_supported, false); + *range = s->volume_range; + return true; +} + uint8_t tuh_audio_stream_count(uint8_t dev_idx) { TU_VERIFY(dev_idx < CFG_TUH_AUDIO_MAX, 0); audioh_interface_t *p_audio = &_audioh_itf[dev_idx]; @@ -1300,21 +1342,42 @@ static void audioh_fu_set_complete(tuh_xfer_t *xfer) { } } +enum { + AUDIOH_FU_VALUE_U16, + AUDIOH_FU_VALUE_BOOL, + AUDIOH_FU_VALUE_I16 +}; + +static void audioh_fu_value_store(audioh_epbuf_t *epbuf) { + if (epbuf->value_type == AUDIOH_FU_VALUE_BOOL) { + *((bool *)epbuf->value) = epbuf->fu_ctrl[0] != 0; + } else if (epbuf->width == 1) { + *((uint16_t *)epbuf->value) = epbuf->fu_ctrl[0]; + } else { + const uint16_t value = tu_le16toh(tu_unaligned_read16(epbuf->fu_ctrl)); + if (epbuf->value_type == AUDIOH_FU_VALUE_I16) { + *((int16_t *)epbuf->value) = (int16_t)value; + } else { + *((uint16_t *)epbuf->value) = value; + } + } +} + // Convert the raw control value to host order and chain to the application callback static void audioh_fu_get_complete(tuh_xfer_t *xfer) { const uint8_t idx = (uint8_t)xfer->user_data; audioh_epbuf_t *epbuf = &_audioh_epbuf[idx]; tuh_xfer_cb_t app_cb = epbuf->complete_cb; uintptr_t user_data = epbuf->user_data; - uint16_t *value = epbuf->value; - const uint8_t width = epbuf->width; epbuf->complete_cb = NULL; epbuf->fu_busy = false; - if (app_cb != NULL && value != NULL && xfer->result == XFER_RESULT_SUCCESS) { - const uint8_t *raw = (const uint8_t *)value; - // The raw bytes are little-endian on the wire: rebuild the host-order value - *value = (width == 1) ? (uint16_t)raw[0] : (uint16_t)((uint16_t)raw[0] | ((uint16_t)raw[1] << 8)); + if (epbuf->value != NULL && xfer->result == XFER_RESULT_SUCCESS) { + if (xfer->actual_len == epbuf->width) { + audioh_fu_value_store(epbuf); + } else { + xfer->result = XFER_RESULT_FAILED; + } } xfer->user_data = user_data; @@ -1323,6 +1386,133 @@ static void audioh_fu_get_complete(tuh_xfer_t *xfer) { } } +enum { + AUDIOH_VOLUME_RANGE_MIN, + AUDIOH_VOLUME_RANGE_MAX, + AUDIOH_VOLUME_RANGE_RES, + AUDIOH_VOLUME_RANGE_COUNT +}; + +static uint8_t audioh_fu_volume_range_request(uint8_t step) { + switch (step) { + case AUDIOH_VOLUME_RANGE_MIN: + return AUDIO10_CS_REQ_GET_MIN; + case AUDIOH_VOLUME_RANGE_MAX: + return AUDIO10_CS_REQ_GET_MAX; + case AUDIOH_VOLUME_RANGE_RES: + return AUDIO10_CS_REQ_GET_RES; + default: + return AUDIO10_CS_REQ_UNDEF; + } +} + +static void audioh_fu_volume_range_store(audioh_epbuf_t *epbuf) { + const uint16_t value = tu_le16toh(tu_unaligned_read16(epbuf->fu_ctrl)); + switch (epbuf->volume_range_step) { + case AUDIOH_VOLUME_RANGE_MIN: + epbuf->volume_range_temp.min = (int16_t)value; + break; + case AUDIOH_VOLUME_RANGE_MAX: + epbuf->volume_range_temp.max = (int16_t)value; + break; + case AUDIOH_VOLUME_RANGE_RES: + epbuf->volume_range_temp.res = value; + break; + default: + break; + } +} + +static void audioh_mount_feature_unit_complete(tuh_xfer_t *xfer); + +static bool audioh_mount_feature_unit_submit(uint8_t idx) { + audioh_interface_t *p_audio = &_audioh_itf[idx]; + audioh_epbuf_t *epbuf = &_audioh_epbuf[idx]; + tuh_audio_stream_t *s = audioh_get_stream_by_idx(p_audio, epbuf->volume_stream_idx); + TU_ASSERT(s != NULL); + + const tusb_control_request_t request = { + .bmRequestType_bit = {.recipient = TUSB_REQ_RCPT_INTERFACE, .type = TUSB_REQ_TYPE_CLASS, .direction = TUSB_DIR_IN}, + .bRequest = audioh_fu_volume_range_request(epbuf->volume_range_step), + .wValue = tu_htole16(tu_u16(AUDIO10_FU_CTRL_VOLUME, 0)), + .wIndex = tu_htole16(tu_u16(s->feature_unit_id, p_audio->ac_itf_num)), + .wLength = tu_htole16(2), + }; + tuh_xfer_t xfer = {.daddr = p_audio->daddr, + .ep_addr = 0, + .setup = &request, + .buffer = epbuf->fu_ctrl, + .complete_cb = audioh_mount_feature_unit_complete, + .user_data = (uintptr_t)idx}; + return tuh_control_xfer(&xfer); +} + +static void audioh_mount_feature_unit_next(uint8_t idx) { + audioh_interface_t *p_audio = &_audioh_itf[idx]; + audioh_epbuf_t *epbuf = &_audioh_epbuf[idx]; + + while (epbuf->volume_stream_idx < p_audio->stream_count) { + tuh_audio_stream_t *s = audioh_get_stream_by_idx(p_audio, epbuf->volume_stream_idx); + TU_ASSERT(s != NULL, ); + if (s->volume_supported) { + epbuf->volume_range_temp = (tuh_audio_volume_range_t){0}; + epbuf->volume_range_step = AUDIOH_VOLUME_RANGE_MIN; + epbuf->fu_busy = true; + if (audioh_mount_feature_unit_submit(idx)) { + return; + } + s->volume_supported = false; + if (!s->mute_supported) { + s->feature_unit_id = 0; + } + epbuf->fu_busy = false; + } + epbuf->volume_stream_idx++; + } + + p_audio->mounted = true; + TU_LOG_DRV(" AUDIO mounted: addr = %u index = %u\r\n", p_audio->daddr, idx); + tuh_audio_mount_cb(idx); + usbh_driver_set_config_complete(p_audio->daddr, p_audio->ac_itf_num); +} + +static void audioh_mount_feature_unit_complete(tuh_xfer_t *xfer) { + const uint8_t idx = (uint8_t)xfer->user_data; + audioh_epbuf_t *epbuf = &_audioh_epbuf[idx]; + if (!epbuf->fu_busy) { + return; + } + audioh_interface_t *p_audio = &_audioh_itf[idx]; + tuh_audio_stream_t *s = audioh_get_stream_by_idx(p_audio, epbuf->volume_stream_idx); + TU_ASSERT(s != NULL, ); + + if (xfer->result == XFER_RESULT_SUCCESS && xfer->actual_len == 2) { + audioh_fu_volume_range_store(epbuf); + epbuf->volume_range_step++; + + if (epbuf->volume_range_step < AUDIOH_VOLUME_RANGE_COUNT) { + if (audioh_mount_feature_unit_submit(idx)) { + return; + } + xfer->result = XFER_RESULT_FAILED; + } else if (epbuf->volume_range_temp.min <= epbuf->volume_range_temp.max && epbuf->volume_range_temp.res > 0) { + s->volume_range = epbuf->volume_range_temp; + } else { + xfer->result = XFER_RESULT_FAILED; + } + } + + if (xfer->result != XFER_RESULT_SUCCESS || xfer->actual_len != 2) { + s->volume_supported = false; + if (!s->mute_supported) { + s->feature_unit_id = 0; + } + } + epbuf->fu_busy = false; + epbuf->volume_stream_idx++; + 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) { TU_VERIFY(idx < CFG_TUH_AUDIO_MAX, false); @@ -1378,8 +1568,8 @@ bool tuh_audio_feature_unit_set(uint8_t idx, uint8_t stream_idx, uint8_t control 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) { +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) { TU_VERIFY(idx < CFG_TUH_AUDIO_MAX, false); audioh_interface_t *p_audio = &_audioh_itf[idx]; TU_VERIFY(p_audio->mounted && value, false); @@ -1391,7 +1581,10 @@ bool tuh_audio_feature_unit_get(uint8_t idx, uint8_t stream_idx, uint8_t control audioh_epbuf_t *epbuf = &_audioh_epbuf[idx]; TU_VERIFY(!epbuf->fu_busy, false); - epbuf->fu_busy = true; + epbuf->fu_busy = true; + epbuf->value = value; + epbuf->width = width; + epbuf->value_type = value_type; const tusb_control_request_t request = {.bmRequestType_bit = {.recipient = TUSB_REQ_RCPT_INTERFACE, .type = TUSB_REQ_TYPE_CLASS, @@ -1407,16 +1600,17 @@ bool tuh_audio_feature_unit_get(uint8_t idx, uint8_t stream_idx, uint8_t control tuh_xfer_t xfer = {.daddr = p_audio->daddr, .ep_addr = 0, .setup = &request, - .buffer = (uint8_t *)value, + .buffer = epbuf->fu_ctrl, .complete_cb = NULL, .user_data = user_data}; if (!tuh_control_xfer(&xfer)) { epbuf->fu_busy = false; return false; } - if (xfer.result == XFER_RESULT_SUCCESS) { - const uint8_t *raw = (const uint8_t *)value; - *value = (width == 1) ? (uint16_t)raw[0] : (uint16_t)((uint16_t)raw[0] | ((uint16_t)raw[1] << 8)); + if (xfer.result == XFER_RESULT_SUCCESS && xfer.actual_len == width) { + audioh_fu_value_store(epbuf); + } else if (xfer.result == XFER_RESULT_SUCCESS && user_data != 0) { + *((tusb_xfer_result_t *)user_data) = XFER_RESULT_FAILED; } epbuf->fu_busy = false; return true; @@ -1425,15 +1619,12 @@ bool tuh_audio_feature_unit_get(uint8_t idx, uint8_t stream_idx, uint8_t control // Async path: chain the host-order conversion to the application callback epbuf->complete_cb = complete_cb; epbuf->user_data = user_data; - epbuf->value = value; - epbuf->width = width; - - tuh_xfer_t xfer = {.daddr = p_audio->daddr, - .ep_addr = 0, - .setup = &request, - .buffer = (uint8_t *)value, // raw bytes, converted in audioh_fu_get_complete() - .complete_cb = audioh_fu_get_complete, - .user_data = (uintptr_t)idx}; + tuh_xfer_t xfer = {.daddr = p_audio->daddr, + .ep_addr = 0, + .setup = &request, + .buffer = epbuf->fu_ctrl, + .complete_cb = audioh_fu_get_complete, + .user_data = (uintptr_t)idx}; if (!tuh_control_xfer(&xfer)) { epbuf->complete_cb = NULL; @@ -1443,4 +1634,38 @@ bool tuh_audio_feature_unit_get(uint8_t idx, uint8_t stream_idx, uint8_t control 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); +} + +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); +} + +bool tuh_audio_volume_set(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(tuh_audio_volume_range_get(idx, stream_idx, &range), false); + TU_VERIFY(volume >= range.min && volume <= range.max, false); + return tuh_audio_feature_unit_set(idx, stream_idx, AUDIO10_FU_CTRL_VOLUME, 0, (uint16_t)volume, 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); +} + #endif diff --git a/src/class/audio/audio_host.h b/src/class/audio/audio_host.h index 98e103208..e850e4b7c 100644 --- a/src/class/audio/audio_host.h +++ b/src/class/audio/audio_host.h @@ -85,6 +85,17 @@ typedef struct { uint8_t channels; } tuh_audio_stream_config_t; +// Volume values are signed 1/256 dB. INT16_MIN represents silence. +#define TUH_AUDIO_VOLUME_SILENCE INT16_MIN + +// One continuous volume range. This matches UAC1 MIN/MAX/RES and the common +// UAC2 RANGE response containing one subrange. +typedef struct { + int16_t min; + int16_t max; + uint16_t res; +} tuh_audio_volume_range_t; + //--------------------------------------------------------------------+ // Stream Enumeration //--------------------------------------------------------------------+ @@ -183,6 +194,10 @@ bool tuh_audio_mounted(uint8_t idx); 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. +bool tuh_audio_volume_range_get(uint8_t idx, uint8_t stream_idx, tuh_audio_volume_range_t *range); //--------------------------------------------------------------------+ // Control Request API @@ -197,10 +212,19 @@ bool tuh_audio_feature_unit_set(uint8_t idx, uint8_t stream_idx, uint8_t control // Get a Feature Unit control (mute, volume, ...) associated with an Audio stream (UAC 1.0) // The value is converted to host byte order before complete_cb is invoked. // Graphic EQ and unknown selectors are unsupported. -// Only one Feature Unit GET or SET request may be in flight per device. +// 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); +// Master mute and volume controls. Capability and range information is cached +// before tuh_audio_mount_cb() is invoked. +bool tuh_audio_mute_set(uint8_t idx, uint8_t stream_idx, bool mute, tuh_xfer_cb_t complete_cb, uintptr_t 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); +bool tuh_audio_volume_set(uint8_t idx, uint8_t stream_idx, int16_t volume, tuh_xfer_cb_t complete_cb, + uintptr_t 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); + //--------------------------------------------------------------------+ // Control Request Sync API // Each Function will make a USB control transfer request to/from device the function will block until request is @@ -220,6 +244,26 @@ TU_ATTR_ALWAYS_INLINE static inline tusb_xfer_result_t tuh_audio_feature_unit_ge TU_API_SYNC(tuh_audio_feature_unit_get, idx, stream_idx, control_selector, channel, value); } +TU_ATTR_ALWAYS_INLINE static inline tusb_xfer_result_t tuh_audio_mute_set_sync(uint8_t idx, uint8_t stream_idx, + bool mute) { + TU_API_SYNC(tuh_audio_mute_set, idx, stream_idx, mute); +} + +TU_ATTR_ALWAYS_INLINE static inline tusb_xfer_result_t tuh_audio_mute_get_sync(uint8_t idx, uint8_t stream_idx, + bool *mute) { + TU_API_SYNC(tuh_audio_mute_get, idx, stream_idx, mute); +} + +TU_ATTR_ALWAYS_INLINE static inline tusb_xfer_result_t tuh_audio_volume_set_sync(uint8_t idx, uint8_t stream_idx, + int16_t volume) { + TU_API_SYNC(tuh_audio_volume_set, idx, stream_idx, volume); +} + +TU_ATTR_ALWAYS_INLINE static inline tusb_xfer_result_t tuh_audio_volume_get_sync(uint8_t idx, uint8_t stream_idx, + int16_t *volume) { + TU_API_SYNC(tuh_audio_volume_get, idx, stream_idx, volume); +} + //--------------------------------------------------------------------+ // Callbacks (Weak is optional) //--------------------------------------------------------------------+ |
