Change ucast client stream design so that: * upper level locks streams to indicate which ones it is using * unused streams are reused when upper level wants a new stream * only locked streams are used for bidi CIS linking * streams (still) correspond 1-to-1 to non-idle ASEs This fixes some issues: * bap_ucast_stream_new() could pick a stream upper level is already using if lpac & rpac match (can occur with multi-stream AC 6(ii) etc) * Avoids assuming ASE enters idle state at end of stream life cycle. This is False for some devices like Sony headsets, which always cache codec config so RELEASING -> CONFIG always, never RELEASING -> IDLE, so ASE never go IDLE again. * Allows reconfiguring an ASE with different codec in this case. * Allows upper level to only QoS some of the streams. Reconfiguring ASE in QOS/CONFIG state with different codec here results to need_reconfig=true state, where ASE and stream configs do not match, and upper level needs to do bt_bap_stream_config() to sync them. --- src/shared/bap.c | 133 ++++++++++++++++++++++++++++++----------------- src/shared/bap.h | 3 ++ 2 files changed, 89 insertions(+), 47 deletions(-) diff --git a/src/shared/bap.c b/src/shared/bap.c index fda1e3560..1c15a4ecb 100644 --- a/src/shared/bap.c +++ b/src/shared/bap.c @@ -296,6 +296,8 @@ struct bt_bap_stream { struct queue *pending_states; bool no_cache_config; bool client; + bool locked; + bool need_reconfig; void *user_data; }; @@ -1925,6 +1927,9 @@ static unsigned int bap_ucast_qos(struct bt_bap_stream *stream, if (!stream->client) return 0; + if (stream->need_reconfig) + return 0; + memset(&qos, 0, sizeof(qos)); /* TODO: Figure out how to pass these values around */ @@ -2327,7 +2332,6 @@ static unsigned int bap_ucast_release(struct bt_bap_stream *stream, /* If stream does not belong to a client session, clean it up now */ if (!bap_stream_valid(stream)) { stream_set_state(stream, BT_BAP_STREAM_STATE_IDLE); - stream = NULL; return 0; } @@ -2610,6 +2614,9 @@ static int bap_ucast_io_link(struct bt_bap_stream *stream, stream->ep->dir == link->ep->dir) return -EINVAL; + if (stream->client && !(stream->locked && link->locked)) + return -EINVAL; + if (!stream->links) stream->links = queue_new(); @@ -5073,6 +5080,8 @@ static void ep_status_config(struct bt_bap *bap, struct bt_bap_endpoint *ep, ep->stream->cc = new0(struct iovec, 1); util_iov_memcpy(ep->stream->cc, cfg->cc, cfg->cc_len); + + ep->stream->need_reconfig = false; } static void bap_stream_config_cfm_cb(struct bt_bap_stream *stream, int err) @@ -5980,43 +5989,6 @@ bool bt_bap_pac_bcast_is_local(struct bt_bap *bap, struct bt_bap_pac *pac) return false; } -static bool find_ep_unused(const void *data, const void *user_data) -{ - const struct bt_bap_endpoint *ep = data; - const struct match_pac *match = user_data; - - if (ep->stream) - return false; - - if (match->rpac) - return ep->dir == match->rpac->type; - else - return true; -} - -static bool find_ep_pacs(const void *data, const void *user_data) -{ - const struct bt_bap_endpoint *ep = data; - const struct match_pac *match = user_data; - - if (!ep->stream) - return false; - - if (ep->stream->lpac != match->lpac) - return false; - - if (ep->stream->rpac != match->rpac) - return false; - - switch (ep->state) { - case BT_BAP_STREAM_STATE_CONFIG: - case BT_BAP_STREAM_STATE_QOS: - return true; - } - - return false; -} - static bool find_ep_source(const void *data, const void *user_data) { const struct bt_bap_endpoint *ep = data; @@ -6196,6 +6168,48 @@ static struct bt_bap_stream *bap_bcast_stream_new(struct bt_bap *bap, return stream; } +static bool find_ep_ucast(const void *data, const void *user_data) +{ + const struct bt_bap_endpoint *ep = data; + const struct match_pac *match = user_data; + + if (ep->stream) { + if (!ep->stream->client) + return false; + if (ep->stream->locked) + return false; + if (!queue_isempty(ep->stream->pending_states)) + return false; + + switch (ep->stream->state) { + case BT_BAP_STREAM_STATE_IDLE: + case BT_BAP_STREAM_STATE_CONFIG: + case BT_BAP_STREAM_STATE_QOS: + break; + default: + return false; + } + } + + if (ep->dir != match->rpac->type) + return false; + + switch (match->lpac->type) { + case BT_BAP_SOURCE: + if (ep->dir != BT_BAP_SINK) + return false; + break; + case BT_BAP_SINK: + if (ep->dir != BT_BAP_SOURCE) + return false; + break; + default: + return false; + } + + return true; +} + static struct bt_bap_stream *bap_ucast_stream_new(struct bt_bap *bap, struct bt_bap_pac *lpac, struct bt_bap_pac *rpac, @@ -6213,20 +6227,26 @@ static struct bt_bap_stream *bap_ucast_stream_new(struct bt_bap *bap, match.lpac = lpac; match.rpac = rpac; - /* Check for existing stream */ - ep = queue_find(bap->remote_eps, find_ep_pacs, &match); + /* Get free ASE */ + ep = queue_find(bap->remote_eps, find_ep_ucast, &match); if (!ep) { - /* Check for unused ASE */ - ep = queue_find(bap->remote_eps, find_ep_unused, &match); - if (!ep) { - DBG(bap, "Unable to find unused ASE"); - return NULL; - } + DBG(bap, "Unable to find usable ASE"); + return NULL; } stream = ep->stream; - if (!stream) + if (stream) { + /* Replace lpac: the stream generally needs to be reconfigured + * after this, otherwise things like codec config not match. + */ + bap_stream_clear_cfm(stream); + stream->lpac = lpac; + util_iov_free(stream->cc, 1); + stream->cc = util_iov_dup(data, 1); + stream->need_reconfig = true; + } else { stream = bap_stream_new(bap, ep, lpac, rpac, data, true); + } return stream; } @@ -6247,6 +6267,25 @@ struct bt_bap_stream *bt_bap_stream_new(struct bt_bap *bap, return bap_bcast_stream_new(bap, lpac, pqos, data); } +void bt_bap_stream_lock(struct bt_bap_stream *stream) +{ + if (!stream || !stream->client) + return; + + /* Reserve stream ASE for use by upper level, so it won't get + * reallocated + */ + stream->locked = true; +} + +void bt_bap_stream_unlock(struct bt_bap_stream *stream) +{ + if (!stream || !stream->client) + return; + + stream->locked = false; +} + struct bt_bap *bt_bap_stream_get_session(struct bt_bap_stream *stream) { if (!stream) diff --git a/src/shared/bap.h b/src/shared/bap.h index d10581428..fba8b6b17 100644 --- a/src/shared/bap.h +++ b/src/shared/bap.h @@ -183,6 +183,9 @@ struct bt_bap_stream *bt_bap_stream_new(struct bt_bap *bap, struct bt_bap_qos *pqos, struct iovec *data); +void bt_bap_stream_lock(struct bt_bap_stream *stream); +void bt_bap_stream_unlock(struct bt_bap_stream *stream); + struct bt_bap *bt_bap_stream_get_session(struct bt_bap_stream *stream); uint8_t bt_bap_stream_get_state(struct bt_bap_stream *stream); -- 2.49.0