From a2ebe49259d12c7c3375669f65ed5703ddd7f33a Mon Sep 17 00:00:00 2001 From: Jens Rehhoff Thomsen Date: Wed, 6 Mar 2024 10:40:16 +0100 Subject: [PATCH] Bluetooth samples: Keep broadcast assistant connected When Broadcast Sink is connected to Broadcast Assistant then keep connection when Broadcast Source is removed. Signed-off-by: Jens Rehhoff Thomsen --- .../bluetooth/broadcast_audio_sink/src/main.c | 243 ++++++++++++------ 1 file changed, 159 insertions(+), 84 deletions(-) diff --git a/samples/bluetooth/broadcast_audio_sink/src/main.c b/samples/bluetooth/broadcast_audio_sink/src/main.c index ae55ca13b128e8..860434d428af42 100644 --- a/samples/bluetooth/broadcast_audio_sink/src/main.c +++ b/samples/bluetooth/broadcast_audio_sink/src/main.c @@ -64,6 +64,7 @@ BUILD_ASSERT(IS_ENABLED(CONFIG_SCAN_SELF) || IS_ENABLED(CONFIG_SCAN_OFFLOAD), #define USB_RING_BUF_SIZE (5 * LC3_MAX_NUM_SAMPLES_STEREO) /* 5 SDUs*/ #endif /* defined(CONFIG_USB_DEVICE_AUDIO) */ +static K_SEM_DEFINE(sem_broadcast_sink_stopped, 0U, 1U); static K_SEM_DEFINE(sem_connected, 0U, 1U); static K_SEM_DEFINE(sem_disconnected, 0U, 1U); static K_SEM_DEFINE(sem_broadcaster_found, 0U, 1U); @@ -75,7 +76,9 @@ static K_SEM_DEFINE(sem_broadcast_code_received, 0U, 1U); static K_SEM_DEFINE(sem_pa_request, 0U, 1U); static K_SEM_DEFINE(sem_past_request, 0U, 1U); static K_SEM_DEFINE(sem_bis_sync_requested, 0U, 1U); -static K_SEM_DEFINE(sem_bis_synced, 0U, CONFIG_BT_BAP_BROADCAST_SNK_STREAM_COUNT); +static K_SEM_DEFINE(sem_stream_connected, 0U, CONFIG_BT_BAP_BROADCAST_SNK_STREAM_COUNT); +static K_SEM_DEFINE(sem_stream_started, 0U, CONFIG_BT_BAP_BROADCAST_SNK_STREAM_COUNT); +static K_SEM_DEFINE(sem_big_synced, 0U, 1U); /* Sample assumes that we only have a single Scan Delegator receive state */ static const struct bt_bap_scan_delegator_recv_state *req_recv_state; @@ -107,8 +110,11 @@ static struct broadcast_sink_stream { } streams[CONFIG_BT_BAP_BROADCAST_SNK_STREAM_COUNT]; static struct bt_bap_stream *streams_p[ARRAY_SIZE(streams)]; +static volatile bool big_synced; +static volatile bool base_received; static struct bt_conn *broadcast_assistant_conn; static struct bt_le_ext_adv *ext_adv; +static volatile uint8_t stream_count; static const struct bt_audio_codec_cap codec_cap = BT_AUDIO_CODEC_CAP_LC3( BT_AUDIO_CODEC_CAP_FREQ_16KHZ | BT_AUDIO_CODEC_CAP_FREQ_24KHZ, @@ -480,6 +486,25 @@ static void usb_data_written_cb(const struct device *dev, struct net_buf *buf, s } #endif /* defined(CONFIG_USB_DEVICE_AUDIO) */ +static void stream_connected_cb(struct bt_bap_stream *stream) +{ + printk("Stream %p connected\n", stream); + + k_sem_give(&sem_stream_connected); +} + +static void stream_disconnected_cb(struct bt_bap_stream *stream, uint8_t reason) +{ + int err; + + printk("Stream %p disconnected with reason 0x%02X\n", stream, reason); + + err = k_sem_take(&sem_stream_connected, K_NO_WAIT); + if (err != 0) { + printk("Failed to take sem_stream_connected: %d\n", err); + } +} + static void stream_started_cb(struct bt_bap_stream *stream) { struct broadcast_sink_stream *sink_stream = @@ -509,7 +534,11 @@ static void stream_started_cb(struct bt_bap_stream *stream) } #endif /* CONFIG_LIBLC3 */ - k_sem_give(&sem_bis_synced); + k_sem_give(&sem_stream_started); + if (k_sem_count_get(&sem_stream_started) == stream_count) { + big_synced = true; + k_sem_give(&sem_big_synced); + } } static void stream_stopped_cb(struct bt_bap_stream *stream, uint8_t reason) @@ -518,9 +547,13 @@ static void stream_stopped_cb(struct bt_bap_stream *stream, uint8_t reason) printk("Stream %p stopped with reason 0x%02X\n", stream, reason); - err = k_sem_take(&sem_bis_synced, K_NO_WAIT); + err = k_sem_take(&sem_stream_started, K_NO_WAIT); if (err != 0) { - printk("Failed to take sem_bis_synced: %d\n", err); + printk("Failed to take sem_stream_started: %d\n", err); + } + + if (k_sem_count_get(&sem_stream_started) != stream_count) { + big_synced = false; } } @@ -563,6 +596,8 @@ static void stream_recv_cb(struct bt_bap_stream *stream, const struct bt_iso_rec } static struct bt_bap_stream_ops stream_ops = { + .connected = stream_connected_cb, + .disconnected = stream_disconnected_cb, .started = stream_started_cb, .stopped = stream_stopped_cb, .recv = stream_recv_cb, @@ -794,7 +829,7 @@ static void base_recv_cb(struct bt_bap_broadcast_sink *sink, const struct bt_bap uint32_t base_bis_index_bitfield = 0U; int err; - if (k_sem_count_get(&sem_base_received) != 0U) { + if (base_received) { return; } @@ -817,17 +852,23 @@ static void base_recv_cb(struct bt_bap_broadcast_sink *sink, const struct bt_bap bis_index_bitfield = base_bis_index_bitfield & bis_index_mask; + printk("bis_index_bitfield = 0x%08x\n", bis_index_bitfield); + if (broadcast_assistant_conn == NULL) { /* No broadcast assistant requesting anything */ requested_bis_sync = BT_BAP_BIS_SYNC_NO_PREF; k_sem_give(&sem_bis_sync_requested); } + base_received = true; k_sem_give(&sem_base_received); } static void syncable_cb(struct bt_bap_broadcast_sink *sink, const struct bt_iso_biginfo *biginfo) { + printk("Broadcast sink (%p) is syncable, BIG %s\n", (void *)sink, + biginfo->encryption ? "encrypted" : "not encrypted"); + k_sem_give(&sem_syncable); if (!biginfo->encryption) { @@ -903,6 +944,18 @@ static int pa_sync_past(struct bt_conn *conn, uint16_t pa_interval) return err; } +static void recv_state_updated_cb(struct bt_conn *conn, + const struct bt_bap_scan_delegator_recv_state *recv_state) +{ + printk("Receive state updated, pa sync state: %u\n", recv_state->pa_sync_state); + + for (uint8_t i = 0; i < recv_state->num_subgroups; i++) { + printk("subgroup %d bis_sync: 0x%08x\n", i, recv_state->subgroups[i].bis_sync); + } + + req_recv_state = recv_state; +} + static int pa_sync_req_cb(struct bt_conn *conn, const struct bt_bap_scan_delegator_recv_state *recv_state, bool past_avail, uint16_t pa_interval) @@ -951,15 +1004,22 @@ static int pa_sync_term_req_cb(struct bt_conn *conn, { int err; + printk("PA sync termination req, pa sync state: %u\n", recv_state->pa_sync_state); + + for (uint8_t i = 0; i < recv_state->num_subgroups; i++) { + printk("subgroup %d bis_sync: 0x%08x\n", i, recv_state->subgroups[i].bis_sync); + } + req_recv_state = recv_state; - err = bt_bap_broadcast_sink_delete(broadcast_sink); + printk("Delete periodic advertising sync\n"); + err = bt_le_per_adv_sync_delete(pa_sync); if (err != 0) { + printk("Could not delete per adv sync: %d\n", err); + return err; } - broadcast_sink = NULL; - return 0; } @@ -982,13 +1042,12 @@ static int bis_sync_req_cb(struct bt_conn *conn, const struct bt_bap_scan_delegator_recv_state *recv_state, const uint32_t bis_sync_req[CONFIG_BT_BAP_BASS_MAX_SUBGROUPS]) { - const bool bis_synced = k_sem_count_get(&sem_bis_synced) > 0U; - - printk("BIS sync request received for %p: 0x%08x\n", - recv_state, bis_sync_req[0]); + printk("BIS sync request received for %p: 0x%08x->0x%08x, broadcast id: 0x%06x, (%s)\n", + recv_state, requested_bis_sync, bis_sync_req[0], recv_state->broadcast_id, + big_synced ? "BIG synced" : "BIG not synced"); /* We only care about a single subgroup in this sample */ - if (bis_synced && requested_bis_sync != bis_sync_req[0]) { + if (big_synced && requested_bis_sync != bis_sync_req[0]) { /* If the BIS sync request is received while we are already * synced, it means that the requested BIS sync has changed. */ @@ -996,8 +1055,8 @@ static int bis_sync_req_cb(struct bt_conn *conn, /* The stream stopped callback will be called as part of this, * and we do not need to wait for any events from the - * controller. Thus, when this returns, the `sem_bis_synced` - * is back to 0. + * controller. Thus, when this returns, the `big_synced` + * is back to false. */ err = bt_bap_broadcast_sink_stop(broadcast_sink); if (err != 0) { @@ -1005,6 +1064,8 @@ static int bis_sync_req_cb(struct bt_conn *conn, return err; } + + k_sem_give(&sem_broadcast_sink_stopped); } requested_bis_sync = bis_sync_req[0]; @@ -1017,6 +1078,7 @@ static int bis_sync_req_cb(struct bt_conn *conn, } static struct bt_bap_scan_delegator_cb scan_delegator_cbs = { + .recv_state_updated = recv_state_updated_cb, .pa_sync_req = pa_sync_req_cb, .pa_sync_term_req = pa_sync_term_req_cb, .broadcast_code = broadcast_code_cb, @@ -1099,21 +1161,19 @@ static bool scan_check_and_sync_broadcast(struct bt_data *data, void *user_data) printk("Found broadcaster with ID 0x%06X and addr %s and sid 0x%02X\n", broadcast_id, le_addr, info->sid); - if (broadcast_assistant_conn == NULL) { - /* Not requested by Broadcast Assistant */ - k_sem_give(&sem_broadcaster_found); - } else if (req_recv_state != NULL && - bt_addr_le_eq(info->addr, &req_recv_state->addr) && - info->sid == req_recv_state->adv_sid && - broadcast_id == req_recv_state->broadcast_id) { + if (broadcast_assistant_conn == NULL /* Not requested by Broadcast Assistant */ || + (req_recv_state != NULL && bt_addr_le_eq(info->addr, &req_recv_state->addr) && + info->sid == req_recv_state->adv_sid && + broadcast_id == req_recv_state->broadcast_id)) { + + /* Store info for PA sync parameters */ + memcpy(&broadcaster_info, info, sizeof(broadcaster_info)); + bt_addr_le_copy(&broadcaster_addr, info->addr); + broadcaster_broadcast_id = broadcast_id; + printk("broadcaster_broadcast_id = 0x%06X\n", broadcaster_broadcast_id); k_sem_give(&sem_broadcaster_found); } - /* Store info for PA sync parameters */ - memcpy(&broadcaster_info, info, sizeof(broadcaster_info)); - bt_addr_le_copy(&broadcaster_addr, info->addr); - broadcaster_broadcast_id = broadcast_id; - /* Stop parsing */ return false; } @@ -1160,11 +1220,11 @@ static void broadcast_scan_recv(const struct bt_le_scan_recv_info *info, struct if (info->interval != 0U) { /* call to bt_data_parse consumes netbufs so shallow clone for verbose output */ - /* If req_recv_state is NULL then we have been requested by a broadcast assistant to - * sync to a specific broadcast source. In that case we do not apply our own - * broadcast name filter. + /* If req_recv_state is not NULL then we have been requested by a broadcast + * assistant to sync to a specific broadcast source. In that case we do not apply + * our own broadcast name filter. */ - if (req_recv_state != NULL && strlen(CONFIG_TARGET_BROADCAST_NAME) > 0U) { + if (req_recv_state == NULL && strlen(CONFIG_TARGET_BROADCAST_NAME) > 0U) { struct net_buf_simple buf_copy; char name[NAME_LEN] = {0}; @@ -1271,9 +1331,14 @@ static int reset(void) { int err; + printk("Reset\n"); + bis_index_bitfield = 0U; requested_bis_sync = 0U; req_recv_state = NULL; + big_synced = false; + base_received = false; + stream_count = 0U; (void)memset(sink_broadcast_code, 0, sizeof(sink_broadcast_code)); (void)memset(&broadcaster_info, 0, sizeof(broadcaster_info)); (void)memset(&broadcaster_addr, 0, sizeof(broadcaster_addr)); @@ -1301,35 +1366,6 @@ static int reset(void) pa_sync = NULL; } - if (IS_ENABLED(CONFIG_SCAN_OFFLOAD)) { - if (broadcast_assistant_conn != NULL) { - err = bt_conn_disconnect(broadcast_assistant_conn, - BT_HCI_ERR_REMOTE_USER_TERM_CONN); - if (err) { - printk("Disconnecting Broadcast Assistant failed (err %d)\n", - err); - - return err; - } - - err = k_sem_take(&sem_disconnected, SEM_TIMEOUT); - if (err != 0) { - printk("Failed to take sem_disconnected: %d\n", err); - - return err; - } - } - - if (ext_adv != NULL) { - stop_adv(); - } - - k_sem_reset(&sem_connected); - k_sem_reset(&sem_disconnected); - k_sem_reset(&sem_pa_request); - k_sem_reset(&sem_past_request); - } - k_sem_reset(&sem_broadcaster_found); k_sem_reset(&sem_pa_synced); k_sem_reset(&sem_base_received); @@ -1337,7 +1373,10 @@ static int reset(void) k_sem_reset(&sem_pa_sync_lost); k_sem_reset(&sem_broadcast_code_received); k_sem_reset(&sem_bis_sync_requested); - k_sem_reset(&sem_bis_synced); + k_sem_reset(&sem_stream_connected); + k_sem_reset(&sem_stream_started); + k_sem_reset(&sem_broadcast_sink_stopped); + return 0; } @@ -1443,28 +1482,46 @@ int main(void) } if (IS_ENABLED(CONFIG_SCAN_OFFLOAD)) { - printk("Starting advertising\n"); - err = start_adv(); - if (err != 0) { - printk("Unable to start advertising connectable: %d\n", - err); + if (broadcast_assistant_conn == NULL) { + k_sem_reset(&sem_connected); - return 0; - } - - printk("Waiting for Broadcast Assistant\n"); - err = k_sem_take(&sem_connected, ADV_TIMEOUT); - if (err != 0) { - printk("No Broadcast Assistant connected\n"); + printk("Starting advertising\n"); + /* Stop advertising before starting if needed */ + if (ext_adv != NULL) { + err = stop_adv(); + if (err != 0) { + printk("Unable to stop advertising: %d\n", err); - err = stop_adv(); + return 0; + } + } + err = start_adv(); if (err != 0) { - printk("Unable to stop advertising: %d\n", + printk("Unable to start advertising connectable: %d\n", err); return 0; } - } else { + + printk("Waiting for Broadcast Assistant\n"); + err = k_sem_take(&sem_connected, ADV_TIMEOUT); + if (err != 0) { + printk("No Broadcast Assistant connected\n"); + + err = stop_adv(); + if (err != 0) { + printk("Unable to stop advertising: %d\n", err); + + return 0; + } + } + } + + if (broadcast_assistant_conn != NULL) { + k_sem_reset(&sem_pa_request); + k_sem_reset(&sem_past_request); + k_sem_reset(&sem_disconnected); + /* Wait for the PA request to determine if we * should start scanning, or wait for PAST */ @@ -1483,8 +1540,8 @@ int main(void) } if (strlen(CONFIG_TARGET_BROADCAST_NAME) > 0U) { - printk("Scanning for broadcast sources containing`" - CONFIG_TARGET_BROADCAST_NAME "`\n"); + printk("Scanning for broadcast sources containing " + "`" CONFIG_TARGET_BROADCAST_NAME "`\n"); } else { printk("Scanning for broadcast sources\n"); } @@ -1496,12 +1553,12 @@ int main(void) return 0; } + printk("Waiting for Broadcaster\n"); err = k_sem_take(&sem_broadcaster_found, SEM_TIMEOUT); if (err != 0) { printk("sem_broadcaster_found timed out, resetting\n"); continue; } - printk("Broadcast source found, waiting for PA sync\n"); err = bt_le_scan_stop(); if (err != 0) { @@ -1539,8 +1596,8 @@ int main(void) printk("sem_base_received timed out, resetting\n"); continue; } - printk("BASE received, waiting for syncable\n"); + printk("BASE received, waiting for syncable\n"); err = k_sem_take(&sem_syncable, SEM_TIMEOUT); if (err != 0) { printk("sem_syncable timed out, resetting\n"); @@ -1565,7 +1622,17 @@ int main(void) } sync_bitfield = bis_index_bitfield & requested_bis_sync; - printk("Syncing to broadcast with bitfield: 0x%08x\n", sync_bitfield); + stream_count = 0; + for (int i = 1; i < BT_ISO_MAX_GROUP_ISO_COUNT; i++) { + if ((sync_bitfield & BIT(i)) != 0) { + stream_count++; + } + } + + printk("Syncing to broadcast with bitfield: 0x%08x = 0x%08x (bis_index) & 0x%08x " + "(req_bis_sync), stream_count = %u\n", + sync_bitfield, bis_index_bitfield, requested_bis_sync, stream_count); + err = bt_bap_broadcast_sink_sync(broadcast_sink, sync_bitfield, streams_p, sink_broadcast_code); if (err != 0) { @@ -1573,15 +1640,23 @@ int main(void) return 0; } - printk("Waiting for BIG sync\n"); - err = k_sem_take(&sem_bis_synced, SEM_TIMEOUT); + printk("Waiting for stream(s) started\n"); + err = k_sem_take(&sem_big_synced, SEM_TIMEOUT); if (err != 0) { - printk("sem_bis_synced timed out, resetting\n"); + printk("sem_big_synced timed out, resetting\n"); continue; } printk("Waiting for PA disconnected\n"); k_sem_take(&sem_pa_sync_lost, K_FOREVER); + + printk("Wainting for sink to stop\n"); + err = k_sem_take(&sem_broadcast_sink_stopped, SEM_TIMEOUT); + if (err != 0) { + printk("sem_broadcast_sink_stopped timed out, resetting\n"); + continue; + } } + return 0; }