From b23e33a1ce07297df2f37219861d78d2a4cc9809 Mon Sep 17 00:00:00 2001 From: HiFiPHile Date: Thu, 27 Aug 2026 06:21:18 +0200 Subject: 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 --- examples/host/audio_host/README.md | 15 ++++++++------- examples/host/audio_host/src/audio_app.c | 24 ++++++++++++------------ 2 files changed, 20 insertions(+), 19 deletions(-) (limited to 'examples/host/audio_host') diff --git a/examples/host/audio_host/README.md b/examples/host/audio_host/README.md index 326b469e8..e7140bf67 100644 --- a/examples/host/audio_host/README.md +++ b/examples/host/audio_host/README.md @@ -68,7 +68,7 @@ make BOARD= flash 2. Connect a USB Audio device (UAC 1.0 or 2.0) to the USB host port 3. Open a serial terminal to view output 4. The example will: - - Print each stream's Feature Unit ID, master mute/volume capabilities, cached volume range, and supported configurations when mounted + - Print each stream's master mute/volume capabilities, cached volume range, and supported configurations when mounted - Look for an S16_LE capture configuration at a preferred sample rate (48 kHz first, 44.1 kHz fallback; stereo preferred, mono accepted) and configure it - Echo captured audio to an S16_LE playback configuration at the same sample rate (same channel count preferred, converted otherwise) - Read/unmute the microphone and speaker Feature Units and set supported master volumes near -6 dB @@ -81,25 +81,25 @@ make BOARD= flash TinyUSB Host USB Audio Example Connect a USB Audio Device (UAC 1.0 or 2.0) to test Audio device mounted: idx=0 addr=1 - capture stream 1 Feature Unit ID: 5, configurations: 2 + capture stream 1, configurations: 2 master mute supported master volume range: min=-23040 max=1536 res=256 (1/256 dB) [0] format=1 rate=44100 channels=2 [1] format=1 rate=48000 channels=2 - playback stream 0 Feature Unit ID: 2, configurations: 2 + playback stream 0, configurations: 2 master mute supported master volume range: min=-23040 max=1536 res=256 (1/256 dB) [0] format=1 rate=44100 channels=2 [1] format=1 rate=48000 channels=2 Configuring 48 kHz S16_LE capture (2 channels) Microphone configured - Microphone Feature Unit 5 master mute: off - Microphone Feature Unit 5 master volume: 0 (1/256 dB) + Microphone master mute: off + Microphone master volume: 0 (1/256 dB) Microphone master volume set: -1536 (1/256 dB) Configuring 48 kHz S16_LE playback (2 channels) Speaker configured - Speaker Feature Unit 2 master mute: off - Speaker Feature Unit 2 master volume: 0 (1/256 dB) + Speaker master mute: off + Speaker master volume: 0 (1/256 dB) Speaker master volume set: -1536 (1/256 dB) ``` @@ -115,6 +115,7 @@ Edit `src/tusb_config.h` to modify: ## Notes +- `tuh_audio_descriptor_cb()` exposes the validated Audio Control descriptor block during enumeration. Applications that need raw entity controls must copy the required entity IDs or descriptor fields before the callback returns, then use `tuh_audio_control_xfer()` after the device mounts. - While a stream is running, the driver keeps one isochronous transfer in flight and re-submits on completion, so transfers follow the endpoint's `bInterval`. `tuh_audio_capture_cb()` / `tuh_audio_playback_cb()` report each completed transfer; `tuh_audio_err_cb()` reports failures. The example restarts the failed stream automatically 100 ms after the error callback. - Capture and playback streams running concurrently in the same Audio Control instance must use the same sample rate. - `tuh_audio_read()` / `tuh_audio_write()` are non-blocking FIFO operations: they return the number of whole frames actually queued/read (0 when the FIFO is empty/full or the stream is not running), and `tuh_audio_read_available()` / `tuh_audio_write_available()` report the FIFO occupancy in frames. `tuh_audio_write()` only queues data; the playback transfer-completion chain sends it, or sends silence when the FIFO does not contain a complete polling interval without consuming the partial data. diff --git a/examples/host/audio_host/src/audio_app.c b/examples/host/audio_host/src/audio_app.c index 4623dff5a..8852ca58c 100644 --- a/examples/host/audio_host/src/audio_app.c +++ b/examples/host/audio_host/src/audio_app.c @@ -360,11 +360,9 @@ void tuh_audio_err_cb(uint8_t idx, uint8_t stream_idx, uint16_t xferred_bytes) { // Print all supported stream configurations static void print_stream_configs(uint8_t idx, uint8_t stream_idx) { - const tuh_audio_direction_t dir = tuh_audio_stream_direction(idx, stream_idx); - const char *dir_name = (dir == TUH_AUDIO_STREAM_CAPTURE) ? "capture" : "playback"; - const uint8_t feature_unit_id = tuh_audio_get_feature_unit_id(idx, stream_idx); - printf(" %s stream %u Feature Unit ID: %u, configurations: %u\r\n", dir_name, stream_idx, feature_unit_id, - tuh_audio_config_count(idx, stream_idx)); + const tuh_audio_direction_t dir = tuh_audio_stream_direction(idx, stream_idx); + const char *dir_name = (dir == TUH_AUDIO_STREAM_CAPTURE) ? "capture" : "playback"; + printf(" %s stream %u, configurations: %u\r\n", dir_name, stream_idx, tuh_audio_config_count(idx, stream_idx)); tuh_audio_volume_range_t range; if (tuh_audio_mute_supported(idx, stream_idx)) { printf(" master mute supported\r\n"); @@ -383,17 +381,14 @@ static void print_stream_configs(uint8_t idx, uint8_t stream_idx) { } static void configure_stream_controls(uint8_t idx, uint8_t stream_idx, const char *stream_name) { - const uint8_t feature_unit_id = tuh_audio_get_feature_unit_id(idx, stream_idx); - if (feature_unit_id == 0) { - printf(" %s stream has no master mute/volume Feature Unit\r\n", stream_name); - return; - } + bool has_control = false; if (tuh_audio_mute_supported(idx, stream_idx)) { + has_control = true; bool mute; tusb_xfer_result_t result = tuh_audio_mute_get_sync(idx, stream_idx, &mute); if (result == XFER_RESULT_SUCCESS) { - printf(" %s Feature Unit %u master mute: %s\r\n", stream_name, feature_unit_id, mute ? "on" : "off"); + printf(" %s master mute: %s\r\n", stream_name, mute ? "on" : "off"); result = tuh_audio_mute_set_sync(idx, stream_idx, false); } if (result != XFER_RESULT_SUCCESS) { @@ -403,10 +398,11 @@ static void configure_stream_controls(uint8_t idx, uint8_t stream_idx, const cha tuh_audio_volume_range_t range; if (tuh_audio_volume_range_get(idx, stream_idx, &range)) { + has_control = true; int16_t volume; tusb_xfer_result_t result = tuh_audio_volume_get_sync(idx, stream_idx, &volume); if (result == XFER_RESULT_SUCCESS) { - printf(" %s Feature Unit %u master volume: %d (1/256 dB)\r\n", stream_name, feature_unit_id, volume); + printf(" %s master volume: %d (1/256 dB)\r\n", stream_name, volume); int32_t target = FEATURE_UNIT_VOLUME_DB; target = TU_MAX(target, range.min); target = TU_MIN(target, range.max); @@ -421,6 +417,10 @@ static void configure_stream_controls(uint8_t idx, uint8_t stream_idx, const cha printf(" Accessing %s master volume failed: result=%u\r\n", stream_name, result); } } + + if (!has_control) { + printf(" %s stream has no master mute/volume control\r\n", stream_name); + } } // Invoked when device with Audio interface is un-mounted -- cgit v1.3.1