From 0185aaacc45445d08d13be7fbde03d48fc1c72d8 Mon Sep 17 00:00:00 2001 From: Emil Gydesen Date: Thu, 6 Jul 2023 13:40:10 +0200 Subject: [PATCH] Bluetooth: CAP: Add support for long read/write Add support for reading and writing long values (> MTU) when using the CAP API. This change does make it slightly slower to execute the CAP procedures as it is now done on a one-by-one basis, which affect multi-ACL setups, but that also means that the buffer requirements for CAP will generally be lower. There is still room for improvement as we can perform the BAP operations in parallel on each ACl, which should return this to its former performance. Signed-off-by: Emil Gydesen --- subsys/bluetooth/audio/cap_initiator.c | 490 +++++++++++------- .../audio/src/cap_initiator_unicast_test.c | 17 +- 2 files changed, 312 insertions(+), 195 deletions(-) diff --git a/subsys/bluetooth/audio/cap_initiator.c b/subsys/bluetooth/audio/cap_initiator.c index a7ef2640c5d64c2..a935c890ab71324 100644 --- a/subsys/bluetooth/audio/cap_initiator.c +++ b/subsys/bluetooth/audio/cap_initiator.c @@ -314,6 +314,23 @@ enum cap_unicast_subproc_type { CAP_UNICAST_SUBPROC_TYPE_RELEASE, }; +struct cap_unicast_proc_param { + struct bt_cap_stream *stream; + union { + struct { + struct bt_conn *conn; + struct bt_bap_ep *ep; + struct bt_audio_codec_cfg codec_cfg; + } start; + struct { + /** Codec Specific Capabilities Metadata count */ + size_t meta_len; + /** Codec Specific Capabilities Metadata */ + uint8_t meta[CONFIG_BT_AUDIO_CODEC_CFG_MAX_METADATA_SIZE]; + } meta_update; + }; +}; + struct cap_unicast_proc { ATOMIC_DEFINE(proc_state_flags, CAP_UNICAST_PROC_STATE_FLAG_NUM); /* Total number of streams in the procedure*/ @@ -326,7 +343,7 @@ struct cap_unicast_proc { enum cap_unicast_subproc_type subproc_type; int err; struct bt_conn *failed_conn; - struct bt_cap_stream *streams[CONFIG_BT_BAP_UNICAST_CLIENT_GROUP_STREAM_COUNT]; + struct cap_unicast_proc_param proc_param[CONFIG_BT_BAP_UNICAST_CLIENT_GROUP_STREAM_COUNT]; struct bt_bap_unicast_group *unicast_group; }; @@ -388,7 +405,7 @@ static bool cap_conn_in_active_proc(const struct bt_conn *conn) } for (size_t i = 0U; i < active_proc.stream_initiated_cnt; i++) { - if (active_proc.streams[i]->bap_stream.conn == conn) { + if (active_proc.proc_param[i].stream->bap_stream.conn == conn) { return true; } } @@ -619,7 +636,7 @@ static bool cap_stream_in_active_proc(const struct bt_cap_stream *cap_stream) } for (size_t i = 0U; i < active_proc.stream_cnt; i++) { - if (active_proc.streams[i] == cap_stream) { + if (active_proc.proc_param[i].stream == cap_stream) { return true; } } @@ -754,71 +771,6 @@ static bool valid_unicast_audio_start_param(const struct bt_cap_unicast_audio_st return true; } -static int cap_initiator_unicast_audio_configure( - const struct bt_cap_unicast_audio_start_param *param) -{ - /** TODO: If this is a CSIP set, then the order of the procedures may - * not match the order in the parameters, and the CSIP ordered access - * procedure should be used. - */ - - /* Store the information about the active procedure so that we can - * continue the procedure after each step - */ - atomic_set_bit(active_proc.proc_state_flags, CAP_UNICAST_PROC_STATE_ACTIVE); - active_proc.stream_cnt = param->count; - - cap_set_subproc(CAP_UNICAST_SUBPROC_TYPE_CODEC_CONFIG); - - for (size_t i = 0U; i < param->count; i++) { - struct bt_cap_unicast_audio_start_stream_param *stream_param = - ¶m->stream_params[i]; - union bt_cap_set_member *member = &stream_param->member; - struct bt_cap_stream *cap_stream = stream_param->stream; - struct bt_bap_stream *bap_stream = &cap_stream->bap_stream; - struct bt_bap_ep *ep = stream_param->ep; - struct bt_audio_codec_cfg *codec_cfg = stream_param->codec_cfg; - struct bt_conn *conn; - int err; - - if (param->type == BT_CAP_SET_TYPE_AD_HOC) { - conn = member->member; - } else { - struct cap_unicast_client *client; - - /* We have verified that `client` wont be NULL in - * `valid_unicast_audio_start_param`. - */ - client = lookup_unicast_client_by_csis(member->csip); - __ASSERT(client != NULL, "client is NULL"); - conn = client->conn; - } - - /* Ensure that ops are registered before any procedures are started */ - bt_cap_stream_ops_register_bap(cap_stream); - - active_proc.streams[i] = cap_stream; - - err = bt_bap_stream_config(conn, bap_stream, ep, codec_cfg); - if (err != 0) { - LOG_DBG("bt_bap_stream_config failed for param->stream_params[%zu]: %d", - i, err); - - if (i > 0U) { - cap_abort_proc(bap_stream->conn, err); - } else { - (void)memset(&active_proc, 0, sizeof(active_proc)); - } - - return err; - } - - active_proc.stream_initiated_cnt++; - } - - return 0; -} - static void cap_initiator_unicast_audio_proc_complete(void) { struct bt_bap_unicast_group *unicast_group; @@ -858,6 +810,82 @@ static void cap_initiator_unicast_audio_proc_complete(void) } } +static int cap_initiator_unicast_audio_configure( + const struct bt_cap_unicast_audio_start_param *param) +{ + struct cap_unicast_proc_param *proc_param; + struct bt_audio_codec_cfg *codec_cfg; + struct bt_bap_stream *bap_stream; + struct bt_bap_ep *ep; + struct bt_conn *conn; + int err; + /** TODO: If this is a CSIP set, then the order of the procedures may + * not match the order in the parameters, and the CSIP ordered access + * procedure should be used. + */ + + for (size_t i = 0U; i < param->count; i++) { + struct bt_cap_unicast_audio_start_stream_param *stream_param = + ¶m->stream_params[i]; + union bt_cap_set_member *member = &stream_param->member; + struct bt_cap_stream *cap_stream = stream_param->stream; + + if (param->type == BT_CAP_SET_TYPE_AD_HOC) { + conn = member->member; + } else { + struct cap_unicast_client *client; + + /* We have verified that `client` wont be NULL in + * `valid_unicast_audio_start_param`. + */ + client = lookup_unicast_client_by_csis(member->csip); + __ASSERT(client != NULL, "client is NULL"); + conn = client->conn; + } + + /* Ensure that ops are registered before any procedures are started */ + bt_cap_stream_ops_register_bap(cap_stream); + + /* Store the necessary parameters as we cannot assume that the supplied parameters + * are kept valid + */ + active_proc.proc_param[i].stream = cap_stream; + active_proc.proc_param[i].start.ep = stream_param->ep; + active_proc.proc_param[i].start.conn = conn; + memcpy(&active_proc.proc_param[i].start.codec_cfg, stream_param->codec_cfg, + sizeof(*stream_param->codec_cfg)); + } + + /* Store the information about the active procedure so that we can + * continue the procedure after each step + */ + atomic_set_bit(active_proc.proc_state_flags, CAP_UNICAST_PROC_STATE_ACTIVE); + active_proc.stream_cnt = param->count; + + cap_set_subproc(CAP_UNICAST_SUBPROC_TYPE_CODEC_CONFIG); + + proc_param = &active_proc.proc_param[0]; + bap_stream = &proc_param->stream->bap_stream; + codec_cfg = &proc_param->start.codec_cfg; + conn = proc_param->start.conn; + ep = proc_param->start.ep; + + /* Since BAP operations may require a write long or a read long on the notification, + * we cannot assume that we can do multiple streams at once, thus do it one at a time. + * TODO: We should always be able to do one per ACL, so there is room for optimization. + */ + err = bt_bap_stream_config(conn, bap_stream, ep, codec_cfg); + if (err != 0) { + LOG_DBG("Failed to config stream %p: %d", proc_param->stream, err); + + (void)memset(&active_proc, 0, sizeof(active_proc)); + } else { + active_proc.stream_initiated_cnt++; + } + + return err; +} + int bt_cap_initiator_unicast_audio_start(const struct bt_cap_unicast_audio_start_param *param, struct bt_bap_unicast_group *unicast_group) { @@ -884,8 +912,9 @@ int bt_cap_initiator_unicast_audio_start(const struct bt_cap_unicast_audio_start void bt_cap_initiator_codec_configured(struct bt_cap_stream *cap_stream) { - struct bt_conn *conns[MIN(CONFIG_BT_MAX_CONN, - CONFIG_BT_BAP_UNICAST_CLIENT_GROUP_STREAM_COUNT)]; + struct bt_conn + *conns[MIN(CONFIG_BT_MAX_CONN, CONFIG_BT_BAP_UNICAST_CLIENT_GROUP_STREAM_COUNT)]; + struct cap_unicast_proc_param *proc_param; struct bt_bap_unicast_group *unicast_group; if (!cap_stream_in_active_proc(cap_stream)) { @@ -905,14 +934,8 @@ void bt_cap_initiator_codec_configured(struct bt_cap_stream *cap_stream) } else { active_proc.stream_done_cnt++; - LOG_DBG("Stream %p configured (%zu/%zu streams done)", - cap_stream, active_proc.stream_done_cnt, - active_proc.stream_cnt); - - if (!cap_proc_is_done()) { - /* Not yet finished, wait for all */ - return; - } + LOG_DBG("Stream %p configured (%zu/%zu streams done)", cap_stream, + active_proc.stream_done_cnt, active_proc.stream_cnt); } if (cap_proc_is_aborted()) { @@ -923,13 +946,43 @@ void bt_cap_initiator_codec_configured(struct bt_cap_stream *cap_stream) return; } + if (!cap_proc_is_done()) { + struct bt_cap_stream *next_cap_stream = + active_proc.proc_param[active_proc.stream_done_cnt].stream; + struct bt_conn *conn = + active_proc.proc_param[active_proc.stream_done_cnt].start.conn; + struct bt_bap_ep *ep = active_proc.proc_param[active_proc.stream_done_cnt].start.ep; + struct bt_audio_codec_cfg *codec_cfg = + &active_proc.proc_param[active_proc.stream_done_cnt].start.codec_cfg; + struct bt_bap_stream *bap_stream = &next_cap_stream->bap_stream; + int err; + + /* Since BAP operations may require a write long or a read long on the notification, + * we cannot assume that we can do multiple streams at once, thus do it one at a + * time. + * TODO: We should always be able to do one per ACL, so there is room for + * optimization. + */ + err = bt_bap_stream_config(conn, bap_stream, ep, codec_cfg); + if (err != 0) { + LOG_DBG("Failed to config stream %p: %d", next_cap_stream, err); + + cap_abort_proc(conn, err); + cap_initiator_unicast_audio_proc_complete(); + } else { + active_proc.stream_initiated_cnt++; + } + + return; + } + /* The QoS Configure procedure works on a set of connections and a * unicast group, so we generate a list of unique connection pointers * for the procedure */ (void)memset(conns, 0, sizeof(conns)); for (size_t i = 0U; i < active_proc.stream_cnt; i++) { - struct bt_conn *stream_conn = active_proc.streams[i]->bap_stream.conn; + struct bt_conn *stream_conn = active_proc.proc_param[i].stream->bap_stream.conn; struct bt_conn **free_conn = NULL; bool already_added = false; @@ -956,7 +1009,8 @@ void bt_cap_initiator_codec_configured(struct bt_cap_stream *cap_stream) /* All streams in the procedure share the same unicast group, so we just * use the reference from the first stream */ - unicast_group = (struct bt_bap_unicast_group *)active_proc.streams[0]->bap_stream.group; + proc_param = &active_proc.proc_param[0]; + unicast_group = (struct bt_bap_unicast_group *)proc_param->stream->bap_stream.group; cap_set_subproc(CAP_UNICAST_SUBPROC_TYPE_QOS_CONFIG); for (size_t i = 0U; i < ARRAY_SIZE(conns); i++) { @@ -990,6 +1044,11 @@ void bt_cap_initiator_codec_configured(struct bt_cap_stream *cap_stream) void bt_cap_initiator_qos_configured(struct bt_cap_stream *cap_stream) { + struct cap_unicast_proc_param *proc_param; + struct bt_cap_stream *next_cap_stream; + struct bt_bap_stream *bap_stream; + int err; + if (!cap_stream_in_active_proc(cap_stream)) { /* State change happened outside of a procedure; ignore */ return; @@ -1001,14 +1060,8 @@ void bt_cap_initiator_qos_configured(struct bt_cap_stream *cap_stream) } else { active_proc.stream_done_cnt++; - LOG_DBG("Stream %p QoS configured (%zu/%zu streams done)", - cap_stream, active_proc.stream_done_cnt, - active_proc.stream_cnt); - - if (!cap_proc_is_done()) { - /* Not yet finished, wait for all */ - return; - } + LOG_DBG("Stream %p QoS configured (%zu/%zu streams done)", cap_stream, + active_proc.stream_done_cnt, active_proc.stream_cnt); } if (cap_proc_is_aborted()) { @@ -1019,36 +1072,35 @@ void bt_cap_initiator_qos_configured(struct bt_cap_stream *cap_stream) return; } - cap_set_subproc(CAP_UNICAST_SUBPROC_TYPE_ENABLE); - - for (size_t i = 0U; i < active_proc.stream_cnt; i++) { - struct bt_cap_stream *cap_stream_active = active_proc.streams[i]; - struct bt_bap_stream *bap_stream = &cap_stream_active->bap_stream; - int err; + if (!cap_proc_is_done()) { + /* Not yet finished, wait for all */ + return; + } - /* TODO: Add metadata */ - err = bt_bap_stream_enable(bap_stream, bap_stream->codec_cfg->meta, - bap_stream->codec_cfg->meta_len); - if (err != 0) { - LOG_DBG("Failed to enable stream %p: %d", - cap_stream_active, err); + cap_set_subproc(CAP_UNICAST_SUBPROC_TYPE_ENABLE); + proc_param = &active_proc.proc_param[0]; + next_cap_stream = proc_param->stream; + bap_stream = &next_cap_stream->bap_stream; - /* End or mark procedure as aborted. - * If we have sent any requests over air, we will abort - * once all sent requests has completed - */ - cap_abort_proc(bap_stream->conn, err); - if (i == 0U) { - cap_initiator_unicast_audio_proc_complete(); - } + /* Since BAP operations may require a write long or a read long on the notification, we + * cannot assume that we can do multiple streams at once, thus do it one at a time. + * TODO: We should always be able to do one per ACL, so there is room for optimization. + */ + err = bt_bap_stream_enable(bap_stream, bap_stream->codec_cfg->meta, + bap_stream->codec_cfg->meta_len); + if (err != 0) { + LOG_DBG("Failed to enable stream %p: %d", next_cap_stream, err); - return; - } + cap_abort_proc(bap_stream->conn, err); + cap_initiator_unicast_audio_proc_complete(); + } else { + active_proc.stream_initiated_cnt++; } } void bt_cap_initiator_enabled(struct bt_cap_stream *cap_stream) { + struct cap_unicast_proc_param *proc_param; struct bt_bap_stream *bap_stream; int err; @@ -1063,14 +1115,8 @@ void bt_cap_initiator_enabled(struct bt_cap_stream *cap_stream) } else { active_proc.stream_done_cnt++; - LOG_DBG("Stream %p enabled (%zu/%zu streams done)", - cap_stream, active_proc.stream_done_cnt, - active_proc.stream_cnt); - - if (!cap_proc_is_done()) { - /* Not yet finished, wait for all */ - return; - } + LOG_DBG("Stream %p enabled (%zu/%zu streams done)", cap_stream, + active_proc.stream_done_cnt, active_proc.stream_cnt); } if (cap_proc_is_aborted()) { @@ -1081,17 +1127,43 @@ void bt_cap_initiator_enabled(struct bt_cap_stream *cap_stream) return; } - cap_set_subproc(CAP_UNICAST_SUBPROC_TYPE_START); + if (!cap_proc_is_done()) { + struct bt_cap_stream *next_cap_stream = + active_proc.proc_param[active_proc.stream_done_cnt].stream; + struct bt_bap_stream *bap_stream = &next_cap_stream->bap_stream; + int err; - bap_stream = &active_proc.streams[0]->bap_stream; + /* Since BAP operations may require a write long or a read long on the notification, + * we cannot assume that we can do multiple streams at once, thus do it one at a + * time. + * TODO: We should always be able to do one per ACL, so there is room for + * optimization. + */ + err = bt_bap_stream_enable(bap_stream, bap_stream->codec_cfg->meta, + bap_stream->codec_cfg->meta_len); + if (err != 0) { + LOG_DBG("Failed to enable stream %p: %d", next_cap_stream, err); - /* Since bt_bap_stream_start connects the ISO, we can, at this point, - * only do this one by one due to a restriction in the ISO layer - * (maximum 1 outstanding ISO connection request at any one time). + cap_abort_proc(bap_stream->conn, err); + cap_initiator_unicast_audio_proc_complete(); + } else { + active_proc.stream_initiated_cnt++; + } + + return; + } + + cap_set_subproc(CAP_UNICAST_SUBPROC_TYPE_START); + proc_param = &active_proc.proc_param[0]; + bap_stream = &proc_param->stream->bap_stream; + + /* Since BAP operations may require a write long or a read long on the notification, we + * cannot assume that we can do multiple streams at once, thus do it one at a time. + * TODO: We should always be able to do one per ACL, so there is room for optimization. */ err = bt_bap_stream_start(bap_stream); if (err != 0) { - LOG_DBG("Failed to start stream %p: %d", active_proc.streams[0], err); + LOG_DBG("Failed to start stream %p: %d", proc_param->stream, err); /* End and mark procedure as aborted. * If we have sent any requests over air, we will abort @@ -1127,14 +1199,15 @@ void bt_cap_initiator_started(struct bt_cap_stream *cap_stream) * (maximum 1 outstanding ISO connection request at any one time). */ if (!cap_proc_is_done()) { - struct bt_cap_stream *next = active_proc.streams[active_proc.stream_done_cnt]; - struct bt_bap_stream *bap_stream = &next->bap_stream; + struct bt_cap_stream *next_cap_stream = + active_proc.proc_param[active_proc.stream_done_cnt].stream; + struct bt_bap_stream *bap_stream = &next_cap_stream->bap_stream; int err; /* Not yet finished, start next */ err = bt_bap_stream_start(bap_stream); if (err != 0) { - LOG_DBG("Failed to start stream %p: %d", next, err); + LOG_DBG("Failed to start stream %p: %d", next_cap_stream, err); /* End and mark procedure as aborted. * If we have sent any requests over air, we will abort @@ -1171,6 +1244,12 @@ static bool can_update_metadata(const struct bt_bap_stream *bap_stream) int bt_cap_initiator_unicast_audio_update(const struct bt_cap_unicast_audio_update_param params[], size_t count) { + struct cap_unicast_proc_param *proc_param; + struct bt_bap_stream *bap_stream; + const uint8_t *meta; + size_t meta_len; + int err; + CHECKIF(params == NULL) { LOG_DBG("params is NULL"); @@ -1190,13 +1269,15 @@ int bt_cap_initiator_unicast_audio_update(const struct bt_cap_unicast_audio_upda } for (size_t i = 0U; i < count; i++) { - CHECKIF(params[i].stream == NULL) { + struct bt_cap_stream *cap_stream = params[i].stream; + + CHECKIF(cap_stream == NULL) { LOG_DBG("params[%zu].stream is NULL", i); return -EINVAL; } - CHECKIF(params[i].stream->bap_stream.conn == NULL) { + CHECKIF(cap_stream->bap_stream.conn == NULL) { LOG_DBG("params[%zu].stream->bap_stream.conn is NULL", i); return -EINVAL; @@ -1210,56 +1291,46 @@ int bt_cap_initiator_unicast_audio_update(const struct bt_cap_unicast_audio_upda } for (size_t j = 0U; j < i; j++) { - if (params[j].stream == params[i].stream) { + if (params[j].stream == cap_stream) { LOG_DBG("param.streams[%zu] is duplicated by param.streams[%zu]", j, i); return -EINVAL; } } - if (!can_update_metadata(¶ms[i].stream->bap_stream)) { + if (!can_update_metadata(&cap_stream->bap_stream)) { LOG_DBG("params[%zu].stream is not in right state to be updated", i); return -EINVAL; } + + active_proc.proc_param[i].stream = cap_stream; + active_proc.proc_param[i].meta_update.meta_len = params[i].meta_len; + memcpy(&active_proc.proc_param[i].meta_update.meta, params[i].meta, + params[i].meta_len); } - atomic_set_bit(active_proc.proc_state_flags, - CAP_UNICAST_PROC_STATE_ACTIVE); + atomic_set_bit(active_proc.proc_state_flags, CAP_UNICAST_PROC_STATE_ACTIVE); active_proc.stream_cnt = count; - active_proc.proc_type = CAP_UNICAST_PROC_TYPE_UPDATE; + active_proc.proc_type = CAP_UNICAST_PROC_TYPE_UPDATE; cap_set_subproc(CAP_UNICAST_SUBPROC_TYPE_META_UPDATE); - /** TODO: If this is a CSIP set, then the order of the procedures may - * not match the order in the parameters, and the CSIP ordered access - * procedure should be used. - */ - for (size_t i = 0U; i < count; i++) { - int err; + proc_param = &active_proc.proc_param[0]; + bap_stream = &proc_param->stream->bap_stream; + meta_len = proc_param->meta_update.meta_len; + meta = proc_param->meta_update.meta; - active_proc.streams[i] = params[i].stream; - - err = bt_bap_stream_metadata(¶ms[i].stream->bap_stream, params[i].meta, - params[i].meta_len); - if (err != 0) { - LOG_DBG("Failed to update metadata for stream %p: %d", - params[i].stream, err); - - if (i > 0U) { - cap_abort_proc(params[i].stream->bap_stream.conn, err); - } else { - (void)memset(&active_proc, 0, - sizeof(active_proc)); - } - - return err; - } + err = bt_bap_stream_metadata(bap_stream, meta, meta_len); + if (err != 0) { + LOG_DBG("Failed to update metadata for stream %p: %d", proc_param->stream, err); + (void)memset(&active_proc, 0, sizeof(active_proc)); + } else { active_proc.stream_initiated_cnt++; } - return 0; + return err; } int bt_cap_initiator_unicast_audio_cancel(void) @@ -1294,10 +1365,7 @@ void bt_cap_initiator_metadata_updated(struct bt_cap_stream *cap_stream) active_proc.stream_cnt); } - if (!cap_proc_is_done()) { - /* Not yet finished, wait for all */ - return; - } else if (cap_proc_is_aborted()) { + if (cap_proc_is_aborted()) { if (cap_proc_all_streams_handled()) { cap_initiator_unicast_audio_proc_complete(); } @@ -1305,6 +1373,37 @@ void bt_cap_initiator_metadata_updated(struct bt_cap_stream *cap_stream) return; } + if (!cap_proc_is_done()) { + const size_t meta_len = + active_proc.proc_param[active_proc.stream_done_cnt].meta_update.meta_len; + const uint8_t *meta = + active_proc.proc_param[active_proc.stream_done_cnt].meta_update.meta; + struct bt_cap_stream *next_cap_stream = + active_proc.proc_param[active_proc.stream_done_cnt].stream; + struct bt_bap_stream *bap_stream = &next_cap_stream->bap_stream; + int err; + + /* Since BAP operations may require a write long or a read long on the notification, + * we cannot assume that we can do multiple streams at once, thus do it one at a + * time. + * TODO: We should always be able to do one per ACL, so there is room for + * optimization. + */ + + err = bt_bap_stream_metadata(bap_stream, meta, meta_len); + if (err != 0) { + LOG_DBG("Failed to update metadata for stream %p: %d", next_cap_stream, + err); + + cap_abort_proc(bap_stream->conn, err); + cap_initiator_unicast_audio_proc_complete(); + } else { + active_proc.stream_initiated_cnt++; + } + + return; + } + cap_initiator_unicast_audio_proc_complete(); } @@ -1329,8 +1428,10 @@ static bool can_release(const struct bt_bap_stream *bap_stream) int bt_cap_initiator_unicast_audio_stop(struct bt_bap_unicast_group *unicast_group) { + struct cap_unicast_proc_param *proc_param; struct bt_bap_stream *bap_stream; size_t stream_cnt; + int err; if (cap_proc_is_active()) { LOG_DBG("A CAP procedure is already in progress"); @@ -1346,6 +1447,9 @@ int bt_cap_initiator_unicast_audio_stop(struct bt_bap_unicast_group *unicast_gro stream_cnt = 0U; SYS_SLIST_FOR_EACH_CONTAINER(&unicast_group->streams, bap_stream, _node) { if (can_release(bap_stream)) { + struct bt_cap_stream *cap_stream = + CONTAINER_OF(bap_stream, struct bt_cap_stream, bap_stream); + active_proc.proc_param[stream_cnt].stream = cap_stream; stream_cnt++; } } @@ -1368,35 +1472,19 @@ int bt_cap_initiator_unicast_audio_stop(struct bt_bap_unicast_group *unicast_gro * not match the order in the parameters, and the CSIP ordered access * procedure should be used. */ - SYS_SLIST_FOR_EACH_CONTAINER(&unicast_group->streams, bap_stream, _node) { - struct bt_cap_stream *cap_stream = - CONTAINER_OF(bap_stream, struct bt_cap_stream, bap_stream); - int err; + proc_param = &active_proc.proc_param[0]; + bap_stream = &proc_param->stream->bap_stream; - if (!can_release(bap_stream)) { - continue; - } - - active_proc.streams[active_proc.stream_initiated_cnt] = cap_stream; - - err = bt_bap_stream_release(bap_stream); - if (err != 0) { - LOG_DBG("Failed to stop bap_stream %p: %d", bap_stream, err); - - if (active_proc.stream_initiated_cnt > 0U) { - cap_abort_proc(bap_stream->conn, err); - } else { - (void)memset(&active_proc, 0, - sizeof(active_proc)); - } - - return err; - } + err = bt_bap_stream_release(bap_stream); + if (err != 0) { + LOG_DBG("Failed to stop bap_stream %p: %d", proc_param->stream, err); + (void)memset(&active_proc, 0, sizeof(active_proc)); + } else { active_proc.stream_initiated_cnt++; } - return 0; + return err; } void bt_cap_initiator_released(struct bt_cap_stream *cap_stream) @@ -1417,10 +1505,7 @@ void bt_cap_initiator_released(struct bt_cap_stream *cap_stream) active_proc.stream_cnt); } - if (!cap_proc_is_done()) { - /* Not yet finished, wait for all */ - return; - } else if (cap_proc_is_aborted()) { + if (cap_proc_is_aborted()) { if (cap_proc_all_streams_handled()) { cap_initiator_unicast_audio_proc_complete(); } @@ -1428,7 +1513,30 @@ void bt_cap_initiator_released(struct bt_cap_stream *cap_stream) return; } - cap_initiator_unicast_audio_proc_complete(); + if (!cap_proc_is_done()) { + struct bt_cap_stream *next_cap_stream = + active_proc.proc_param[active_proc.stream_done_cnt].stream; + struct bt_bap_stream *bap_stream = &next_cap_stream->bap_stream; + int err; + + /* Since BAP operations may require a write long or a read long on the notification, + * we cannot assume that we can do multiple streams at once, thus do it one at a + * time. + * TODO: We should always be able to do one per ACL, so there is room for + * optimization. + */ + err = bt_bap_stream_release(bap_stream); + if (err != 0) { + LOG_DBG("Failed to release stream %p: %d", next_cap_stream, err); + + cap_abort_proc(bap_stream->conn, err); + cap_initiator_unicast_audio_proc_complete(); + } else { + active_proc.stream_initiated_cnt++; + } + } else { + cap_initiator_unicast_audio_proc_complete(); + } } #endif /* CONFIG_BT_BAP_UNICAST_CLIENT */ diff --git a/tests/bsim/bluetooth/audio/src/cap_initiator_unicast_test.c b/tests/bsim/bluetooth/audio/src/cap_initiator_unicast_test.c index 3c068a0c0612fd3..67c36a27855b011 100644 --- a/tests/bsim/bluetooth/audio/src/cap_initiator_unicast_test.c +++ b/tests/bsim/bluetooth/audio/src/cap_initiator_unicast_test.c @@ -729,15 +729,23 @@ static void unicast_audio_update_inval(void) static void unicast_audio_update(void) { struct bt_cap_unicast_audio_update_param param[2]; + uint8_t new_meta[] = { + 3, + BT_AUDIO_METADATA_TYPE_STREAM_CONTEXT, + BT_BYTES_LIST_LE16(BT_AUDIO_CONTEXT_TYPE_UNSPECIFIED), + LONG_META_LEN, + BT_AUDIO_METADATA_TYPE_VENDOR, + LONG_META, + }; int err; param[0].stream = &unicast_client_sink_streams[0]; - param[0].meta = unicast_preset_16_2_1.codec_cfg.meta; - param[0].meta_len = unicast_preset_16_2_1.codec_cfg.meta_len; + param[0].meta = new_meta; + param[0].meta_len = ARRAY_SIZE(new_meta); param[1].stream = &unicast_client_source_streams[0]; - param[1].meta = unicast_preset_16_2_1.codec_cfg.meta; - param[1].meta_len = unicast_preset_16_2_1.codec_cfg.meta_len; + param[1].meta = new_meta; + param[1].meta_len = ARRAY_SIZE(new_meta); UNSET_FLAG(flag_updated); @@ -748,6 +756,7 @@ static void unicast_audio_update(void) } WAIT_FOR_FLAG(flag_updated); + printk("READ LONG META\n"); } static void unicast_audio_stop_inval(void)