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 <emil.gydesen@nordicsemi.no>
This commit is contained in:
Emil Gydesen 2024-07-05 10:21:40 +02:00 committed by Benjamin Cabé
commit ab9ee0817d
10 changed files with 416 additions and 45 deletions

View file

@ -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 */ /** Broadcast Source stream parameters */
struct bt_bap_broadcast_source_stream_param { struct bt_bap_broadcast_source_stream_param {
/** Audio stream */ /** Audio stream */

View file

@ -1,7 +1,7 @@
/* Bluetooth Audio Broadcast Source */ /* Bluetooth Audio Broadcast Source */
/* /*
* Copyright (c) 2021-2023 Nordic Semiconductor ASA * Copyright (c) 2021-2024 Nordic Semiconductor ASA
* *
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
@ -25,6 +25,7 @@
#include <zephyr/kernel.h> #include <zephyr/kernel.h>
#include <zephyr/logging/log.h> #include <zephyr/logging/log.h>
#include <zephyr/net_buf.h> #include <zephyr/net_buf.h>
#include <zephyr/sys/__assert.h>
#include <zephyr/sys/byteorder.h> #include <zephyr/sys/byteorder.h>
#include <zephyr/sys/check.h> #include <zephyr/sys/check.h>
#include <zephyr/sys/slist.h> #include <zephyr/sys/slist.h>
@ -55,6 +56,7 @@ static struct bt_bap_broadcast_subgroup
broadcast_source_subgroups[CONFIG_BT_BAP_BROADCAST_SRC_COUNT] broadcast_source_subgroups[CONFIG_BT_BAP_BROADCAST_SRC_COUNT]
[CONFIG_BT_BAP_BROADCAST_SRC_SUBGROUP_COUNT]; [CONFIG_BT_BAP_BROADCAST_SRC_SUBGROUP_COUNT];
static struct bt_bap_broadcast_source broadcast_sources[CONFIG_BT_BAP_BROADCAST_SRC_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 * 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 = { static struct bt_iso_chan_ops broadcast_source_iso_ops = {
.sent = broadcast_source_iso_sent, .sent = broadcast_source_iso_sent,
.connected = broadcast_source_iso_connected, .connected = broadcast_source_iso_connected,
.disconnected = broadcast_source_iso_disconnected, .disconnected = broadcast_source_iso_disconnected,
}; };
bool bt_bap_ep_is_broadcast_src(const struct bt_bap_ep *ep) 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; streams_encoded = 0;
SYS_SLIST_FOR_EACH_CONTAINER(&source->subgroups, subgroup, _node) { SYS_SLIST_FOR_EACH_CONTAINER(&source->subgroups, subgroup, _node) {
if (!encode_base_subgroup(subgroup, if (!encode_base_subgroup(subgroup, &source->stream_data[streams_encoded],
&source->stream_data[streams_encoded],
&streams_encoded, buf)) { &streams_encoded, buf)) {
return false; 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; struct bt_bap_broadcast_subgroup *subgroup, *next_subgroup;
SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&source->subgroups, subgroup, SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&source->subgroups, subgroup, next_subgroup, _node) {
next_subgroup, _node) {
struct bt_bap_stream *stream, *next_stream; struct bt_bap_stream *stream, *next_stream;
SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&subgroup->streams, stream, SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&subgroup->streams, stream, next_stream, _node) {
next_stream, _node) {
bt_bap_iso_unbind_ep(stream->ep->iso, stream->ep); bt_bap_iso_unbind_ep(stream->ep->iso, stream->ep);
stream->ep->stream = NULL; stream->ep->stream = NULL;
stream->ep = NULL; stream->ep = NULL;
@ -467,8 +466,7 @@ static void broadcast_source_cleanup(struct bt_bap_broadcast_source *source)
stream->qos = NULL; stream->qos = NULL;
stream->group = NULL; stream->group = NULL;
sys_slist_remove(&subgroup->streams, NULL, sys_slist_remove(&subgroup->streams, NULL, &stream->_node);
&stream->_node);
} }
sys_slist_remove(&source->subgroups, NULL, &subgroup->_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++; bis_count++;
} }
err = broadcast_source_setup_stream(index, stream, err = broadcast_source_setup_stream(index, stream, codec_cfg, qos, source);
codec_cfg, qos, source);
if (err != 0) { if (err != 0) {
LOG_DBG("Failed to setup streams[%zu]: %d", i, err); LOG_DBG("Failed to setup streams[%zu]: %d", i, err);
broadcast_source_cleanup(source); 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) 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_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; struct bt_bap_broadcast_subgroup *subgroup;
enum bt_bap_ep_state broadcast_state; enum bt_bap_ep_state broadcast_state;
struct bt_bap_stream *stream; 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.latency = source->qos->latency;
param.encryption = source->encryption; param.encryption = source->encryption;
if (param.encryption) { if (param.encryption) {
(void)memcpy(param.bcode, source->broadcast_code, (void)memcpy(param.bcode, source->broadcast_code, sizeof(param.bcode));
sizeof(param.bcode));
} }
#if defined(CONFIG_BT_ISO_TEST_PARAMS) #if defined(CONFIG_BT_ISO_TEST_PARAMS)
param.irc = source->irc; param.irc = source->irc;
@ -1125,14 +1121,12 @@ int bt_bap_broadcast_source_stop(struct bt_bap_broadcast_source *source)
return -EALREADY; return -EALREADY;
} }
err = bt_iso_big_terminate(source->big); err = bt_iso_big_terminate(source->big);
if (err) { if (err) {
LOG_DBG("Failed to terminate BIG (err %d)", err); LOG_DBG("Failed to terminate BIG (err %d)", err);
return err; return err;
} }
source->big = NULL;
return 0; return 0;
} }
@ -1188,3 +1182,102 @@ int bt_bap_broadcast_source_get_base(struct bt_bap_broadcast_source *source,
return 0; 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;
}

View file

@ -15,4 +15,5 @@ target_include_directories(testbinary PRIVATE include)
target_sources(testbinary target_sources(testbinary
PRIVATE PRIVATE
src/main.c src/main.c
src/test_callback_register.c
) )

View file

@ -1,28 +1,41 @@
/* main.c - Application main entry point */ /* 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 * SPDX-License-Identifier: Apache-2.0
*/ */
#include <errno.h>
#include <stdint.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <zephyr/bluetooth/audio/audio.h> #include <zephyr/bluetooth/audio/audio.h>
#include <zephyr/bluetooth/audio/bap.h> #include <zephyr/bluetooth/audio/bap.h>
#include <zephyr/bluetooth/audio/lc3.h>
#include <zephyr/bluetooth/byteorder.h>
#include <zephyr/bluetooth/hci_types.h>
#include <zephyr/bluetooth/iso.h> #include <zephyr/bluetooth/iso.h>
#include <zephyr/fff.h> #include <zephyr/fff.h>
#include <zephyr/kernel.h> #include <zephyr/kernel.h>
#include <zephyr/net/buf.h>
#include <zephyr/sys/printk.h>
#include <zephyr/sys/util_macro.h> #include <zephyr/sys/util_macro.h>
#include "bluetooth.h" #include "bap_broadcast_source.h"
#include "bap_stream.h"
#include "bap_stream_expects.h" #include "bap_stream_expects.h"
#include "bluetooth.h"
#include "expects_util.h"
#include "ztest_assert.h"
#include "ztest_test.h"
DEFINE_FFF_GLOBALS; DEFINE_FFF_GLOBALS;
static void mock_init_rule_before(const struct ztest_unit_test *test, void *fixture) static void mock_init_rule_before(const struct ztest_unit_test *test, void *fixture)
{ {
mock_bap_broadcast_source_init();
mock_bap_stream_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) static void bap_broadcast_source_test_suite_before(void *f)
{ {
int err;
memset(f, 0, sizeof(struct bap_broadcast_source_test_suite_fixture)); memset(f, 0, sizeof(struct bap_broadcast_source_test_suite_fixture));
bap_broadcast_source_test_suite_fixture_init(f); 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) 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->params);
free(param->qos); free(param->qos);
free(param); free(param);
bt_bap_broadcast_source_unregister_cb(&mock_bap_broadcast_source_cb);
} }
static void bap_broadcast_source_test_suite_teardown(void *f) 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); mock_bap_stream_connected_cb_fake.call_count);
zexpect_call_count("bt_bap_stream_ops.started", fixture->stream_cnt, zexpect_call_count("bt_bap_stream_ops.started", fixture->stream_cnt,
mock_bap_stream_started_cb_fake.call_count); 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 i = 0U; i < create_param->params_count; i++) {
for (size_t j = 0U; j < create_param->params[i].params_count; j++) { 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); mock_bap_stream_disconnected_cb_fake.call_count);
zexpect_call_count("bt_bap_stream_ops.stopped", fixture->stream_cnt, zexpect_call_count("bt_bap_stream_ops.stopped", fixture->stream_cnt,
mock_bap_stream_stopped_cb_fake.call_count); 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); err = bt_bap_broadcast_source_delete(fixture->source);
zassert_equal(0, err, "Unable to delete broadcast source: err %d", err); zassert_equal(0, err, "Unable to delete broadcast source: err %d", err);

View file

@ -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 <errno.h>
#include <stddef.h>
#include <zephyr/bluetooth/audio/bap.h>
#include <zephyr/fff.h>
#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);
}

View file

@ -14,6 +14,7 @@ add_library(uut STATIC
${ZEPHYR_BASE}/subsys/bluetooth/audio/codec.c ${ZEPHYR_BASE}/subsys/bluetooth/audio/codec.c
${ZEPHYR_BASE}/subsys/logging/log_minimal.c ${ZEPHYR_BASE}/subsys/logging/log_minimal.c
${ZEPHYR_BASE}/lib/net_buf/buf_simple.c ${ZEPHYR_BASE}/lib/net_buf/buf_simple.c
bap_broadcast_source.c
) )
add_subdirectory(${ZEPHYR_BASE}/tests/bluetooth/audio/mocks mocks) add_subdirectory(${ZEPHYR_BASE}/tests/bluetooth/audio/mocks mocks)

View file

@ -0,0 +1,31 @@
/*
* Copyright (c) 2024 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdint.h>
#include <zephyr/bluetooth/audio/bap.h>
#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);
}

View file

@ -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 <stdint.h>
#include <zephyr/bluetooth/audio/bap.h>
#include <zephyr/fff.h>
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_ */

View file

@ -1,10 +1,13 @@
/* /*
* Copyright (c) 2023 Codecoup * Copyright (c) 2023 Codecoup
* Copyright (c) 2024 Nordic Semiconductor ASA
* *
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
#include <errno.h>
#include <stdlib.h> #include <stdlib.h>
#include <zephyr/bluetooth/iso.h> #include <zephyr/bluetooth/iso.h>
#include "conn.h" #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) #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, int bt_iso_big_create(struct bt_le_ext_adv *padv, struct bt_iso_big_create_param *param,
struct bt_iso_big **out_big) 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; *out_big = big;
if (iso_cb != NULL && iso_cb->started != NULL) {
iso_cb->started(big);
}
return 0; 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); 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); free(big);
return 0; return 0;

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2021-2023 Nordic Semiconductor ASA * Copyright (c) 2021-2024 Nordic Semiconductor ASA
* *
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
@ -35,6 +35,8 @@
#define SUPPORTED_MAX_FRAMES_PER_SDU 1 #define SUPPORTED_MAX_FRAMES_PER_SDU 1
#if defined(CONFIG_BT_BAP_BROADCAST_SOURCE) #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 /* When BROADCAST_ENQUEUE_COUNT > 1 we can enqueue enough buffers to ensure that
* the controller is never idle * the controller is never idle
*/ */
@ -62,8 +64,8 @@ static uint8_t bis_codec_data[] = {
BT_BYTES_LIST_LE32(BT_AUDIO_LOCATION_FRONT_CENTER)), 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_stream_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_stopped, 0U, ARRAY_SIZE(broadcast_source_streams));
static void validate_stream_codec_cfg(const struct bt_bap_stream *stream) 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 audio_test_stream *test_stream = audio_test_stream_from_bap_stream(stream);
struct bt_bap_ep_info info; 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); printk("Stream %p started\n", stream);
validate_stream_codec_cfg(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); 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) 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 = { static struct bt_bap_stream_ops stream_ops = {
.started = started_cb, .started = stream_started_cb,
.stopped = stopped_cb, .stopped = steam_stopped_cb,
.sent = stream_sent_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) static int setup_broadcast_source(struct bt_bap_broadcast_source **source, bool encryption)
{ {
struct bt_bap_broadcast_source_stream_param 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 */ /* 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++) { 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, 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 */ /* 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++) { 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) 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; return 0;
} }
static void test_main(void) static void init(void)
{ {
struct bt_bap_broadcast_source *source; static struct bt_bap_broadcast_source_cb broadcast_source_cb = {
struct bt_le_ext_adv *adv; .started = source_started_cb,
.stopped = source_stopped_cb,
};
int err; int err;
err = bt_enable(NULL); err = bt_enable(NULL);
@ -578,6 +598,21 @@ static void test_main(void)
printk("Bluetooth initialized\n"); 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); err = setup_broadcast_source(&source, false);
if (err != 0) { if (err != 0) {
FAIL("Unable to setup broadcast source: %d\n", err); 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; struct bt_le_ext_adv *adv;
int err; int err;
err = bt_enable(NULL); init();
if (err) {
FAIL("Bluetooth init failed (err %d)\n", err);
return;
}
printk("Bluetooth initialized\n");
err = setup_broadcast_source(&source, true); err = setup_broadcast_source(&source, true);
if (err != 0) { if (err != 0) {