samples: Add Broadcast Assistant sample
First version of Broadcast Assistant sample with hard coded selection of sink and source. Signed-off-by: Lars Knudsen <LAKD@demant.com>
This commit is contained in:
parent
9cd0ad0ae4
commit
b77df76e98
11
samples/bluetooth/broadcast_audio_assistant/CMakeLists.txt
Normal file
11
samples/bluetooth/broadcast_audio_assistant/CMakeLists.txt
Normal file
|
@ -0,0 +1,11 @@
|
|||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
cmake_minimum_required(VERSION 3.20.0)
|
||||
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
|
||||
project(broadcast_audio_assistant)
|
||||
|
||||
target_sources(app PRIVATE
|
||||
src/main.c
|
||||
)
|
||||
|
||||
zephyr_library_include_directories(${ZEPHYR_BASE}/samples/bluetooth)
|
24
samples/bluetooth/broadcast_audio_assistant/Kconfig
Normal file
24
samples/bluetooth/broadcast_audio_assistant/Kconfig
Normal file
|
@ -0,0 +1,24 @@
|
|||
# Copyright (c) 2024 Demant A/S
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
mainmenu "Bluetooth: Broadcast Audio Assistant"
|
||||
|
||||
config SELECT_SOURCE_NAME
|
||||
string "Selected Broadcast Source name"
|
||||
default ""
|
||||
help
|
||||
Name of broadcast source device to select. This will be used as a
|
||||
substring matched against both BT device name and broadcast name.
|
||||
If empty, the first broadcast source found will be chosen.
|
||||
Matching is not case sensitive.
|
||||
|
||||
config SELECT_SINK_NAME
|
||||
string "Selected Broadcast Sink name"
|
||||
default ""
|
||||
help
|
||||
Name of broadcast sink device to select. This will be used as a
|
||||
substring matched against the BT device name.
|
||||
If empty, the first broadcast sink found will be chosen.
|
||||
Matching is not case sensitive.
|
||||
|
||||
source "Kconfig.zephyr"
|
56
samples/bluetooth/broadcast_audio_assistant/README.rst
Normal file
56
samples/bluetooth/broadcast_audio_assistant/README.rst
Normal file
|
@ -0,0 +1,56 @@
|
|||
.. zephyr:code-sample:: bluetooth_broadcast_audio_assistant
|
||||
:name: Bluetooth: Broadcast Audio Assistant
|
||||
:relevant-api: bt_bap
|
||||
|
||||
Use LE Audio Broadcast Assistant functionality
|
||||
|
||||
Overview
|
||||
********
|
||||
|
||||
Application demonstrating the LE Audio broadcast assistant functionality.
|
||||
|
||||
The sample will automatically try to connect to a device in the BAP Scan Delegator
|
||||
role (advertising support for the Broadcast Audio Scan Service (BASS)).
|
||||
It will then search for a broadcast source and (if found) add the broadcast ID to
|
||||
the BAP Scan Delegator.
|
||||
|
||||
Practical use of this sample requires a sink (e.g. the Broadcast Audio Sink sample or
|
||||
a set of LE Audio Broadcast capable earbuds) and a source (e.g. the Broadcast Audio
|
||||
Source sample).
|
||||
|
||||
This sample can be found under
|
||||
:zephyr_file:`samples/bluetooth/broadcast_audio_assistant` in the Zephyr tree.
|
||||
|
||||
Check the :ref:`bluetooth samples section <bluetooth-samples>` for general information.
|
||||
|
||||
Requirements
|
||||
************
|
||||
|
||||
* BlueZ running on the host, or
|
||||
* A board with Bluetooth Low Energy 5.2 support
|
||||
* Broadcast Audio Source and Sink devices
|
||||
|
||||
Building and Running
|
||||
********************
|
||||
|
||||
The application will act as a broadcast assistant with optionally preconfigured
|
||||
filtering of broadcast sink and broadcast source names. By default, the application will
|
||||
search for and connect to the first broadcast audio sink found (advertising PACS and
|
||||
BASS UUIDs) and then search for and select the first broadcast audio source found
|
||||
(advertising a broadcast ID).
|
||||
|
||||
Filter these by modifying the following configs:
|
||||
|
||||
``CONFIG_SELECT_SINK_NAME``: Substring of BT name of the sink.
|
||||
|
||||
and
|
||||
|
||||
``CONFIG_SELECT_SOURCE_NAME``: Substring of BT name or broadcast name of the source.
|
||||
|
||||
Building for an nrf52840dk
|
||||
--------------------------
|
||||
|
||||
.. zephyr-app-commands::
|
||||
:zephyr-app: samples/bluetooth/broadcast_audio_assistant/
|
||||
:board: nrf52840dk_nrf52840
|
||||
:goals: build
|
18
samples/bluetooth/broadcast_audio_assistant/prj.conf
Normal file
18
samples/bluetooth/broadcast_audio_assistant/prj.conf
Normal file
|
@ -0,0 +1,18 @@
|
|||
CONFIG_BT=y
|
||||
CONFIG_LOG=y
|
||||
CONFIG_BT_CENTRAL=y
|
||||
CONFIG_BT_AUDIO=y
|
||||
CONFIG_BT_SMP=y
|
||||
CONFIG_BT_BONDABLE=n
|
||||
CONFIG_BT_BUF_ACL_RX_SIZE=255
|
||||
CONFIG_BT_BUF_ACL_TX_SIZE=251
|
||||
CONFIG_BT_CTLR_SCAN_DATA_LEN_MAX=191
|
||||
|
||||
CONFIG_BT_TINYCRYPT_ECC=y
|
||||
|
||||
CONFIG_BT_EXT_ADV=y
|
||||
CONFIG_BT_BAP_BROADCAST_ASSISTANT=y
|
||||
|
||||
# CONFIG_BT_BAP_SCAN_DELEGATOR=y is required until the following
|
||||
# bug is fixed: https://github.com/zephyrproject-rtos/zephyr/issues/68338
|
||||
CONFIG_BT_BAP_SCAN_DELEGATOR=y
|
11
samples/bluetooth/broadcast_audio_assistant/sample.yaml
Normal file
11
samples/bluetooth/broadcast_audio_assistant/sample.yaml
Normal file
|
@ -0,0 +1,11 @@
|
|||
sample:
|
||||
description: Bluetooth Low Energy Broadcast Assistant sample
|
||||
name: Bluetooth Low Energy Broadcast Assistant sample
|
||||
tests:
|
||||
sample.bluetooth.broadcast_audio_assistant:
|
||||
harness: bluetooth
|
||||
platform_allow:
|
||||
- nrf52840dk_nrf52840
|
||||
integration_platforms:
|
||||
- nrf52840dk_nrf52840
|
||||
tags: bluetooth
|
520
samples/bluetooth/broadcast_audio_assistant/src/main.c
Normal file
520
samples/bluetooth/broadcast_audio_assistant/src/main.c
Normal file
|
@ -0,0 +1,520 @@
|
|||
/*
|
||||
* Copyright (c) 2024 Demant A/S
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <zephyr/types.h>
|
||||
#include <stddef.h>
|
||||
#include <strings.h>
|
||||
#include <errno.h>
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/sys/printk.h>
|
||||
|
||||
#include <zephyr/bluetooth/bluetooth.h>
|
||||
#include <zephyr/bluetooth/audio/audio.h>
|
||||
#include <zephyr/bluetooth/audio/bap.h>
|
||||
#include <zephyr/sys/byteorder.h>
|
||||
|
||||
#define NAME_LEN 30
|
||||
|
||||
/* Broadcast IDs are 24bit, so this is out of valid range */
|
||||
#define INVALID_BROADCAST_ID 0xFFFFFFFFU
|
||||
|
||||
static void scan_for_broadcast_sink(void);
|
||||
|
||||
/* Struct to collect information from scanning
|
||||
* for Broadcast Source or Sink
|
||||
*/
|
||||
struct scan_recv_info {
|
||||
char bt_name[NAME_LEN];
|
||||
char broadcast_name[NAME_LEN];
|
||||
uint32_t broadcast_id;
|
||||
bool has_bass;
|
||||
bool has_pacs;
|
||||
};
|
||||
|
||||
static struct bt_conn *broadcast_sink_conn;
|
||||
static uint32_t selected_broadcast_id;
|
||||
static uint8_t selected_sid;
|
||||
static uint16_t selected_pa_interval;
|
||||
static bt_addr_le_t selected_addr;
|
||||
|
||||
static bool scanning_for_broadcast_source;
|
||||
|
||||
static K_SEM_DEFINE(sem_source_discovered, 0, 1);
|
||||
static K_SEM_DEFINE(sem_sink_discovered, 0, 1);
|
||||
static K_SEM_DEFINE(sem_sink_connected, 0, 1);
|
||||
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 bool device_found(struct bt_data *data, void *user_data)
|
||||
{
|
||||
struct scan_recv_info *sr_info = (struct scan_recv_info *)user_data;
|
||||
struct bt_uuid_16 adv_uuid;
|
||||
|
||||
switch (data->type) {
|
||||
case BT_DATA_NAME_SHORTENED:
|
||||
case BT_DATA_NAME_COMPLETE:
|
||||
memcpy(sr_info->bt_name, data->data, MIN(data->data_len, NAME_LEN - 1));
|
||||
return true;
|
||||
case BT_DATA_BROADCAST_NAME:
|
||||
memcpy(sr_info->broadcast_name, data->data, MIN(data->data_len, NAME_LEN - 1));
|
||||
return true;
|
||||
case BT_DATA_SVC_DATA16:
|
||||
/* Check for Broadcast ID */
|
||||
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) != 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
sr_info->broadcast_id = sys_get_le24(data->data + BT_UUID_SIZE_16);
|
||||
return true;
|
||||
case BT_DATA_UUID16_SOME:
|
||||
case BT_DATA_UUID16_ALL:
|
||||
/* NOTE: According to the BAP 1.0.1 Spec,
|
||||
* Section 3.9.2. Additional Broadcast Audio Scan Service requirements,
|
||||
* If the Scan Delegator implements a Broadcast Sink, it should also
|
||||
* advertise a Service Data field containing the Broadcast Audio
|
||||
* Scan Service (BASS) UUID.
|
||||
*
|
||||
* However, it seems that this is not the case with the sinks available
|
||||
* while developing this sample application. Therefore, we instead,
|
||||
* search for the existence of BASS and PACS in the list of service UUIDs,
|
||||
* which does seem to exist in the sinks available.
|
||||
*/
|
||||
|
||||
/* Check for BASS and PACS */
|
||||
if (data->data_len % sizeof(uint16_t) != 0U) {
|
||||
printk("UUID16 AD malformed\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < data->data_len; i += sizeof(uint16_t)) {
|
||||
const struct bt_uuid *uuid;
|
||||
uint16_t u16;
|
||||
|
||||
memcpy(&u16, &data->data[i], sizeof(u16));
|
||||
uuid = BT_UUID_DECLARE_16(sys_le16_to_cpu(u16));
|
||||
|
||||
if (bt_uuid_cmp(uuid, BT_UUID_BASS)) {
|
||||
sr_info->has_bass = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (bt_uuid_cmp(uuid, BT_UUID_PACS)) {
|
||||
sr_info->has_pacs = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
static bool is_substring(const char *substr, const char *str)
|
||||
{
|
||||
const size_t str_len = strlen(str);
|
||||
const size_t sub_str_len = strlen(substr);
|
||||
|
||||
if (sub_str_len > str_len) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t pos = 0; pos < str_len; pos++) {
|
||||
if (pos + sub_str_len > str_len) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (strncasecmp(substr, &str[pos], sub_str_len) == 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
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};
|
||||
|
||||
if (scanning_for_broadcast_source) {
|
||||
/* Scan for and select Broadcast Source */
|
||||
|
||||
sr_info.broadcast_id = 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) {
|
||||
return;
|
||||
}
|
||||
|
||||
bt_data_parse(ad, device_found, (void *)&sr_info);
|
||||
|
||||
if (sr_info.broadcast_id != INVALID_BROADCAST_ID) {
|
||||
printk("Broadcast Source Found:\n");
|
||||
printk(" BT Name: %s\n", sr_info.bt_name);
|
||||
printk(" Broadcast Name: %s\n", sr_info.broadcast_name);
|
||||
printk(" Broadcast ID: 0x%06x\n\n", sr_info.broadcast_id);
|
||||
|
||||
if (strlen(CONFIG_SELECT_SOURCE_NAME) > 0U) {
|
||||
/* Compare names with CONFIG_SELECT_SOURCE_NAME */
|
||||
if (is_substring(CONFIG_SELECT_SOURCE_NAME, sr_info.bt_name) ||
|
||||
is_substring(CONFIG_SELECT_SOURCE_NAME,
|
||||
sr_info.broadcast_name)) {
|
||||
printk("Match found for '%s'\n", CONFIG_SELECT_SOURCE_NAME);
|
||||
} else {
|
||||
printk("'%s' not found in names\n\n",
|
||||
CONFIG_SELECT_SOURCE_NAME);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
err = bt_le_scan_stop();
|
||||
if (err != 0) {
|
||||
printk("bt_le_scan_stop failed with %d\n", err);
|
||||
}
|
||||
|
||||
/* TODO: Add support for syncing to the PA and parsing the BASE
|
||||
* in order to obtain the right subgroup information to send to
|
||||
* the sink when adding a broadcast source (see in main function below).
|
||||
*/
|
||||
|
||||
printk("Selecting Broadcast ID: 0x%06x\n", sr_info.broadcast_id);
|
||||
|
||||
selected_broadcast_id = sr_info.broadcast_id;
|
||||
selected_sid = info->sid;
|
||||
selected_pa_interval = info->interval;
|
||||
bt_addr_le_copy(&selected_addr, info->addr);
|
||||
|
||||
k_sem_give(&sem_source_discovered);
|
||||
}
|
||||
} else {
|
||||
/* Scan for and connect to Broadcast Sink */
|
||||
|
||||
/* We are only interested in connectable advertisers */
|
||||
if ((info->adv_props & BT_GAP_ADV_PROP_CONNECTABLE) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
bt_data_parse(ad, device_found, (void *)&sr_info);
|
||||
|
||||
if (sr_info.has_bass && sr_info.has_pacs) {
|
||||
printk("Broadcast Sink Found:\n");
|
||||
printk(" BT Name: %s\n", sr_info.bt_name);
|
||||
|
||||
if (strlen(CONFIG_SELECT_SINK_NAME) > 0U) {
|
||||
/* Compare names with CONFIG_SELECT_SINK_NAME */
|
||||
if (is_substring(CONFIG_SELECT_SINK_NAME, sr_info.bt_name)) {
|
||||
printk("Match found for '%s'\n", CONFIG_SELECT_SINK_NAME);
|
||||
} else {
|
||||
printk("'%s' not found in names\n\n",
|
||||
CONFIG_SELECT_SINK_NAME);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
err = bt_le_scan_stop();
|
||||
if (err != 0) {
|
||||
printk("bt_le_scan_stop failed with %d\n", err);
|
||||
}
|
||||
|
||||
printk("Connecting to Broadcast Sink: %s\n", sr_info.bt_name);
|
||||
|
||||
err = bt_conn_le_create(info->addr, BT_CONN_LE_CREATE_CONN,
|
||||
BT_LE_CONN_PARAM_DEFAULT,
|
||||
&broadcast_sink_conn);
|
||||
if (err != 0) {
|
||||
printk("Failed creating connection (err=%u)\n", err);
|
||||
scan_for_broadcast_sink();
|
||||
}
|
||||
|
||||
k_sem_give(&sem_sink_discovered);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void scan_timeout_cb(void)
|
||||
{
|
||||
printk("Scan timeout\n");
|
||||
}
|
||||
|
||||
static struct bt_le_scan_cb scan_callbacks = {
|
||||
.recv = scan_recv_cb,
|
||||
.timeout = scan_timeout_cb,
|
||||
};
|
||||
|
||||
static void scan_for_broadcast_source(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
scanning_for_broadcast_source = true;
|
||||
|
||||
err = bt_le_scan_start(BT_LE_SCAN_PASSIVE, NULL);
|
||||
if (err) {
|
||||
printk("Scanning failed to start (err %d)\n", err);
|
||||
return;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
static void scan_for_broadcast_sink(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
scanning_for_broadcast_source = false;
|
||||
|
||||
err = bt_le_scan_start(BT_LE_SCAN_PASSIVE, NULL);
|
||||
if (err) {
|
||||
printk("Scanning failed to start (err %d)\n", err);
|
||||
return;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
static void connected(struct bt_conn *conn, uint8_t err)
|
||||
{
|
||||
char addr[BT_ADDR_LE_STR_LEN];
|
||||
|
||||
(void)bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
|
||||
|
||||
if (err != 0) {
|
||||
printk("Failed to connect to %s (%u)\n", addr, err);
|
||||
|
||||
bt_conn_unref(broadcast_sink_conn);
|
||||
broadcast_sink_conn = NULL;
|
||||
|
||||
scan_for_broadcast_sink();
|
||||
return;
|
||||
}
|
||||
|
||||
if (conn != broadcast_sink_conn) {
|
||||
return;
|
||||
}
|
||||
|
||||
printk("Connected: %s\n", addr);
|
||||
k_sem_give(&sem_sink_connected);
|
||||
}
|
||||
|
||||
static void disconnected(struct bt_conn *conn, uint8_t reason)
|
||||
{
|
||||
char addr[BT_ADDR_LE_STR_LEN];
|
||||
|
||||
if (conn != broadcast_sink_conn) {
|
||||
return;
|
||||
}
|
||||
|
||||
(void)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_sink_conn);
|
||||
broadcast_sink_conn = NULL;
|
||||
|
||||
k_sem_give(&sem_sink_disconnected);
|
||||
}
|
||||
|
||||
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);
|
||||
k_sem_give(&sem_security_updated);
|
||||
} else {
|
||||
printk("Failed to set security level: %u\n", err);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
k_sem_give(&sem_bass_discovered);
|
||||
} else {
|
||||
printk("BASS discover failed (%d)\n", err);
|
||||
}
|
||||
}
|
||||
|
||||
static void bap_broadcast_assistant_add_src_cb(struct bt_conn *conn, int err)
|
||||
{
|
||||
if (err == 0) {
|
||||
printk("BASS add source successful\n");
|
||||
} else {
|
||||
printk("BASS add source failed (%d)\n", err);
|
||||
}
|
||||
}
|
||||
|
||||
static struct bt_bap_broadcast_assistant_cb ba_cbs = {
|
||||
.discover = bap_broadcast_assistant_discover_cb,
|
||||
.add_src = bap_broadcast_assistant_add_src_cb,
|
||||
};
|
||||
|
||||
static void reset(void)
|
||||
{
|
||||
printk("\n\nReset...\n\n");
|
||||
|
||||
broadcast_sink_conn = NULL;
|
||||
selected_broadcast_id = INVALID_BROADCAST_ID;
|
||||
selected_sid = 0;
|
||||
selected_pa_interval = 0;
|
||||
(void)memset(&selected_addr, 0, sizeof(selected_addr));
|
||||
|
||||
k_sem_reset(&sem_source_discovered);
|
||||
k_sem_reset(&sem_sink_discovered);
|
||||
k_sem_reset(&sem_sink_connected);
|
||||
k_sem_reset(&sem_sink_disconnected);
|
||||
k_sem_reset(&sem_security_updated);
|
||||
k_sem_reset(&sem_bass_discovered);
|
||||
}
|
||||
|
||||
BT_CONN_CB_DEFINE(conn_callbacks) = {
|
||||
.connected = connected,
|
||||
.disconnected = disconnected,
|
||||
.security_changed = security_changed_cb
|
||||
};
|
||||
|
||||
int main(void)
|
||||
{
|
||||
int err;
|
||||
struct bt_bap_scan_delegator_subgroup subgroup = { 0 };
|
||||
struct bt_bap_broadcast_assistant_add_src_param param = { 0 };
|
||||
|
||||
err = bt_enable(NULL);
|
||||
if (err) {
|
||||
printk("Bluetooth init failed (err %d)\n", err);
|
||||
return 0;
|
||||
}
|
||||
|
||||
printk("Bluetooth initialized\n");
|
||||
|
||||
bt_le_scan_cb_register(&scan_callbacks);
|
||||
bt_bap_broadcast_assistant_register_cb(&ba_cbs);
|
||||
|
||||
while (true) {
|
||||
scan_for_broadcast_sink();
|
||||
|
||||
err = k_sem_take(&sem_sink_connected, K_FOREVER);
|
||||
if (err != 0) {
|
||||
printk("Failed to take sem_sink_connected (err %d)\n", err);
|
||||
}
|
||||
|
||||
err = bt_bap_broadcast_assistant_discover(broadcast_sink_conn);
|
||||
if (err != 0) {
|
||||
printk("Failed to discover BASS on the sink (err %d)\n", err);
|
||||
}
|
||||
|
||||
err = k_sem_take(&sem_security_updated, K_SECONDS(10));
|
||||
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();
|
||||
continue;
|
||||
}
|
||||
|
||||
err = k_sem_take(&sem_bass_discovered, K_SECONDS(10));
|
||||
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();
|
||||
continue;
|
||||
}
|
||||
|
||||
/* TODO: Discover and parse the PACS on the sink and use the information
|
||||
* when discovering and adding a source to the sink.
|
||||
* Also, before populating the parameters to sync to the broadcast source
|
||||
* first, parse the source BASE and determine if the sink supports the source.
|
||||
* If not, then look for another source.
|
||||
*/
|
||||
|
||||
scan_for_broadcast_source();
|
||||
|
||||
/* FIX NEEDED: It should be valid to assign BT_BAP_BIS_SYNC_NO_PREF
|
||||
* to bis_sync, but currently (2024-01-30), the broadcast_audio_sink
|
||||
* sample seems to reject it (err=19) while other sinks don't.
|
||||
*
|
||||
* Also, if the source contains more than one stream (e.g. stereo),
|
||||
* some sinks have been observed to have issues. In this case,
|
||||
* set only one bit in bis_sync, e.g. subgroup.bis_sync = BIT(1).
|
||||
*
|
||||
* When PA sync and BASE is parsed (see note in the scan_recv_cb function),
|
||||
* the available bits can be used for proper selection.
|
||||
*/
|
||||
subgroup.bis_sync = BT_BAP_BIS_SYNC_NO_PREF;
|
||||
|
||||
bt_addr_le_copy(¶m.addr, &selected_addr);
|
||||
param.adv_sid = selected_sid;
|
||||
param.pa_interval = selected_pa_interval;
|
||||
param.broadcast_id = selected_broadcast_id;
|
||||
param.pa_sync = true;
|
||||
|
||||
/* TODO: Obtain the and set the correct subgroup information.
|
||||
* See above in the broadcast audio source discovery part
|
||||
* of the scan_recv_cb function.
|
||||
*/
|
||||
param.num_subgroups = 1;
|
||||
param.subgroups = &subgroup;
|
||||
|
||||
err = bt_bap_broadcast_assistant_add_src(broadcast_sink_conn, ¶m);
|
||||
if (err) {
|
||||
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();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
Loading…
Reference in a new issue