blob: 1d8613386fbe9882fb14a79e7091177f620480ae [file] [log] [blame]
/*
* Copyright (c) 2022-2023 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/bluetooth/bluetooth.h>
#include <zephyr/bluetooth/audio/audio.h>
#include <zephyr/bluetooth/audio/bap.h>
#include <zephyr/bluetooth/audio/pacs.h>
#include <zephyr/sys/byteorder.h>
BUILD_ASSERT(IS_ENABLED(CONFIG_SCAN_SELF) || IS_ENABLED(CONFIG_SCAN_OFFLOAD),
"Either SCAN_SELF or SCAN_OFFLOAD must be enabled");
#define SEM_TIMEOUT K_SECONDS(10)
#define BROADCAST_ASSISTANT_TIMEOUT K_SECONDS(120) /* 2 minutes */
#if defined(CONFIG_SCAN_SELF)
#define ADV_TIMEOUT K_SECONDS(CONFIG_SCAN_DELAY)
#else /* !CONFIG_SCAN_SELF */
#define ADV_TIMEOUT K_FOREVER
#endif /* CONFIG_SCAN_SELF */
#define INVALID_BROADCAST_ID (BT_AUDIO_BROADCAST_ID_MAX + 1)
#define SYNC_RETRY_COUNT 6 /* similar to retries for connections */
#define PA_SYNC_SKIP 5
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);
static K_SEM_DEFINE(sem_pa_synced, 0U, 1U);
static K_SEM_DEFINE(sem_base_received, 0U, 1U);
static K_SEM_DEFINE(sem_syncable, 0U, 1U);
static K_SEM_DEFINE(sem_pa_sync_lost, 0U, 1U);
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);
/* Sample assumes that we only have a single Scan Delegator receive state */
static const struct bt_bap_scan_delegator_recv_state *req_recv_state;
static struct bt_bap_broadcast_sink *broadcast_sink;
static struct bt_le_scan_recv_info broadcaster_info;
static bt_addr_le_t broadcaster_addr;
static struct bt_le_per_adv_sync *pa_sync;
static uint32_t broadcaster_broadcast_id;
static struct bt_bap_stream streams[CONFIG_BT_BAP_BROADCAST_SNK_STREAM_COUNT];
static struct bt_bap_stream *streams_p[ARRAY_SIZE(streams)];
static struct bt_conn *broadcast_assistant_conn;
static struct bt_le_ext_adv *ext_adv;
static const struct bt_audio_codec_cap codec_cap = BT_AUDIO_CODEC_CAP_LC3(
BT_AUDIO_CODEC_LC3_FREQ_16KHZ | BT_AUDIO_CODEC_LC3_FREQ_24KHZ,
BT_AUDIO_CODEC_LC3_DURATION_10, BT_AUDIO_CODEC_LC3_CHAN_COUNT_SUPPORT(1), 40u, 60u, 1u,
(BT_AUDIO_CONTEXT_TYPE_CONVERSATIONAL | BT_AUDIO_CONTEXT_TYPE_MEDIA));
/* 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
* 0.
*/
static const uint32_t bis_index_mask = BIT_MASK(ARRAY_SIZE(streams) + 1U);
static uint32_t requested_bis_sync;
static uint32_t bis_index_bitfield;
static uint8_t sink_broadcast_code[BT_AUDIO_BROADCAST_CODE_SIZE];
static void stream_started_cb(struct bt_bap_stream *stream)
{
printk("Stream %p started\n", stream);
k_sem_give(&sem_bis_synced);
}
static void stream_stopped_cb(struct bt_bap_stream *stream, uint8_t reason)
{
int err;
printk("Stream %p stopped with reason 0x%02X\n", stream, reason);
err = k_sem_take(&sem_bis_synced, K_NO_WAIT);
if (err != 0) {
printk("Failed to take sem_bis_synced: %d\n", err);
}
}
static void stream_recv_cb(struct bt_bap_stream *stream,
const struct bt_iso_recv_info *info,
struct net_buf *buf)
{
static uint32_t recv_cnt;
if (info->flags & BT_ISO_FLAGS_ERROR) {
printk("ISO receive error\n");
return;
}
if (info->flags & BT_ISO_FLAGS_LOST) {
printk("ISO receive lost\n");
return;
}
recv_cnt++;
if ((recv_cnt % 1000U) == 0U) {
printk("Received %u total ISO packets\n", recv_cnt);
}
}
static struct bt_bap_stream_ops stream_ops = {
.started = stream_started_cb,
.stopped = stream_stopped_cb,
.recv = stream_recv_cb
};
static void base_recv_cb(struct bt_bap_broadcast_sink *sink, const struct bt_bap_base *base)
{
uint32_t base_bis_index_bitfield = 0U;
if (k_sem_count_get(&sem_base_received) != 0U) {
return;
}
printk("Received BASE with %u subgroups from broadcast sink %p\n",
base->subgroup_count, sink);
for (size_t i = 0U; i < base->subgroup_count; i++) {
for (size_t j = 0U; j < base->subgroups[i].bis_count; j++) {
const uint8_t index = base->subgroups[i].bis_data[j].index;
base_bis_index_bitfield |= BIT(index);
}
}
bis_index_bitfield = base_bis_index_bitfield & bis_index_mask;
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);
}
k_sem_give(&sem_base_received);
}
static void syncable_cb(struct bt_bap_broadcast_sink *sink, bool encrypted)
{
k_sem_give(&sem_syncable);
if (!encrypted) {
/* Use the semaphore as a boolean */
k_sem_reset(&sem_broadcast_code_received);
k_sem_give(&sem_broadcast_code_received);
}
}
static struct bt_bap_broadcast_sink_cb broadcast_sink_cbs = {
.base_recv = base_recv_cb,
.syncable = syncable_cb,
};
static void pa_timer_handler(struct k_work *work)
{
if (req_recv_state != NULL) {
enum bt_bap_pa_state pa_state;
if (req_recv_state->pa_sync_state == BT_BAP_PA_STATE_INFO_REQ) {
pa_state = BT_BAP_PA_STATE_NO_PAST;
} else {
pa_state = BT_BAP_PA_STATE_FAILED;
}
bt_bap_scan_delegator_set_pa_state(req_recv_state->src_id,
pa_state);
}
printk("PA timeout\n");
}
static K_WORK_DELAYABLE_DEFINE(pa_timer, pa_timer_handler);
static uint16_t interval_to_sync_timeout(uint16_t pa_interval)
{
uint16_t pa_timeout;
if (pa_interval == BT_BAP_PA_INTERVAL_UNKNOWN) {
/* Use maximum value to maximize chance of success */
pa_timeout = BT_GAP_PER_ADV_MAX_TIMEOUT;
} else {
/* Ensure that the following calculation does not overflow silently */
__ASSERT(SYNC_RETRY_COUNT < 10,
"SYNC_RETRY_COUNT shall be less than 10");
/* Add retries and convert to unit in 10's of ms */
pa_timeout = ((uint32_t)pa_interval * SYNC_RETRY_COUNT) / 10;
/* Enforce restraints */
pa_timeout = CLAMP(pa_timeout, BT_GAP_PER_ADV_MIN_TIMEOUT,
BT_GAP_PER_ADV_MAX_TIMEOUT);
}
return pa_timeout;
}
static int pa_sync_past(struct bt_conn *conn, uint16_t pa_interval)
{
struct bt_le_per_adv_sync_transfer_param param = { 0 };
int err;
param.skip = PA_SYNC_SKIP;
param.timeout = interval_to_sync_timeout(pa_interval);
err = bt_le_per_adv_sync_transfer_subscribe(conn, &param);
if (err != 0) {
printk("Could not do PAST subscribe: %d\n", err);
} else {
printk("Syncing with PAST: %d\n", err);
(void)k_work_reschedule(&pa_timer, K_MSEC(param.timeout * 10));
}
return err;
}
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)
{
int err;
req_recv_state = recv_state;
if (recv_state->pa_sync_state == BT_BAP_PA_STATE_SYNCED ||
recv_state->pa_sync_state == BT_BAP_PA_STATE_INFO_REQ) {
/* Already syncing */
/* TODO: Terminate existing sync and then sync to new?*/
return -1;
}
if (IS_ENABLED(CONFIG_BT_PER_ADV_SYNC_TRANSFER_RECEIVER) && past_avail) {
err = pa_sync_past(conn, pa_interval);
k_sem_give(&sem_past_request);
} else {
/* start scan */
err = 0;
}
k_sem_give(&sem_pa_request);
return err;
}
static int pa_sync_term_req_cb(struct bt_conn *conn,
const struct bt_bap_scan_delegator_recv_state *recv_state)
{
int err;
req_recv_state = recv_state;
err = bt_bap_broadcast_sink_delete(broadcast_sink);
if (err != 0) {
return err;
}
broadcast_sink = NULL;
return 0;
}
static void broadcast_code_cb(struct bt_conn *conn,
const struct bt_bap_scan_delegator_recv_state *recv_state,
const uint8_t broadcast_code[BT_AUDIO_BROADCAST_CODE_SIZE])
{
printk("Broadcast code received for %p\n", recv_state);
req_recv_state = recv_state;
(void)memcpy(sink_broadcast_code, broadcast_code, BT_AUDIO_BROADCAST_CODE_SIZE);
/* Use the semaphore as a boolean */
k_sem_reset(&sem_broadcast_code_received);
k_sem_give(&sem_broadcast_code_received);
}
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[BT_BAP_SCAN_DELEGATOR_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]);
/* We only care about a single subgroup in this sample */
if (bis_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.
*/
int err;
/* 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.
*/
err = bt_bap_broadcast_sink_stop(broadcast_sink);
if (err != 0) {
printk("Failed to stop Broadcast Sink: %d\n", err);
return err;
}
}
requested_bis_sync = bis_sync_req[0];
broadcaster_broadcast_id = recv_state->broadcast_id;
if (bis_sync_req[0] != 0) {
k_sem_give(&sem_bis_sync_requested);
}
return 0;
}
static struct bt_bap_scan_delegator_cb scan_delegator_cbs = {
.pa_sync_req = pa_sync_req_cb,
.pa_sync_term_req = pa_sync_term_req_cb,
.broadcast_code = broadcast_code_cb,
.bis_sync_req = bis_sync_req_cb,
};
static void connected(struct bt_conn *conn, uint8_t err)
{
char addr[BT_ADDR_LE_STR_LEN];
bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
if (err != 0U) {
printk("Failed to connect to %s (%u)\n", addr, err);
broadcast_assistant_conn = NULL;
return;
}
printk("Connected: %s\n", addr);
broadcast_assistant_conn = bt_conn_ref(conn);
k_sem_give(&sem_connected);
}
static void disconnected(struct bt_conn *conn, uint8_t reason)
{
char addr[BT_ADDR_LE_STR_LEN];
if (conn != broadcast_assistant_conn) {
return;
}
bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
printk("Disconnected: %s (reason 0x%02x)\n", addr, reason);
bt_conn_unref(broadcast_assistant_conn);
broadcast_assistant_conn = NULL;
k_sem_give(&sem_disconnected);
}
BT_CONN_CB_DEFINE(conn_callbacks) = {
.connected = connected,
.disconnected = disconnected,
};
static struct bt_pacs_cap cap = {
.codec_cap = &codec_cap,
};
static bool scan_check_and_sync_broadcast(struct bt_data *data, void *user_data)
{
const struct bt_le_scan_recv_info *info = user_data;
char le_addr[BT_ADDR_LE_STR_LEN];
struct bt_uuid_16 adv_uuid;
uint32_t broadcast_id;
if (data->type != BT_DATA_SVC_DATA16) {
return true;
}
if (data->data_len < BT_UUID_SIZE_16 + BT_AUDIO_BROADCAST_ID_SIZE) {
return true;
}
if (!bt_uuid_create(&adv_uuid.uuid, data->data, BT_UUID_SIZE_16)) {
return true;
}
if (bt_uuid_cmp(&adv_uuid.uuid, BT_UUID_BROADCAST_AUDIO)) {
return true;
}
broadcast_id = sys_get_le24(data->data + BT_UUID_SIZE_16);
bt_addr_le_to_str(info->addr, le_addr, sizeof(le_addr));
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) {
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;
}
static void broadcast_scan_recv(const struct bt_le_scan_recv_info *info, struct net_buf_simple *ad)
{
if (info->interval != 0U) {
bt_data_parse(ad, scan_check_and_sync_broadcast, (void *)info);
}
}
static struct bt_le_scan_cb bap_scan_cb = {
.recv = broadcast_scan_recv,
};
static void bap_pa_sync_synced_cb(struct bt_le_per_adv_sync *sync,
struct bt_le_per_adv_sync_synced_info *info)
{
if (sync == pa_sync) {
printk("PA sync %p synced for broadcast sink with broadcast ID 0x%06X\n", sync,
broadcaster_broadcast_id);
k_sem_give(&sem_pa_synced);
}
}
static void bap_pa_sync_terminated_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 lost with reason %u\n", sync, info->reason);
pa_sync = NULL;
k_sem_give(&sem_pa_sync_lost);
}
}
static struct bt_le_per_adv_sync_cb bap_pa_sync_cb = {
.synced = bap_pa_sync_synced_cb,
.term = bap_pa_sync_terminated_cb,
};
static int init(void)
{
int err;
err = bt_enable(NULL);
if (err) {
printk("Bluetooth enable failed (err %d)\n", err);
return err;
}
printk("Bluetooth initialized\n");
err = bt_pacs_cap_register(BT_AUDIO_DIR_SINK, &cap);
if (err) {
printk("Capability register failed (err %d)\n", err);
return err;
}
bt_bap_broadcast_sink_register_cb(&broadcast_sink_cbs);
bt_bap_scan_delegator_register_cb(&scan_delegator_cbs);
bt_le_per_adv_sync_cb_register(&bap_pa_sync_cb);
bt_le_scan_cb_register(&bap_scan_cb);
for (size_t i = 0U; i < ARRAY_SIZE(streams); i++) {
streams[i].ops = &stream_ops;
}
return 0;
}
static int reset(void)
{
int err;
bis_index_bitfield = 0U;
requested_bis_sync = 0U;
req_recv_state = NULL;
(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));
broadcaster_broadcast_id = INVALID_BROADCAST_ID;
if (broadcast_sink != NULL) {
err = bt_bap_broadcast_sink_delete(broadcast_sink);
if (err) {
printk("Deleting broadcast sink failed (err %d)\n", err);
return err;
}
broadcast_sink = NULL;
}
if (pa_sync != NULL) {
bt_le_per_adv_sync_delete(pa_sync);
if (err) {
printk("Deleting PA sync failed (err %d)\n", err);
return err;
}
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;
}
} else if (ext_adv != NULL) { /* advertising still running */
err = bt_le_ext_adv_stop(ext_adv);
if (err) {
printk("Stopping advertising set failed (err %d)\n",
err);
return err;
}
err = bt_le_ext_adv_delete(ext_adv);
if (err) {
printk("Deleting advertising set failed (err %d)\n",
err);
return err;
}
ext_adv = NULL;
}
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);
k_sem_reset(&sem_syncable);
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);
return 0;
}
static int start_adv(void)
{
const struct bt_data ad[] = {
BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)),
BT_DATA_BYTES(BT_DATA_UUID16_ALL,
BT_UUID_16_ENCODE(BT_UUID_BASS_VAL),
BT_UUID_16_ENCODE(BT_UUID_PACS_VAL)),
};
int err;
/* Create a non-connectable non-scannable advertising set */
err = bt_le_ext_adv_create(BT_LE_EXT_ADV_CONN_NAME, NULL, &ext_adv);
if (err != 0) {
printk("Failed to create advertising set (err %d)\n", err);
return err;
}
err = bt_le_ext_adv_set_data(ext_adv, ad, ARRAY_SIZE(ad), NULL, 0);
if (err != 0) {
printk("Failed to set advertising data (err %d)\n", err);
return err;
}
err = bt_le_ext_adv_start(ext_adv, BT_LE_EXT_ADV_START_DEFAULT);
if (err != 0) {
printk("Failed to start advertising set (err %d)\n", err);
return err;
}
return 0;
}
static int stop_adv(void)
{
int err;
err = bt_le_ext_adv_stop(ext_adv);
if (err != 0) {
printk("Failed to stop advertising set (err %d)\n", err);
return err;
}
err = bt_le_ext_adv_delete(ext_adv);
if (err != 0) {
printk("Failed to delete advertising set (err %d)\n", err);
return err;
}
ext_adv = NULL;
return 0;
}
static int pa_sync_create(void)
{
struct bt_le_per_adv_sync_param create_params = {0};
bt_addr_le_copy(&create_params.addr, &broadcaster_addr);
create_params.options = BT_LE_PER_ADV_SYNC_OPT_FILTER_DUPLICATE;
create_params.sid = broadcaster_info.sid;
create_params.skip = PA_SYNC_SKIP;
create_params.timeout = interval_to_sync_timeout(broadcaster_info.interval);
return bt_le_per_adv_sync_create(&create_params, &pa_sync);
}
int main(void)
{
int err;
err = init();
if (err) {
printk("Init failed (err %d)\n", err);
return 0;
}
for (size_t i = 0U; i < ARRAY_SIZE(streams_p); i++) {
streams_p[i] = &streams[i];
}
while (true) {
err = reset();
if (err != 0) {
printk("Resetting failed: %d - Aborting\n", err);
return 0;
}
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);
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");
err = stop_adv();
if (err != 0) {
printk("Unable to stop advertising: %d\n",
err);
return 0;
}
} else {
/* Wait for the PA request to determine if we
* should start scanning, or wait for PAST
*/
err = k_sem_take(&sem_pa_request,
BROADCAST_ASSISTANT_TIMEOUT);
if (err != 0) {
printk("sem_pa_request timed out, resetting\n");
continue;
}
if (k_sem_take(&sem_past_request, K_NO_WAIT) == 0) {
goto wait_for_pa_sync;
} /* else continue with scanning below */
}
}
printk("Scanning for broadcast sources\n");
err = bt_le_scan_start(BT_LE_SCAN_ACTIVE, NULL);
if (err != 0 && err != -EALREADY) {
printk("Unable to start scan for broadcast sources: %d\n",
err);
return 0;
}
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) {
printk("bt_le_scan_stop failed with %d, resetting\n", err);
continue;
}
printk("Attempting to PA sync to the broadcaster with id 0x%06X\n",
broadcaster_broadcast_id);
err = pa_sync_create();
if (err != 0) {
printk("Could not create Broadcast PA sync: %d, resetting\n", err);
continue;
}
wait_for_pa_sync:
printk("Waiting for PA synced\n");
err = k_sem_take(&sem_pa_synced, SEM_TIMEOUT);
if (err != 0) {
printk("sem_pa_synced timed out, resetting\n");
continue;
}
printk("Broadcast source PA synced, creating Broadcast Sink\n");
err = bt_bap_broadcast_sink_create(pa_sync, broadcaster_broadcast_id,
&broadcast_sink);
if (err != 0) {
printk("Failed to create broadcast sink: %d\n", err);
continue;
}
printk("Broadcast Sink created, waiting for BASE\n");
err = k_sem_take(&sem_base_received, SEM_TIMEOUT);
if (err != 0) {
printk("sem_base_received timed out, resetting\n");
continue;
}
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");
continue;
}
/* sem_broadcast_code_received is also given if the
* broadcast is not encrypted
*/
printk("Waiting for broadcast code OK\n");
err = k_sem_take(&sem_broadcast_code_received, SEM_TIMEOUT);
if (err != 0) {
printk("sem_syncable timed out, resetting\n");
continue;
}
printk("Waiting for BIS sync request\n");
err = k_sem_take(&sem_bis_sync_requested, SEM_TIMEOUT);
if (err != 0) {
printk("sem_syncable timed out, resetting\n");
continue;
}
printk("Syncing to broadcast\n");
err = bt_bap_broadcast_sink_sync(broadcast_sink,
bis_index_bitfield & requested_bis_sync,
streams_p, sink_broadcast_code);
if (err != 0) {
printk("Unable to sync to broadcast source: %d\n", err);
return 0;
}
printk("Waiting for BIG sync\n");
err = k_sem_take(&sem_bis_synced, SEM_TIMEOUT);
if (err != 0) {
printk("sem_bis_synced timed out, resetting\n");
continue;
}
printk("Waiting for PA disconnected\n");
k_sem_take(&sem_pa_sync_lost, K_FOREVER);
}
return 0;
}