diff options
| author | HiFiPHile <[email protected]> | 2026-08-25 09:29:07 +0200 |
|---|---|---|
| committer | HiFiPHile <[email protected]> | 2026-08-25 09:29:07 +0200 |
| commit | 62243107cec5e48f447661ea7a17969d05f76adb (patch) | |
| tree | 21117acb436d1009c7d1ea1c0c77a4289adc5b6d /src | |
| parent | 0fd39954d7800777774c17617e41825e5a3561da (diff) | |
fix(audio): keep stream state consistent on control failures
Route asynchronous activation and sampling-frequency failures through the stream error callback. Keep a running stream active when SET_INTERFACE alt 0 cannot be submitted so stop can be retried without diverging from device state.
Signed-off-by: HiFiPHile <[email protected]>
Diffstat (limited to 'src')
| -rw-r--r-- | src/class/audio/audio_host.c | 30 | ||||
| -rw-r--r-- | src/class/audio/audio_host.h | 5 |
2 files changed, 21 insertions, 14 deletions
diff --git a/src/class/audio/audio_host.c b/src/class/audio/audio_host.c index 57aaf7d52..78ecd9525 100644 --- a/src/class/audio/audio_host.c +++ b/src/class/audio/audio_host.c @@ -373,6 +373,12 @@ static void audioh_stream_fail(tuh_audio_stream_t *s) { s->running = false; } +static void audioh_stream_error(tuh_audio_stream_t *s, uint16_t xferred_bytes) { + s->running = false; + tu_edpt_stream_clear(&s->edpt); + tuh_audio_err_cb(s->idx, s->stream_idx, xferred_bytes); +} + // Set the endpoint sampling frequency (3 bytes little-endian) static bool audioh_stream_set_freq(tuh_audio_stream_t *s, tuh_xfer_cb_t complete_cb) { const audioh_stream_map_t *map = &s->map[s->active_config]; @@ -496,9 +502,7 @@ bool audioh_xfer_cb(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t result, uin // Failed, stalled, or aborted transfers never carry valid audio data if (result != XFER_RESULT_SUCCESS) { TU_LOG_DRV(" AUDIO transfer failed: addr=%u ep=%02x result=%u\r\n", dev_addr, ep_addr, result); - s->running = false; - tu_edpt_stream_clear(&s->edpt); // discard queued data - tuh_audio_err_cb(s->idx, s->stream_idx, (uint16_t)xferred_bytes); + audioh_stream_error(s, (uint16_t)xferred_bytes); return true; } @@ -1080,7 +1084,7 @@ static void audioh_stream_start_set_freq_complete(tuh_xfer_t *xfer) { } if (xfer->result != XFER_RESULT_SUCCESS) { TU_LOG_DRV(" AUDIO set sampling frequency failed: result=%u\r\n", xfer->result); - s->running = false; + audioh_stream_error(s, 0); return; } audioh_stream_start_xfer(s); @@ -1090,7 +1094,7 @@ static bool audioh_stream_start_active(tuh_audio_stream_t *s) { const audioh_stream_map_t *map = &s->map[s->active_config]; if (map->sam_freq_ctrl) { if (!audioh_stream_set_freq(s, audioh_stream_start_set_freq_complete)) { - s->running = false; + audioh_stream_error(s, 0); return false; } } else { @@ -1106,7 +1110,7 @@ static void audioh_stream_start_complete(tuh_xfer_t *xfer) { } if (xfer->result != XFER_RESULT_SUCCESS) { TU_LOG_DRV(" AUDIO SET_INTERFACE activate failed: result=%u\r\n", xfer->result); - s->running = false; + audioh_stream_error(s, 0); return; } (void)audioh_stream_start_active(s); @@ -1152,16 +1156,18 @@ bool tuh_audio_stop(uint8_t dev_idx, uint8_t stream_idx) { tuh_audio_stream_t *s = audioh_get_stream_by_idx(p_audio, stream_idx); TU_VERIFY(s && s->state == STREAM_STATE_READY, false); + const audioh_stream_map_t *map = &s->map[s->active_config]; + // Leave the stream running if SET_INTERFACE cannot be submitted, so the + // caller can retry without the host and device states diverging. + TU_VERIFY(tuh_interface_set(s->daddr, map->itf_num, 0, audioh_stream_stop_complete, (uintptr_t)s), false); + // The in-flight transfer (if any) completes and its data is discarded; - // queued frames are dropped as well. The interface is deactivated (alt 0) - // so the device stops transferring. + // queued frames are dropped as well. The interface is being deactivated so + // the device stops transferring. s->running = false; tu_edpt_stream_clear(&s->edpt); s->rem_acc = 0; // restart the pacing accumulator on the next tuh_audio_start() - - // Keep stop retryable if EP0 is busy and SET_INTERFACE cannot be submitted. - const audioh_stream_map_t *map = &s->map[s->active_config]; - return tuh_interface_set(s->daddr, map->itf_num, 0, audioh_stream_stop_complete, (uintptr_t)s); + return true; } uint32_t tuh_audio_write(uint8_t dev_idx, uint8_t stream_idx, const void *buffer, uint32_t frame_count) { diff --git a/src/class/audio/audio_host.h b/src/class/audio/audio_host.h index b1e2a9169..98e103208 100644 --- a/src/class/audio/audio_host.h +++ b/src/class/audio/audio_host.h @@ -239,8 +239,9 @@ void tuh_audio_capture_cb(uint8_t idx, uint8_t stream_idx, uint16_t xferred_byte // a complete packet is not queued. void tuh_audio_playback_cb(uint8_t idx, uint8_t stream_idx, uint16_t xferred_bytes); -// Invoked when an isochronous transfer fails. The stream is stopped -// (tuh_audio_start() must be called again to resume). +// Invoked when asynchronous stream activation or an isochronous transfer +// fails. The stream is stopped (tuh_audio_start() must be called again to +// resume). xferred_bytes is zero for an activation failure. void tuh_audio_err_cb(uint8_t idx, uint8_t stream_idx, uint16_t xferred_bytes); //--------------------------------------------------------------------+ |
