Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
181 changes: 100 additions & 81 deletions samples/bluetooth/bap_broadcast_assistant/src/main.c
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
/*
* Copyright (c) 2024 Demant A/S
* Copyright (c) 2024 Nordic Semiconductor ASA
* Copyright (c) 2024-2025 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/

#include <errno.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <string.h>
Expand All @@ -24,15 +25,18 @@
#include <zephyr/bluetooth/uuid.h>
#include <zephyr/kernel.h>
#include <zephyr/net_buf.h>
#include <zephyr/sys/__assert.h>
#include <zephyr/sys/byteorder.h>
#include <zephyr/sys/printk.h>
#include <zephyr/sys/util.h>
#include <zephyr/types.h>

#define NAME_LEN 30
#define PA_SYNC_SKIP 5
#define NAME_LEN 30
#define PA_SYNC_SKIP 5
#define PA_SYNC_INTERVAL_TO_TIMEOUT_RATIO 20 /* Set the timeout relative to interval */
/* Broadcast IDs are 24bit, so this is out of valid range */
/* Default semaphore timeout when waiting for an action */
#define SEM_TIMEOUT K_SECONDS(10)

static void scan_for_broadcast_sink(void);

Expand All @@ -55,8 +59,7 @@ static bt_addr_le_t selected_addr;
static struct bt_le_per_adv_sync *pa_sync;
static uint8_t received_base[UINT8_MAX];
static size_t received_base_size;
static struct bt_bap_bass_subgroup
bass_subgroups[CONFIG_BT_BAP_BASS_MAX_SUBGROUPS];
static struct bt_bap_bass_subgroup bass_subgroups[CONFIG_BT_BAP_BASS_MAX_SUBGROUPS];

static bool scanning_for_broadcast_source;

Expand All @@ -68,6 +71,7 @@ static K_SEM_DEFINE(sem_sink_disconnected, 0, 1);
static K_SEM_DEFINE(sem_security_updated, 0, 1);
static K_SEM_DEFINE(sem_bass_discovered, 0, 1);
static K_SEM_DEFINE(sem_pa_synced, 0, 1);
static K_SEM_DEFINE(sem_pa_sync_terminted, 0, 1);
static K_SEM_DEFINE(sem_received_base_subgroups, 0, 1);

static bool device_found(struct bt_data *data, void *user_data)
Expand Down Expand Up @@ -147,6 +151,7 @@ static bool base_store(struct bt_data *data, void *user_data)
const struct bt_bap_base *base = bt_bap_base_get_base_from_ad(data);
int base_size;
int base_subgroup_count;
int err;

/* Base is NULL if the data does not contain a valid BASE */
if (base == NULL) {
Expand All @@ -168,22 +173,27 @@ static bool base_store(struct bt_data *data, void *user_data)
}

/* Compare BASE and copy if different */
k_mutex_lock(&base_store_mutex, K_FOREVER);
err = k_mutex_lock(&base_store_mutex, K_MSEC(100));
if (err != 0) {
/* Could not get BASE mutex, wait for new to avoid blocking */
return false;
}

if ((size_t)base_size != received_base_size ||
memcmp(base, received_base, (size_t)base_size) != 0) {
(void)memcpy(received_base, base, base_size);
received_base_size = (size_t)base_size;
}
k_mutex_unlock(&base_store_mutex);
err = k_mutex_unlock(&base_store_mutex);
__ASSERT_NO_MSG(err == 0);

/* Stop parsing */
k_sem_give(&sem_received_base_subgroups);
return false;
}

static void pa_recv(struct bt_le_per_adv_sync *sync,
const struct bt_le_per_adv_sync_recv_info *info,
struct net_buf_simple *buf)
const struct bt_le_per_adv_sync_recv_info *info, struct net_buf_simple *buf)
{
bt_data_parse(buf, base_store, NULL);
}
Expand Down Expand Up @@ -288,8 +298,7 @@ static int pa_sync_create(void)
return bt_le_per_adv_sync_create(&create_params, &pa_sync);
}

static void scan_recv_cb(const struct bt_le_scan_recv_info *info,
struct net_buf_simple *ad)
static void scan_recv_cb(const struct bt_le_scan_recv_info *info, struct net_buf_simple *ad)
{
int err;
struct scan_recv_info sr_info = {0};
Expand All @@ -300,8 +309,7 @@ static void scan_recv_cb(const struct bt_le_scan_recv_info *info,
sr_info.broadcast_id = BT_BAP_INVALID_BROADCAST_ID;

/* We are only interested in non-connectable periodic advertisers */
if ((info->adv_props & BT_GAP_ADV_PROP_CONNECTABLE) != 0 ||
info->interval == 0) {
if ((info->adv_props & BT_GAP_ADV_PROP_CONNECTABLE) != 0 || info->interval == 0) {
return;
}

Expand Down Expand Up @@ -419,9 +427,7 @@ static void scan_for_broadcast_source(void)
printk("Scanning for Broadcast Source successfully started\n");

err = k_sem_take(&sem_source_discovered, K_FOREVER);
if (err != 0) {
printk("Failed to take sem_source_discovered (err %d)\n", err);
}
__ASSERT_NO_MSG(err == 0);
}

static void scan_for_broadcast_sink(void)
Expand All @@ -439,9 +445,7 @@ static void scan_for_broadcast_sink(void)
printk("Scanning for Broadcast Sink successfully started\n");

err = k_sem_take(&sem_sink_discovered, K_FOREVER);
if (err != 0) {
printk("Failed to take sem_sink_discovered (err %d)\n", err);
}
__ASSERT_NO_MSG(err == 0);
}

static void connected(struct bt_conn *conn, uint8_t err)
Expand Down Expand Up @@ -486,8 +490,7 @@ static void disconnected(struct bt_conn *conn, uint8_t reason)
k_sem_give(&sem_sink_disconnected);
}

static void security_changed_cb(struct bt_conn *conn, bt_security_t level,
enum bt_security_err err)
static void security_changed_cb(struct bt_conn *conn, bt_security_t level, enum bt_security_err err)
{
if (err == 0) {
printk("Security level changed: %u\n", level);
Expand All @@ -501,8 +504,7 @@ static void bap_broadcast_assistant_discover_cb(struct bt_conn *conn, int err,
uint8_t recv_state_count)
{
if (err == 0) {
printk("BASS discover done with %u recv states\n",
recv_state_count);
printk("BASS discover done with %u recv states\n", recv_state_count);
k_sem_give(&sem_bass_discovered);
} else {
printk("BASS discover failed (%d)\n", err);
Expand All @@ -528,6 +530,16 @@ static void pa_sync_synced_cb(struct bt_le_per_adv_sync *sync,
k_sem_give(&sem_pa_synced);
}
}
static void pa_sync_term_cb(struct bt_le_per_adv_sync *sync,
const struct bt_le_per_adv_sync_term_info *info)
{
if (sync == pa_sync) {
printk("PA sync %p terminated with reason %u\n", sync, info->reason);

k_sem_give(&sem_pa_sync_terminted);
pa_sync = NULL;
}
}

static struct bt_bap_broadcast_assistant_cb ba_cbs = {
.discover = bap_broadcast_assistant_discover_cb,
Expand All @@ -536,14 +548,49 @@ static struct bt_bap_broadcast_assistant_cb ba_cbs = {

static struct bt_le_per_adv_sync_cb pa_synced_cb = {
.synced = pa_sync_synced_cb,
.term = pa_sync_term_cb,
.recv = pa_recv,
};

static void reset(void)
{
printk("\n\nReset...\n\n");
int err;

printk("\n\nResetting...\n\n");

if (broadcast_sink_conn != NULL) {
err = bt_conn_disconnect(broadcast_sink_conn, BT_HCI_ERR_LOCALHOST_TERM_CONN);

if (err != 0) {
printk("bt_conn_disconnect failed with %d\n", err);
} else {
if (k_sem_take(&sem_sink_disconnected, SEM_TIMEOUT) != 0) {
/* This should not happen */

__ASSERT_NO_MSG(false);
}
}
__ASSERT_NO_MSG(err == 0);
}

/* Ignore return value as scanning may already be stopped */
(void)bt_le_scan_stop();

if (pa_sync != NULL) {
err = bt_le_per_adv_sync_delete(pa_sync);

if (err != 0) {
printk("bt_le_per_adv_sync_delete failed with %d\n", err);
} else {
if (k_sem_take(&sem_pa_sync_terminted, SEM_TIMEOUT) != 0) {
/* This should not happen */

__ASSERT_NO_MSG(false);
}
}
__ASSERT_NO_MSG(err == 0);
}

broadcast_sink_conn = NULL;
selected_broadcast_id = BT_BAP_INVALID_BROADCAST_ID;
selected_sid = 0;
selected_pa_interval = 0;
Expand All @@ -559,16 +606,13 @@ static void reset(void)
k_sem_reset(&sem_received_base_subgroups);
}

BT_CONN_CB_DEFINE(conn_callbacks) = {
.connected = connected,
.disconnected = disconnected,
.security_changed = security_changed_cb
};
BT_CONN_CB_DEFINE(conn_callbacks) = {.connected = connected,
.disconnected = disconnected,
.security_changed = security_changed_cb};

int main(void)
{
int err;
struct bt_bap_broadcast_assistant_add_src_param param = { 0 };

err = bt_enable(NULL);
if (err) {
Expand All @@ -585,45 +629,33 @@ int main(void)
k_mutex_init(&base_store_mutex);

while (true) {
struct bt_bap_broadcast_assistant_add_src_param param = {0};

reset();

scan_for_broadcast_sink();

err = k_sem_take(&sem_sink_connected, K_FOREVER);
err = k_sem_take(&sem_sink_connected, SEM_TIMEOUT);
if (err != 0) {
printk("Failed to take sem_sink_connected (err %d)\n", err);
continue;
}

err = bt_bap_broadcast_assistant_discover(broadcast_sink_conn);
if (err != 0) {
printk("Failed to discover BASS on the sink (err %d)\n", err);
continue;
}

err = k_sem_take(&sem_security_updated, K_SECONDS(10));
err = k_sem_take(&sem_security_updated, SEM_TIMEOUT);
if (err != 0) {
printk("Failed to take sem_security_updated (err %d), resetting\n", err);
bt_conn_disconnect(broadcast_sink_conn, BT_HCI_ERR_AUTH_FAIL);

if (k_sem_take(&sem_sink_disconnected, K_SECONDS(10)) != 0) {
/* This should not happen */
return -ETIMEDOUT;
}

reset();
printk("Failed to take sem_security_updated (err %d)\n", err);
continue;
}

err = k_sem_take(&sem_bass_discovered, K_SECONDS(10));
err = k_sem_take(&sem_bass_discovered, SEM_TIMEOUT);
if (err != 0) {
if (err == -EAGAIN) {
printk("Failed to take sem_bass_discovered (err %d)\n", err);
}
bt_conn_disconnect(broadcast_sink_conn, BT_HCI_ERR_UNSUPP_REMOTE_FEATURE);

if (k_sem_take(&sem_sink_disconnected, K_SECONDS(10)) != 0) {
/* This should not happen */
return -ETIMEDOUT;
}

reset();
printk("Failed to take sem_bass_discovered (err %d)\n", err);
continue;
}

Expand All @@ -636,19 +668,19 @@ int main(void)

scan_for_broadcast_source();

printk("Scan stopped, attempting to PA sync to the broadcaster with id 0x%06X\n",
printk("Attempting to PA sync to the broadcaster with id 0x%06X\n",
selected_broadcast_id);
err = pa_sync_create();
if (err != 0) {
printk("Could not create Broadcast PA sync: %d, resetting\n", err);
return -ETIMEDOUT;
printk("Could not create Broadcast PA sync: %d\n", err);
continue;
}

printk("Waiting for PA synced\n");
err = k_sem_take(&sem_pa_synced, K_FOREVER);
err = k_sem_take(&sem_pa_synced, SEM_TIMEOUT);
if (err != 0) {
printk("sem_pa_synced timed out, resetting\n");
return err;
printk("Failed to take sem_pa_synced (err %d)\n", err);
continue;
}

memset(bass_subgroups, 0, sizeof(bass_subgroups));
Expand All @@ -661,42 +693,29 @@ int main(void)

/* Wait to receive subgroups */
err = k_sem_take(&sem_received_base_subgroups, K_FOREVER);
if (err != 0) {
printk("Failed to take sem_received_base_subgroups (err %d)\n", err);
return err;
}
__ASSERT_NO_MSG(err == 0);

k_mutex_lock(&base_store_mutex, K_FOREVER);
err = k_mutex_lock(&base_store_mutex, K_FOREVER);
__ASSERT_NO_MSG(err == 0);
err = bt_bap_base_foreach_subgroup((const struct bt_bap_base *)received_base,
add_pa_sync_base_subgroup_cb, &param);
k_mutex_unlock(&base_store_mutex);
err = k_mutex_unlock(&base_store_mutex);
__ASSERT_NO_MSG(err == 0);

if (err < 0) {
if (err != 0) {
printk("Could not add BASE to params %d\n", err);
continue;
}

err = bt_bap_broadcast_assistant_add_src(broadcast_sink_conn, &param);
if (err) {
if (err != 0) {
printk("Failed to add source: %d\n", err);
bt_conn_disconnect(broadcast_sink_conn, BT_HCI_ERR_UNSUPP_REMOTE_FEATURE);

if (k_sem_take(&sem_sink_disconnected, K_SECONDS(10)) != 0) {
/* This should not happen */
return -ETIMEDOUT;
}

reset();
continue;
}

/* Reset if the sink disconnects */
err = k_sem_take(&sem_sink_disconnected, K_FOREVER);
if (err != 0) {
printk("Failed to take sem_sink_disconnected (err %d)\n", err);
}

reset();
__ASSERT_NO_MSG(err == 0);
}

return 0;
Expand Down
Loading