Bluetooth: Audio: Add BAP broadcast source support

Add the BAP broadcast source implementation. This role
allows a device to broadcast ISO data.

Signed-off-by: Emil Gydesen <emil.gydesen@nordicsemi.no>
This commit is contained in:
Emil Gydesen 2022-01-10 15:06:08 +01:00 committed by Johan Hedberg
commit ab87e0a2ba
7 changed files with 783 additions and 8 deletions

View file

@ -30,6 +30,8 @@
extern "C" { extern "C" {
#endif #endif
#define BT_AUDIO_BROADCAST_ID_SIZE 3 /* octets */
/* Audio Context Type, Generic Audio */ /* Audio Context Type, Generic Audio */
#define BT_AUDIO_CONTEXT_TYPE_PROHIBITED BIT(0) #define BT_AUDIO_CONTEXT_TYPE_PROHIBITED BIT(0)
#define BT_AUDIO_CONTEXT_TYPE_UNSPECIFIED BIT(1) #define BT_AUDIO_CONTEXT_TYPE_UNSPECIFIED BIT(1)

View file

@ -48,3 +48,4 @@ zephyr_library_sources_ifdef(CONFIG_BT_AUDIO_STREAM stream.c)
zephyr_library_sources_ifdef(CONFIG_BT_AUDIO_UNICAST_SERVER unicast_server.c) zephyr_library_sources_ifdef(CONFIG_BT_AUDIO_UNICAST_SERVER unicast_server.c)
zephyr_library_sources_ifdef(CONFIG_BT_AUDIO_CAPABILITIES capabilities.c) zephyr_library_sources_ifdef(CONFIG_BT_AUDIO_CAPABILITIES capabilities.c)
zephyr_library_sources_ifdef(CONFIG_BT_AUDIO_UNICAST_CLIENT unicast_client.c) zephyr_library_sources_ifdef(CONFIG_BT_AUDIO_UNICAST_CLIENT unicast_client.c)
zephyr_library_sources_ifdef(CONFIG_BT_AUDIO_BROADCAST_SOURCE broadcast_source.c)

View file

@ -120,13 +120,34 @@ config BT_AUDIO_UNICAST_CLIENT_ASE_SRC_COUNT
endif # BT_AUDIO_UNICAST_CLIENT endif # BT_AUDIO_UNICAST_CLIENT
endif # BT_AUDIO_UNICAST endif # BT_AUDIO_UNICAST
config BT_AUDIO_BROADCAST config BT_AUDIO_BROADCAST_SOURCE
bool "Bluetooth Broadcast Audio Support" bool "Bluetooth Broadcast Source Audio Support [EXPERIMENTAL]"
select EXPERIMENTAL
select BT_ISO_BROADCASTER select BT_ISO_BROADCASTER
select BT_ISO_SYNC_RECEIVER
help help
This option enables support for Bluetooth Broadcast Audio using This option enables support for Bluetooth Broadcast Source Audio using
Isochronous channels. Isochronous channels.
if BT_AUDIO_BROADCAST_SOURCE
config BT_AUDIO_BROADCAST_SRC_COUNT
int "Basic Audio Broadcaster source count"
default 1
range 0 BT_ISO_MAX_BIG
help
This option sets the number of broadcast sources to support.
One broadcast source can send multiple streams
(up to BT_AUDIO_BROADCAST_SRC_STREAM_COUNT per broadcast source).
config BT_AUDIO_BROADCAST_SRC_STREAM_COUNT
int "Basic Audio Broadcast Source Stream count"
default 1
range 0 BT_ISO_MAX_CHAN
help
This option sets the maximum number of streams per broadcast source
to support.
endif # BT_AUDIO_BROADCAST_SOURCE
config BT_AUDIO_DEBUG config BT_AUDIO_DEBUG
bool "Enable debug logs" bool "Enable debug logs"
@ -179,12 +200,19 @@ config BT_AUDIO_DEBUG_UNICAST_CLIENT
Use this option to enable Basic Audio Profile debug logs for the Use this option to enable Basic Audio Profile debug logs for the
Bluetooth Audio functionality. Bluetooth Audio functionality.
config BT_AUDIO_DEBUG_BROADCAST_SOURCE
bool "Bluetooth Audio Broadcast Source debug"
depends on BT_AUDIO_BROADCAST_SOURCE
help
Use this option to enable Bluetooth Audio Broadcast Source debug logs
for the Bluetooth Audio functionality.
endif # BT_AUDIO_DEBUG endif # BT_AUDIO_DEBUG
config BT_AUDIO_STREAM config BT_AUDIO_STREAM
# Virtual/hidden option # Virtual/hidden option
bool bool
default y if BT_ASCS || BT_AUDIO_UNICAST_CLIENT default y if BT_ASCS || BT_AUDIO_UNICAST_CLIENT || BT_AUDIO_BROADCAST_SOURCE
config BT_AUDIO_CAPABILITIES config BT_AUDIO_CAPABILITIES
# Virtual/hidden option # Virtual/hidden option

View file

@ -0,0 +1,739 @@
/* Bluetooth Audio Broadcast Source */
/*
* Copyright (c) 2021-2022 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr.h>
#include <sys/byteorder.h>
#include <sys/check.h>
#include <bluetooth/bluetooth.h>
#include <bluetooth/conn.h>
#include <bluetooth/gatt.h>
#include <bluetooth/audio/audio.h>
#define BT_DBG_ENABLED IS_ENABLED(CONFIG_BT_AUDIO_DEBUG_BROADCAST_SOURCE)
#define LOG_MODULE_NAME bt_audio_broadcast_source
#include "common/log.h"
#include "endpoint.h"
/* internal bt_audio_base_ad* structs are primarily designed to help
* calculate the maximum advertising data length
*/
struct bt_audio_base_ad_bis_specific_data {
uint8_t index;
uint8_t codec_config_len; /* currently unused and shall always be 0 */
} __packed;
struct bt_audio_base_ad_codec_data {
uint8_t type;
uint8_t data_len;
uint8_t data[CONFIG_BT_CODEC_MAX_DATA_LEN];
} __packed;
struct bt_audio_base_ad_codec_metadata {
uint8_t type;
uint8_t data_len;
uint8_t data[CONFIG_BT_CODEC_MAX_METADATA_LEN];
} __packed;
struct bt_audio_base_ad_subgroup {
uint8_t bis_cnt;
uint8_t codec_id;
uint16_t company_id;
uint16_t vendor_id;
uint8_t codec_config_len;
struct bt_audio_base_ad_codec_data codec_config[CONFIG_BT_CODEC_MAX_DATA_COUNT];
uint8_t metadata_len;
struct bt_audio_base_ad_codec_metadata metadata[CONFIG_BT_CODEC_MAX_METADATA_COUNT];
struct bt_audio_base_ad_bis_specific_data bis_data[BROADCAST_STREAM_CNT];
} __packed;
struct bt_audio_base_ad {
uint16_t uuid_val;
struct bt_audio_base_ad_subgroup subgroups[BROADCAST_SUBGROUP_CNT];
} __packed;
static struct bt_audio_ep broadcast_source_eps
[CONFIG_BT_AUDIO_BROADCAST_SRC_COUNT][BROADCAST_STREAM_CNT];
static struct bt_audio_broadcast_source broadcast_sources[CONFIG_BT_AUDIO_BROADCAST_SRC_COUNT];
static int bt_audio_set_base(const struct bt_audio_broadcast_source *source,
struct bt_codec *codec);
static void broadcast_source_set_ep_state(struct bt_audio_ep *ep, uint8_t state)
{
uint8_t old_state;
old_state = ep->status.state;
BT_DBG("ep %p id 0x%02x %s -> %s", ep, ep->status.id,
bt_audio_ep_state_str(old_state),
bt_audio_ep_state_str(state));
switch (old_state) {
case BT_AUDIO_EP_STATE_IDLE:
if (state != BT_AUDIO_EP_STATE_QOS_CONFIGURED) {
BT_DBG("Invalid broadcast sync endpoint state transition");
return;
}
break;
case BT_AUDIO_EP_STATE_QOS_CONFIGURED:
if (state != BT_AUDIO_EP_STATE_IDLE &&
state != BT_AUDIO_EP_STATE_STREAMING) {
BT_DBG("Invalid broadcast sync endpoint state transition");
return;
}
case BT_AUDIO_EP_STATE_STREAMING:
if (state != BT_AUDIO_EP_STATE_QOS_CONFIGURED) {
BT_DBG("Invalid broadcast sync endpoint state transition");
return;
}
break;
default:
BT_ERR("Invalid broadcast sync endpoint state: %s",
bt_audio_ep_state_str(old_state));
return;
}
ep->status.state = state;
if (state == BT_AUDIO_EP_STATE_IDLE) {
struct bt_audio_stream *stream = ep->stream;
if (stream != NULL) {
stream->ep = NULL;
stream->codec = NULL;
ep->stream = NULL;
}
}
}
static void broadcast_source_iso_recv(struct bt_iso_chan *chan,
const struct bt_iso_recv_info *info,
struct net_buf *buf)
{
struct bt_audio_ep *ep = CONTAINER_OF(chan, struct bt_audio_ep, iso);
struct bt_audio_stream_ops *ops = ep->stream->ops;
BT_DBG("stream %p ep %p len %zu", chan, ep, net_buf_frags_len(buf));
if (ops != NULL && ops->recv != NULL) {
ops->recv(ep->stream, buf);
}
}
static void broadcast_source_iso_connected(struct bt_iso_chan *chan)
{
struct bt_audio_ep *ep = CONTAINER_OF(chan, struct bt_audio_ep, iso);
struct bt_audio_stream_ops *ops = ep->stream->ops;
BT_DBG("stream %p ep %p", chan, ep);
broadcast_source_set_ep_state(ep, BT_AUDIO_EP_STATE_STREAMING);
if (ops != NULL && ops->connected != NULL) {
ops->connected(ep->stream);
}
}
static void broadcast_source_iso_disconnected(struct bt_iso_chan *chan, uint8_t reason)
{
struct bt_audio_ep *ep = CONTAINER_OF(chan, struct bt_audio_ep, iso);
struct bt_audio_stream *stream = ep->stream;
struct bt_audio_stream_ops *ops = stream->ops;
BT_DBG("stream %p ep %p reason 0x%02x", chan, ep, reason);
broadcast_source_set_ep_state(ep, BT_AUDIO_EP_STATE_IDLE);
if (ops != NULL && ops->disconnected != NULL) {
ops->disconnected(stream, reason);
}
}
static struct bt_iso_chan_ops broadcast_source_iso_ops = {
.recv = broadcast_source_iso_recv,
.connected = broadcast_source_iso_connected,
.disconnected = broadcast_source_iso_disconnected,
};
bool bt_audio_ep_is_broadcast_src(const struct bt_audio_ep *ep)
{
for (int i = 0; i < ARRAY_SIZE(broadcast_source_eps); i++) {
if (PART_OF_ARRAY(broadcast_source_eps[i], ep)) {
return true;
}
}
return false;
}
static void broadcast_source_ep_init(struct bt_audio_ep *ep)
{
BT_DBG("ep %p", ep);
(void)memset(ep, 0, sizeof(*ep));
ep->iso.ops = &broadcast_source_iso_ops;
ep->iso.qos = &ep->iso_qos;
ep->iso.qos->rx = &ep->iso_rx;
ep->iso.qos->tx = &ep->iso_tx;
}
static struct bt_audio_ep *broadcast_source_new_ep(uint8_t index)
{
struct bt_audio_ep *cache = NULL;
size_t size;
cache = broadcast_source_eps[index];
size = ARRAY_SIZE(broadcast_source_eps[index]);
for (size_t i = 0; i < ARRAY_SIZE(broadcast_source_eps[index]); i++) {
struct bt_audio_ep *ep = &cache[i];
/* If ep->stream is NULL the endpoint is unallocated */
if (ep->stream == NULL) {
/* Initialize - It is up to the caller to allocate the
* stream pointer.
*/
broadcast_source_ep_init(ep);
return ep;
}
}
return NULL;
}
static int bt_audio_broadcast_source_setup_stream(uint8_t index,
struct bt_audio_stream *stream,
struct bt_codec *codec,
struct bt_codec_qos *qos)
{
struct bt_audio_ep *ep;
int err;
if (stream->group != NULL) {
BT_DBG("Channel %p already in group %p", stream, stream->group);
return -EALREADY;
}
ep = broadcast_source_new_ep(index);
if (ep == NULL) {
BT_DBG("Could not allocate new broadcast endpoint");
return -ENOMEM;
}
bt_audio_stream_attach(NULL, stream, ep, codec);
stream->qos = qos;
err = bt_audio_codec_qos_to_iso_qos(stream->iso->qos, qos);
if (err) {
BT_ERR("Unable to convert codec QoS to ISO QoS");
return err;
}
return 0;
}
static void bt_audio_encode_base(const struct bt_audio_broadcast_source *source,
struct bt_codec *codec,
struct net_buf_simple *buf)
{
uint8_t bis_index;
uint8_t *start;
uint8_t len;
__ASSERT(source->subgroup_count == BROADCAST_SUBGROUP_CNT,
"Cannot encode BASE with more than a single subgroup");
net_buf_simple_add_le16(buf, BT_UUID_BASIC_AUDIO_VAL);
net_buf_simple_add_le24(buf, source->pd);
net_buf_simple_add_u8(buf, source->subgroup_count);
/* TODO: The following encoding should be done for each subgroup once
* supported
*/
net_buf_simple_add_u8(buf, source->stream_count);
net_buf_simple_add_u8(buf, codec->id);
net_buf_simple_add_le16(buf, codec->cid);
net_buf_simple_add_le16(buf, codec->vid);
/* Insert codec configuration data in LTV format */
start = net_buf_simple_add(buf, sizeof(len));
for (int i = 0; i < codec->data_count; i++) {
const struct bt_data *codec_data = &codec->data[i].data;
net_buf_simple_add_u8(buf, codec_data->data_len);
net_buf_simple_add_u8(buf, codec_data->type);
net_buf_simple_add_mem(buf, codec_data->data,
codec_data->data_len -
sizeof(codec_data->type));
}
/* Calcute length of codec config data */
len = net_buf_simple_tail(buf) - start - sizeof(len);
/* Update the length field */
*start = len;
/* Insert codec metadata in LTV format*/
start = net_buf_simple_add(buf, sizeof(len));
for (int i = 0; i < codec->meta_count; i++) {
const struct bt_data *metadata = &codec->meta[i].data;
net_buf_simple_add_u8(buf, metadata->data_len);
net_buf_simple_add_u8(buf, metadata->type);
net_buf_simple_add_mem(buf, metadata->data,
metadata->data_len -
sizeof(metadata->type));
}
/* Calcute length of codec config data */
len = net_buf_simple_tail(buf) - start - sizeof(len);
/* Update the length field */
*start = len;
/* Create BIS index bitfield */
bis_index = 0;
for (int i = 0; i < source->stream_count; i++) {
bis_index++;
net_buf_simple_add_u8(buf, bis_index);
net_buf_simple_add_u8(buf, 0); /* unused length field */
}
/* NOTE: It is also possible to have the codec configuration data per
* BIS index. As our API does not support such specialized BISes we
* currently don't do that.
*/
}
static int generate_broadcast_id(struct bt_audio_broadcast_source *source)
{
bool unique;
do {
int err;
err = bt_rand(&source->broadcast_id,
BT_AUDIO_BROADCAST_ID_SIZE);
if (err) {
return err;
}
/* Ensure uniqueness */
unique = true;
for (int i = 0; i < ARRAY_SIZE(broadcast_sources); i++) {
if (&broadcast_sources[i] == source) {
continue;
}
if (broadcast_sources[i].broadcast_id == source->broadcast_id) {
unique = false;
break;
}
}
} while (!unique);
return 0;
}
static int bt_audio_set_base(const struct bt_audio_broadcast_source *source,
struct bt_codec *codec)
{
struct bt_data base_ad_data;
int err;
/* Broadcast Audio Streaming Endpoint advertising data */
NET_BUF_SIMPLE_DEFINE(base_buf, sizeof(struct bt_audio_base_ad));
bt_audio_encode_base(source, codec, &base_buf);
base_ad_data.type = BT_DATA_SVC_DATA16;
base_ad_data.data_len = base_buf.len;
base_ad_data.data = base_buf.data;
err = bt_le_per_adv_set_data(source->adv, &base_ad_data, 1);
if (err != 0) {
BT_DBG("Failed to set extended advertising data (err %d)", err);
return err;
}
return 0;
}
static void broadcast_source_cleanup(struct bt_audio_broadcast_source *source)
{
for (size_t i = 0; i < source->stream_count; i++) {
struct bt_audio_stream *stream = &source->streams[i];
stream->ep->stream = NULL;
stream->ep = NULL;
stream->codec = NULL;
stream->qos = NULL;
stream->iso = NULL;
stream->group = NULL;
}
(void)memset(source, 0, sizeof(*source));
}
int bt_audio_broadcast_source_create(struct bt_audio_stream *streams,
uint8_t num_stream,
struct bt_codec *codec,
struct bt_codec_qos *qos,
struct bt_audio_broadcast_source **out_source)
{
struct bt_audio_broadcast_source *source;
struct bt_data ad;
uint8_t index;
int err;
/* Broadcast Audio Streaming Endpoint advertising data */
NET_BUF_SIMPLE_DEFINE(ad_buf,
BT_UUID_SIZE_16 + BT_AUDIO_BROADCAST_ID_SIZE);
/* TODO: Validate codec and qos values */
/* TODO: The API currently only requires a bt_audio_stream object from
* the user. We could modify the API such that the extended (and
* periodic advertising enabled) advertiser was provided by the user as
* well (similar to the ISO API), or even provide the BIG.
*
* The caveat of that type of API, instead of this, where we, the stack,
* control the advertiser, is that the user will be able to change the
* advertising data (thus making the broadcast source non-functional in
* terms of BAP compliance), or even stop the advertiser without
* stopping the BIG (which also goes against the BAP specification).
*/
CHECKIF(out_source == NULL) {
BT_DBG("out_source is NULL");
return -EINVAL;
}
/* Set out_source to NULL until the source has actually been created */
*out_source = NULL;
CHECKIF(streams == NULL) {
BT_DBG("streams is NULL");
return -EINVAL;
}
CHECKIF(codec == NULL) {
BT_DBG("codec is NULL");
return -EINVAL;
}
CHECKIF(num_stream > BROADCAST_STREAM_CNT) {
BT_DBG("Too many streams provided: %u/%u",
num_stream, BROADCAST_STREAM_CNT);
return -EINVAL;
}
source = NULL;
for (index = 0; index < ARRAY_SIZE(broadcast_sources); index++) {
if (broadcast_sources[index].bis[0] == NULL) { /* Find free entry */
source = &broadcast_sources[index];
break;
}
}
if (source == NULL) {
BT_DBG("Could not allocate any more broadcast sources");
return -ENOMEM;
}
source->streams = streams;
source->stream_count = num_stream;
for (size_t i = 0; i < num_stream; i++) {
struct bt_audio_stream *stream = &streams[i];
err = bt_audio_broadcast_source_setup_stream(index, stream,
codec, qos);
if (err != 0) {
BT_DBG("Failed to setup streams[%zu]: %d", i, err);
broadcast_source_cleanup(source);
return err;
}
source->bis[i] = &stream->ep->iso;
}
/* Create a non-connectable non-scannable advertising set */
err = bt_le_ext_adv_create(BT_LE_EXT_ADV_NCONN_NAME, NULL,
&source->adv);
if (err != 0) {
BT_DBG("Failed to create advertising set (err %d)", err);
broadcast_source_cleanup(source);
return err;
}
/* Set periodic advertising parameters */
err = bt_le_per_adv_set_param(source->adv, BT_LE_PER_ADV_DEFAULT);
if (err != 0) {
BT_DBG("Failed to set periodic advertising parameters (err %d)",
err);
broadcast_source_cleanup(source);
return err;
}
/* TODO: If updating the API to have a user-supplied advertiser, we
* should simply add the data here, instead of changing all of it.
* Similar, if the application changes the data, we should ensure
* that the audio advertising data is still present, similar to how
* the GAP device name is added.
*/
err = generate_broadcast_id(source);
if (err != 0) {
BT_DBG("Could not generate broadcast id: %d", err);
return err;
}
net_buf_simple_add_le16(&ad_buf, BT_UUID_BROADCAST_AUDIO_VAL);
net_buf_simple_add_le24(&ad_buf, source->broadcast_id);
ad.type = BT_DATA_SVC_DATA16;
ad.data_len = ad_buf.len + sizeof(ad.type);
ad.data = ad_buf.data;
err = bt_le_ext_adv_set_data(source->adv, &ad, 1, NULL, 0);
if (err != 0) {
BT_DBG("Failed to set extended advertising data (err %d)", err);
broadcast_source_cleanup(source);
return err;
}
source->subgroup_count = BROADCAST_SUBGROUP_CNT;
source->pd = qos->pd;
err = bt_audio_set_base(source, codec);
if (err != 0) {
BT_DBG("Failed to set base data (err %d)", err);
broadcast_source_cleanup(source);
return err;
}
/* Enable Periodic Advertising */
err = bt_le_per_adv_start(source->adv);
if (err != 0) {
BT_DBG("Failed to enable periodic advertising (err %d)", err);
broadcast_source_cleanup(source);
return err;
}
/* Start extended advertising */
err = bt_le_ext_adv_start(source->adv,
BT_LE_EXT_ADV_START_DEFAULT);
if (err != 0) {
BT_DBG("Failed to start extended advertising (err %d)", err);
broadcast_source_cleanup(source);
return err;
}
for (size_t i = 0; i < source->stream_count; i++) {
struct bt_audio_ep *ep = streams[i].ep;
ep->broadcast_source = source;
broadcast_source_set_ep_state(ep,
BT_AUDIO_EP_STATE_QOS_CONFIGURED);
}
source->qos = qos;
BT_DBG("Broadcasting with ID 0x%6X", source->broadcast_id);
*out_source = source;
return 0;
}
int bt_audio_broadcast_source_reconfig(struct bt_audio_broadcast_source *source,
struct bt_codec *codec,
struct bt_codec_qos *qos)
{
struct bt_audio_stream *stream;
int err;
CHECKIF(source == NULL) {
BT_DBG("source is NULL");
return -EINVAL;
}
stream = &source->streams[0];
if (stream == NULL) {
BT_DBG("stream is NULL");
return -EINVAL;
}
if (stream->ep == NULL) {
BT_DBG("stream->ep is NULL");
return -EINVAL;
}
if (stream->ep->status.state != BT_AUDIO_EP_STATE_QOS_CONFIGURED) {
BT_DBG("Broadcast source stream %p invalid state: %u",
stream, stream->ep->status.state);
return -EBADMSG;
}
for (size_t i = 0; i < source->stream_count; i++) {
stream = &source->streams[i];
bt_audio_stream_attach(NULL, stream, stream->ep, codec);
}
err = bt_audio_set_base(source, codec);
if (err != 0) {
BT_DBG("Failed to set base data (err %d)", err);
return err;
}
source->qos = qos;
return 0;
}
int bt_audio_broadcast_source_start(struct bt_audio_broadcast_source *source)
{
struct bt_iso_big_create_param param = { 0 };
struct bt_audio_stream *stream;
int err;
CHECKIF(source == NULL) {
BT_DBG("source is NULL");
return -EINVAL;
}
stream = &source->streams[0];
if (stream == NULL) {
BT_DBG("stream is NULL");
return -EINVAL;
}
if (stream->ep == NULL) {
BT_DBG("stream->ep is NULL");
return -EINVAL;
}
if (stream->ep->status.state != BT_AUDIO_EP_STATE_QOS_CONFIGURED) {
BT_DBG("Broadcast source stream %p invalid state: %u",
stream, stream->ep->status.state);
return -EBADMSG;
}
/* Create BIG */
param.num_bis = source->stream_count;
param.bis_channels = source->bis;
param.framing = source->qos->framing;
param.packing = 0; /* TODO: Add to QoS struct */
param.interval = source->qos->interval;
param.latency = source->qos->latency;
err = bt_iso_big_create(source->adv, &param, &source->big);
if (err != 0) {
BT_DBG("Failed to create BIG: %d", err);
return err;
}
return 0;
}
int bt_audio_broadcast_source_stop(struct bt_audio_broadcast_source *source)
{
struct bt_audio_stream *stream;
int err;
CHECKIF(source == NULL) {
BT_DBG("source is NULL");
return -EINVAL;
}
stream = &source->streams[0];
if (stream == NULL) {
BT_DBG("stream is NULL");
return -EINVAL;
}
if (stream->ep == NULL) {
BT_DBG("stream->ep is NULL");
return -EINVAL;
}
if (stream->ep->status.state != BT_AUDIO_EP_STATE_STREAMING) {
BT_DBG("Broadcast source stream %p invalid state: %u",
stream, stream->ep->status.state);
return -EBADMSG;
}
if (source->big == NULL) {
BT_DBG("Source is not started");
return -EALREADY;
}
err = bt_iso_big_terminate(source->big);
if (err) {
BT_DBG("Failed to terminate BIG (err %d)", err);
return err;
}
source->big = NULL;
return 0;
}
int bt_audio_broadcast_source_delete(struct bt_audio_broadcast_source *source)
{
struct bt_audio_stream *stream;
struct bt_le_ext_adv *adv;
int err;
CHECKIF(source == NULL) {
BT_DBG("source is NULL");
return -EINVAL;
}
stream = &source->streams[0];
if (stream != NULL) {
if (stream->ep == NULL) {
BT_DBG("stream->ep is NULL");
return -EINVAL;
}
if (stream->ep->status.state != BT_AUDIO_EP_STATE_QOS_CONFIGURED) {
BT_DBG("Broadcast source stream %p invalid state: %u",
stream, stream->ep->status.state);
return -EBADMSG;
}
}
adv = source->adv;
__ASSERT(adv != NULL, "source %p adv is NULL", source);
/* Stop periodic advertising */
err = bt_le_per_adv_stop(adv);
if (err != 0) {
BT_DBG("Failed to stop periodic advertising (err %d)", err);
return err;
}
/* Stop extended advertising */
err = bt_le_ext_adv_stop(adv);
if (err != 0) {
BT_DBG("Failed to stop extended advertising (err %d)", err);
return err;
}
/* Delete extended advertising set */
err = bt_le_ext_adv_delete(adv);
if (err != 0) {
BT_DBG("Failed to delete extended advertising set (err %d)", err);
return err;
}
/* Reset the broadcast source */
broadcast_source_cleanup(source);
return 0;
}

View file

@ -17,7 +17,11 @@
#define UNICAST_GROUP_CNT 0 #define UNICAST_GROUP_CNT 0
#define UNICAST_GROUP_STREAM_CNT 0 #define UNICAST_GROUP_STREAM_CNT 0
#endif /* CONFIG_BT_AUDIO_UNICAST_CLIENT && CONFIG_BT_AUDIO_UNICAST */ #endif /* CONFIG_BT_AUDIO_UNICAST_CLIENT && CONFIG_BT_AUDIO_UNICAST */
#if defined(CONFIG_BT_AUDIO_BROADCAST_SOURCE)
#define BROADCAST_STREAM_CNT CONFIG_BT_AUDIO_BROADCAST_SRC_STREAM_COUNT
#else /* !CONFIG_BT_AUDIO_BROADCAST_SOURCE */
#define BROADCAST_STREAM_CNT 0 #define BROADCAST_STREAM_CNT 0
#endif /* CONFIG_BT_AUDIO_BROADCAST_SOURCE */
#define BT_AUDIO_EP_LOCAL 0x00 #define BT_AUDIO_EP_LOCAL 0x00
#define BT_AUDIO_EP_REMOTE 0x01 #define BT_AUDIO_EP_REMOTE 0x01

View file

@ -185,8 +185,9 @@ bool bt_audio_valid_qos(const struct bt_codec_qos *qos)
static bool bt_audio_stream_is_broadcast(const struct bt_audio_stream *stream) static bool bt_audio_stream_is_broadcast(const struct bt_audio_stream *stream)
{ {
/* TODO: Implement */ /* TODO: Add broadcast sink */
return false; return (IS_ENABLED(CONFIG_BT_AUDIO_BROADCAST_SOURCE) &&
bt_audio_ep_is_broadcast_src(stream->ep));
} }
bool bt_audio_valid_stream_qos(const struct bt_audio_stream *stream, bool bt_audio_valid_stream_qos(const struct bt_audio_stream *stream,

View file

@ -38,7 +38,7 @@ CONFIG_BT_AUTO_PHY_UPDATE=y
CONFIG_BT_AUDIO=y CONFIG_BT_AUDIO=y
CONFIG_BT_AUDIO_UNICAST_SERVER=y CONFIG_BT_AUDIO_UNICAST_SERVER=y
CONFIG_BT_AUDIO_UNICAST_CLIENT=y CONFIG_BT_AUDIO_UNICAST_CLIENT=y
CONFIG_BT_AUDIO_BROADCAST=y CONFIG_BT_AUDIO_BROADCAST_SOURCE=y
CONFIG_BT_ISO_TX_BUF_COUNT=5 CONFIG_BT_ISO_TX_BUF_COUNT=5
CONFIG_BT_VOCS_MAX_INSTANCE_COUNT=1 CONFIG_BT_VOCS_MAX_INSTANCE_COUNT=1
CONFIG_BT_VOCS_CLIENT_MAX_INSTANCE_COUNT=1 CONFIG_BT_VOCS_CLIENT_MAX_INSTANCE_COUNT=1