From 27ee8cb28844c0bf774e239a6333d2ca560fb796 Mon Sep 17 00:00:00 2001 From: Emil Gydesen Date: Tue, 24 Sep 2024 18:13:13 +0200 Subject: [PATCH] Bluetooth: CAP: Add support for handling ASE errors If we get an error/rejection from the CAP acceptor when performing the Unicast Audio Start or Stop procedure then we need to abort the procedure and let the application determine what the next step is. This change triggered a corner case when connecting to multiple CAP acceptors as the CAP initiatior that was fixed as part of this. Signed-off-by: Emil Gydesen --- subsys/bluetooth/audio/cap_initiator.c | 140 ++++++++++++++---- subsys/bluetooth/audio/cap_internal.h | 3 + subsys/bluetooth/audio/cap_stream.c | 33 +++++ .../cap_initiator/uut/bap_unicast_client.c | 50 +++++++ .../bluetooth/audio/src/cap_acceptor_test.c | 28 ++-- .../audio/src/cap_initiator_unicast_test.c | 71 ++++++++- .../test_scripts/cap_unicast_ase_error.sh | 28 ++++ 7 files changed, 306 insertions(+), 47 deletions(-) create mode 100755 tests/bsim/bluetooth/audio/test_scripts/cap_unicast_ase_error.sh diff --git a/subsys/bluetooth/audio/cap_initiator.c b/subsys/bluetooth/audio/cap_initiator.c index 23295848b14359..62c61c295a6090 100644 --- a/subsys/bluetooth/audio/cap_initiator.c +++ b/subsys/bluetooth/audio/cap_initiator.c @@ -440,7 +440,7 @@ static void update_proc_done_cnt(struct bt_cap_common_proc *active_proc) * the states to determine how far we are. */ for (size_t i = 0U; i < active_proc->proc_cnt; i++) { - const struct bt_cap_initiator_proc_param *proc_param; + struct bt_cap_initiator_proc_param *proc_param; struct bt_cap_stream *cap_stream; struct bt_bap_stream *bap_stream; enum bt_bap_ep_state state; @@ -455,11 +455,13 @@ static void update_proc_done_cnt(struct bt_cap_common_proc *active_proc) case BT_CAP_COMMON_SUBPROC_TYPE_CODEC_CONFIG: if (state > BT_BAP_EP_STATE_IDLE) { proc_done_cnt++; + proc_param->in_progress = false; } break; case BT_CAP_COMMON_SUBPROC_TYPE_QOS_CONFIG: if (state > BT_BAP_EP_STATE_CODEC_CONFIGURED) { proc_done_cnt++; + proc_param->in_progress = false; } else if (state < BT_BAP_EP_STATE_CODEC_CONFIGURED) { /* Unexpected state - Abort */ bt_cap_common_abort_proc(bap_stream->conn, -EBADMSG); @@ -468,6 +470,7 @@ static void update_proc_done_cnt(struct bt_cap_common_proc *active_proc) case BT_CAP_COMMON_SUBPROC_TYPE_ENABLE: if (state > BT_BAP_EP_STATE_QOS_CONFIGURED) { proc_done_cnt++; + proc_param->in_progress = false; } else if (state < BT_BAP_EP_STATE_QOS_CONFIGURED) { /* Unexpected state - Abort */ bt_cap_common_abort_proc(bap_stream->conn, -EBADMSG); @@ -479,11 +482,13 @@ static void update_proc_done_cnt(struct bt_cap_common_proc *active_proc) bt_cap_common_abort_proc(bap_stream->conn, -EBADMSG); } else if (proc_param->start.connected) { proc_done_cnt++; + proc_param->in_progress = false; } break; case BT_CAP_COMMON_SUBPROC_TYPE_START: if (state > BT_BAP_EP_STATE_ENABLING) { proc_done_cnt++; + proc_param->in_progress = false; } else if (state < BT_BAP_EP_STATE_ENABLING || !iso_is_in_state(cap_stream, BT_ISO_STATE_CONNECTED)) { /* Unexpected state - Abort */ @@ -501,7 +506,7 @@ static void update_proc_done_cnt(struct bt_cap_common_proc *active_proc) * the states to determine how far we are. */ for (size_t i = 0U; i < active_proc->proc_cnt; i++) { - const struct bt_cap_initiator_proc_param *proc_param; + struct bt_cap_initiator_proc_param *proc_param; struct bt_cap_stream *cap_stream; struct bt_bap_stream *bap_stream; enum bt_bap_ep_state state; @@ -517,17 +522,20 @@ static void update_proc_done_cnt(struct bt_cap_common_proc *active_proc) if (state == BT_BAP_EP_STATE_QOS_CONFIGURED || state == BT_BAP_EP_STATE_DISABLING) { proc_done_cnt++; + proc_param->in_progress = false; } break; case BT_CAP_COMMON_SUBPROC_TYPE_STOP: if (state == BT_BAP_EP_STATE_QOS_CONFIGURED) { proc_done_cnt++; + proc_param->in_progress = false; } break; case BT_CAP_COMMON_SUBPROC_TYPE_RELEASE: if (state == BT_BAP_EP_STATE_IDLE || state == BT_BAP_EP_STATE_CODEC_CONFIGURED) { proc_done_cnt++; + proc_param->in_progress = false; } break; default: @@ -539,7 +547,7 @@ static void update_proc_done_cnt(struct bt_cap_common_proc *active_proc) /* For metadata we cannot check the states for all streams, as it does not trigger a * state change */ - const struct bt_cap_initiator_proc_param *proc_param; + struct bt_cap_initiator_proc_param *proc_param; struct bt_cap_stream *cap_stream; struct bt_bap_stream *bap_stream; enum bt_bap_ep_state state; @@ -555,6 +563,7 @@ static void update_proc_done_cnt(struct bt_cap_common_proc *active_proc) if (state == BT_BAP_EP_STATE_ENABLING || state == BT_BAP_EP_STATE_STREAMING) { proc_done_cnt = active_proc->proc_done_cnt + 1U; + proc_param->in_progress = false; } else { /* Unexpected state - Abort */ bt_cap_common_abort_proc(bap_stream->conn, -EBADMSG); @@ -588,6 +597,11 @@ get_next_proc_param(struct bt_cap_common_proc *active_proc) enum bt_bap_ep_state state; proc_param = &active_proc->proc_param.initiator[i]; + + if (proc_param->in_progress) { + continue; + } + cap_stream = proc_param->stream; bap_stream = &cap_stream->bap_stream; state = stream_get_state(bap_stream); @@ -813,6 +827,39 @@ static void cap_initiator_unicast_audio_proc_complete(void) } } +void bt_cap_initiator_cp_cb(struct bt_cap_stream *cap_stream, enum bt_bap_ascs_rsp_code rsp_code, + enum bt_bap_ascs_reason reason) +{ + if (!bt_cap_common_stream_in_active_proc(cap_stream)) { + /* State change happened outside of a procedure; ignore */ + return; + } + + if (rsp_code != BT_BAP_ASCS_RSP_CODE_SUCCESS) { + struct bt_cap_common_proc *active_proc = bt_cap_common_get_active_proc(); + + /* In the case that the control point write is rejected, we will not get a ASE state + * change notification. This is considered an error that shall abort the current + * procedure. + */ + active_proc->proc_done_cnt++; + + LOG_DBG("Control point operation on stream %p failed with %d and reason %d", + cap_stream, rsp_code, reason); + + /* Unexpected callback - Abort */ + bt_cap_common_abort_proc(cap_stream->bap_stream.conn, -EBADMSG); + + if (bt_cap_common_proc_is_aborted()) { + if (bt_cap_common_proc_all_handled()) { + cap_initiator_unicast_audio_proc_complete(); + } + + return; + } + } +} + static int cap_initiator_unicast_audio_configure( const struct bt_cap_unicast_audio_start_param *param) { @@ -868,6 +915,7 @@ static int cap_initiator_unicast_audio_configure( conn = proc_param->start.conn; ep = proc_param->start.ep; active_proc->proc_initiated_cnt++; + proc_param->in_progress = true; /* 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. @@ -970,6 +1018,7 @@ void bt_cap_initiator_codec_configured(struct bt_cap_stream *cap_stream) codec_cfg = proc_param->start.codec_cfg; next_bap_stream = &next_cap_stream->bap_stream; active_proc->proc_initiated_cnt++; + proc_param->in_progress = true; err = bt_bap_stream_config(conn, next_bap_stream, ep, codec_cfg); if (err != 0) { @@ -1038,6 +1087,7 @@ void bt_cap_initiator_codec_configured(struct bt_cap_stream *cap_stream) } active_proc->proc_initiated_cnt++; + proc_param->in_progress = true; err = bt_bap_stream_qos(conns[i], unicast_group); if (err != 0) { @@ -1114,6 +1164,7 @@ void bt_cap_initiator_qos_configured(struct bt_cap_stream *cap_stream) next_cap_stream = proc_param->stream; bap_stream = &next_cap_stream->bap_stream; active_proc->proc_initiated_cnt++; + proc_param->in_progress = true; err = bt_bap_stream_enable(bap_stream, bap_stream->codec_cfg->meta, bap_stream->codec_cfg->meta_len); @@ -1135,6 +1186,7 @@ void bt_cap_initiator_qos_configured(struct bt_cap_stream *cap_stream) next_cap_stream = proc_param->stream; next_bap_stream = &next_cap_stream->bap_stream; active_proc->proc_initiated_cnt++; + proc_param->in_progress = true; err = bt_bap_stream_release(next_bap_stream); if (err != 0) { @@ -1188,6 +1240,7 @@ void bt_cap_initiator_enabled(struct bt_cap_stream *cap_stream) next_bap_stream = &next_cap_stream->bap_stream; active_proc->proc_initiated_cnt++; + proc_param->in_progress = true; err = bt_bap_stream_enable(next_bap_stream, next_bap_stream->codec_cfg->meta, next_bap_stream->codec_cfg->meta_len); @@ -1212,6 +1265,7 @@ void bt_cap_initiator_enabled(struct bt_cap_stream *cap_stream) } bap_stream = &proc_param->stream->bap_stream; + proc_param->in_progress = true; err = bt_bap_stream_connect(bap_stream); if (err == -EALREADY) { @@ -1219,6 +1273,7 @@ void bt_cap_initiator_enabled(struct bt_cap_stream *cap_stream) * NOTE: It's important that we do not do any additional functionality after * calling this */ + proc_param->in_progress = false; bt_cap_initiator_connected(proc_param->stream); } else if (err != 0) { LOG_DBG("Failed to connect stream %p: %d", proc_param->stream, err); @@ -1276,21 +1331,23 @@ void bt_cap_initiator_connected(struct bt_cap_stream *cap_stream) struct bt_bap_stream *next_bap_stream; proc_param = get_next_proc_param(active_proc); - __ASSERT(proc_param != NULL, "proc is not done, but could not get next proc_param"); - next_cap_stream = proc_param->stream; - next_bap_stream = &next_cap_stream->bap_stream; + if (proc_param != NULL) { + next_cap_stream = proc_param->stream; + next_bap_stream = &next_cap_stream->bap_stream; - active_proc->proc_initiated_cnt++; + active_proc->proc_initiated_cnt++; + proc_param->in_progress = true; - err = bt_bap_stream_connect(next_bap_stream); - if (err == 0 || err == -EALREADY) { - /* Pending connected - wait for connected callback */ - } else if (err != 0) { - LOG_DBG("Failed to connect stream %p: %d", next_cap_stream, err); + err = bt_bap_stream_connect(next_bap_stream); + if (err == 0 || err == -EALREADY) { + /* Pending connected - wait for connected callback */ + } else if (err != 0) { + LOG_DBG("Failed to connect stream %p: %d", next_cap_stream, err); - bt_cap_common_abort_proc(next_bap_stream->conn, err); - cap_initiator_unicast_audio_proc_complete(); - } + bt_cap_common_abort_proc(next_bap_stream->conn, err); + cap_initiator_unicast_audio_proc_complete(); + } + } /* else pending connection - wait for connected callback */ return; } @@ -1311,6 +1368,8 @@ void bt_cap_initiator_connected(struct bt_cap_stream *cap_stream) bap_stream = &proc_param->stream->bap_stream; if (stream_is_dir(bap_stream, BT_AUDIO_DIR_SOURCE)) { + proc_param->in_progress = true; + err = bt_bap_stream_start(bap_stream); if (err != 0) { LOG_DBG("Failed to start stream %p: %d", proc_param->stream, err); @@ -1335,8 +1394,12 @@ void bt_cap_initiator_started(struct bt_cap_stream *cap_stream) } /* Streams may go into the streaming state while we are connecting or starting them */ - if (!bt_cap_common_subproc_is_type(BT_CAP_COMMON_SUBPROC_TYPE_START) && - !bt_cap_common_subproc_is_type(BT_CAP_COMMON_SUBPROC_TYPE_CONNECT)) { + if (bt_cap_common_subproc_is_type(BT_CAP_COMMON_SUBPROC_TYPE_CONNECT)) { + /* If we are still connecting the streams, we terminate early as to not perform any + * start operations until all streams are connected + */ + return; + } else if (!bt_cap_common_subproc_is_type(BT_CAP_COMMON_SUBPROC_TYPE_START)) { /* Unexpected callback - Abort */ bt_cap_common_abort_proc(cap_stream->bap_stream.conn, -EBADMSG); } else { @@ -1352,25 +1415,29 @@ void bt_cap_initiator_started(struct bt_cap_stream *cap_stream) struct bt_bap_stream *next_bap_stream; proc_param = get_next_proc_param(active_proc); - __ASSERT(proc_param != NULL, "proc is not done, but could not get next proc_param"); - next_cap_stream = proc_param->stream; - next_bap_stream = &next_cap_stream->bap_stream; + if (proc_param != NULL) { + next_cap_stream = proc_param->stream; + next_bap_stream = &next_cap_stream->bap_stream; - if (stream_is_dir(next_bap_stream, BT_AUDIO_DIR_SOURCE)) { - int err; + if (stream_is_dir(next_bap_stream, BT_AUDIO_DIR_SOURCE)) { + int err; - err = bt_bap_stream_start(next_bap_stream); - if (err != 0) { - LOG_DBG("Failed to start stream %p: %d", next_cap_stream, err); + proc_param->in_progress = true; - /* End and mark procedure as aborted. - * If we have sent any requests over air, we will abort - * once all sent requests has completed - */ - bt_cap_common_abort_proc(next_bap_stream->conn, err); - cap_initiator_unicast_audio_proc_complete(); + err = bt_bap_stream_start(next_bap_stream); + if (err != 0) { + LOG_DBG("Failed to start stream %p: %d", next_cap_stream, + err); - return; + /* End and mark procedure as aborted. + * If we have sent any requests over air, we will abort + * once all sent requests has completed + */ + bt_cap_common_abort_proc(next_bap_stream->conn, err); + cap_initiator_unicast_audio_proc_complete(); + + return; + } } } /* else await notifications from server */ @@ -1520,6 +1587,7 @@ int bt_cap_initiator_unicast_audio_update(const struct bt_cap_unicast_audio_upda meta_len = proc_param->meta_update.meta_len; meta = proc_param->meta_update.meta; active_proc->proc_initiated_cnt++; + proc_param->in_progress = true; err = bt_bap_stream_metadata(bap_stream, meta, meta_len); if (err != 0) { @@ -1587,6 +1655,7 @@ void bt_cap_initiator_metadata_updated(struct bt_cap_stream *cap_stream) next_cap_stream = proc_param->stream; bap_stream = &next_cap_stream->bap_stream; active_proc->proc_initiated_cnt++; + proc_param->in_progress = true; err = bt_bap_stream_metadata(bap_stream, meta, meta_len); if (err != 0) { @@ -1793,6 +1862,7 @@ int bt_cap_initiator_unicast_audio_stop(const struct bt_cap_unicast_audio_stop_p "proc is not started, but could not get next proc_param"); bap_stream = &proc_param->stream->bap_stream; active_proc->proc_initiated_cnt++; + proc_param->in_progress = true; err = bt_bap_stream_disable(bap_stream); if (err != 0) { @@ -1811,6 +1881,7 @@ int bt_cap_initiator_unicast_audio_stop(const struct bt_cap_unicast_audio_stop_p "proc is not started, but could not get next proc_param"); bap_stream = &proc_param->stream->bap_stream; active_proc->proc_initiated_cnt++; + proc_param->in_progress = true; err = bt_bap_stream_stop(bap_stream); if (err != 0) { @@ -1829,6 +1900,7 @@ int bt_cap_initiator_unicast_audio_stop(const struct bt_cap_unicast_audio_stop_p "proc is not started, but could not get next proc_param"); bap_stream = &proc_param->stream->bap_stream; active_proc->proc_initiated_cnt++; + proc_param->in_progress = true; err = bt_bap_stream_release(bap_stream); if (err != 0) { @@ -1879,6 +1951,7 @@ void bt_cap_initiator_disabled(struct bt_cap_stream *cap_stream) next_cap_stream = proc_param->stream; next_bap_stream = &next_cap_stream->bap_stream; active_proc->proc_initiated_cnt++; + proc_param->in_progress = true; err = bt_bap_stream_disable(next_bap_stream); if (err != 0) { @@ -1908,6 +1981,7 @@ void bt_cap_initiator_disabled(struct bt_cap_stream *cap_stream) next_cap_stream = proc_param->stream; next_bap_stream = &next_cap_stream->bap_stream; active_proc->proc_initiated_cnt++; + proc_param->in_progress = true; err = bt_bap_stream_stop(next_bap_stream); if (err != 0) { @@ -1965,6 +2039,7 @@ void bt_cap_initiator_stopped(struct bt_cap_stream *cap_stream) next_bap_stream = &next_cap_stream->bap_stream; active_proc->proc_initiated_cnt++; + proc_param->in_progress = true; err = bt_bap_stream_stop(next_bap_stream); if (err != 0) { @@ -2039,6 +2114,7 @@ void bt_cap_initiator_released(struct bt_cap_stream *cap_stream) next_cap_stream = proc_param->stream; next_bap_stream = &next_cap_stream->bap_stream; active_proc->proc_initiated_cnt++; + proc_param->in_progress = true; err = bt_bap_stream_release(next_bap_stream); if (err != 0) { diff --git a/subsys/bluetooth/audio/cap_internal.h b/subsys/bluetooth/audio/cap_internal.h index 3543b35c3876e7..43cf22a58afa61 100644 --- a/subsys/bluetooth/audio/cap_internal.h +++ b/subsys/bluetooth/audio/cap_internal.h @@ -34,6 +34,8 @@ void bt_cap_initiator_disabled(struct bt_cap_stream *cap_stream); void bt_cap_initiator_stopped(struct bt_cap_stream *cap_stream); void bt_cap_initiator_released(struct bt_cap_stream *cap_stream); void bt_cap_stream_ops_register_bap(struct bt_cap_stream *cap_stream); +void bt_cap_initiator_cp_cb(struct bt_cap_stream *cap_stream, enum bt_bap_ascs_rsp_code rsp_code, + enum bt_bap_ascs_reason reason); enum bt_cap_common_proc_state { BT_CAP_COMMON_PROC_STATE_ACTIVE, @@ -87,6 +89,7 @@ struct bt_cap_initiator_proc_param { bool release; } stop; }; + bool in_progress; }; #if defined(CONFIG_BT_BAP_BROADCAST_ASSISTANT) diff --git a/subsys/bluetooth/audio/cap_stream.c b/subsys/bluetooth/audio/cap_stream.c index bbc645daa3e9d1..53c29fa53a6492 100644 --- a/subsys/bluetooth/audio/cap_stream.c +++ b/subsys/bluetooth/audio/cap_stream.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -280,9 +281,41 @@ static struct bt_bap_stream_ops bap_stream_ops = { .disconnected = cap_stream_disconnected_cb, }; +static void unicast_client_cp_cb(struct bt_bap_stream *bap_stream, + enum bt_bap_ascs_rsp_code rsp_code, enum bt_bap_ascs_reason reason) +{ + if (IS_ENABLED(CONFIG_BT_CAP_INITIATOR) && IS_ENABLED(CONFIG_BT_BAP_UNICAST_CLIENT) && + stream_is_central(bap_stream)) { + struct bt_cap_stream *cap_stream = + CONTAINER_OF(bap_stream, struct bt_cap_stream, bap_stream); + + bt_cap_initiator_cp_cb(cap_stream, rsp_code, reason); + } +} + void bt_cap_stream_ops_register_bap(struct bt_cap_stream *cap_stream) { bt_bap_stream_cb_register(&cap_stream->bap_stream, &bap_stream_ops); + + if (IS_ENABLED(CONFIG_BT_CAP_INITIATOR) && IS_ENABLED(CONFIG_BT_BAP_UNICAST_CLIENT)) { + /* The CAP initiator can use the same callback for all of these as the result is the + * same: Abort current procedure + */ + static struct bt_bap_unicast_client_cb unicast_client_cb = { + .config = unicast_client_cp_cb, + .qos = unicast_client_cp_cb, + .enable = unicast_client_cp_cb, + .start = unicast_client_cp_cb, + .stop = unicast_client_cp_cb, + .disable = unicast_client_cp_cb, + .metadata = unicast_client_cp_cb, + .release = unicast_client_cp_cb, + }; + int err; + + err = bt_bap_unicast_client_register_cb(&unicast_client_cb); + __ASSERT_NO_MSG(err == 0 || err == -EEXIST); + } } void bt_cap_stream_ops_register(struct bt_cap_stream *stream, diff --git a/tests/bluetooth/audio/cap_initiator/uut/bap_unicast_client.c b/tests/bluetooth/audio/cap_initiator/uut/bap_unicast_client.c index 387aeaf41ba327..db70a000394470 100644 --- a/tests/bluetooth/audio/cap_initiator/uut/bap_unicast_client.c +++ b/tests/bluetooth/audio/cap_initiator/uut/bap_unicast_client.c @@ -12,6 +12,7 @@ #include #include +#include #include #include #include @@ -20,6 +21,8 @@ #include "bap_endpoint.h" #include "ztest_assert.h" +static struct bt_bap_unicast_client_cb *unicast_client_cb; + bool bt_bap_ep_is_unicast_client(const struct bt_bap_ep *ep) { return false; @@ -48,6 +51,11 @@ int bt_bap_unicast_client_config(struct bt_bap_stream *stream, stream->ops->configured(stream, &pref); } + if (unicast_client_cb != NULL && unicast_client_cb->config != NULL) { + unicast_client_cb->config(stream, BT_BAP_ASCS_RSP_CODE_SUCCESS, + BT_BAP_ASCS_REASON_NONE); + } + return 0; } @@ -79,6 +87,11 @@ int bt_bap_unicast_client_qos(struct bt_conn *conn, struct bt_bap_unicast_group stream->ops->qos_set(stream); } } + + if (unicast_client_cb != NULL && unicast_client_cb->qos != NULL) { + unicast_client_cb->qos(stream, BT_BAP_ASCS_RSP_CODE_SUCCESS, + BT_BAP_ASCS_REASON_NONE); + } } return 0; @@ -104,6 +117,11 @@ int bt_bap_unicast_client_enable(struct bt_bap_stream *stream, const uint8_t met stream->ops->enabled(stream); } + if (unicast_client_cb != NULL && unicast_client_cb->enable != NULL) { + unicast_client_cb->enable(stream, BT_BAP_ASCS_RSP_CODE_SUCCESS, + BT_BAP_ASCS_REASON_NONE); + } + return 0; } @@ -126,6 +144,11 @@ int bt_bap_unicast_client_metadata(struct bt_bap_stream *stream, const uint8_t m stream->ops->metadata_updated(stream); } + if (unicast_client_cb != NULL && unicast_client_cb->metadata != NULL) { + unicast_client_cb->metadata(stream, BT_BAP_ASCS_RSP_CODE_SUCCESS, + BT_BAP_ASCS_REASON_NONE); + } + return 0; } @@ -179,6 +202,11 @@ int bt_bap_unicast_client_start(struct bt_bap_stream *stream) stream->ops->started(stream); } + if (unicast_client_cb != NULL && unicast_client_cb->start != NULL) { + unicast_client_cb->start(stream, BT_BAP_ASCS_RSP_CODE_SUCCESS, + BT_BAP_ASCS_REASON_NONE); + } + return 0; } @@ -226,6 +254,11 @@ int bt_bap_unicast_client_disable(struct bt_bap_stream *stream) } } + if (unicast_client_cb != NULL && unicast_client_cb->disable != NULL) { + unicast_client_cb->disable(stream, BT_BAP_ASCS_RSP_CODE_SUCCESS, + BT_BAP_ASCS_REASON_NONE); + } + return 0; } @@ -255,6 +288,11 @@ int bt_bap_unicast_client_stop(struct bt_bap_stream *stream) stream->ops->qos_set(stream); } + if (unicast_client_cb != NULL && unicast_client_cb->stop != NULL) { + unicast_client_cb->stop(stream, BT_BAP_ASCS_RSP_CODE_SUCCESS, + BT_BAP_ASCS_REASON_NONE); + } + return 0; } @@ -284,5 +322,17 @@ int bt_bap_unicast_client_release(struct bt_bap_stream *stream) stream->ops->released(stream); } + if (unicast_client_cb != NULL && unicast_client_cb->release != NULL) { + unicast_client_cb->release(stream, BT_BAP_ASCS_RSP_CODE_SUCCESS, + BT_BAP_ASCS_REASON_NONE); + } + + return 0; +} + +int bt_bap_unicast_client_register_cb(struct bt_bap_unicast_client_cb *cb) +{ + unicast_client_cb = cb; + return 0; } diff --git a/tests/bsim/bluetooth/audio/src/cap_acceptor_test.c b/tests/bsim/bluetooth/audio/src/cap_acceptor_test.c index c4012a583973c9..23addc8056a592 100644 --- a/tests/bsim/bluetooth/audio/src/cap_acceptor_test.c +++ b/tests/bsim/bluetooth/audio/src/cap_acceptor_test.c @@ -503,12 +503,25 @@ static int unicast_server_qos(struct bt_bap_stream *stream, const struct bt_audi return 0; } +static bool ascs_data_func_cb(struct bt_data *data, void *user_data) +{ + struct bt_bap_ascs_rsp *rsp = (struct bt_bap_ascs_rsp *)user_data; + + if (!BT_AUDIO_METADATA_TYPE_IS_KNOWN(data->type)) { + printk("Invalid metadata type %u or length %u\n", data->type, data->data_len); + *rsp = BT_BAP_ASCS_RSP(BT_BAP_ASCS_RSP_CODE_METADATA_REJECTED, data->type); + return false; + } + + return true; +} + static int unicast_server_enable(struct bt_bap_stream *stream, const uint8_t meta[], size_t meta_len, struct bt_bap_ascs_rsp *rsp) { printk("Enable: stream %p meta_len %zu\n", stream, meta_len); - return 0; + return bt_audio_data_parse(meta, meta_len, ascs_data_func_cb, rsp); } static int unicast_server_start(struct bt_bap_stream *stream, struct bt_bap_ascs_rsp *rsp) @@ -518,19 +531,6 @@ static int unicast_server_start(struct bt_bap_stream *stream, struct bt_bap_ascs return 0; } -static bool ascs_data_func_cb(struct bt_data *data, void *user_data) -{ - struct bt_bap_ascs_rsp *rsp = (struct bt_bap_ascs_rsp *)user_data; - - if (!BT_AUDIO_METADATA_TYPE_IS_KNOWN(data->type)) { - printk("Invalid metadata type %u or length %u\n", data->type, data->data_len); - *rsp = BT_BAP_ASCS_RSP(BT_BAP_ASCS_RSP_CODE_METADATA_REJECTED, data->type); - return false; - } - - return true; -} - static int unicast_server_metadata(struct bt_bap_stream *stream, const uint8_t meta[], size_t meta_len, struct bt_bap_ascs_rsp *rsp) { 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 fac55233cf69cd..f6773153d543f1 100644 --- a/tests/bsim/bluetooth/audio/src/cap_initiator_unicast_test.c +++ b/tests/bsim/bluetooth/audio/src/cap_initiator_unicast_test.c @@ -85,6 +85,7 @@ CREATE_FLAG(flag_discovered); CREATE_FLAG(flag_codec_found); CREATE_FLAG(flag_endpoint_found); CREATE_FLAG(flag_started); +CREATE_FLAG(flag_start_failed); CREATE_FLAG(flag_start_timeout); CREATE_FLAG(flag_updated); CREATE_FLAG(flag_stopped); @@ -237,7 +238,8 @@ static void unicast_start_complete_cb(int err, struct bt_conn *conn) if (err == -ECANCELED) { SET_FLAG(flag_start_timeout); } else if (err != 0) { - FAIL("Failed to start (failing conn %p): %d", conn, err); + printk("Failed to start (failing conn %p): %d\n", conn, err); + SET_FLAG(flag_start_failed); } else { SET_FLAG(flag_started); } @@ -880,6 +882,67 @@ static void test_cap_initiator_unicast_timeout(void) PASS("CAP initiator unicast timeout passed\n"); } +static void set_invalid_metadata_type(uint8_t type) +{ + const uint8_t val = 0xFF; + int err; + + err = bt_audio_codec_cfg_meta_set_val(&unicast_preset_16_2_1.codec_cfg, type, &val, + sizeof(val)); + if (err < 0) { + FAIL("Failed to set invalid metadata type: %d\n", err); + return; + } +} + +static void unset_invalid_metadata_type(uint8_t type) +{ + int err; + + err = bt_audio_codec_cfg_meta_unset_val(&unicast_preset_16_2_1.codec_cfg, type); + if (err < 0) { + FAIL("Failed to unset invalid metadata type: %d\n", err); + return; + } +} + +static void test_cap_initiator_unicast_ase_error(void) +{ + struct bt_bap_unicast_group *unicast_group; + const uint8_t inval_type = 0xFD; + + init(); + + scan_and_connect(); + + WAIT_FOR_FLAG(flag_mtu_exchanged); + + discover_cas(default_conn); + discover_sink(default_conn); + discover_source(default_conn); + + unicast_group_create(&unicast_group); + + set_invalid_metadata_type(inval_type); + + /* With invalid metadata type, start should fail */ + unicast_audio_start(unicast_group, false); + WAIT_FOR_FLAG(flag_start_failed); + + /* Remove invalid type and retry */ + unset_invalid_metadata_type(inval_type); + + /* Without invalid metadata type, start should pass */ + unicast_audio_start(unicast_group, true); + + unicast_audio_stop(unicast_group); + + unicast_group_delete(unicast_group); + unicast_group = NULL; + + PASS("CAP initiator unicast ASE error passed\n"); +} + static const struct named_lc3_preset *cap_get_named_preset(const char *preset_arg) { for (size_t i = 0U; i < ARRAY_SIZE(lc3_unicast_presets); i++) { @@ -1563,6 +1626,12 @@ static const struct bst_test_instance test_cap_initiator_unicast[] = { .test_tick_f = test_tick, .test_main_f = test_cap_initiator_unicast_timeout, }, + { + .test_id = "cap_initiator_unicast_ase_error", + .test_pre_init_f = test_init, + .test_tick_f = test_tick, + .test_main_f = test_cap_initiator_unicast_ase_error, + }, { .test_id = "cap_initiator_unicast_inval", .test_pre_init_f = test_init, diff --git a/tests/bsim/bluetooth/audio/test_scripts/cap_unicast_ase_error.sh b/tests/bsim/bluetooth/audio/test_scripts/cap_unicast_ase_error.sh new file mode 100755 index 00000000000000..b6faaddfd6e03a --- /dev/null +++ b/tests/bsim/bluetooth/audio/test_scripts/cap_unicast_ase_error.sh @@ -0,0 +1,28 @@ +#!/usr/bin/env bash +# +# Copyright (c) 2024 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: Apache-2.0 + +SIMULATION_ID="cap_unicast_ase_error" +VERBOSITY_LEVEL=2 + +source ${ZEPHYR_BASE}/tests/bsim/sh_common.source + +cd ${BSIM_OUT_PATH}/bin + +printf "\n\n======== Running CAP unicast ASE error test =========\n\n" + +Execute ./bs_${BOARD_TS}_tests_bsim_bluetooth_audio_prj_conf \ + -v=${VERBOSITY_LEVEL} -s=${SIMULATION_ID} -d=0 -testid=cap_initiator_unicast_ase_error \ + -RealEncryption=1 -rs=46 -D=2 + +Execute ./bs_${BOARD_TS}_tests_bsim_bluetooth_audio_prj_conf \ + -v=${VERBOSITY_LEVEL} -s=${SIMULATION_ID} -d=1 -testid=cap_acceptor_unicast \ + -RealEncryption=1 -rs=23 -D=2 + +# Simulation time should be larger than the WAIT_TIME in common.h +Execute ./bs_2G4_phy_v1 -v=${VERBOSITY_LEVEL} -s=${SIMULATION_ID} \ + -D=2 -sim_length=60e6 $@ + +wait_for_background_jobs