summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/class/audio/audio_host.c171
1 files changed, 82 insertions, 89 deletions
diff --git a/src/class/audio/audio_host.c b/src/class/audio/audio_host.c
index 722b6fcbc..57aaf7d52 100644
--- a/src/class/audio/audio_host.c
+++ b/src/class/audio/audio_host.c
@@ -554,9 +554,9 @@ static const uint8_t *audioh_parse_as(audioh_interface_t *p_audio, const tusb_de
uint8_t sam_freq_count = 0;
uint32_t sam_freq[CFG_TUH_AUDIO_MAX_SAM_FREQ] = {0};
- // An alternate setting can expose an endpoint in each direction. Explicit
- // feedback endpoints are skipped; implicit-feedback data endpoints remain
- // normal audio endpoints.
+ // An AS alternate setting has one audio data endpoint and may have one
+ // explicit feedback endpoint. Implicit-feedback endpoints are data endpoints
+ // and are handled normally when they are the AS interface's data endpoint.
typedef struct {
uint8_t ep_addr;
uint16_t ep_size;
@@ -565,8 +565,8 @@ static const uint8_t *audioh_parse_as(audioh_interface_t *p_audio, const tusb_de
uint8_t ep_usage;
bool sam_freq_ctrl;
} audioh_ep_info_t;
- audioh_ep_info_t ep_info[2] = {0};
- uint8_t ep_count = 0;
+ audioh_ep_info_t ep_info = {0};
+ bool has_data_ep = false;
// The CS_ENDPOINT descriptor carries the sampling-frequency control bit of
// its endpoint. Devices differ in whether it precedes or follows the
// standard endpoint descriptor, so attribute it in either order.
@@ -612,8 +612,8 @@ static const uint8_t *audioh_parse_as(audioh_interface_t *p_audio, const tusb_de
const bool sam_freq_ctrl = (desc_ep->bmAttributes & 0x01) != 0;
if (unassigned_ep) {
// Standard order: the CS_ENDPOINT follows its endpoint descriptor
- ep_info[ep_count - 1].sam_freq_ctrl = sam_freq_ctrl;
- unassigned_ep = false;
+ ep_info.sam_freq_ctrl = sam_freq_ctrl;
+ unassigned_ep = false;
} else {
// Non-standard order: the CS_ENDPOINT precedes its endpoint descriptor
pending_sam_freq_ctrl = sam_freq_ctrl;
@@ -640,21 +640,26 @@ static const uint8_t *audioh_parse_as(audioh_interface_t *p_audio, const tusb_de
break;
}
- if ((usage == (TUSB_ISO_EP_ATT_DATA >> 4) || implicit_feedback) && ep_count < 2) {
- audioh_ep_info_t *ep = &ep_info[ep_count];
- ep->ep_addr = desc_endpoint->bEndpointAddress;
- ep->ep_size = tu_edpt_packet_size(desc_endpoint);
- ep->ep_interval = desc_endpoint->bInterval;
+ if (usage == (TUSB_ISO_EP_ATT_DATA >> 4) || implicit_feedback) {
+ if (has_data_ep) {
+ TU_LOG_DRV(" AUDIO AS itf %u alt %u: extra data ep %02x ignored\r\n", itf_num, alt,
+ desc_endpoint->bEndpointAddress);
+ break;
+ }
+
+ ep_info.ep_addr = desc_endpoint->bEndpointAddress;
+ ep_info.ep_size = tu_edpt_packet_size(desc_endpoint);
+ ep_info.ep_interval = desc_endpoint->bInterval;
// bInterval must be in [1, 16] for isochronous endpoints
- if (ep->ep_interval == 0 || ep->ep_interval > 16) {
- ep->ep_interval = 1;
+ if (ep_info.ep_interval == 0 || ep_info.ep_interval > 16) {
+ ep_info.ep_interval = 1;
}
- ep->ep_sync = desc_endpoint->bmAttributes.sync;
- ep->ep_usage = desc_endpoint->bmAttributes.usage;
- ep->sam_freq_ctrl = pending_sam_freq_ctrl;
+ ep_info.ep_sync = desc_endpoint->bmAttributes.sync;
+ ep_info.ep_usage = desc_endpoint->bmAttributes.usage;
+ ep_info.sam_freq_ctrl = pending_sam_freq_ctrl;
pending_sam_freq_ctrl = false;
- unassigned_ep = !ep->sam_freq_ctrl;
- ep_count++;
+ unassigned_ep = !ep_info.sam_freq_ctrl;
+ has_data_ep = true;
}
break;
}
@@ -664,7 +669,7 @@ static const uint8_t *audioh_parse_as(audioh_interface_t *p_audio, const tusb_de
p_desc = tu_desc_next(p_desc);
}
- if (ep_count == 0) {
+ if (!has_data_ep) {
return p_desc;
}
@@ -692,77 +697,73 @@ static const uint8_t *audioh_parse_as(audioh_interface_t *p_audio, const tusb_de
}
const uint16_t frame_bytes = (uint16_t)frame_bytes_32;
- // Register one configuration per (endpoint, discrete sampling frequency)
- for (uint8_t e = 0; e < ep_count; e++) {
- const audioh_ep_info_t *ep = &ep_info[e];
- tuh_audio_stream_t *stream = audioh_get_stream(p_audio, tu_edpt_dir(ep->ep_addr));
- if (stream == NULL) {
- continue;
- }
+ // Register one configuration per discrete sampling frequency
+ const audioh_ep_info_t *ep = &ep_info;
+ tuh_audio_stream_t *stream = audioh_get_stream(p_audio, tu_edpt_dir(ep->ep_addr));
+ TU_ASSERT(stream != NULL, p_desc);
+
+ const uint16_t epbuf_size = (stream->dir == TUSB_DIR_IN) ? CFG_TUH_AUDIO_EPIN_BUFSIZE : CFG_TUH_AUDIO_EPOUT_BUFSIZE;
+
+ if (ep->ep_size == 0 || ep->ep_size > iso_xfer_size) {
+ TU_LOG_DRV(" AUDIO AS itf %u alt %u: invalid isochronous ep size %u\r\n", itf_num, alt, ep->ep_size);
+ return p_desc;
+ }
- const uint16_t epbuf_size = (stream->dir == TUSB_DIR_IN) ? CFG_TUH_AUDIO_EPIN_BUFSIZE : CFG_TUH_AUDIO_EPOUT_BUFSIZE;
+ // Capture: the device can deliver up to its max packet size per poll
+ // interval, the transfer buffer must fit it
+ if (stream->dir == TUSB_DIR_IN && (ep->ep_size > epbuf_size || ep->ep_size > CFG_TUH_AUDIO_STREAM_BUFSIZE)) {
+ TU_LOG_DRV(" AUDIO AS itf %u alt %u: capture ep size %u exceeds buffer capacity\r\n", itf_num, alt, ep->ep_size);
+ return p_desc;
+ }
- if (ep->ep_size == 0 || ep->ep_size > iso_xfer_size) {
- TU_LOG_DRV(" AUDIO AS itf %u alt %u: invalid isochronous ep size %u\r\n", itf_num, alt, ep->ep_size);
+ for (uint8_t i = 0; i < sam_freq_count; i++) {
+ if (sam_freq[i] == 0) {
continue;
}
- // Capture: the device can deliver up to its max packet size per poll
- // interval, the transfer buffer must fit it
- if (stream->dir == TUSB_DIR_IN && (ep->ep_size > epbuf_size || ep->ep_size > CFG_TUH_AUDIO_STREAM_BUFSIZE)) {
- TU_LOG_DRV(" AUDIO AS itf %u alt %u: capture ep size %u exceeds buffer capacity\r\n", itf_num, alt, ep->ep_size);
+ // The largest whole-frame packet for one poll interval must fit the
+ // endpoint. Playback must also stage it in the transfer buffer and FIFO.
+ const uint64_t frames_numerator = (uint64_t)sam_freq[i] * audioh_interval_us(ep->ep_interval, p_audio->daddr);
+ const uint64_t max_frames = (frames_numerator + 999999u) / 1000000u;
+ const uint64_t packet_bytes = max_frames * frame_bytes;
+ if (packet_bytes == 0 || packet_bytes > ep->ep_size ||
+ (stream->dir == TUSB_DIR_OUT && (packet_bytes > epbuf_size || packet_bytes > CFG_TUH_AUDIO_STREAM_BUFSIZE))) {
+ TU_LOG_DRV(" AUDIO AS itf %u alt %u: packet per interval does not fit endpoint/buffers (ep size %u)\r\n",
+ itf_num, alt, ep->ep_size);
continue;
}
-
- for (uint8_t i = 0; i < sam_freq_count; i++) {
- if (sam_freq[i] == 0) {
- continue;
- }
-
- // The largest whole-frame packet for one poll interval must fit the
- // endpoint. Playback must also stage it in the transfer buffer and FIFO.
- const uint64_t frames_numerator = (uint64_t)sam_freq[i] * audioh_interval_us(ep->ep_interval, p_audio->daddr);
- const uint64_t max_frames = (frames_numerator + 999999u) / 1000000u;
- const uint64_t packet_bytes = max_frames * frame_bytes;
- if (packet_bytes == 0 || packet_bytes > ep->ep_size ||
- (stream->dir == TUSB_DIR_OUT && (packet_bytes > epbuf_size || packet_bytes > CFG_TUH_AUDIO_STREAM_BUFSIZE))) {
- TU_LOG_DRV(" AUDIO AS itf %u alt %u: packet per interval does not fit endpoint/buffers (ep size %u)\r\n",
- itf_num, alt, ep->ep_size);
- continue;
- }
- // Skip duplicate configurations
- bool duplicate = false;
- for (uint8_t j = 0; j < stream->config_count; j++) {
- if (stream->config[j].format == format && stream->config[j].sample_rate == sam_freq[i] &&
- stream->config[j].channels == num_channels) {
- duplicate = true;
- break;
- }
- }
- if (duplicate) {
- continue;
- }
-
- if (stream->config_count >= AUDIOH_MAX_CONFIGS) {
- TU_LOG_DRV(" AUDIO AS itf %u alt %u: reach max configurations %u\r\n", itf_num, alt, AUDIOH_MAX_CONFIGS);
- return p_desc;
+ // Skip duplicate configurations
+ bool duplicate = false;
+ for (uint8_t j = 0; j < stream->config_count; j++) {
+ if (stream->config[j].format == format && stream->config[j].sample_rate == sam_freq[i] &&
+ stream->config[j].channels == num_channels) {
+ duplicate = true;
+ break;
}
+ }
+ if (duplicate) {
+ continue;
+ }
- stream->config[stream->config_count].dir =
- (stream->dir == TUSB_DIR_IN) ? TUH_AUDIO_STREAM_CAPTURE : TUH_AUDIO_STREAM_PLAYBACK;
- stream->config[stream->config_count].format = format;
- stream->config[stream->config_count].sample_rate = sam_freq[i];
- stream->config[stream->config_count].channels = num_channels;
- stream->map[stream->config_count].itf_num = itf_num;
- stream->map[stream->config_count].alt_setting = alt;
- stream->map[stream->config_count].ep_addr = ep->ep_addr;
- stream->map[stream->config_count].ep_size = ep->ep_size;
- stream->map[stream->config_count].ep_interval = ep->ep_interval;
- stream->map[stream->config_count].ep_sync = ep->ep_sync;
- stream->map[stream->config_count].ep_usage = ep->ep_usage;
- stream->map[stream->config_count].sam_freq_ctrl = ep->sam_freq_ctrl;
- stream->config_count++;
+ if (stream->config_count >= AUDIOH_MAX_CONFIGS) {
+ TU_LOG_DRV(" AUDIO AS itf %u alt %u: reach max configurations %u\r\n", itf_num, alt, AUDIOH_MAX_CONFIGS);
+ return p_desc;
}
+
+ stream->config[stream->config_count].dir =
+ (stream->dir == TUSB_DIR_IN) ? TUH_AUDIO_STREAM_CAPTURE : TUH_AUDIO_STREAM_PLAYBACK;
+ stream->config[stream->config_count].format = format;
+ stream->config[stream->config_count].sample_rate = sam_freq[i];
+ stream->config[stream->config_count].channels = num_channels;
+ stream->map[stream->config_count].itf_num = itf_num;
+ stream->map[stream->config_count].alt_setting = alt;
+ stream->map[stream->config_count].ep_addr = ep->ep_addr;
+ stream->map[stream->config_count].ep_size = ep->ep_size;
+ stream->map[stream->config_count].ep_interval = ep->ep_interval;
+ stream->map[stream->config_count].ep_sync = ep->ep_sync;
+ stream->map[stream->config_count].ep_usage = ep->ep_usage;
+ stream->map[stream->config_count].sam_freq_ctrl = ep->sam_freq_ctrl;
+ stream->config_count++;
}
return p_desc;
@@ -1022,7 +1023,6 @@ bool tuh_audio_configure(uint8_t dev_idx, uint8_t stream_idx, uint8_t config_idx
TU_VERIFY(!usbh_edpt_busy(s->daddr, s->edpt.ep_addr), false);
}
- // A shared AS interface must not be left in two different alternate settings
tuh_audio_stream_t *other = (s == &p_audio->out_stream) ? &p_audio->in_stream : &p_audio->out_stream;
if (other->active_config != TUSB_INDEX_INVALID_8) {
const tuh_audio_stream_config_t *other_cfg = &other->config[other->active_config];
@@ -1031,13 +1031,6 @@ bool tuh_audio_configure(uint8_t dev_idx, uint8_t stream_idx, uint8_t config_idx
(unsigned long)cfg->sample_rate, (unsigned long)other_cfg->sample_rate);
return false;
}
-
- const audioh_stream_map_t *m1 = &s->map[config_idx];
- const audioh_stream_map_t *m2 = &other->map[other->active_config];
- if (m1->itf_num == m2->itf_num && m1->alt_setting != m2->alt_setting) {
- TU_LOG_DRV(" AUDIO configure failed: shared AS itf %u in conflicting alt settings\r\n", m1->itf_num);
- return false;
- }
}
// The HCD endpoint must be reopened even when the new configuration uses