Use BT_CONN_TX_USER_DATA_SIZE when defining pools of buffers that will go through `bt_conn_send_cb()`. Signed-off-by: Jonathan Rico <jonathan.rico@nordicsemi.no>
307 lines
8.3 KiB
C
307 lines
8.3 KiB
C
/*
|
|
* 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/bap_lc3_preset.h>
|
|
|
|
/* When BROADCAST_ENQUEUE_COUNT > 1 we can enqueue enough buffers to ensure that
|
|
* the controller is never idle
|
|
*/
|
|
#define BROADCAST_ENQUEUE_COUNT 2U
|
|
#define TOTAL_BUF_NEEDED (BROADCAST_ENQUEUE_COUNT * CONFIG_BT_BAP_BROADCAST_SRC_STREAM_COUNT)
|
|
|
|
BUILD_ASSERT(CONFIG_BT_ISO_TX_BUF_COUNT >= TOTAL_BUF_NEEDED,
|
|
"CONFIG_BT_ISO_TX_BUF_COUNT should be at least "
|
|
"BROADCAST_ENQUEUE_COUNT * CONFIG_BT_BAP_BROADCAST_SRC_STREAM_COUNT");
|
|
|
|
static struct bt_bap_lc3_preset preset_16_2_1 = BT_BAP_LC3_BROADCAST_PRESET_16_2_1(
|
|
BT_AUDIO_LOCATION_FRONT_LEFT, BT_AUDIO_CONTEXT_TYPE_UNSPECIFIED);
|
|
static struct bt_bap_stream streams[CONFIG_BT_BAP_BROADCAST_SRC_STREAM_COUNT];
|
|
static struct bt_bap_broadcast_source *broadcast_source;
|
|
|
|
NET_BUF_POOL_FIXED_DEFINE(tx_pool,
|
|
TOTAL_BUF_NEEDED,
|
|
BT_ISO_SDU_BUF_SIZE(CONFIG_BT_ISO_TX_MTU),
|
|
CONFIG_BT_CONN_TX_USER_DATA_SIZE, NULL);
|
|
static uint8_t mock_data[CONFIG_BT_ISO_TX_MTU];
|
|
static uint16_t seq_num;
|
|
static bool stopping;
|
|
|
|
static K_SEM_DEFINE(sem_started, 0U, ARRAY_SIZE(streams));
|
|
static K_SEM_DEFINE(sem_stopped, 0U, ARRAY_SIZE(streams));
|
|
|
|
#define BROADCAST_SOURCE_LIFETIME 120U /* seconds */
|
|
|
|
static void stream_started_cb(struct bt_bap_stream *stream)
|
|
{
|
|
k_sem_give(&sem_started);
|
|
}
|
|
|
|
static void stream_stopped_cb(struct bt_bap_stream *stream, uint8_t reason)
|
|
{
|
|
k_sem_give(&sem_stopped);
|
|
}
|
|
|
|
static void stream_sent_cb(struct bt_bap_stream *stream)
|
|
{
|
|
static uint32_t sent_cnt;
|
|
struct net_buf *buf;
|
|
int ret;
|
|
|
|
if (stopping) {
|
|
return;
|
|
}
|
|
|
|
buf = net_buf_alloc(&tx_pool, K_FOREVER);
|
|
if (buf == NULL) {
|
|
printk("Could not allocate buffer when sending on %p\n",
|
|
stream);
|
|
return;
|
|
}
|
|
|
|
net_buf_reserve(buf, BT_ISO_CHAN_SEND_RESERVE);
|
|
net_buf_add_mem(buf, mock_data, preset_16_2_1.qos.sdu);
|
|
ret = bt_bap_stream_send(stream, buf, seq_num++,
|
|
BT_ISO_TIMESTAMP_NONE);
|
|
if (ret < 0) {
|
|
/* This will end broadcasting on this stream. */
|
|
printk("Unable to broadcast data on %p: %d\n", stream, ret);
|
|
net_buf_unref(buf);
|
|
return;
|
|
}
|
|
|
|
sent_cnt++;
|
|
if ((sent_cnt % 1000U) == 0U) {
|
|
printk("Sent %u total ISO packets\n", sent_cnt);
|
|
}
|
|
}
|
|
|
|
static struct bt_bap_stream_ops stream_ops = {
|
|
.started = stream_started_cb,
|
|
.stopped = stream_stopped_cb,
|
|
.sent = stream_sent_cb
|
|
};
|
|
|
|
static int setup_broadcast_source(struct bt_bap_broadcast_source **source)
|
|
{
|
|
struct bt_bap_broadcast_source_stream_param
|
|
stream_params[CONFIG_BT_BAP_BROADCAST_SRC_STREAM_COUNT];
|
|
struct bt_bap_broadcast_source_subgroup_param
|
|
subgroup_param[CONFIG_BT_BAP_BROADCAST_SRC_SUBGROUP_COUNT];
|
|
struct bt_bap_broadcast_source_create_param create_param;
|
|
const size_t streams_per_subgroup = ARRAY_SIZE(stream_params) / ARRAY_SIZE(subgroup_param);
|
|
int err;
|
|
|
|
(void)memset(streams, 0, sizeof(streams));
|
|
|
|
for (size_t i = 0U; i < ARRAY_SIZE(subgroup_param); i++) {
|
|
subgroup_param[i].params_count = streams_per_subgroup;
|
|
subgroup_param[i].params = stream_params + i * streams_per_subgroup;
|
|
subgroup_param[i].codec = &preset_16_2_1.codec;
|
|
}
|
|
|
|
for (size_t j = 0U; j < ARRAY_SIZE(stream_params); j++) {
|
|
stream_params[j].stream = &streams[j];
|
|
stream_params[j].data = NULL;
|
|
stream_params[j].data_count = 0U;
|
|
bt_bap_stream_cb_register(stream_params[j].stream, &stream_ops);
|
|
}
|
|
|
|
create_param.params_count = ARRAY_SIZE(subgroup_param);
|
|
create_param.params = subgroup_param;
|
|
create_param.qos = &preset_16_2_1.qos;
|
|
create_param.encryption = false;
|
|
create_param.packing = BT_ISO_PACKING_SEQUENTIAL;
|
|
|
|
printk("Creating broadcast source with %zu subgroups with %zu streams\n",
|
|
ARRAY_SIZE(subgroup_param),
|
|
ARRAY_SIZE(subgroup_param) * streams_per_subgroup);
|
|
|
|
err = bt_bap_broadcast_source_create(&create_param, source);
|
|
if (err != 0) {
|
|
printk("Unable to create broadcast source: %d\n", err);
|
|
return err;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int main(void)
|
|
{
|
|
struct bt_le_ext_adv *adv;
|
|
int err;
|
|
|
|
err = bt_enable(NULL);
|
|
if (err) {
|
|
printk("Bluetooth init failed (err %d)\n", err);
|
|
return 0;
|
|
}
|
|
printk("Bluetooth initialized\n");
|
|
|
|
for (size_t i = 0U; i < ARRAY_SIZE(mock_data); i++) {
|
|
/* Initialize mock data */
|
|
mock_data[i] = i;
|
|
}
|
|
|
|
while (true) {
|
|
/* Broadcast Audio Streaming Endpoint advertising data */
|
|
NET_BUF_SIMPLE_DEFINE(ad_buf,
|
|
BT_UUID_SIZE_16 + BT_AUDIO_BROADCAST_ID_SIZE);
|
|
NET_BUF_SIMPLE_DEFINE(base_buf, 128);
|
|
struct bt_data ext_ad;
|
|
struct bt_data per_ad;
|
|
uint32_t broadcast_id;
|
|
|
|
/* Create a non-connectable non-scannable advertising set */
|
|
err = bt_le_ext_adv_create(BT_LE_EXT_ADV_NCONN_NAME, NULL, &adv);
|
|
if (err != 0) {
|
|
printk("Unable to create extended advertising set: %d\n",
|
|
err);
|
|
return 0;
|
|
}
|
|
|
|
/* Set periodic advertising parameters */
|
|
err = bt_le_per_adv_set_param(adv, BT_LE_PER_ADV_DEFAULT);
|
|
if (err) {
|
|
printk("Failed to set periodic advertising parameters"
|
|
" (err %d)\n", err);
|
|
return 0;
|
|
}
|
|
|
|
printk("Creating broadcast source\n");
|
|
err = setup_broadcast_source(&broadcast_source);
|
|
if (err != 0) {
|
|
printk("Unable to setup broadcast source: %d\n", err);
|
|
return 0;
|
|
}
|
|
|
|
err = bt_bap_broadcast_source_get_id(broadcast_source, &broadcast_id);
|
|
if (err != 0) {
|
|
printk("Unable to get broadcast ID: %d\n", err);
|
|
return 0;
|
|
}
|
|
|
|
/* Setup extended advertising data */
|
|
net_buf_simple_add_le16(&ad_buf, BT_UUID_BROADCAST_AUDIO_VAL);
|
|
net_buf_simple_add_le24(&ad_buf, broadcast_id);
|
|
ext_ad.type = BT_DATA_SVC_DATA16;
|
|
ext_ad.data_len = ad_buf.len;
|
|
ext_ad.data = ad_buf.data;
|
|
err = bt_le_ext_adv_set_data(adv, &ext_ad, 1, NULL, 0);
|
|
if (err != 0) {
|
|
printk("Failed to set extended advertising data: %d\n",
|
|
err);
|
|
return 0;
|
|
}
|
|
|
|
/* Setup periodic advertising data */
|
|
err = bt_bap_broadcast_source_get_base(broadcast_source, &base_buf);
|
|
if (err != 0) {
|
|
printk("Failed to get encoded BASE: %d\n", err);
|
|
return 0;
|
|
}
|
|
|
|
per_ad.type = BT_DATA_SVC_DATA16;
|
|
per_ad.data_len = base_buf.len;
|
|
per_ad.data = base_buf.data;
|
|
err = bt_le_per_adv_set_data(adv, &per_ad, 1);
|
|
if (err != 0) {
|
|
printk("Failed to set periodic advertising data: %d\n",
|
|
err);
|
|
return 0;
|
|
}
|
|
|
|
/* Start extended advertising */
|
|
err = bt_le_ext_adv_start(adv, BT_LE_EXT_ADV_START_DEFAULT);
|
|
if (err) {
|
|
printk("Failed to start extended advertising: %d\n",
|
|
err);
|
|
return 0;
|
|
}
|
|
|
|
/* Enable Periodic Advertising */
|
|
err = bt_le_per_adv_start(adv);
|
|
if (err) {
|
|
printk("Failed to enable periodic advertising: %d\n",
|
|
err);
|
|
return 0;
|
|
}
|
|
|
|
printk("Starting broadcast source\n");
|
|
stopping = false;
|
|
err = bt_bap_broadcast_source_start(broadcast_source, adv);
|
|
if (err != 0) {
|
|
printk("Unable to start broadcast source: %d\n", err);
|
|
return 0;
|
|
}
|
|
|
|
/* Wait for all to be started */
|
|
for (size_t i = 0U; i < ARRAY_SIZE(streams); i++) {
|
|
k_sem_take(&sem_started, K_FOREVER);
|
|
}
|
|
printk("Broadcast source started\n");
|
|
|
|
/* Initialize sending */
|
|
for (size_t i = 0U; i < ARRAY_SIZE(streams); i++) {
|
|
for (unsigned int j = 0U; j < BROADCAST_ENQUEUE_COUNT; j++) {
|
|
stream_sent_cb(&streams[i]);
|
|
}
|
|
}
|
|
|
|
printk("Waiting %u seconds before stopped\n",
|
|
BROADCAST_SOURCE_LIFETIME);
|
|
k_sleep(K_SECONDS(BROADCAST_SOURCE_LIFETIME));
|
|
|
|
printk("Stopping broadcast source\n");
|
|
stopping = true;
|
|
err = bt_bap_broadcast_source_stop(broadcast_source);
|
|
if (err != 0) {
|
|
printk("Unable to stop broadcast source: %d\n", err);
|
|
return 0;
|
|
}
|
|
|
|
/* Wait for all to be stopped */
|
|
for (size_t i = 0U; i < ARRAY_SIZE(streams); i++) {
|
|
k_sem_take(&sem_stopped, K_FOREVER);
|
|
}
|
|
printk("Broadcast source stopped\n");
|
|
|
|
printk("Deleting broadcast source\n");
|
|
err = bt_bap_broadcast_source_delete(broadcast_source);
|
|
if (err != 0) {
|
|
printk("Unable to delete broadcast source: %d\n", err);
|
|
return 0;
|
|
}
|
|
printk("Broadcast source deleted\n");
|
|
broadcast_source = NULL;
|
|
seq_num = 0;
|
|
|
|
err = bt_le_per_adv_stop(adv);
|
|
if (err) {
|
|
printk("Failed to stop periodic advertising (err %d)\n",
|
|
err);
|
|
return 0;
|
|
}
|
|
|
|
err = bt_le_ext_adv_stop(adv);
|
|
if (err) {
|
|
printk("Failed to stop extended advertising (err %d)\n",
|
|
err);
|
|
return 0;
|
|
}
|
|
|
|
err = bt_le_ext_adv_delete(adv);
|
|
if (err) {
|
|
printk("Failed to delete extended advertising (err %d)\n",
|
|
err);
|
|
return 0;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|