summaryrefslogtreecommitdiff
path: root/src/class
diff options
context:
space:
mode:
Diffstat (limited to 'src/class')
-rw-r--r--src/class/audio/audio_host.c164
-rw-r--r--src/class/audio/audio_host.h24
2 files changed, 72 insertions, 116 deletions
diff --git a/src/class/audio/audio_host.c b/src/class/audio/audio_host.c
index b682d2d8a..9115ce4ca 100644
--- a/src/class/audio/audio_host.c
+++ b/src/class/audio/audio_host.c
@@ -38,8 +38,8 @@
* supported configurations during enumeration.
*
* The driver owns:
- * 1. Endpoint selection and opening (only the alternate setting selected by
- * tuh_audio_configure() is ever activated).
+ * 1. Endpoint selection and opening; only the alternate setting selected by
+ * tuh_audio_configure() is activated by tuh_audio_start().
* 2. Endpoint sampling-frequency control (SET_CUR, 3 bytes little-endian).
*/
@@ -100,8 +100,7 @@ TU_ATTR_WEAK void tuh_audio_err_cb(uint8_t idx, uint8_t stream_idx, uint16_t xfe
// Stream state machine
enum {
- STREAM_STATE_IDLE = 0, // not configured, no configuration in progress
- STREAM_STATE_CONFIG, // tuh_audio_configure() sequence in progress
+ STREAM_STATE_IDLE = 0, // not configured
STREAM_STATE_READY // configured, ready to start/stop
};
@@ -149,10 +148,6 @@ typedef struct {
uint32_t frames_rem;
uint32_t rem_acc;
- // Configure state machine
- tuh_audio_configure_cb_t complete_cb;
- uintptr_t user_data;
-
// FIFO + endpoint transfer helper (see tu_edpt_stream, used by the MIDI
// host driver): the FIFO decouples the application's frame-based read/write
// from the endpoint's isochronous transfer cadence. ep_buf is bound at init from
@@ -286,7 +281,6 @@ static void audioh_stream_reset(tuh_audio_stream_t *s) {
s->frames_per_interval = 0;
s->frames_rem = 0;
s->rem_acc = 0;
- s->complete_cb = NULL;
tu_edpt_stream_close(&s->edpt);
tu_edpt_stream_clear(&s->edpt);
}
@@ -374,47 +368,15 @@ static bool audioh_stream_close_ep(tuh_audio_stream_t *s) {
return true;
}
-static void audioh_stream_fail(tuh_audio_stream_t *s, tusb_xfer_result_t result) {
+static void audioh_stream_fail(tuh_audio_stream_t *s) {
(void)audioh_stream_close_ep(s);
s->state = STREAM_STATE_IDLE;
s->active_config = TUSB_INDEX_INVALID_8;
s->running = false;
-
- tuh_audio_configure_cb_t cb = s->complete_cb;
- uintptr_t user_data = s->user_data;
- s->complete_cb = NULL;
- if (cb != NULL) {
- cb(s->idx, s->stream_idx, result, user_data);
- }
-}
-
-static void audioh_stream_ready(tuh_audio_stream_t *s) {
- s->state = STREAM_STATE_READY;
-
- tuh_audio_configure_cb_t cb = s->complete_cb;
- uintptr_t user_data = s->user_data;
- s->complete_cb = NULL;
- if (cb != NULL) {
- cb(s->idx, s->stream_idx, XFER_RESULT_SUCCESS, user_data);
- }
-}
-
-static void audioh_stream_set_freq_complete(tuh_xfer_t *xfer) {
- tuh_audio_stream_t *s = (tuh_audio_stream_t *)xfer->user_data;
- if (s->daddr != xfer->daddr || s->state != STREAM_STATE_CONFIG) {
- return; // device is gone or configuration was aborted
- }
-
- if (xfer->result != XFER_RESULT_SUCCESS) {
- TU_LOG_DRV(" AUDIO set sampling frequency failed: result=%u\r\n", xfer->result);
- audioh_stream_fail(s, xfer->result);
- return;
- }
- audioh_stream_ready(s);
}
-// Set the endpoint sampling frequency (3 bytes little-endian) when supported
-static void audioh_stream_set_freq(tuh_audio_stream_t *s) {
+// 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];
const tuh_audio_stream_config_t *cfg = &s->config[s->active_config];
uint8_t *ctrl = _audioh_epbuf[s->idx].sam_freq;
@@ -434,15 +396,13 @@ static void audioh_stream_set_freq(tuh_audio_stream_t *s) {
.ep_addr = 0,
.setup = &request,
.buffer = ctrl,
- .complete_cb = audioh_stream_set_freq_complete,
+ .complete_cb = complete_cb,
.user_data = (uintptr_t)s};
- if (!tuh_control_xfer(&xfer)) {
- audioh_stream_fail(s, XFER_RESULT_FAILED);
- }
+ return tuh_control_xfer(&xfer);
}
// Reconstruct the endpoint descriptor of the selected configuration and open it
-static void audioh_stream_open_ep(tuh_audio_stream_t *s) {
+static bool audioh_stream_open_ep(tuh_audio_stream_t *s) {
const audioh_stream_map_t *map = &s->map[s->active_config];
const tusb_desc_endpoint_t desc_ep = {.bLength = sizeof(tusb_desc_endpoint_t),
@@ -456,35 +416,16 @@ static void audioh_stream_open_ep(tuh_audio_stream_t *s) {
if (!tuh_edpt_open(s->daddr, &desc_ep)) {
TU_LOG_DRV(" AUDIO open endpoint failed: addr=%u ep=%02x\r\n", s->daddr, map->ep_addr);
- audioh_stream_fail(s, XFER_RESULT_FAILED);
- return;
+ audioh_stream_fail(s);
+ return false;
}
// Bind the transfer helper to the endpoint and start with an empty FIFO
const uint16_t xfer_len = (s->dir == TUSB_DIR_IN) ? CFG_TUH_AUDIO_EPIN_BUFSIZE : CFG_TUH_AUDIO_EPOUT_BUFSIZE;
tu_edpt_stream_open(&s->edpt, s->daddr, &desc_ep, xfer_len);
tu_edpt_stream_clear(&s->edpt);
-
- if (map->sam_freq_ctrl) {
- audioh_stream_set_freq(s);
- } else {
- audioh_stream_ready(s);
- }
-}
-
-static void audioh_stream_set_interface_complete(tuh_xfer_t *xfer) {
- tuh_audio_stream_t *s = (tuh_audio_stream_t *)xfer->user_data;
- if (s->daddr != xfer->daddr || s->state != STREAM_STATE_CONFIG) {
- return; // device is gone or configuration was aborted
- }
-
- if (xfer->result != XFER_RESULT_SUCCESS) {
- TU_LOG_DRV(" AUDIO SET_INTERFACE failed: itf=%u alt=%u result=%u\r\n", s->map[s->active_config].itf_num,
- s->map[s->active_config].alt_setting, xfer->result);
- audioh_stream_fail(s, xfer->result);
- return;
- }
- audioh_stream_open_ep(s);
+ s->state = STREAM_STATE_READY;
+ return true;
}
//--------------------------------------------------------------------+
@@ -534,12 +475,8 @@ void audioh_close(uint8_t daddr) {
tuh_audio_umount_cb(idx);
}
- // Abort a configuration in progress so the application callback still fires
for (uint8_t s = 0; s < 2; s++) {
tuh_audio_stream_t *stream = (s == 0) ? &p_audio->in_stream : &p_audio->out_stream;
- if (stream->state == STREAM_STATE_CONFIG && stream->complete_cb != NULL) {
- audioh_stream_fail(stream, XFER_RESULT_ABORTED);
- }
audioh_stream_reset(stream);
}
@@ -1013,7 +950,7 @@ bool audioh_set_config(uint8_t dev_addr, uint8_t itf_num) {
if (idx == TUSB_INDEX_INVALID_8) {
// Audio Streaming interface (or another driver's interface): nothing to do at mount.
- // Alternate settings are activated by tuh_audio_configure().
+ // Alternate settings are activated by tuh_audio_start().
usbh_driver_set_config_complete(dev_addr, itf_num);
return true;
}
@@ -1106,19 +1043,17 @@ bool tuh_audio_config_get(uint8_t dev_idx, uint8_t stream_idx, uint8_t config_id
return true;
}
-bool tuh_audio_configure(uint8_t dev_idx, uint8_t stream_idx, uint8_t config_idx, tuh_audio_configure_cb_t complete_cb,
- uintptr_t user_data) {
+bool tuh_audio_configure(uint8_t dev_idx, uint8_t stream_idx, uint8_t config_idx) {
TU_VERIFY(dev_idx < CFG_TUH_AUDIO_MAX, false);
audioh_interface_t *p_audio = &_audioh_itf[dev_idx];
TU_VERIFY(p_audio->mounted, false);
tuh_audio_stream_t *s = audioh_get_stream_by_idx(p_audio, stream_idx);
- TU_VERIFY(s && complete_cb, false);
+ TU_VERIFY(s, false);
TU_VERIFY(config_idx < s->config_count, false);
const tuh_audio_stream_config_t *cfg = &s->config[config_idx];
- // Reconfiguration is allowed from a stopped stream; only one configuration
- // may be in progress
- TU_VERIFY(s->state != STREAM_STATE_CONFIG && !s->running, false);
+ // Reconfiguration is allowed from a stopped stream.
+ TU_VERIFY(!s->running, false);
if (s->state == STREAM_STATE_READY) {
// Wait for any in-flight transfer to complete and be discarded
TU_VERIFY(!usbh_edpt_busy(s->daddr, s->edpt.ep_addr), false);
@@ -1153,33 +1088,64 @@ bool tuh_audio_configure(uint8_t dev_idx, uint8_t stream_idx, uint8_t config_idx
s->frames_per_interval = (uint16_t)(frames_numerator / 1000000u);
s->frames_rem = (uint32_t)(frames_numerator % 1000000u);
s->rem_acc = 0;
- s->complete_cb = complete_cb;
- s->user_data = user_data;
- s->state = STREAM_STATE_CONFIG;
+ s->state = STREAM_STATE_IDLE;
if (s->dir == TUSB_DIR_IN) {
// A byte FIFO can overwrite only complete audio frames when its depth is
// an exact multiple of the configured frame size.
const uint16_t fifo_depth = CFG_TUH_AUDIO_STREAM_BUFSIZE - (CFG_TUH_AUDIO_STREAM_BUFSIZE % s->frame_bytes);
- TU_VERIFY(tu_fifo_config(&s->edpt.ff, s->ff_buf, fifo_depth, true), false);
+ if (!tu_fifo_config(&s->edpt.ff, s->ff_buf, fifo_depth, true)) {
+ audioh_stream_fail(s);
+ return false;
+ }
}
TU_LOG_DRV(" AUDIO configure %s stream %u: itf %u alt %u ep %02x\r\n",
(s->dir == TUSB_DIR_IN) ? "capture" : "playback", s->stream_idx, map->itf_num, map->alt_setting,
map->ep_addr);
- if (!tuh_interface_set(s->daddr, map->itf_num, map->alt_setting, audioh_stream_set_interface_complete,
- (uintptr_t)s)) {
- audioh_stream_fail(s, XFER_RESULT_FAILED);
- return false;
+ return audioh_stream_open_ep(s);
+}
+
+// Invoked when the SET_INTERFACE activating the stream's interface completes:
+// the interface is active, set its sampling frequency before submitting
+// transfers
+static void audioh_stream_start_xfer(tuh_audio_stream_t *s) {
+ if (s->dir == TUSB_DIR_IN) {
+ audioh_stream_capture_xfer(s); // feed the capture endpoint
+ } else {
+ audioh_stream_playback_xfer(s); // start the continuous playback transfer chain
+ }
+}
+
+static void audioh_stream_start_set_freq_complete(tuh_xfer_t *xfer) {
+ tuh_audio_stream_t *s = (tuh_audio_stream_t *)xfer->user_data;
+ if (s->daddr != xfer->daddr || s->state != STREAM_STATE_READY || !s->running) {
+ return; // device is gone or the stream was stopped meanwhile
+ }
+ if (xfer->result != XFER_RESULT_SUCCESS) {
+ TU_LOG_DRV(" AUDIO set sampling frequency failed: result=%u\r\n", xfer->result);
+ s->running = false;
+ return;
+ }
+ audioh_stream_start_xfer(s);
+}
+
+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;
+ return false;
+ }
+ } else {
+ audioh_stream_start_xfer(s);
}
return true;
}
-// Invoked when the SET_INTERFACE activating the stream's interface completes:
-// the interface is active, start submitting transfers
static void audioh_stream_start_complete(tuh_xfer_t *xfer) {
tuh_audio_stream_t *s = (tuh_audio_stream_t *)xfer->user_data;
- if (s->daddr != xfer->daddr || !s->running) {
+ if (s->daddr != xfer->daddr || s->state != STREAM_STATE_READY || !s->running) {
return; // device is gone or the stream was stopped meanwhile
}
if (xfer->result != XFER_RESULT_SUCCESS) {
@@ -1187,11 +1153,7 @@ static void audioh_stream_start_complete(tuh_xfer_t *xfer) {
s->running = false;
return;
}
- if (s->dir == TUSB_DIR_IN) {
- audioh_stream_capture_xfer(s); // feed the capture endpoint
- } else {
- audioh_stream_playback_xfer(s); // start the continuous playback transfer chain
- }
+ (void)audioh_stream_start_active(s);
}
bool tuh_audio_start(uint8_t dev_idx, uint8_t stream_idx) {
@@ -1205,9 +1167,9 @@ bool tuh_audio_start(uint8_t dev_idx, uint8_t stream_idx) {
// Wait for any in-flight transfer to complete and be discarded
TU_VERIFY(!usbh_edpt_busy(s->daddr, s->map[s->active_config].ep_addr), false);
+ s->running = true;
// Activate the interface's alternate setting asynchronously: transfers
- // begin once SET_INTERFACE completes (audioh_stream_start_complete)
- s->running = true;
+ // begin once SET_INTERFACE and sampling-frequency control complete.
const audioh_stream_map_t *map = &s->map[s->active_config];
if (!tuh_interface_set(s->daddr, map->itf_num, map->alt_setting, audioh_stream_start_complete, (uintptr_t)s)) {
s->running = false;
diff --git a/src/class/audio/audio_host.h b/src/class/audio/audio_host.h
index 9fcbe2577..b1e2a9169 100644
--- a/src/class/audio/audio_host.h
+++ b/src/class/audio/audio_host.h
@@ -85,10 +85,6 @@ typedef struct {
uint8_t channels;
} tuh_audio_stream_config_t;
-// Asynchronous completion callback of tuh_audio_configure().
-typedef void (*tuh_audio_configure_cb_t)(uint8_t dev_idx, uint8_t stream_idx, tusb_xfer_result_t result,
- uintptr_t user_data);
-
//--------------------------------------------------------------------+
// Stream Enumeration
//--------------------------------------------------------------------+
@@ -118,26 +114,24 @@ uint8_t tuh_audio_active_config(uint8_t dev_idx, uint8_t stream_idx);
bool tuh_audio_config_get(uint8_t dev_idx, uint8_t stream_idx, uint8_t config_idx, tuh_audio_stream_config_t *config);
//--------------------------------------------------------------------+
-// Configuration (ALSA hw_params analogue, asynchronous)
+// Configuration (ALSA hw_params analogue)
//--------------------------------------------------------------------+
-// Configure the stream with the discrete configuration identified by
-// config_idx. The driver asynchronously:
+// Synchronously configure the stream with the discrete configuration identified by
+// config_idx. The driver:
// 1. resolves the AS interface and alternate setting,
-// 2. issues SET_INTERFACE (checking submission and transfer result),
-// 3. opens / reconfigures only the selected endpoint,
-// 4. sets the endpoint sampling frequency when supported,
-// 5. initializes the FIFO and packet scheduler.
-// complete_cb is invoked with the final XFER_RESULT_* status.
-bool tuh_audio_configure(uint8_t dev_idx, uint8_t stream_idx, uint8_t config_idx, tuh_audio_configure_cb_t complete_cb,
- uintptr_t user_data);
+// 2. initializes the FIFO and packet scheduler,
+// 3. opens / reconfigures only the selected endpoint.
+bool tuh_audio_configure(uint8_t dev_idx, uint8_t stream_idx, uint8_t config_idx);
//--------------------------------------------------------------------+
// Stream Control / Frame-based Data
//--------------------------------------------------------------------+
-// Start/stop transferring data on a configured stream.
+// Start transferring data by activating the alternate setting selected by
+// configure, then set the endpoint sampling frequency when supported.
bool tuh_audio_start(uint8_t dev_idx, uint8_t stream_idx);
+// Stop transferring and deactivate the Audio Streaming interface (alt 0).
bool tuh_audio_stop(uint8_t dev_idx, uint8_t stream_idx);
// Frame-based transfer. One frame = channels * bytes per sample.