Bluetooth: Audio: Broadcast source subgroup and BIS codec support

Updates the broadcast source API to create subgroups and
to set BIS specific codec configuration

Signed-off-by: Emil Gydesen <emil.gydesen@nordicsemi.no>
This commit is contained in:
Emil Gydesen 2022-09-05 11:29:06 +02:00 committed by Carles Cufí
commit 40e3930ebd
10 changed files with 597 additions and 285 deletions

View file

@ -126,13 +126,18 @@ config BT_AUDIO_BROADCAST_SOURCE
if BT_AUDIO_BROADCAST_SOURCE
config BT_AUDIO_BROADCAST_SRC_SUBGROUP_COUNT
int # hidden: TODO: Update once the API supports it
int "Basic Audio Broadcast Source subgroup count"
default 1
range 1 BT_ISO_MAX_CHAN if BT_ISO_MAX_CHAN < 31
range 1 31
help
This option sets the maximum number of subgroups per broadcast source
to support.
config BT_AUDIO_BROADCAST_SRC_COUNT
int "Basic Audio Broadcaster source count"
default 1
range 0 BT_ISO_MAX_BIG
range 1 BT_ISO_MAX_BIG
help
This option sets the number of broadcast sources to support.
One broadcast source can send multiple streams
@ -142,7 +147,8 @@ config BT_AUDIO_BROADCAST_SRC_COUNT
config BT_AUDIO_BROADCAST_SRC_STREAM_COUNT
int "Basic Audio Broadcast Source Stream count"
default 1
range 0 BT_ISO_MAX_CHAN
range 1 BT_ISO_MAX_CHAN if BT_ISO_MAX_CHAN < 31
range 1 31
help
This option sets the maximum number of streams per broadcast source
to support.
@ -164,7 +170,8 @@ if BT_AUDIO_BROADCAST_SINK
config BT_AUDIO_BROADCAST_SNK_SUBGROUP_COUNT
int "Basic Audio Profile Broadcast Sink subgroup count"
default 1
range 1 BT_ISO_MAX_CHAN
range 1 BT_ISO_MAX_CHAN if BT_ISO_MAX_CHAN < 31
range 1 31
help
This option sets the maximum number of subgroups per broadcast sink
to support.

View file

@ -22,10 +22,46 @@
#include "audio_iso.h"
#include "endpoint.h"
struct bt_audio_broadcast_subgroup {
/* The streams used to create the broadcast source */
sys_slist_t streams;
/* The codec of the subgroup */
struct bt_codec *codec;
/* List node */
sys_snode_t _node;
};
static struct bt_audio_ep broadcast_source_eps
[CONFIG_BT_AUDIO_BROADCAST_SRC_COUNT][BROADCAST_STREAM_CNT];
static struct bt_audio_broadcast_subgroup broadcast_source_subgroups
[CONFIG_BT_AUDIO_BROADCAST_SRC_COUNT][CONFIG_BT_AUDIO_BROADCAST_SRC_SUBGROUP_COUNT];
static struct bt_audio_broadcast_source broadcast_sources[CONFIG_BT_AUDIO_BROADCAST_SRC_COUNT];
/**
* 2 octets UUID
* 3 octets presentation delay
* 1 octet number of subgroups
*
* Each subgroup then has
* 1 octet of number of BIS
* 5 octets of Codec_ID
* 1 octet codec specific configuration len
* 0-n octets of codec specific configuration
* 1 octet metadata len
* 0-n octets of metadata
*
* For each BIS in the subgroup there is
* 1 octet for the BIS index
* 1 octet codec specific configuration len
* 0-n octets of codec specific configuration
*
* For a minimal BASE with 1 subgroup and 1 BIS without and other data the
* total comes to 16
*/
#define MINIMUM_BASE_SIZE 16
static void broadcast_source_set_ep_state(struct bt_audio_ep *ep, uint8_t state)
{
uint8_t old_state;
@ -214,19 +250,29 @@ static struct bt_audio_ep *broadcast_source_new_ep(uint8_t index)
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)
static struct bt_audio_broadcast_subgroup *broadcast_source_new_subgroup(uint8_t index)
{
for (size_t i = 0; i < ARRAY_SIZE(broadcast_source_subgroups[index]); i++) {
struct bt_audio_broadcast_subgroup *subgroup =
&broadcast_source_subgroups[index][i];
if (sys_slist_is_empty(&subgroup->streams)) {
return subgroup;
}
}
return NULL;
}
static int broadcast_source_setup_stream(uint8_t index,
struct bt_audio_stream *stream,
struct bt_codec *codec,
struct bt_codec_qos *qos,
struct bt_audio_broadcast_source *source)
{
struct bt_audio_iso *iso;
struct bt_audio_ep *ep;
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");
@ -249,44 +295,43 @@ static int bt_audio_broadcast_source_setup_stream(uint8_t index,
bt_audio_stream_attach(NULL, stream, ep, codec);
stream->qos = qos;
ep->broadcast_source = source;
return 0;
}
static bool bt_audio_encode_base(const struct bt_audio_broadcast_source *source,
struct net_buf_simple *buf)
static bool encode_base_subgroup(struct bt_audio_broadcast_subgroup *subgroup,
struct bt_audio_broadcast_stream_data *stream_data,
uint8_t *streams_encoded,
struct net_buf_simple *buf)
{
const struct bt_codec *codec = source->codec;
struct bt_audio_stream *stream;
const struct bt_codec *codec;
uint8_t stream_count;
uint8_t bis_index;
uint8_t *start;
uint8_t len;
__ASSERT(source->subgroup_count == CONFIG_BT_AUDIO_BROADCAST_SRC_SUBGROUP_COUNT,
"Cannot encode BASE with more than a %u subgroups",
CONFIG_BT_AUDIO_BROADCAST_SRC_SUBGROUP_COUNT);
/* 13 is the size of the fixed size values following this check */
if ((buf->len + buf->size) < 13) {
return false;
stream_count = 0;
SYS_SLIST_FOR_EACH_CONTAINER(&subgroup->streams, stream, _node) {
stream_count++;
}
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);
codec = subgroup->codec;
net_buf_simple_add_u8(buf, 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;
if ((buf->len + buf->size) < (sizeof(codec_data->data_len) +
if ((buf->size - buf->len) < (sizeof(codec_data->data_len) +
sizeof(codec_data->type) +
codec_data->data_len)) {
BT_DBG("No room for codec[%d] with len %u",
@ -305,7 +350,7 @@ static bool bt_audio_encode_base(const struct bt_audio_broadcast_source *source,
/* Update the length field */
*start = len;
if ((buf->len + buf->size) < sizeof(len)) {
if ((buf->size - buf->len) < sizeof(len)) {
BT_DBG("No room for metadata length");
return false;
@ -316,7 +361,7 @@ static bool bt_audio_encode_base(const struct bt_audio_broadcast_source *source,
for (int i = 0; i < codec->meta_count; i++) {
const struct bt_data *metadata = &codec->meta[i].data;
if ((buf->len + buf->size) < (sizeof(metadata->data_len) +
if ((buf->size - buf->len) < (sizeof(metadata->data_len) +
sizeof(metadata->type) +
metadata->data_len)) {
BT_DBG("No room for metadata[%d] with len %u",
@ -336,22 +381,88 @@ static bool bt_audio_encode_base(const struct bt_audio_broadcast_source *source,
/* Create BIS index bitfield */
bis_index = 0;
for (int i = 0; i < source->stream_count; i++) {
for (int i = 0; i < stream_count; i++) {
bis_index++;
if ((buf->len + buf->size) < (sizeof(bis_index) + sizeof(uint8_t))) {
BT_DBG("No room for BIS[%d] data", i);
if ((buf->size - buf->len) < (sizeof(bis_index) + sizeof(uint8_t))) {
BT_DBG("No room for BIS[%d] index", i);
return false;
}
net_buf_simple_add_u8(buf, bis_index);
net_buf_simple_add_u8(buf, 0); /* unused length field */
if ((buf->size - buf->len) < sizeof(len)) {
BT_DBG("No room for bis codec config length");
return false;
}
/* Insert codec configuration data in LTV format */
start = net_buf_simple_add(buf, sizeof(len));
#if defined(CONFIG_BT_CODEC_MAX_DATA_COUNT)
for (size_t j = 0U; j < stream_data[i].data_count; j++) {
const struct bt_data *codec_data = &stream_data[i].data[j].data;
if ((buf->size - buf->len) < (sizeof(codec_data->data_len) +
sizeof(codec_data->type) +
codec_data->data_len)) {
BT_DBG("No room for BIS [%u] codec[%zu] with len %u",
bis_index, j, codec_data->data_len);
return false;
}
net_buf_simple_add_u8(buf, codec_data->data_len + sizeof(codec_data->type));
net_buf_simple_add_u8(buf, codec_data->type);
net_buf_simple_add_mem(buf, codec_data->data, codec_data->data_len);
}
#endif /* CONFIG_BT_CODEC_MAX_DATA_COUNT */
/* Calculate length of codec config data */
len = net_buf_simple_tail(buf) - start - sizeof(len);
/* Update the length field */
*start = len;
streams_encoded++;
}
/* 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.
return true;
}
static bool encode_base(struct bt_audio_broadcast_source *source,
struct net_buf_simple *buf)
{
struct bt_audio_broadcast_subgroup *subgroup;
uint8_t streams_encoded;
uint8_t subgroup_count;
/* 13 is the size of the fixed size values following this check */
if ((buf->size - buf->len) < MINIMUM_BASE_SIZE) {
return false;
}
subgroup_count = 0U;
SYS_SLIST_FOR_EACH_CONTAINER(&source->subgroups, subgroup, _node) {
subgroup_count++;
}
net_buf_simple_add_le16(buf, BT_UUID_BASIC_AUDIO_VAL);
net_buf_simple_add_le24(buf, source->qos->pd);
net_buf_simple_add_u8(buf, subgroup_count);
/* Since the `stream_data` is only stored in the broadcast source,
* we need to provide that information when encoding each subgroup
*/
streams_encoded = 0;
SYS_SLIST_FOR_EACH_CONTAINER(&source->subgroups, subgroup, _node) {
if (!encode_base_subgroup(subgroup,
&source->stream_data[streams_encoded],
&streams_encoded, buf)) {
return false;
}
}
return true;
}
@ -388,34 +499,137 @@ static int generate_broadcast_id(struct bt_audio_broadcast_source *source)
static void broadcast_source_cleanup(struct bt_audio_broadcast_source *source)
{
struct bt_audio_stream *stream, *next;
struct bt_audio_broadcast_subgroup *subgroup, *next_subgroup;
SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&source->streams, stream, next, _node) {
bt_audio_iso_unbind_ep(stream->ep->iso, stream->ep);
stream->ep->stream = NULL;
stream->ep = NULL;
stream->codec = NULL;
stream->qos = NULL;
stream->group = NULL;
SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&source->subgroups, subgroup,
next_subgroup, _node) {
struct bt_audio_stream *stream, *next_stream;
sys_slist_remove(&source->streams, NULL, &stream->_node);
SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&subgroup->streams, stream,
next_stream, _node) {
bt_audio_iso_unbind_ep(stream->ep->iso, stream->ep);
stream->ep->stream = NULL;
stream->ep = NULL;
stream->codec = NULL;
stream->qos = NULL;
stream->group = NULL;
sys_slist_remove(&subgroup->streams, NULL,
&stream->_node);
}
sys_slist_remove(&source->subgroups, NULL, &subgroup->_node);
}
(void)memset(source, 0, sizeof(*source));
}
int bt_audio_broadcast_source_create(struct bt_audio_stream *streams[],
size_t num_stream,
struct bt_codec *codec,
struct bt_codec_qos *qos,
static bool valid_create_param(const struct bt_audio_broadcast_source_create_param *param)
{
const struct bt_codec_qos *qos;
CHECKIF(param == NULL) {
BT_DBG("param is NULL");
return false;
}
CHECKIF(param->params_count == 0U) {
BT_DBG("param->params_count is 0");
return false;
}
qos = param->qos;
CHECKIF(qos == NULL) {
BT_DBG("param->qos is NULL");
return false;
}
CHECKIF(!bt_audio_valid_qos(qos)) {
BT_DBG("param->qos is invalid");
return false;
}
for (size_t i = 0U; i < param->params_count; i++) {
const struct bt_audio_broadcast_source_subgroup_param *subgroup_param;
subgroup_param = &param->params[i];
CHECKIF(subgroup_param->params_count == 0U) {
BT_DBG("subgroup_params[%zu].count is 0", i);
return false;
}
CHECKIF(subgroup_param->codec == NULL) {
BT_DBG("subgroup_params[%zu].codec is NULL", i);
return false;
}
for (size_t j = 0U; j < subgroup_param->params_count; j++) {
const struct bt_audio_broadcast_source_stream_param *stream_param;
stream_param = &subgroup_param->params[j];
CHECKIF(stream_param->stream == NULL) {
BT_DBG("subgroup_params[%zu].stream_params[%zu]->stream is NULL",
i, j);
return false;
}
CHECKIF(stream_param->stream->group != NULL) {
BT_DBG("subgroup_params[%zu].stream_params[%zu]->stream is "
"already part of group %p",
i, j, stream_param->stream->group);
return false;
}
}
}
return true;
}
static enum bt_audio_state broadcast_source_get_state(struct bt_audio_broadcast_source *source)
{
struct bt_audio_broadcast_subgroup *subgroup;
struct bt_audio_stream *stream;
sys_snode_t *head_node;
if (source == NULL) {
BT_DBG("source is NULL");
return BT_AUDIO_EP_STATE_IDLE;
}
if (sys_slist_is_empty(&source->subgroups)) {
BT_DBG("Source does not have any streams");
return BT_AUDIO_EP_STATE_IDLE;
}
/* Get the first stream */
head_node = sys_slist_peek_head(&source->subgroups);
subgroup = CONTAINER_OF(head_node, struct bt_audio_broadcast_subgroup, _node);
head_node = sys_slist_peek_head(&subgroup->streams);
stream = CONTAINER_OF(head_node, struct bt_audio_stream, _node);
/* All streams in a broadcast source is in the same state,
* so we can just check the first stream
*/
if (stream->ep == NULL) {
BT_DBG("stream->ep is NULL");
return BT_AUDIO_EP_STATE_IDLE;
}
return stream->ep->status.state;
}
int bt_audio_broadcast_source_create(struct bt_audio_broadcast_source_create_param *param,
struct bt_audio_broadcast_source **out_source)
{
struct bt_audio_broadcast_subgroup *subgroup;
struct bt_audio_broadcast_source *source;
struct bt_codec_qos *qos;
size_t stream_count;
uint8_t index;
int err;
/* TODO: Validate codec and qos values */
CHECKIF(out_source == NULL) {
BT_DBG("out_source is NULL");
return -EINVAL;
@ -423,32 +637,14 @@ int bt_audio_broadcast_source_create(struct bt_audio_stream *streams[],
/* Set out_source to NULL until the source has actually been created */
*out_source = NULL;
CHECKIF(streams == NULL) {
BT_DBG("streams is NULL");
if (!valid_create_param(param)) {
BT_DBG("Invalid parameters");
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;
}
for (size_t i = 0; i < num_stream; i++) {
CHECKIF(streams[i] == NULL) {
BT_DBG("streams[%zu] is NULL", i);
return -EINVAL;
}
}
source = NULL;
for (index = 0; index < ARRAY_SIZE(broadcast_sources); index++) {
if (broadcast_sources[index].bis[0] == NULL) { /* Find free entry */
if (sys_slist_is_empty(&broadcast_sources[index].subgroups)) { /* Find free entry */
source = &broadcast_sources[index];
break;
}
@ -459,20 +655,66 @@ int bt_audio_broadcast_source_create(struct bt_audio_stream *streams[],
return -ENOMEM;
}
for (size_t i = 0; i < num_stream; i++) {
struct bt_audio_stream *stream = streams[i];
stream_count = 0U;
err = bt_audio_broadcast_source_setup_stream(index, stream,
codec, qos);
if (err != 0) {
BT_DBG("Failed to setup streams[%zu]: %d", i, err);
qos = param->qos;
/* Go through all subgroups and streams and setup each setup with an
* endpoint
*/
for (size_t i = 0U; i < param->params_count; i++) {
const struct bt_audio_broadcast_source_subgroup_param *subgroup_param;
subgroup_param = &param->params[i];
subgroup = broadcast_source_new_subgroup(index);
if (subgroup == NULL) {
BT_DBG("Could not allocate new broadcast subgroup");
broadcast_source_cleanup(source);
return err;
return -ENOMEM;
}
source->bis[i] = bt_audio_stream_iso_chan_get(stream);
sys_slist_append(&source->streams, &stream->_node);
source->stream_count++;
subgroup->codec = subgroup_param->codec;
sys_slist_append(&source->subgroups, &subgroup->_node);
/* Check that we are not above the maximum BIS count */
if (subgroup_param->params_count + stream_count > BROADCAST_STREAM_CNT) {
BT_DBG("Cannot create broadcaster with %zu streams",
stream_count);
broadcast_source_cleanup(source);
return -ENOMEM;
}
for (size_t j = 0U; j < subgroup_param->params_count; j++) {
const struct bt_audio_broadcast_source_stream_param *stream_param;
struct bt_audio_stream *stream;
stream_param = &subgroup_param->params[j];
stream = stream_param->stream;
err = broadcast_source_setup_stream(index, stream,
subgroup_param->codec,
qos, source);
if (err != 0) {
BT_DBG("Failed to setup streams[%zu]: %d", i, err);
broadcast_source_cleanup(source);
return err;
}
/* Store the BIS specific codec configuration data in
* the broadcast source. It is stored in the broadcast
* source, instead of the stream object, as this is
* only relevant for the broadcast source, and not used
* for unicast or broadcast sink.
*/
(void)memcpy(source->stream_data[stream_count].data,
stream_param->data,
stream_param->data_count * sizeof(*stream_param->data));
source->stream_data[stream_count].data_count = stream_param->data_count;
sys_slist_append(&subgroup->streams, &stream->_node);
stream_count++;
}
}
err = generate_broadcast_id(source);
@ -481,19 +723,16 @@ int bt_audio_broadcast_source_create(struct bt_audio_stream *streams[],
return err;
}
source->subgroup_count = CONFIG_BT_AUDIO_BROADCAST_SRC_SUBGROUP_COUNT;
source->pd = qos->pd;
/* Finalize state changes and store information */
SYS_SLIST_FOR_EACH_CONTAINER(&source->subgroups, subgroup, _node) {
struct bt_audio_stream *stream;
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);
SYS_SLIST_FOR_EACH_CONTAINER(&subgroup->streams, stream, _node) {
broadcast_source_set_ep_state(stream->ep,
BT_AUDIO_EP_STATE_QOS_CONFIGURED);
}
}
source->qos = qos;
source->codec = codec;
BT_DBG("Broadcasting with ID 0x%6X", source->broadcast_id);
@ -506,42 +745,29 @@ int bt_audio_broadcast_source_reconfig(struct bt_audio_broadcast_source *source,
struct bt_codec *codec,
struct bt_codec_qos *qos)
{
struct bt_audio_broadcast_subgroup *subgroup;
enum bt_audio_state broadcast_state;
struct bt_audio_stream *stream;
sys_snode_t *head_node;
CHECKIF(source == NULL) {
BT_DBG("source is NULL");
return -EINVAL;
}
if (sys_slist_is_empty(&source->streams)) {
BT_DBG("Source does not have any streams");
return -EINVAL;
}
head_node = sys_slist_peek_head(&source->streams);
stream = CONTAINER_OF(head_node, struct bt_audio_stream, _node);
/* All streams in a broadcast source is in the same state,
* so we can just check the first stream
*/
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);
broadcast_state = broadcast_source_get_state(source);
if (broadcast_source_get_state(source) != BT_AUDIO_EP_STATE_QOS_CONFIGURED) {
BT_DBG("Broadcast source invalid state: %u",
broadcast_state);
return -EBADMSG;
}
SYS_SLIST_FOR_EACH_CONTAINER(&source->streams, stream, _node) {
bt_audio_stream_attach(NULL, stream, stream->ep, codec);
SYS_SLIST_FOR_EACH_CONTAINER(&source->subgroups, subgroup, _node) {
SYS_SLIST_FOR_EACH_CONTAINER(&subgroup->streams, stream, _node) {
bt_audio_stream_attach(NULL, stream, stream->ep, codec);
}
}
source->qos = qos;
source->codec = codec;
return 0;
}
@ -549,9 +775,12 @@ int bt_audio_broadcast_source_reconfig(struct bt_audio_broadcast_source *source,
int bt_audio_broadcast_source_start(struct bt_audio_broadcast_source *source,
struct bt_le_ext_adv *adv)
{
struct bt_iso_chan *bis[BROADCAST_STREAM_CNT];
struct bt_iso_big_create_param param = { 0 };
struct bt_audio_broadcast_subgroup *subgroup;
enum bt_audio_state broadcast_state;
struct bt_audio_stream *stream;
sys_snode_t *head_node;
size_t bis_count;
int err;
CHECKIF(source == NULL) {
@ -559,28 +788,22 @@ int bt_audio_broadcast_source_start(struct bt_audio_broadcast_source *source,
return -EINVAL;
}
if (sys_slist_is_empty(&source->streams)) {
BT_DBG("Source does not have any streams");
return -EINVAL;
}
head_node = sys_slist_peek_head(&source->streams);
stream = CONTAINER_OF(head_node, struct bt_audio_stream, _node);
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);
broadcast_state = broadcast_source_get_state(source);
if (broadcast_source_get_state(source) != BT_AUDIO_EP_STATE_QOS_CONFIGURED) {
BT_DBG("Broadcast source invalid state: %u", broadcast_state);
return -EBADMSG;
}
bis_count = 0;
SYS_SLIST_FOR_EACH_CONTAINER(&source->subgroups, subgroup, _node) {
SYS_SLIST_FOR_EACH_CONTAINER(&subgroup->streams, stream, _node) {
bis[bis_count++] = bt_audio_stream_iso_chan_get(stream);
}
}
/* Create BIG */
param.num_bis = source->stream_count;
param.bis_channels = source->bis;
param.num_bis = bis_count;
param.bis_channels = bis;
param.framing = source->qos->framing;
param.packing = 0; /* TODO: Add to QoS struct */
param.interval = source->qos->interval;
@ -592,10 +815,12 @@ int bt_audio_broadcast_source_start(struct bt_audio_broadcast_source *source,
return err;
}
SYS_SLIST_FOR_EACH_CONTAINER(&source->streams, stream, _node) {
struct bt_audio_ep *ep = stream->ep;
SYS_SLIST_FOR_EACH_CONTAINER(&source->subgroups, subgroup, _node) {
SYS_SLIST_FOR_EACH_CONTAINER(&subgroup->streams, stream, _node) {
struct bt_audio_ep *ep = stream->ep;
broadcast_source_set_ep_state(ep, BT_AUDIO_EP_STATE_ENABLING);
broadcast_source_set_ep_state(ep, BT_AUDIO_EP_STATE_ENABLING);
}
}
return 0;
@ -603,8 +828,7 @@ int bt_audio_broadcast_source_start(struct bt_audio_broadcast_source *source,
int bt_audio_broadcast_source_stop(struct bt_audio_broadcast_source *source)
{
struct bt_audio_stream *stream;
sys_snode_t *head_node;
enum bt_audio_state broadcast_state;
int err;
CHECKIF(source == NULL) {
@ -612,26 +836,10 @@ int bt_audio_broadcast_source_stop(struct bt_audio_broadcast_source *source)
return -EINVAL;
}
if (sys_slist_is_empty(&source->streams)) {
BT_DBG("Source does not have any streams");
return -EINVAL;
}
head_node = sys_slist_peek_head(&source->streams);
stream = CONTAINER_OF(head_node, struct bt_audio_stream, _node);
/* All streams in a broadcast source is in the same state,
* so we can just check the first stream
*/
if (stream->ep == NULL) {
BT_DBG("stream->ep is NULL");
return -EINVAL;
}
if (stream->ep->status.state != BT_AUDIO_EP_STATE_STREAMING &&
stream->ep->status.state != BT_AUDIO_EP_STATE_ENABLING) {
BT_DBG("Broadcast source stream %p invalid state: %u",
stream, stream->ep->status.state);
broadcast_state = broadcast_source_get_state(source);
if (broadcast_state != BT_AUDIO_EP_STATE_STREAMING &&
broadcast_state != BT_AUDIO_EP_STATE_ENABLING) {
BT_DBG("Broadcast source invalid state: %u", broadcast_state);
return -EBADMSG;
}
@ -653,30 +861,16 @@ int bt_audio_broadcast_source_stop(struct bt_audio_broadcast_source *source)
int bt_audio_broadcast_source_delete(struct bt_audio_broadcast_source *source)
{
struct bt_audio_stream *stream;
sys_snode_t *head_node;
enum bt_audio_state broadcast_state;
CHECKIF(source == NULL) {
BT_DBG("source is NULL");
return -EINVAL;
}
if (sys_slist_is_empty(&source->streams)) {
BT_DBG("Source does not have any streams");
return -EINVAL;
}
head_node = sys_slist_peek_head(&source->streams);
stream = CONTAINER_OF(head_node, struct bt_audio_stream, _node);
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);
broadcast_state = broadcast_source_get_state(source);
if (broadcast_state != BT_AUDIO_EP_STATE_QOS_CONFIGURED) {
BT_DBG("Broadcast source invalid state: %u", broadcast_state);
return -EBADMSG;
}
@ -704,10 +898,10 @@ int bt_audio_broadcast_source_get_id(const struct bt_audio_broadcast_source *sou
return 0;
}
int bt_audio_broadcast_source_get_base(const struct bt_audio_broadcast_source *source,
int bt_audio_broadcast_source_get_base(struct bt_audio_broadcast_source *source,
struct net_buf_simple *base_buf)
{
if (!bt_audio_encode_base(source, base_buf)) {
if (!encode_base(source, base_buf)) {
BT_DBG("base_buf %p with size %u not large enough",
base_buf, base_buf->size);

View file

@ -79,18 +79,27 @@ struct bt_audio_unicast_group {
sys_slist_t streams;
};
struct bt_audio_broadcast_stream_data {
#if defined(CONFIG_BT_CODEC_MAX_DATA_COUNT)
/** Codec Specific Data count */
size_t data_count;
/** Codec Specific Data */
struct bt_codec_data data[CONFIG_BT_CODEC_MAX_DATA_COUNT];
#endif /* CONFIG_BT_CODEC_MAX_DATA_COUNT */
};
struct bt_audio_broadcast_source {
uint8_t stream_count;
uint8_t subgroup_count;
uint32_t pd; /** QoS Presentation Delay */
uint32_t broadcast_id; /* 24 bit */
struct bt_iso_big *big;
struct bt_iso_chan *bis[BROADCAST_STREAM_CNT];
struct bt_codec_qos *qos;
struct bt_codec *codec;
/* The streams used to create the broadcast source */
sys_slist_t streams;
/* The codec specific configured data for each stream in the subgroup */
struct bt_audio_broadcast_stream_data stream_data[BROADCAST_STREAM_CNT];
/* The subgroups containing the streams used to create the broadcast source */
sys_slist_t subgroups;
};
struct bt_audio_broadcast_sink {

View file

@ -95,7 +95,48 @@ struct bt_iso_chan *bt_audio_stream_iso_chan_get(struct bt_audio_stream *stream)
return NULL;
}
void bt_audio_stream_cb_register(struct bt_audio_stream *stream,
struct bt_audio_stream_ops *ops)
{
stream->ops = ops;
}
#if defined(CONFIG_BT_AUDIO_UNICAST) || defined(CONFIG_BT_AUDIO_BROADCAST_SOURCE)
bool bt_audio_valid_qos(const struct bt_codec_qos *qos)
{
if (qos->interval < BT_ISO_SDU_INTERVAL_MIN ||
qos->interval > BT_ISO_SDU_INTERVAL_MAX) {
BT_DBG("Interval not within allowed range: %u (%u-%u)",
qos->interval, BT_ISO_SDU_INTERVAL_MIN, BT_ISO_SDU_INTERVAL_MAX);
return false;
}
if (qos->framing > BT_CODEC_QOS_FRAMED) {
BT_DBG("Invalid Framing 0x%02x", qos->framing);
return false;
}
if (qos->phy != BT_CODEC_QOS_1M &&
qos->phy != BT_CODEC_QOS_2M &&
qos->phy != BT_CODEC_QOS_CODED) {
BT_DBG("Invalid PHY 0x%02x", qos->phy);
return false;
}
if (qos->sdu > BT_ISO_MAX_SDU) {
BT_DBG("Invalid SDU %u", qos->sdu);
return false;
}
if (qos->latency < BT_ISO_LATENCY_MIN ||
qos->latency > BT_ISO_LATENCY_MAX) {
BT_DBG("Invalid Latency %u", qos->latency);
return false;
}
return true;
}
int bt_audio_stream_send(struct bt_audio_stream *stream, struct net_buf *buf,
uint16_t seq_num, uint32_t ts)
{
@ -201,41 +242,6 @@ done:
}
#endif /* CONFIG_BT_AUDIO_UNICAST_SERVER */
bool bt_audio_valid_qos(const struct bt_codec_qos *qos)
{
if (qos->interval < BT_ISO_SDU_INTERVAL_MIN ||
qos->interval > BT_ISO_SDU_INTERVAL_MAX) {
BT_DBG("Interval not within allowed range: %u (%u-%u)",
qos->interval, BT_ISO_SDU_INTERVAL_MIN, BT_ISO_SDU_INTERVAL_MAX);
return false;
}
if (qos->framing > BT_CODEC_QOS_FRAMED) {
BT_DBG("Invalid Framing 0x%02x", qos->framing);
return false;
}
if (qos->phy != BT_CODEC_QOS_1M &&
qos->phy != BT_CODEC_QOS_2M &&
qos->phy != BT_CODEC_QOS_CODED) {
BT_DBG("Invalid PHY 0x%02x", qos->phy);
return false;
}
if (qos->sdu > BT_ISO_MAX_SDU) {
BT_DBG("Invalid SDU %u", qos->sdu);
return false;
}
if (qos->latency < BT_ISO_LATENCY_MIN ||
qos->latency > BT_ISO_LATENCY_MAX) {
BT_DBG("Invalid Latency %u", qos->latency);
return false;
}
return true;
}
static bool bt_audio_stream_is_broadcast(const struct bt_audio_stream *stream)
{
return (IS_ENABLED(CONFIG_BT_AUDIO_BROADCAST_SOURCE) &&
@ -321,12 +327,6 @@ void bt_audio_stream_reset(struct bt_audio_stream *stream)
bt_audio_stream_detach(stream);
}
void bt_audio_stream_cb_register(struct bt_audio_stream *stream,
struct bt_audio_stream_ops *ops)
{
stream->ops = ops;
}
#if defined(CONFIG_BT_AUDIO_UNICAST_CLIENT)
int bt_audio_stream_config(struct bt_conn *conn,

View file

@ -1499,7 +1499,10 @@ static int cmd_select_broadcast_source(const struct shell *sh, size_t argc,
static int cmd_create_broadcast(const struct shell *sh, size_t argc,
char *argv[])
{
struct bt_audio_stream *streams[ARRAY_SIZE(broadcast_source_streams)];
struct bt_audio_broadcast_source_stream_param
stream_params[ARRAY_SIZE(broadcast_source_streams)];
struct bt_audio_broadcast_source_subgroup_param subgroup_param;
struct bt_audio_broadcast_source_create_param create_param;
struct named_lc3_preset *named_preset;
int err;
@ -1519,15 +1522,18 @@ static int cmd_create_broadcast(const struct shell *sh, size_t argc,
}
}
(void)memset(streams, 0, sizeof(streams));
for (size_t i = 0; i < ARRAY_SIZE(streams); i++) {
streams[i] = &broadcast_source_streams[i];
(void)memset(stream_params, 0, sizeof(stream_params));
for (size_t i = 0; i < ARRAY_SIZE(stream_params); i++) {
stream_params[i].stream = &broadcast_source_streams[i];
}
subgroup_param.params_count = ARRAY_SIZE(stream_params);
subgroup_param.params = stream_params;
subgroup_param.codec = &named_preset->preset.codec;
create_param.params_count = 1U;
create_param.params = &subgroup_param;
create_param.qos = &named_preset->preset.qos;
err = bt_audio_broadcast_source_create(streams, ARRAY_SIZE(streams),
&named_preset->preset.codec,
&named_preset->preset.qos,
&default_source);
err = bt_audio_broadcast_source_create(&create_param, &default_source);
if (err != 0) {
shell_error(sh, "Unable to create broadcast source: %d", err);
return err;
@ -1537,7 +1543,7 @@ static int cmd_create_broadcast(const struct shell *sh, size_t argc,
named_preset->name);
if (default_stream == NULL) {
default_stream = streams[0];
default_stream = &broadcast_source_streams[0];
}
return 0;