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 <emil.gydesen@nordicsemi.no>
This commit is contained in:
Emil Gydesen 2024-07-05 17:21:25 +02:00 committed by Benjamin Cabé
commit 829519dd6f
3 changed files with 147 additions and 38 deletions

View file

@ -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);

View file

@ -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;
}

View file

@ -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");