From 372b8853a4fa156108bdbfde92c16ef2c24dfcfb Mon Sep 17 00:00:00 2001 From: Emil Gydesen Date: Fri, 5 Jul 2024 10:20:17 +0200 Subject: [PATCH 1/6] Bluetooth: ISO: Add BIG callbacks Add callbacks that is called for the entire BIG. The BIG state is from an HCI perspective a single state change that we previously only propagated as a state change for each channel. However it may be simpler for applications and higher layers to use BIG changes to trigger their behavior. Signed-off-by: Emil Gydesen --- include/zephyr/bluetooth/iso.h | 37 ++++++++++++++++++++++++ subsys/bluetooth/host/iso.c | 51 ++++++++++++++++++++++++++++++++++ 2 files changed, 88 insertions(+) diff --git a/include/zephyr/bluetooth/iso.h b/include/zephyr/bluetooth/iso.h index 4251a2c87cb1a..fea94f6212c6c 100644 --- a/include/zephyr/bluetooth/iso.h +++ b/include/zephyr/bluetooth/iso.h @@ -34,6 +34,7 @@ #include #include #include +#include #ifdef __cplusplus extern "C" { @@ -1102,6 +1103,42 @@ int bt_iso_chan_get_info(const struct bt_iso_chan *chan, struct bt_iso_info *inf */ int bt_iso_chan_get_tx_sync(const struct bt_iso_chan *chan, struct bt_iso_tx_info *info); +/** + * @brief Struct to hold the Broadcast Isochronous Group callbacks + * + * These can be registered for usage with bt_iso_big_register_cb(). + */ +struct bt_iso_big_cb { + /** + * @brief The BIG has started and all of the streams are ready for data + * + * @param big The started BIG + */ + void (*started)(struct bt_iso_big *big); + + /** + * @brief The BIG has stopped and none of the streams are ready for data + * + * @param big The stopped BIG + * @param reason The reason why the BIG stopped (see the BT_HCI_ERR_* values) + */ + void (*stopped)(struct bt_iso_big *big, uint8_t reason); + + /** @internal Internally used field for list handling */ + sys_snode_t _node; +}; + +/** + * @brief Registers callbacks for Broadcast Sources + * + * @param cb Pointer to the callback structure. + * + * @retval 0 on success + * @retval -EINVAL if @p cb is NULL + * @retval -EEXIST if @p cb is already registered + */ +int bt_iso_big_register_cb(struct bt_iso_big_cb *cb); + /** * @brief Creates a BIG as a broadcaster * diff --git a/subsys/bluetooth/host/iso.c b/subsys/bluetooth/host/iso.c index 3b363d13f4c41..f44acbcc522d3 100644 --- a/subsys/bluetooth/host/iso.c +++ b/subsys/bluetooth/host/iso.c @@ -2525,6 +2525,8 @@ int bt_iso_chan_connect(const struct bt_iso_connect_param *param, size_t count) #endif /* CONFIG_BT_ISO_UNICAST */ #if defined(CONFIG_BT_ISO_BROADCAST) +static sys_slist_t iso_big_cbs = SYS_SLIST_STATIC_INIT(&iso_big_cbs); + static struct bt_iso_big *lookup_big_by_handle(uint8_t big_handle) { return &bigs[big_handle]; @@ -2587,6 +2589,16 @@ static void big_disconnect(struct bt_iso_big *big, uint8_t reason) bt_iso_chan_disconnected(bis, reason); } + + if (!sys_slist_is_empty(&iso_big_cbs)) { + struct bt_iso_big_cb *listener; + + SYS_SLIST_FOR_EACH_CONTAINER(&iso_big_cbs, listener, _node) { + if (listener->stopped != NULL) { + listener->stopped(big, reason); + } + } + } } static int big_init_bis(struct bt_iso_big *big, struct bt_iso_chan **bis_channels, uint8_t num_bis, @@ -2617,6 +2629,25 @@ static int big_init_bis(struct bt_iso_big *big, struct bt_iso_chan **bis_channel return 0; } +int bt_iso_big_register_cb(struct bt_iso_big_cb *cb) +{ + CHECKIF(cb == NULL) { + LOG_DBG("cb is NULL"); + + return -EINVAL; + } + + if (sys_slist_find(&iso_big_cbs, &cb->_node, NULL)) { + LOG_DBG("cb %p is already registered", cb); + + return -EEXIST; + } + + sys_slist_append(&iso_big_cbs, &cb->_node); + + return 0; +} + #if defined(CONFIG_BT_ISO_BROADCASTER) static int hci_le_create_big(struct bt_le_ext_adv *padv, struct bt_iso_big *big, struct bt_iso_big_create_param *param) @@ -3006,6 +3037,16 @@ void hci_le_big_complete(struct net_buf *buf) store_bis_broadcaster_info(evt, &iso_conn->iso.info); bt_conn_set_state(iso_conn, BT_CONN_CONNECTED); } + + if (!sys_slist_is_empty(&iso_big_cbs)) { + struct bt_iso_big_cb *listener; + + SYS_SLIST_FOR_EACH_CONTAINER(&iso_big_cbs, listener, _node) { + if (listener->started != NULL) { + listener->started(big); + } + } + } } void hci_le_big_terminate(struct net_buf *buf) @@ -3186,6 +3227,16 @@ void hci_le_big_sync_established(struct net_buf *buf) store_bis_sync_receiver_info(evt, &iso_conn->iso.info); bt_conn_set_state(iso_conn, BT_CONN_CONNECTED); } + + if (!sys_slist_is_empty(&iso_big_cbs)) { + struct bt_iso_big_cb *listener; + + SYS_SLIST_FOR_EACH_CONTAINER(&iso_big_cbs, listener, _node) { + if (listener->started != NULL) { + listener->started(big); + } + } + } } void hci_le_big_sync_lost(struct net_buf *buf) From c0d94bb50c274cec2fbb20f58e5d849b084fbaa3 Mon Sep 17 00:00:00 2001 From: Emil Gydesen Date: Fri, 5 Jul 2024 10:21:40 +0200 Subject: [PATCH 2/6] Bluetooth: BAP: Add broadcast source callback structs These callbacks are trigger for changes that affect the entire broadcast source, such as the BIG started and terminated events. Signed-off-by: Emil Gydesen --- include/zephyr/bluetooth/audio/bap.h | 47 +++++++ subsys/bluetooth/audio/bap_broadcast_source.c | 133 +++++++++++++++--- .../audio/bap_broadcast_source/CMakeLists.txt | 1 + .../audio/bap_broadcast_source/src/main.c | 28 +++- .../src/test_callback_register.c | 98 +++++++++++++ .../bap_broadcast_source/uut/CMakeLists.txt | 1 + .../uut/bap_broadcast_source.c | 31 ++++ .../mocks/include/bap_broadcast_source.h | 23 +++ tests/bluetooth/audio/mocks/src/iso.c | 24 ++++ .../audio/src/bap_broadcast_source_test.c | 75 +++++++--- 10 files changed, 416 insertions(+), 45 deletions(-) create mode 100644 tests/bluetooth/audio/bap_broadcast_source/src/test_callback_register.c create mode 100644 tests/bluetooth/audio/bap_broadcast_source/uut/bap_broadcast_source.c create mode 100644 tests/bluetooth/audio/mocks/include/bap_broadcast_source.h diff --git a/include/zephyr/bluetooth/audio/bap.h b/include/zephyr/bluetooth/audio/bap.h index 38356a175271b..33ba8ca3cff28 100644 --- a/include/zephyr/bluetooth/audio/bap.h +++ b/include/zephyr/bluetooth/audio/bap.h @@ -2001,6 +2001,53 @@ int bt_bap_base_subgroup_bis_codec_to_codec_cfg(const struct bt_bap_base_subgrou * @{ */ +/** + * @brief Struct to hold the Broadcast Source callbacks + * + * These can be registered for usage with bt_bap_broadcast_source_register_cb(). + */ +struct bt_bap_broadcast_source_cb { + /** + * @brief The Broadcast Source has started and all of the streams are ready for audio data + * + * @param source The started Broadcast Source + */ + void (*started)(struct bt_bap_broadcast_source *source); + + /** + * @brief The Broadcast Source has stopped and none of the streams are ready for audio data + * + * @param source The stopped Broadcast Source + * @param reason The reason why the Broadcast Source stopped (see the BT_HCI_ERR_* values) + */ + void (*stopped)(struct bt_bap_broadcast_source *source, uint8_t reason); + + /** @internal Internally used field for list handling */ + sys_snode_t _node; +}; + +/** + * @brief Registers callbacks for Broadcast Sources + * + * @param cb Pointer to the callback structure. + * + * @retval 0 on success + * @retval -EINVAL if @p cb is NULL + * @retval -EEXIST if @p cb is already registered + */ +int bt_bap_broadcast_source_register_cb(struct bt_bap_broadcast_source_cb *cb); + +/** + * @brief Unregisters callbacks for Broadcast Sources + * + * @param cb Pointer to the callback structure. + * + * @retval 0 on success + * @retval -EINVAL if @p cb is NULL + * @retval -ENOENT if @p cb is not registered + */ +int bt_bap_broadcast_source_unregister_cb(struct bt_bap_broadcast_source_cb *cb); + /** Broadcast Source stream parameters */ struct bt_bap_broadcast_source_stream_param { /** Audio stream */ diff --git a/subsys/bluetooth/audio/bap_broadcast_source.c b/subsys/bluetooth/audio/bap_broadcast_source.c index 93186af67f0f7..132dca5580a37 100644 --- a/subsys/bluetooth/audio/bap_broadcast_source.c +++ b/subsys/bluetooth/audio/bap_broadcast_source.c @@ -1,7 +1,7 @@ /* Bluetooth Audio Broadcast Source */ /* - * Copyright (c) 2021-2023 Nordic Semiconductor ASA + * Copyright (c) 2021-2024 Nordic Semiconductor ASA * * SPDX-License-Identifier: Apache-2.0 */ @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -55,6 +56,7 @@ static struct bt_bap_broadcast_subgroup broadcast_source_subgroups[CONFIG_BT_BAP_BROADCAST_SRC_COUNT] [CONFIG_BT_BAP_BROADCAST_SRC_SUBGROUP_COUNT]; static struct bt_bap_broadcast_source broadcast_sources[CONFIG_BT_BAP_BROADCAST_SRC_COUNT]; +static sys_slist_t bap_broadcast_source_cbs = SYS_SLIST_STATIC_INIT(&bap_broadcast_source_cbs); /** * 2 octets UUID @@ -238,9 +240,9 @@ static void broadcast_source_iso_disconnected(struct bt_iso_chan *chan, uint8_t } static struct bt_iso_chan_ops broadcast_source_iso_ops = { - .sent = broadcast_source_iso_sent, - .connected = broadcast_source_iso_connected, - .disconnected = broadcast_source_iso_disconnected, + .sent = broadcast_source_iso_sent, + .connected = broadcast_source_iso_connected, + .disconnected = broadcast_source_iso_disconnected, }; bool bt_bap_ep_is_broadcast_src(const struct bt_bap_ep *ep) @@ -440,8 +442,7 @@ static bool encode_base(struct bt_bap_broadcast_source *source, struct net_buf_s */ streams_encoded = 0; SYS_SLIST_FOR_EACH_CONTAINER(&source->subgroups, subgroup, _node) { - if (!encode_base_subgroup(subgroup, - &source->stream_data[streams_encoded], + if (!encode_base_subgroup(subgroup, &source->stream_data[streams_encoded], &streams_encoded, buf)) { return false; } @@ -454,12 +455,10 @@ static void broadcast_source_cleanup(struct bt_bap_broadcast_source *source) { struct bt_bap_broadcast_subgroup *subgroup, *next_subgroup; - SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&source->subgroups, subgroup, - next_subgroup, _node) { + SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&source->subgroups, subgroup, next_subgroup, _node) { struct bt_bap_stream *stream, *next_stream; - SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&subgroup->streams, stream, - next_stream, _node) { + SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&subgroup->streams, stream, next_stream, _node) { bt_bap_iso_unbind_ep(stream->ep->iso, stream->ep); stream->ep->stream = NULL; stream->ep = NULL; @@ -467,8 +466,7 @@ static void broadcast_source_cleanup(struct bt_bap_broadcast_source *source) stream->qos = NULL; stream->group = NULL; - sys_slist_remove(&subgroup->streams, NULL, - &stream->_node); + sys_slist_remove(&subgroup->streams, NULL, &stream->_node); } sys_slist_remove(&source->subgroups, NULL, &subgroup->_node); } @@ -777,8 +775,7 @@ int bt_bap_broadcast_source_create(struct bt_bap_broadcast_source_param *param, bis_count++; } - err = broadcast_source_setup_stream(index, stream, - codec_cfg, qos, source); + err = broadcast_source_setup_stream(index, stream, codec_cfg, qos, source); if (err != 0) { LOG_DBG("Failed to setup streams[%zu]: %d", i, err); broadcast_source_cleanup(source); @@ -1039,7 +1036,7 @@ int bt_bap_broadcast_source_update_metadata(struct bt_bap_broadcast_source *sour int bt_bap_broadcast_source_start(struct bt_bap_broadcast_source *source, struct bt_le_ext_adv *adv) { struct bt_iso_chan *bis[BROADCAST_STREAM_CNT]; - struct bt_iso_big_create_param param = { 0 }; + struct bt_iso_big_create_param param = {0}; struct bt_bap_broadcast_subgroup *subgroup; enum bt_bap_ep_state broadcast_state; struct bt_bap_stream *stream; @@ -1078,8 +1075,7 @@ int bt_bap_broadcast_source_start(struct bt_bap_broadcast_source *source, struct param.latency = source->qos->latency; param.encryption = source->encryption; if (param.encryption) { - (void)memcpy(param.bcode, source->broadcast_code, - sizeof(param.bcode)); + (void)memcpy(param.bcode, source->broadcast_code, sizeof(param.bcode)); } #if defined(CONFIG_BT_ISO_TEST_PARAMS) param.irc = source->irc; @@ -1125,14 +1121,12 @@ int bt_bap_broadcast_source_stop(struct bt_bap_broadcast_source *source) return -EALREADY; } - err = bt_iso_big_terminate(source->big); + err = bt_iso_big_terminate(source->big); if (err) { LOG_DBG("Failed to terminate BIG (err %d)", err); return err; } - source->big = NULL; - return 0; } @@ -1188,3 +1182,102 @@ int bt_bap_broadcast_source_get_base(struct bt_bap_broadcast_source *source, return 0; } + +static struct bt_bap_broadcast_source *get_broadcast_source_by_big(const struct bt_iso_big *big) +{ + for (size_t i = 0U; i < ARRAY_SIZE(broadcast_sources); i++) { + if (broadcast_sources[i].big == big) { + return &broadcast_sources[i]; + } + } + + return NULL; +} + +static void big_started_cb(struct bt_iso_big *big) +{ + struct bt_bap_broadcast_source *source = get_broadcast_source_by_big(big); + struct bt_bap_broadcast_source_cb *listener; + + if (source == NULL) { + /* Not one of ours */ + return; + } + + SYS_SLIST_FOR_EACH_CONTAINER(&bap_broadcast_source_cbs, listener, _node) { + if (listener->started != NULL) { + listener->started(source); + } + } +} + +static void big_stopped_cb(struct bt_iso_big *big, uint8_t reason) +{ + struct bt_bap_broadcast_source *source = get_broadcast_source_by_big(big); + struct bt_bap_broadcast_source_cb *listener; + + if (source == NULL) { + /* Not one of ours */ + return; + } + + source->big = NULL; + + SYS_SLIST_FOR_EACH_CONTAINER(&bap_broadcast_source_cbs, listener, _node) { + if (listener->stopped != NULL) { + listener->stopped(source, reason); + } + } +} + +int bt_bap_broadcast_source_register_cb(struct bt_bap_broadcast_source_cb *cb) +{ + static bool iso_big_cb_registered; + + CHECKIF(cb == NULL) { + LOG_DBG("cb is NULL"); + + return -EINVAL; + } + + if (sys_slist_find(&bap_broadcast_source_cbs, &cb->_node, NULL)) { + LOG_DBG("cb %p is already registered", cb); + + return -EEXIST; + } + + if (!iso_big_cb_registered) { + static struct bt_iso_big_cb big_cb = { + .started = big_started_cb, + .stopped = big_stopped_cb, + }; + const int err = bt_iso_big_register_cb(&big_cb); + + if (err != 0) { + __ASSERT(false, "Failed to register ISO BIG callbacks: %d", err); + } + + iso_big_cb_registered = true; + } + + sys_slist_append(&bap_broadcast_source_cbs, &cb->_node); + + return 0; +} + +int bt_bap_broadcast_source_unregister_cb(struct bt_bap_broadcast_source_cb *cb) +{ + CHECKIF(cb == NULL) { + LOG_DBG("cb is NULL"); + + return -EINVAL; + } + + if (!sys_slist_find_and_remove(&bap_broadcast_source_cbs, &cb->_node)) { + LOG_DBG("cb %p is not registered", cb); + + return -ENOENT; + } + + return 0; +} diff --git a/tests/bluetooth/audio/bap_broadcast_source/CMakeLists.txt b/tests/bluetooth/audio/bap_broadcast_source/CMakeLists.txt index ef097b64e7a95..d79b0f147a697 100644 --- a/tests/bluetooth/audio/bap_broadcast_source/CMakeLists.txt +++ b/tests/bluetooth/audio/bap_broadcast_source/CMakeLists.txt @@ -15,4 +15,5 @@ target_include_directories(testbinary PRIVATE include) target_sources(testbinary PRIVATE src/main.c + src/test_callback_register.c ) diff --git a/tests/bluetooth/audio/bap_broadcast_source/src/main.c b/tests/bluetooth/audio/bap_broadcast_source/src/main.c index 82e6406919f1b..38a2b0a6e5061 100644 --- a/tests/bluetooth/audio/bap_broadcast_source/src/main.c +++ b/tests/bluetooth/audio/bap_broadcast_source/src/main.c @@ -1,28 +1,41 @@ /* main.c - Application main entry point */ /* - * Copyright (c) 2023 Nordic Semiconductor ASA + * Copyright (c) 2023-2024 Nordic Semiconductor ASA * * SPDX-License-Identifier: Apache-2.0 */ +#include +#include #include #include #include #include +#include +#include +#include #include #include #include +#include +#include #include -#include "bluetooth.h" +#include "bap_broadcast_source.h" +#include "bap_stream.h" #include "bap_stream_expects.h" +#include "bluetooth.h" +#include "expects_util.h" +#include "ztest_assert.h" +#include "ztest_test.h" DEFINE_FFF_GLOBALS; static void mock_init_rule_before(const struct ztest_unit_test *test, void *fixture) { + mock_bap_broadcast_source_init(); mock_bap_stream_init(); } @@ -136,8 +149,13 @@ static void *bap_broadcast_source_test_suite_setup(void) static void bap_broadcast_source_test_suite_before(void *f) { + int err; + memset(f, 0, sizeof(struct bap_broadcast_source_test_suite_fixture)); bap_broadcast_source_test_suite_fixture_init(f); + + err = bt_bap_broadcast_source_register_cb(&mock_bap_broadcast_source_cb); + zassert_equal(0, err, "Unexpected return value %d", err); } static void bap_broadcast_source_test_suite_after(void *f) @@ -164,6 +182,8 @@ static void bap_broadcast_source_test_suite_after(void *f) free(param->params); free(param->qos); free(param); + + bt_bap_broadcast_source_unregister_cb(&mock_bap_broadcast_source_cb); } static void bap_broadcast_source_test_suite_teardown(void *f) @@ -224,6 +244,8 @@ ZTEST_F(bap_broadcast_source_test_suite, test_broadcast_source_create_start_send mock_bap_stream_connected_cb_fake.call_count); zexpect_call_count("bt_bap_stream_ops.started", fixture->stream_cnt, mock_bap_stream_started_cb_fake.call_count); + zexpect_call_count("bt_bap_broadcast_source_cb.started", 1, + mock_bap_broadcast_source_started_cb_fake.call_count); for (size_t i = 0U; i < create_param->params_count; i++) { for (size_t j = 0U; j < create_param->params[i].params_count; j++) { @@ -260,6 +282,8 @@ ZTEST_F(bap_broadcast_source_test_suite, test_broadcast_source_create_start_send mock_bap_stream_disconnected_cb_fake.call_count); zexpect_call_count("bt_bap_stream_ops.stopped", fixture->stream_cnt, mock_bap_stream_stopped_cb_fake.call_count); + zexpect_call_count("bt_bap_broadcast_source_cb.stopped", 1, + mock_bap_broadcast_source_stopped_cb_fake.call_count); err = bt_bap_broadcast_source_delete(fixture->source); zassert_equal(0, err, "Unable to delete broadcast source: err %d", err); diff --git a/tests/bluetooth/audio/bap_broadcast_source/src/test_callback_register.c b/tests/bluetooth/audio/bap_broadcast_source/src/test_callback_register.c new file mode 100644 index 0000000000000..0e221a685512b --- /dev/null +++ b/tests/bluetooth/audio/bap_broadcast_source/src/test_callback_register.c @@ -0,0 +1,98 @@ +/* test_callback_register.c - Test bt_bap_broadcast_source_register and unregister */ + +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#include +#include + +#include "bap_broadcast_source.h" +#include "ztest_assert.h" +#include "ztest_test.h" + +#define FFF_GLOBALS + +static void mock_init_rule_before(const struct ztest_unit_test *test, void *fixture) +{ + mock_bap_broadcast_source_init(); +} + +ZTEST_RULE(mock_rule, mock_init_rule_before, NULL); + +static void bap_broadcast_source_test_cb_register_suite_after(void *f) +{ + bt_bap_broadcast_source_unregister_cb(&mock_bap_broadcast_source_cb); +} + +ZTEST_SUITE(bap_broadcast_source_test_cb_register_suite, NULL, NULL, NULL, + bap_broadcast_source_test_cb_register_suite_after, NULL); + +static ZTEST(bap_broadcast_source_test_cb_register_suite, test_broadcast_source_register_cb) +{ + int err; + + err = bt_bap_broadcast_source_register_cb(&mock_bap_broadcast_source_cb); + zassert_equal(0, err, "Unexpected return value %d", err); +} + +static ZTEST(bap_broadcast_source_test_cb_register_suite, + test_broadcast_source_register_cb_inval_param_null) +{ + int err; + + err = bt_bap_broadcast_source_register_cb(NULL); + zassert_equal(err, -EINVAL, "Unexpected return value %d", err); +} + +static ZTEST(bap_broadcast_source_test_cb_register_suite, + test_broadcast_source_register_cb_inval_double_register) +{ + int err; + + err = bt_bap_broadcast_source_register_cb(&mock_bap_broadcast_source_cb); + zassert_equal(err, 0, "Unexpected return value %d", err); + + err = bt_bap_broadcast_source_register_cb(&mock_bap_broadcast_source_cb); + zassert_equal(err, -EEXIST, "Unexpected return value %d", err); +} + +static ZTEST(bap_broadcast_source_test_cb_register_suite, test_broadcast_source_unregister_cb) +{ + int err; + + err = bt_bap_broadcast_source_register_cb(&mock_bap_broadcast_source_cb); + zassert_equal(err, 0, "Unexpected return value %d", err); + + err = bt_bap_broadcast_source_unregister_cb(&mock_bap_broadcast_source_cb); + zassert_equal(err, 0, "Unexpected return value %d", err); +} + +static ZTEST(bap_broadcast_source_test_cb_register_suite, + test_broadcast_source_unregister_cb_inval_param_null) +{ + int err; + + err = bt_bap_broadcast_source_unregister_cb(NULL); + zassert_equal(err, -EINVAL, "Unexpected return value %d", err); +} + +static ZTEST(bap_broadcast_source_test_cb_register_suite, + test_broadcast_source_unregister_cb_inval_double_unregister) +{ + int err; + + err = bt_bap_broadcast_source_register_cb(&mock_bap_broadcast_source_cb); + zassert_equal(err, 0, "Unexpected return value %d", err); + + err = bt_bap_broadcast_source_unregister_cb(&mock_bap_broadcast_source_cb); + zassert_equal(err, 0, "Unexpected return value %d", err); + + err = bt_bap_broadcast_source_unregister_cb(&mock_bap_broadcast_source_cb); + zassert_equal(err, -ENOENT, "Unexpected return value %d", err); +} diff --git a/tests/bluetooth/audio/bap_broadcast_source/uut/CMakeLists.txt b/tests/bluetooth/audio/bap_broadcast_source/uut/CMakeLists.txt index 879a50720b3b7..76dd64501cf8c 100644 --- a/tests/bluetooth/audio/bap_broadcast_source/uut/CMakeLists.txt +++ b/tests/bluetooth/audio/bap_broadcast_source/uut/CMakeLists.txt @@ -14,6 +14,7 @@ add_library(uut STATIC ${ZEPHYR_BASE}/subsys/bluetooth/audio/codec.c ${ZEPHYR_BASE}/subsys/logging/log_minimal.c ${ZEPHYR_BASE}/lib/net_buf/buf_simple.c + bap_broadcast_source.c ) add_subdirectory(${ZEPHYR_BASE}/tests/bluetooth/audio/mocks mocks) diff --git a/tests/bluetooth/audio/bap_broadcast_source/uut/bap_broadcast_source.c b/tests/bluetooth/audio/bap_broadcast_source/uut/bap_broadcast_source.c new file mode 100644 index 0000000000000..e0e2944836b1d --- /dev/null +++ b/tests/bluetooth/audio/bap_broadcast_source/uut/bap_broadcast_source.c @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include + +#include + +#include "bap_broadcast_source.h" +#include "zephyr/fff.h" + +/* List of fakes used by this unit tester */ +#define FFF_FAKES_LIST(FAKE) \ + FAKE(mock_bap_broadcast_source_started_cb) \ + FAKE(mock_bap_broadcast_source_stopped_cb) + +DEFINE_FAKE_VOID_FUNC(mock_bap_broadcast_source_started_cb, struct bt_bap_broadcast_source *); + +DEFINE_FAKE_VOID_FUNC(mock_bap_broadcast_source_stopped_cb, struct bt_bap_broadcast_source *, + uint8_t); + +struct bt_bap_broadcast_source_cb mock_bap_broadcast_source_cb = { + .started = mock_bap_broadcast_source_started_cb, + .stopped = mock_bap_broadcast_source_stopped_cb, +}; + +void mock_bap_broadcast_source_init(void) +{ + FFF_FAKES_LIST(RESET_FAKE); +} diff --git a/tests/bluetooth/audio/mocks/include/bap_broadcast_source.h b/tests/bluetooth/audio/mocks/include/bap_broadcast_source.h new file mode 100644 index 0000000000000..d6b75e7c51aa4 --- /dev/null +++ b/tests/bluetooth/audio/mocks/include/bap_broadcast_source.h @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef MOCKS_BAP_BROADCAST_SOURCE_H_ +#define MOCKS_BAP_BROADCAST_SOURCE_H_ + +#include + +#include +#include + +extern struct bt_bap_broadcast_source_cb mock_bap_broadcast_source_cb; + +void mock_bap_broadcast_source_init(void); + +DECLARE_FAKE_VOID_FUNC(mock_bap_broadcast_source_started_cb, struct bt_bap_broadcast_source *); +DECLARE_FAKE_VOID_FUNC(mock_bap_broadcast_source_stopped_cb, struct bt_bap_broadcast_source *, + uint8_t); + +#endif /* MOCKS_BAP_BROADCAST_SOURCE_H_ */ diff --git a/tests/bluetooth/audio/mocks/src/iso.c b/tests/bluetooth/audio/mocks/src/iso.c index 7271a66242eaa..bb344f81a773d 100644 --- a/tests/bluetooth/audio/mocks/src/iso.c +++ b/tests/bluetooth/audio/mocks/src/iso.c @@ -1,10 +1,13 @@ /* * Copyright (c) 2023 Codecoup + * Copyright (c) 2024 Nordic Semiconductor ASA * * SPDX-License-Identifier: Apache-2.0 */ +#include #include + #include #include "conn.h" @@ -123,6 +126,19 @@ int mock_bt_iso_disconnected(struct bt_iso_chan *chan, uint8_t err) } #if defined(CONFIG_BT_BAP_BROADCAST_SOURCE) +static struct bt_iso_big_cb *iso_cb; + +int bt_iso_big_register_cb(struct bt_iso_big_cb *cb) +{ + if (cb == NULL) { + return -EINVAL; + } + + iso_cb = cb; + + return 0; +} + int bt_iso_big_create(struct bt_le_ext_adv *padv, struct bt_iso_big_create_param *param, struct bt_iso_big **out_big) { @@ -153,6 +169,10 @@ int bt_iso_big_create(struct bt_le_ext_adv *padv, struct bt_iso_big_create_param *out_big = big; + if (iso_cb != NULL && iso_cb->started != NULL) { + iso_cb->started(big); + } + return 0; } @@ -169,6 +189,10 @@ int bt_iso_big_terminate(struct bt_iso_big *big) mock_bt_iso_disconnected(bis, BT_HCI_ERR_LOCALHOST_TERM_CONN); } + if (iso_cb != NULL && iso_cb->stopped != NULL) { + iso_cb->stopped(big, BT_HCI_ERR_LOCALHOST_TERM_CONN); + } + free(big); return 0; diff --git a/tests/bsim/bluetooth/audio/src/bap_broadcast_source_test.c b/tests/bsim/bluetooth/audio/src/bap_broadcast_source_test.c index de0d776477982..0006c54508be3 100644 --- a/tests/bsim/bluetooth/audio/src/bap_broadcast_source_test.c +++ b/tests/bsim/bluetooth/audio/src/bap_broadcast_source_test.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021-2023 Nordic Semiconductor ASA + * Copyright (c) 2021-2024 Nordic Semiconductor ASA * * SPDX-License-Identifier: Apache-2.0 */ @@ -35,6 +35,8 @@ #define SUPPORTED_MAX_FRAMES_PER_SDU 1 #if defined(CONFIG_BT_BAP_BROADCAST_SOURCE) +CREATE_FLAG(flag_source_started); + /* When BROADCAST_ENQUEUE_COUNT > 1 we can enqueue enough buffers to ensure that * the controller is never idle */ @@ -62,8 +64,8 @@ static uint8_t bis_codec_data[] = { BT_BYTES_LIST_LE32(BT_AUDIO_LOCATION_FRONT_CENTER)), }; -static K_SEM_DEFINE(sem_started, 0U, ARRAY_SIZE(broadcast_source_streams)); -static K_SEM_DEFINE(sem_stopped, 0U, ARRAY_SIZE(broadcast_source_streams)); +static K_SEM_DEFINE(sem_stream_started, 0U, ARRAY_SIZE(broadcast_source_streams)); +static K_SEM_DEFINE(sem_stream_stopped, 0U, ARRAY_SIZE(broadcast_source_streams)); static void validate_stream_codec_cfg(const struct bt_bap_stream *stream) { @@ -174,7 +176,7 @@ static void validate_stream_codec_cfg(const struct bt_bap_stream *stream) } } -static void started_cb(struct bt_bap_stream *stream) +static void stream_started_cb(struct bt_bap_stream *stream) { struct audio_test_stream *test_stream = audio_test_stream_from_bap_stream(stream); struct bt_bap_ep_info info; @@ -216,13 +218,13 @@ static void started_cb(struct bt_bap_stream *stream) printk("Stream %p started\n", stream); validate_stream_codec_cfg(stream); - k_sem_give(&sem_started); + k_sem_give(&sem_stream_started); } -static void stopped_cb(struct bt_bap_stream *stream, uint8_t reason) +static void steam_stopped_cb(struct bt_bap_stream *stream, uint8_t reason) { printk("Stream %p stopped with reason 0x%02X\n", stream, reason); - k_sem_give(&sem_stopped); + k_sem_give(&sem_stream_stopped); } static void stream_sent_cb(struct bt_bap_stream *stream) @@ -265,11 +267,23 @@ static void stream_sent_cb(struct bt_bap_stream *stream) } static struct bt_bap_stream_ops stream_ops = { - .started = started_cb, - .stopped = stopped_cb, + .started = stream_started_cb, + .stopped = steam_stopped_cb, .sent = stream_sent_cb, }; +static void source_started_cb(struct bt_bap_broadcast_source *source) +{ + printk("Broadcast source %p started\n", source); + SET_FLAG(flag_source_started); +} + +static void source_stopped_cb(struct bt_bap_broadcast_source *source, uint8_t reason) +{ + printk("Broadcast source %p stopped with reason 0x%02X\n", source, reason); + UNSET_FLAG(flag_source_started); +} + static int setup_broadcast_source(struct bt_bap_broadcast_source **source, bool encryption) { struct bt_bap_broadcast_source_stream_param @@ -467,10 +481,12 @@ static void test_broadcast_source_start(struct bt_bap_broadcast_source *source, } /* Wait for all to be started */ - printk("Waiting for streams to be started\n"); + printk("Waiting for %zu streams to be started\n", ARRAY_SIZE(broadcast_source_streams)); for (size_t i = 0U; i < ARRAY_SIZE(broadcast_source_streams); i++) { - k_sem_take(&sem_started, K_FOREVER); + k_sem_take(&sem_stream_started, K_FOREVER); } + + WAIT_FOR_FLAG(flag_source_started); } static void test_broadcast_source_update_metadata(struct bt_bap_broadcast_source *source, @@ -520,10 +536,12 @@ static void test_broadcast_source_stop(struct bt_bap_broadcast_source *source) } /* Wait for all to be stopped */ - printk("Waiting for streams to be stopped\n"); + printk("Waiting for %zu streams to be stopped\n", ARRAY_SIZE(broadcast_source_streams)); for (size_t i = 0U; i < ARRAY_SIZE(broadcast_source_streams); i++) { - k_sem_take(&sem_stopped, K_FOREVER); + k_sem_take(&sem_stream_stopped, K_FOREVER); } + + WAIT_FOR_UNSET_FLAG(flag_source_started); } static void test_broadcast_source_delete(struct bt_bap_broadcast_source *source) @@ -564,10 +582,12 @@ static int stop_extended_adv(struct bt_le_ext_adv *adv) return 0; } -static void test_main(void) +static void init(void) { - struct bt_bap_broadcast_source *source; - struct bt_le_ext_adv *adv; + static struct bt_bap_broadcast_source_cb broadcast_source_cb = { + .started = source_started_cb, + .stopped = source_stopped_cb, + }; int err; err = bt_enable(NULL); @@ -578,6 +598,21 @@ static void test_main(void) printk("Bluetooth initialized\n"); + err = bt_bap_broadcast_source_register_cb(&broadcast_source_cb); + if (err != 0) { + FAIL("Failed to register broadcast source callbacks (err %d)\n", err); + return; + } +} + +static void test_main(void) +{ + struct bt_bap_broadcast_source *source; + struct bt_le_ext_adv *adv; + int err; + + init(); + err = setup_broadcast_source(&source, false); if (err != 0) { FAIL("Unable to setup broadcast source: %d\n", err); @@ -650,13 +685,7 @@ static void test_main_encrypted(void) struct bt_le_ext_adv *adv; int err; - err = bt_enable(NULL); - if (err) { - FAIL("Bluetooth init failed (err %d)\n", err); - return; - } - - printk("Bluetooth initialized\n"); + init(); err = setup_broadcast_source(&source, true); if (err != 0) { From 4ee17270d67ff4e59c0fe8f61a6adf1ab94e60fc Mon Sep 17 00:00:00 2001 From: Emil Gydesen Date: Fri, 5 Jul 2024 15:10:12 +0200 Subject: [PATCH 3/6] Bluetooth: CAP: Add broadcast source callback structs These callbacks are trigger for changes that affect the entire broadcast source, such as the BIG started and terminated events. Signed-off-by: Emil Gydesen --- include/zephyr/bluetooth/audio/cap.h | 16 +++++ subsys/bluetooth/audio/cap_initiator.c | 60 ++++++++++++++++++ .../cap_initiator/include/cap_initiator.h | 3 + .../audio/cap_initiator/uut/cap_initiator.c | 7 +++ .../audio/src/cap_initiator_broadcast_test.c | 61 ++++++++++++++----- 5 files changed, 132 insertions(+), 15 deletions(-) diff --git a/include/zephyr/bluetooth/audio/cap.h b/include/zephyr/bluetooth/audio/cap.h index 34b5b0f3e1cc0..b539a463c2689 100644 --- a/include/zephyr/bluetooth/audio/cap.h +++ b/include/zephyr/bluetooth/audio/cap.h @@ -128,6 +128,22 @@ struct bt_cap_initiator_cb { */ void (*unicast_stop_complete)(int err, struct bt_conn *conn); #endif /* CONFIG_BT_BAP_UNICAST_CLIENT */ +#if defined(CONFIG_BT_BAP_BROADCAST_SOURCE) + /** + * @brief The Broadcast Source has started and all of the streams are ready for audio data + * + * @param source The started Broadcast Source + */ + void (*broadcast_started)(struct bt_cap_broadcast_source *source); + + /** + * @brief The Broadcast Source has stopped and none of the streams are ready for audio data + * + * @param source The stopped Broadcast Source + * @param reason The reason why the Broadcast Source stopped (see the BT_HCI_ERR_* values) + */ + void (*broadcast_stopped)(struct bt_cap_broadcast_source *source, uint8_t reason); +#endif /* CONFIG_BT_BAP_BROADCAST_SOURCE */ }; /** diff --git a/subsys/bluetooth/audio/cap_initiator.c b/subsys/bluetooth/audio/cap_initiator.c index d6485e5f094a8..b1f8bedb734ff 100644 --- a/subsys/bluetooth/audio/cap_initiator.c +++ b/subsys/bluetooth/audio/cap_initiator.c @@ -257,9 +257,54 @@ int bt_cap_initiator_broadcast_audio_create( &(*broadcast_source)->bap_broadcast); } +static struct bt_cap_broadcast_source *get_cap_broadcast_source_by_bap_broadcast_source( + const struct bt_bap_broadcast_source *bap_broadcast_source) +{ + for (size_t i = 0U; i < ARRAY_SIZE(broadcast_sources); i++) { + if (broadcast_sources[i].bap_broadcast == bap_broadcast_source) { + return &broadcast_sources[i]; + } + } + + return NULL; +} + +static void broadcast_source_started_cb(struct bt_bap_broadcast_source *bap_broadcast_source) +{ + if (cap_cb && cap_cb->broadcast_started) { + struct bt_cap_broadcast_source *source = + get_cap_broadcast_source_by_bap_broadcast_source(bap_broadcast_source); + + if (source == NULL) { + /* Not one of ours */ + return; + } + + cap_cb->broadcast_started(source); + } +} + +static void broadcast_source_stopped_cb(struct bt_bap_broadcast_source *bap_broadcast_source, + uint8_t reason) +{ + if (cap_cb && cap_cb->broadcast_stopped) { + struct bt_cap_broadcast_source *source = + get_cap_broadcast_source_by_bap_broadcast_source(bap_broadcast_source); + + if (source == NULL) { + /* Not one of ours */ + return; + } + + cap_cb->broadcast_stopped(source, reason); + } +} + int bt_cap_initiator_broadcast_audio_start(struct bt_cap_broadcast_source *broadcast_source, struct bt_le_ext_adv *adv) { + static bool broadcast_source_cbs_registered; + CHECKIF(adv == NULL) { LOG_DBG("adv is NULL"); return -EINVAL; @@ -270,6 +315,21 @@ int bt_cap_initiator_broadcast_audio_start(struct bt_cap_broadcast_source *broad return -EINVAL; } + if (!broadcast_source_cbs_registered) { + static struct bt_bap_broadcast_source_cb broadcast_source_cb = { + .started = broadcast_source_started_cb, + .stopped = broadcast_source_stopped_cb, + }; + const int err = bt_bap_broadcast_source_register_cb(&broadcast_source_cb); + + if (err != 0) { + __ASSERT(false, "Failed to register BAP broadcast source callbacks: %d", + err); + } + + broadcast_source_cbs_registered = true; + } + return bt_bap_broadcast_source_start(broadcast_source->bap_broadcast, adv); } diff --git a/tests/bluetooth/audio/cap_initiator/include/cap_initiator.h b/tests/bluetooth/audio/cap_initiator/include/cap_initiator.h index 9b69f7d837f7e..57104e7ef03e0 100644 --- a/tests/bluetooth/audio/cap_initiator/include/cap_initiator.h +++ b/tests/bluetooth/audio/cap_initiator/include/cap_initiator.h @@ -23,5 +23,8 @@ DECLARE_FAKE_VOID_FUNC(mock_cap_initiator_unicast_discovery_complete_cb, struct DECLARE_FAKE_VOID_FUNC(mock_cap_initiator_unicast_start_complete_cb, int, struct bt_conn *); DECLARE_FAKE_VOID_FUNC(mock_cap_initiator_unicast_update_complete_cb, int, struct bt_conn *); DECLARE_FAKE_VOID_FUNC(mock_cap_initiator_unicast_stop_complete_cb, int, struct bt_conn *); +DECLARE_FAKE_VOID_FUNC(mock_cap_initiator_broadcast_started_cb, struct bt_cap_broadcast_source *); +DECLARE_FAKE_VOID_FUNC(mock_cap_initiator_broadcast_stopped_cb, struct bt_cap_broadcast_source *, + uint8_t); #endif /* MOCKS_CAP_INITIATOR_H_ */ diff --git a/tests/bluetooth/audio/cap_initiator/uut/cap_initiator.c b/tests/bluetooth/audio/cap_initiator/uut/cap_initiator.c index d094cc3c5b944..78dea5f4685c7 100644 --- a/tests/bluetooth/audio/cap_initiator/uut/cap_initiator.c +++ b/tests/bluetooth/audio/cap_initiator/uut/cap_initiator.c @@ -25,6 +25,9 @@ DEFINE_FAKE_VOID_FUNC(mock_cap_initiator_unicast_discovery_complete_cb, struct b DEFINE_FAKE_VOID_FUNC(mock_cap_initiator_unicast_start_complete_cb, int, struct bt_conn *); DEFINE_FAKE_VOID_FUNC(mock_cap_initiator_unicast_update_complete_cb, int, struct bt_conn *); DEFINE_FAKE_VOID_FUNC(mock_cap_initiator_unicast_stop_complete_cb, int, struct bt_conn *); +DEFINE_FAKE_VOID_FUNC(mock_cap_initiator_broadcast_started_cb, struct bt_cap_broadcast_source *); +DEFINE_FAKE_VOID_FUNC(mock_cap_initiator_broadcast_stopped_cb, struct bt_cap_broadcast_source *, + uint8_t); const struct bt_cap_initiator_cb mock_cap_initiator_cb = { #if defined(CONFIG_BT_BAP_UNICAST_CLIENT) @@ -33,6 +36,10 @@ const struct bt_cap_initiator_cb mock_cap_initiator_cb = { .unicast_update_complete = mock_cap_initiator_unicast_update_complete_cb, .unicast_stop_complete = mock_cap_initiator_unicast_stop_complete_cb, #endif /* CONFIG_BT_BAP_UNICAST_CLIENT */ +#if defined(CONFIG_BT_BAP_BROADCAST_SOURCE) + .broadcast_started = mock_cap_initiator_broadcast_started_cb, + .broadcast_stopped = mock_cap_initiator_broadcast_stopped_cb, +#endif /* CONFIG_BT_BAP_BROADCAST_SOURCE */ }; void mock_cap_initiator_init(void) diff --git a/tests/bsim/bluetooth/audio/src/cap_initiator_broadcast_test.c b/tests/bsim/bluetooth/audio/src/cap_initiator_broadcast_test.c index ea6eacef4fb7a..9fd54af563c04 100644 --- a/tests/bsim/bluetooth/audio/src/cap_initiator_broadcast_test.c +++ b/tests/bsim/bluetooth/audio/src/cap_initiator_broadcast_test.c @@ -33,6 +33,8 @@ #include "common.h" #if defined(CONFIG_BT_CAP_INITIATOR) && defined(CONFIG_BT_BAP_BROADCAST_SOURCE) +CREATE_FLAG(flag_source_started); + /* Zephyr Controller works best while Extended Advertising interval to be a multiple * of the ISO Interval minus 10 ms (max. advertising random delay). This is * required to place the AUX_ADV_IND PDUs in a non-overlapping interval with the @@ -75,8 +77,8 @@ static struct bt_bap_lc3_preset broadcast_preset_16_2_1 = BT_BAP_LC3_BROADCAST_PRESET_16_2_1(LOCATION, CONTEXT); static size_t stream_count; -static K_SEM_DEFINE(sem_broadcast_started, 0U, ARRAY_SIZE(broadcast_streams)); -static K_SEM_DEFINE(sem_broadcast_stopped, 0U, ARRAY_SIZE(broadcast_streams)); +static K_SEM_DEFINE(sem_broadcast_stream_started, 0U, ARRAY_SIZE(broadcast_streams)); +static K_SEM_DEFINE(sem_broadcast_stream_stopped, 0U, ARRAY_SIZE(broadcast_streams)); static const struct named_lc3_preset lc3_broadcast_presets[] = { {"8_1_1", BT_BAP_LC3_BROADCAST_PRESET_8_1_1(LOCATION, CONTEXT)}, @@ -114,7 +116,7 @@ static const struct named_lc3_preset lc3_broadcast_presets[] = { {"48_6_2", BT_BAP_LC3_BROADCAST_PRESET_48_6_2(LOCATION, CONTEXT)}, }; -static void broadcast_started_cb(struct bt_bap_stream *stream) +static void broadcast_stream_started_cb(struct bt_bap_stream *stream) { struct audio_test_stream *test_stream = audio_test_stream_from_bap_stream(stream); @@ -122,16 +124,16 @@ static void broadcast_started_cb(struct bt_bap_stream *stream) test_stream->tx_cnt = 0U; printk("Stream %p started\n", stream); - k_sem_give(&sem_broadcast_started); + k_sem_give(&sem_broadcast_stream_started); } -static void broadcast_stopped_cb(struct bt_bap_stream *stream, uint8_t reason) +static void broadcast_stream_stopped_cb(struct bt_bap_stream *stream, uint8_t reason) { printk("Stream %p stopped with reason 0x%02X\n", stream, reason); - k_sem_give(&sem_broadcast_stopped); + k_sem_give(&sem_broadcast_stream_stopped); } -static void broadcast_sent_cb(struct bt_bap_stream *bap_stream) +static void broadcast_stream_sent_cb(struct bt_bap_stream *bap_stream) { struct audio_test_stream *test_stream = audio_test_stream_from_bap_stream(bap_stream); struct bt_cap_stream *cap_stream = cap_stream_from_audio_test_stream(test_stream); @@ -178,13 +180,30 @@ static void broadcast_sent_cb(struct bt_bap_stream *bap_stream) } static struct bt_bap_stream_ops broadcast_stream_ops = { - .started = broadcast_started_cb, - .stopped = broadcast_stopped_cb, - .sent = broadcast_sent_cb, + .started = broadcast_stream_started_cb, + .stopped = broadcast_stream_stopped_cb, + .sent = broadcast_stream_sent_cb, }; +static void broadcast_source_started_cb(struct bt_cap_broadcast_source *broadcast_source) +{ + printk("Broadcast source %p started\n", broadcast_source); + SET_FLAG(flag_source_started); +} + +static void broadcast_source_stopped_cb(struct bt_cap_broadcast_source *broadcast_source, + uint8_t reason) +{ + printk("Broadcast source %p stopped with reason 0x%02X\n", broadcast_source, reason); + UNSET_FLAG(flag_source_started); +} + static void init(void) { + static struct bt_cap_initiator_cb broadcast_cbs = { + .broadcast_started = broadcast_source_started_cb, + .broadcast_stopped = broadcast_source_stopped_cb, + }; int err; err = bt_enable(NULL); @@ -221,6 +240,12 @@ static void init(void) printk("Registered GTBS\n"); } + + err = bt_cap_initiator_register_cb(&broadcast_cbs); + if (err != 0) { + FAIL("Failed to register broadcast callbacks: %d\n", err); + return; + } } static void setup_extended_adv(struct bt_le_ext_adv **adv) @@ -605,9 +630,11 @@ static void test_broadcast_audio_stop(struct bt_cap_broadcast_source *broadcast_ /* Wait for all to be stopped */ printk("Waiting for broadcast_streams to be stopped\n"); for (size_t i = 0U; i < stream_count; i++) { - k_sem_take(&sem_broadcast_stopped, K_FOREVER); + k_sem_take(&sem_broadcast_stream_stopped, K_FOREVER); } + WAIT_FOR_UNSET_FLAG(flag_source_started); + printk("Broadcast source stopped\n"); /* Verify that it cannot be stopped twice */ @@ -679,9 +706,11 @@ static void test_main_cap_initiator_broadcast(void) /* Wait for all to be started */ printk("Waiting for broadcast_streams to be started\n"); for (size_t i = 0U; i < stream_count; i++) { - k_sem_take(&sem_broadcast_started, K_FOREVER); + k_sem_take(&sem_broadcast_stream_started, K_FOREVER); } + WAIT_FOR_FLAG(flag_source_started); + /* Initialize sending */ for (size_t i = 0U; i < stream_count; i++) { struct audio_test_stream *test_stream = &broadcast_source_streams[i]; @@ -689,7 +718,7 @@ static void test_main_cap_initiator_broadcast(void) test_stream->tx_active = true; for (unsigned int j = 0U; j < BROADCAST_ENQUEUE_COUNT; j++) { - broadcast_sent_cb(bap_stream_from_audio_test_stream(test_stream)); + broadcast_stream_sent_cb(bap_stream_from_audio_test_stream(test_stream)); } } @@ -785,9 +814,11 @@ static int test_cap_initiator_ac(const struct cap_initiator_ac_param *param) /* Wait for all to be started */ printk("Waiting for broadcast_streams to be started\n"); for (size_t i = 0U; i < stream_count; i++) { - k_sem_take(&sem_broadcast_started, K_FOREVER); + k_sem_take(&sem_broadcast_stream_started, K_FOREVER); } + WAIT_FOR_FLAG(flag_source_started); + /* Initialize sending */ for (size_t i = 0U; i < stream_count; i++) { struct audio_test_stream *test_stream = &broadcast_source_streams[i]; @@ -795,7 +826,7 @@ static int test_cap_initiator_ac(const struct cap_initiator_ac_param *param) test_stream->tx_active = true; for (unsigned int j = 0U; j < BROADCAST_ENQUEUE_COUNT; j++) { - broadcast_sent_cb(bap_stream_from_audio_test_stream(test_stream)); + broadcast_stream_sent_cb(bap_stream_from_audio_test_stream(test_stream)); } } From a260d72a7b11b415db01e749e01bfccd8f4debe8 Mon Sep 17 00:00:00 2001 From: Emil Gydesen Date: Fri, 5 Jul 2024 17:21:25 +0200 Subject: [PATCH 4/6] Bluetooth: BAP: Add broadcast sink callback structs These callbacks are trigger for changes that affect the entire broadcast sink, such as the BIG synced and terminated events. Signed-off-by: Emil Gydesen --- include/zephyr/bluetooth/audio/bap.h | 21 ++++- subsys/bluetooth/audio/bap_broadcast_sink.c | 80 ++++++++++++++++-- .../audio/src/bap_broadcast_sink_test.c | 84 +++++++++++++------ 3 files changed, 147 insertions(+), 38 deletions(-) diff --git a/include/zephyr/bluetooth/audio/bap.h b/include/zephyr/bluetooth/audio/bap.h index 33ba8ca3cff28..17e53ca6c14f8 100644 --- a/include/zephyr/bluetooth/audio/bap.h +++ b/include/zephyr/bluetooth/audio/bap.h @@ -2292,6 +2292,22 @@ struct bt_bap_broadcast_sink_cb { */ void (*syncable)(struct bt_bap_broadcast_sink *sink, const struct bt_iso_biginfo *biginfo); + /** + * @brief The Broadcast Sink has started and audio data may be received from all of the + * streams + * + * @param sink The started Broadcast Sink + */ + void (*started)(struct bt_bap_broadcast_sink *sink); + + /** + * @brief The Broadcast Sink has stopped and none of the streams will receive audio data + * + * @param sink The stopped Broadcast Sink + * @param reason The reason why the Broadcast Sink stopped (see the BT_HCI_ERR_* values) + */ + void (*stopped)(struct bt_bap_broadcast_sink *sink, uint8_t reason); + /** @internal Internally used list node */ sys_snode_t _node; }; @@ -2302,11 +2318,12 @@ struct bt_bap_broadcast_sink_cb { * It is possible to register multiple struct of callbacks, but a single struct can only be * registered once. * Registering the same callback multiple times is undefined behavior and may break the stack. - * + * @param cb Broadcast sink callback structure. * - * @retval 0 in case of success + * @retval 0 on success * @retval -EINVAL if @p cb is NULL + * @retval -EALREADY if @p cb was already registered */ int bt_bap_broadcast_sink_register_cb(struct bt_bap_broadcast_sink_cb *cb); diff --git a/subsys/bluetooth/audio/bap_broadcast_sink.c b/subsys/bluetooth/audio/bap_broadcast_sink.c index d6b8578696805..6a6056ec0ce42 100644 --- a/subsys/bluetooth/audio/bap_broadcast_sink.c +++ b/subsys/bluetooth/audio/bap_broadcast_sink.c @@ -1,7 +1,7 @@ /* Bluetooth Audio Broadcast Sink */ /* - * Copyright (c) 2021-2023 Nordic Semiconductor ASA + * Copyright (c) 2021-2024 Nordic Semiconductor ASA * * SPDX-License-Identifier: Apache-2.0 */ @@ -402,11 +402,6 @@ static void broadcast_sink_iso_disconnected(struct bt_iso_chan *chan, if (!sys_slist_find_and_remove(&sink->streams, &stream->_node)) { LOG_DBG("Could not find and remove stream %p from sink %p", stream, sink); } - - /* Clear sink->big if not already cleared */ - if (sys_slist_is_empty(&sink->streams) && sink->big) { - broadcast_sink_clear_big(sink, reason); - } } if (ops != NULL && ops->stopped != NULL) { @@ -449,6 +444,17 @@ static struct bt_bap_broadcast_sink *broadcast_sink_get_by_pa(struct bt_le_per_a return NULL; } +static struct bt_bap_broadcast_sink *broadcast_sink_get_by_big(const struct bt_iso_big *big) +{ + for (size_t i = 0U; i < ARRAY_SIZE(broadcast_sinks); i++) { + if (broadcast_sinks[i].big == big) { + return &broadcast_sinks[i]; + } + } + + return NULL; +} + static void broadcast_sink_add_src(struct bt_bap_broadcast_sink *sink) { struct bt_bap_scan_delegator_add_src_param add_src_param; @@ -966,13 +972,72 @@ static uint16_t interval_to_sync_timeout(uint16_t interval) return (uint16_t)timeout; } +static void big_started_cb(struct bt_iso_big *big) +{ + struct bt_bap_broadcast_sink *sink = broadcast_sink_get_by_big(big); + struct bt_bap_broadcast_sink_cb *listener; + + if (sink == NULL) { + /* Not one of ours */ + return; + } + + SYS_SLIST_FOR_EACH_CONTAINER(&sink_cbs, listener, _node) { + if (listener->started != NULL) { + listener->started(sink); + } + } +} + +static void big_stopped_cb(struct bt_iso_big *big, uint8_t reason) +{ + struct bt_bap_broadcast_sink *sink = broadcast_sink_get_by_big(big); + struct bt_bap_broadcast_sink_cb *listener; + + if (sink == NULL) { + /* Not one of ours */ + return; + } + + broadcast_sink_clear_big(sink, reason); + + SYS_SLIST_FOR_EACH_CONTAINER(&sink_cbs, listener, _node) { + if (listener->stopped != NULL) { + listener->stopped(sink, reason); + } + } +} + int bt_bap_broadcast_sink_register_cb(struct bt_bap_broadcast_sink_cb *cb) { + static bool iso_big_cb_registered; + CHECKIF(cb == NULL) { LOG_DBG("cb is NULL"); + return -EINVAL; } + if (sys_slist_find(&sink_cbs, &cb->_node, NULL)) { + LOG_DBG("cb %p is already registered", cb); + + return -EEXIST; + } + + if (!iso_big_cb_registered) { + static struct bt_iso_big_cb big_cb = { + .started = big_started_cb, + .stopped = big_stopped_cb, + }; + const int err = bt_iso_big_register_cb(&big_cb); + + if (err != 0) { + __ASSERT(false, "Failed to register ISO BIG callbacks: %d", err); + } + + iso_big_cb_registered = true; + } + sys_slist_append(&sink_cbs, &cb->_node); return 0; @@ -1314,9 +1379,6 @@ int bt_bap_broadcast_sink_stop(struct bt_bap_broadcast_sink *sink) return err; } - broadcast_sink_clear_big(sink, BT_HCI_ERR_LOCALHOST_TERM_CONN); - /* Channel states will be updated in the broadcast_sink_iso_disconnected function */ - return 0; } diff --git a/tests/bsim/bluetooth/audio/src/bap_broadcast_sink_test.c b/tests/bsim/bluetooth/audio/src/bap_broadcast_sink_test.c index 52df6b4337381..6fadeb3f0106e 100644 --- a/tests/bsim/bluetooth/audio/src/bap_broadcast_sink_test.c +++ b/tests/bsim/bluetooth/audio/src/bap_broadcast_sink_test.c @@ -48,6 +48,7 @@ CREATE_FLAG(flag_pa_sync_lost); CREATE_FLAG(flag_pa_request); CREATE_FLAG(flag_bis_sync_requested); CREATE_FLAG(flag_big_sync_mic_failure); +CREATE_FLAG(flag_sink_started); static struct bt_bap_broadcast_sink *g_sink; static struct bt_le_scan_recv_info broadcaster_info; @@ -80,8 +81,8 @@ static const struct bt_audio_codec_cap codec_cap = BT_AUDIO_CODEC_CAP_LC3( SUPPORTED_MIN_OCTETS_PER_FRAME, SUPPORTED_MAX_OCTETS_PER_FRAME, SUPPORTED_MAX_FRAMES_PER_SDU, SUPPORTED_CONTEXTS); -static K_SEM_DEFINE(sem_started, 0U, ARRAY_SIZE(streams)); -static K_SEM_DEFINE(sem_stopped, 0U, ARRAY_SIZE(streams)); +static K_SEM_DEFINE(sem_stream_started, 0U, ARRAY_SIZE(streams)); +static K_SEM_DEFINE(sem_stream_stopped, 0U, ARRAY_SIZE(streams)); /* Create a mask for the maximum BIS we can sync to using the number of streams * we have. We add an additional 1 since the bis indexes start from 1 and not @@ -257,9 +258,27 @@ static void syncable_cb(struct bt_bap_broadcast_sink *sink, const struct bt_iso_ SET_FLAG(flag_syncable); } +static void broadcast_sink_started_cb(struct bt_bap_broadcast_sink *sink) +{ + printk("Broadcast sink %p started\n", sink); + SET_FLAG(flag_sink_started); +} + +static void broadcast_sink_stopped_cb(struct bt_bap_broadcast_sink *sink, uint8_t reason) +{ + printk("Broadcast sink %p stopped with reason 0x%02X\n", sink, reason); + UNSET_FLAG(flag_sink_started); + + if (reason == BT_HCI_ERR_TERM_DUE_TO_MIC_FAIL) { + SET_FLAG(flag_big_sync_mic_failure); + } +} + static struct bt_bap_broadcast_sink_cb broadcast_sink_cbs = { .base_recv = base_recv_cb, .syncable = syncable_cb, + .started = broadcast_sink_started_cb, + .stopped = broadcast_sink_stopped_cb, }; static bool scan_check_and_sync_broadcast(struct bt_data *data, void *user_data) @@ -526,7 +545,7 @@ static void validate_stream_codec_cfg(const struct bt_bap_stream *stream) } } -static void started_cb(struct bt_bap_stream *stream) +static void stream_started_cb(struct bt_bap_stream *stream) { struct audio_test_stream *test_stream = audio_test_stream_from_bap_stream(stream); struct bt_bap_ep_info info; @@ -567,24 +586,20 @@ static void started_cb(struct bt_bap_stream *stream) } printk("Stream %p started\n", stream); - k_sem_give(&sem_started); + k_sem_give(&sem_stream_started); validate_stream_codec_cfg(stream); } -static void stopped_cb(struct bt_bap_stream *stream, uint8_t reason) +static void stream_stopped_cb(struct bt_bap_stream *stream, uint8_t reason) { printk("Stream %p stopped with reason 0x%02X\n", stream, reason); - k_sem_give(&sem_stopped); - - if (reason == BT_HCI_ERR_TERM_DUE_TO_MIC_FAIL) { - SET_FLAG(flag_big_sync_mic_failure); - } + k_sem_give(&sem_stream_stopped); } static struct bt_bap_stream_ops stream_ops = { - .started = started_cb, - .stopped = stopped_cb, + .started = stream_started_cb, + .stopped = stream_stopped_cb, .recv = bap_stream_rx_recv_cb, }; @@ -707,6 +722,8 @@ static void test_broadcast_sink_create(void) FAIL("Unable to create the sink: %d\n", err); return; } + + printk("Created broadcast sink %p\n", g_sink); } static void test_broadcast_sink_create_inval(void) @@ -736,7 +753,7 @@ static void test_broadcast_sync(const uint8_t broadcast_code[BT_ISO_BROADCAST_CO { int err; - printk("Syncing the sink\n"); + printk("Syncing sink %p\n", g_sink); err = bt_bap_broadcast_sink_sync(g_sink, bis_index_bitfield, streams, broadcast_code); if (err != 0) { FAIL("Unable to sync the sink: %d\n", err); @@ -808,16 +825,20 @@ static void test_broadcast_stop(void) { int err; + printk("Stopping broadcast sink %p\n", g_sink); + err = bt_bap_broadcast_sink_stop(g_sink); if (err != 0) { FAIL("Unable to stop sink: %d", err); return; } - printk("Waiting for streams to be stopped\n"); + printk("Waiting for %zu streams to be stopped\n", ARRAY_SIZE(streams)); for (size_t i = 0U; i < ARRAY_SIZE(streams); i++) { - k_sem_take(&sem_stopped, K_FOREVER); + k_sem_take(&sem_stream_stopped, K_FOREVER); } + + WAIT_FOR_UNSET_FLAG(flag_sink_started); } static void test_broadcast_stop_inval(void) @@ -914,10 +935,12 @@ static void test_common(void) test_broadcast_sync_inval(); test_broadcast_sync(NULL); + WAIT_FOR_FLAG(flag_sink_started); + /* Wait for all to be started */ - printk("Waiting for streams to be started\n"); + printk("Waiting for %zu streams to be started\n", ARRAY_SIZE(streams)); for (size_t i = 0U; i < ARRAY_SIZE(streams); i++) { - k_sem_take(&sem_started, K_FOREVER); + k_sem_take(&sem_stream_started, K_FOREVER); } printk("Waiting for data\n"); @@ -944,10 +967,11 @@ static void test_main(void) printk("Waiting for PA disconnected\n"); WAIT_FOR_FLAG(flag_pa_sync_lost); - printk("Waiting for streams to be stopped\n"); + printk("Waiting for %zu streams to be stopped\n", ARRAY_SIZE(streams)); for (size_t i = 0U; i < ARRAY_SIZE(streams); i++) { - k_sem_take(&sem_stopped, K_FOREVER); + k_sem_take(&sem_stream_stopped, K_FOREVER); } + WAIT_FOR_UNSET_FLAG(flag_sink_started); PASS("Broadcast sink passed\n"); } @@ -962,10 +986,12 @@ static void test_sink_disconnect(void) /* Retry sync*/ test_broadcast_sync(NULL); + WAIT_FOR_FLAG(flag_sink_started); + /* Wait for all to be started */ - printk("Waiting for streams to be started\n"); + printk("Waiting for %zu streams to be started\n", ARRAY_SIZE(streams)); for (size_t i = 0U; i < ARRAY_SIZE(streams); i++) { - k_sem_take(&sem_started, K_FOREVER); + k_sem_take(&sem_stream_started, K_FOREVER); } test_broadcast_stop(); @@ -1001,10 +1027,12 @@ static void test_sink_encrypted(void) test_broadcast_sync(BROADCAST_CODE); + WAIT_FOR_FLAG(flag_sink_started); + /* Wait for all to be started */ - printk("Waiting for streams to be started\n"); + printk("Waiting for %zu streams to be started\n", ARRAY_SIZE(streams)); for (size_t i = 0U; i < ARRAY_SIZE(streams); i++) { - k_sem_take(&sem_started, K_FOREVER); + k_sem_take(&sem_stream_started, K_FOREVER); } printk("Waiting for data\n"); @@ -1021,9 +1049,9 @@ static void test_sink_encrypted(void) printk("Waiting for PA disconnected\n"); WAIT_FOR_FLAG(flag_pa_sync_lost); - printk("Waiting for streams to be stopped\n"); + printk("Waiting for %zu streams to be stopped\n", ARRAY_SIZE(streams)); for (size_t i = 0U; i < ARRAY_SIZE(streams); i++) { - k_sem_take(&sem_stopped, K_FOREVER); + k_sem_take(&sem_stream_stopped, K_FOREVER); } PASS("Broadcast sink encrypted passed\n"); @@ -1090,10 +1118,12 @@ static void broadcast_sink_with_assistant(void) WAIT_FOR_FLAG(flag_bis_sync_requested); test_broadcast_sync(NULL); + WAIT_FOR_FLAG(flag_sink_started); + /* Wait for all to be started */ - printk("Waiting for streams to be started\n"); + printk("Waiting for %zu streams to be started\n", ARRAY_SIZE(streams)); for (size_t i = 0U; i < ARRAY_SIZE(streams); i++) { - k_sem_take(&sem_started, K_FOREVER); + k_sem_take(&sem_stream_started, K_FOREVER); } printk("Waiting for data\n"); From 0ff04dc8cb4815ea411f9936de78b2656e3a0c6b Mon Sep 17 00:00:00 2001 From: Emil Gydesen Date: Fri, 5 Jul 2024 20:28:24 +0200 Subject: [PATCH 5/6] samples: Bluetooth: Use broadcast source cbs in broadcast source sample Use the broadcast source callbacks to set the semaphores rather than the stream callbacks, as the broadcast source callbacks are better for that as they are called when all streams are ready. Signed-off-by: Emil Gydesen --- .../bluetooth/bap_broadcast_source/src/main.c | 48 ++++++++++++------- 1 file changed, 31 insertions(+), 17 deletions(-) diff --git a/samples/bluetooth/bap_broadcast_source/src/main.c b/samples/bluetooth/bap_broadcast_source/src/main.c index b0238ade6bd02..0df0773cbf846 100644 --- a/samples/bluetooth/bap_broadcast_source/src/main.c +++ b/samples/bluetooth/bap_broadcast_source/src/main.c @@ -153,8 +153,8 @@ static int16_t send_pcm_data[MAX_NUM_SAMPLES]; static uint16_t seq_num; static bool stopping; -static K_SEM_DEFINE(sem_started, 0U, ARRAY_SIZE(streams)); -static K_SEM_DEFINE(sem_stopped, 0U, ARRAY_SIZE(streams)); +static K_SEM_DEFINE(sem_started, 0U, 1U); +static K_SEM_DEFINE(sem_stopped, 0U, 1U); #define BROADCAST_SOURCE_LIFETIME 120U /* seconds */ @@ -365,12 +365,6 @@ static void stream_started_cb(struct bt_bap_stream *stream) source_stream->seq_num = 0U; source_stream->sent_cnt = 0U; - k_sem_give(&sem_started); -} - -static void stream_stopped_cb(struct bt_bap_stream *stream, uint8_t reason) -{ - k_sem_give(&sem_stopped); } static void stream_sent_cb(struct bt_bap_stream *stream) @@ -387,7 +381,9 @@ static void stream_sent_cb(struct bt_bap_stream *stream) } static struct bt_bap_stream_ops stream_ops = { - .started = stream_started_cb, .stopped = stream_stopped_cb, .sent = stream_sent_cb}; + .started = stream_started_cb, + .sent = stream_sent_cb, +}; static int setup_broadcast_source(struct bt_bap_broadcast_source **source) { @@ -439,8 +435,24 @@ static int setup_broadcast_source(struct bt_bap_broadcast_source **source) return 0; } +static void source_started_cb(struct bt_bap_broadcast_source *source) +{ + printk("Broadcast source %p started\n", source); + k_sem_give(&sem_started); +} + +static void source_stopped_cb(struct bt_bap_broadcast_source *source, uint8_t reason) +{ + printk("Broadcast source %p stopped with reason 0x%02X\n", source, reason); + k_sem_give(&sem_stopped); +} + int main(void) { + static struct bt_bap_broadcast_source_cb broadcast_source_cb = { + .started = source_started_cb, + .stopped = source_stopped_cb, + }; struct bt_le_ext_adv *adv; int err; @@ -451,6 +463,12 @@ int main(void) } printk("Bluetooth initialized\n"); + err = bt_bap_broadcast_source_register_cb(&broadcast_source_cb); + if (err != 0) { + printk("Failed to register broadcast source callbacks (err %d)\n", err); + return 0; + } + for (size_t i = 0U; i < ARRAY_SIZE(send_pcm_data); i++) { /* Initialize mock data */ send_pcm_data[i] = i; @@ -581,10 +599,8 @@ int main(void) return 0; } - /* Wait for all to be started */ - for (size_t i = 0U; i < ARRAY_SIZE(streams); i++) { - k_sem_take(&sem_started, K_FOREVER); - } + /* Wait for broadcast source to be started */ + k_sem_take(&sem_started, K_FOREVER); printk("Broadcast source started\n"); /* Initialize sending */ @@ -608,10 +624,8 @@ int main(void) return 0; } - /* Wait for all to be stopped */ - for (size_t i = 0U; i < ARRAY_SIZE(streams); i++) { - k_sem_take(&sem_stopped, K_FOREVER); - } + /* Wait for broadcast source to be stopped */ + k_sem_take(&sem_stopped, K_FOREVER); printk("Broadcast source stopped\n"); printk("Deleting broadcast source\n"); From 1810815fb07bf959cef3b1a07cca3a572574d7df Mon Sep 17 00:00:00 2001 From: Emil Gydesen Date: Fri, 5 Jul 2024 20:30:09 +0200 Subject: [PATCH 6/6] samples: Bluetooth: Use broadcast sink cbs in broadcast sink sample Use the broadcast sink callbacks to set the semaphores rather than the stream callbacks, as the broadcast sink callbacks are better for that as they are called when all streams are ready. Signed-off-by: Emil Gydesen --- .../bluetooth/bap_broadcast_sink/src/main.c | 34 +++++++++++-------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/samples/bluetooth/bap_broadcast_sink/src/main.c b/samples/bluetooth/bap_broadcast_sink/src/main.c index cee4f5b008cb9..902670aaee56a 100644 --- a/samples/bluetooth/bap_broadcast_sink/src/main.c +++ b/samples/bluetooth/bap_broadcast_sink/src/main.c @@ -536,11 +536,6 @@ static void stream_started_cb(struct bt_bap_stream *stream) #endif /* CONFIG_LIBLC3 */ k_sem_give(&sem_stream_started); - if (k_sem_count_get(&sem_stream_started) == stream_count) { - big_synced = true; - printk("BIG synced\n"); - k_sem_give(&sem_big_synced); - } } static void stream_stopped_cb(struct bt_bap_stream *stream, uint8_t reason) @@ -553,11 +548,6 @@ static void stream_stopped_cb(struct bt_bap_stream *stream, uint8_t reason) if (err != 0) { printk("Failed to take sem_stream_started: %d\n", err); } - - if (k_sem_count_get(&sem_stream_started) != stream_count) { - printk("BIG sync terminated\n"); - big_synced = false; - } } static void stream_recv_cb(struct bt_bap_stream *stream, const struct bt_iso_recv_info *info, @@ -818,9 +808,27 @@ static void syncable_cb(struct bt_bap_broadcast_sink *sink, const struct bt_iso_ } } +static void broadcast_sink_started_cb(struct bt_bap_broadcast_sink *sink) +{ + printk("Broadcast sink %p started\n", sink); + + big_synced = true; + k_sem_give(&sem_big_synced); +} + +static void broadcast_sink_stopped_cb(struct bt_bap_broadcast_sink *sink, uint8_t reason) +{ + printk("Broadcast sink %p stopped with reason 0x%02X\n", sink, reason); + + big_synced = false; + k_sem_give(&sem_broadcast_sink_stopped); +} + static struct bt_bap_broadcast_sink_cb broadcast_sink_cbs = { .base_recv = base_recv_cb, .syncable = syncable_cb, + .started = broadcast_sink_started_cb, + .stopped = broadcast_sink_stopped_cb, }; static void pa_timer_handler(struct k_work *work) @@ -1027,8 +1035,6 @@ static int bis_sync_req_cb(struct bt_conn *conn, return err; } - - k_sem_give(&sem_broadcast_sink_stopped); } broadcaster_broadcast_id = recv_state->broadcast_id; @@ -1242,7 +1248,7 @@ static void bap_pa_sync_terminated_cb(struct bt_le_per_adv_sync *sync, if (info->reason != BT_HCI_ERR_LOCALHOST_TERM_CONN && req_recv_state != NULL) { int err; - if (k_sem_count_get(&sem_stream_connected) > 0) { + if (big_synced) { err = bt_bap_broadcast_sink_stop(broadcast_sink); if (err != 0) { printk("Failed to stop Broadcast Sink: %d\n", err); @@ -1257,8 +1263,6 @@ static void bap_pa_sync_terminated_cb(struct bt_le_per_adv_sync *sync, return; } - - k_sem_give(&sem_broadcast_sink_stopped); } } }