Bluetooth: BAP: Refactor bt_bap_base

This removes the fixed size bt_bap_base, which provides
2 improvements:
1) The RAM usage of the broadcast sink has been reduced.
   For the Broadcast Sink sample it is a reduction of 120
   octets, but with much better scaling for supporting
   more or larger BASEs.
2) The functions to parse BASEs now support arbitrary sized
   BASEs, where they were previously restricted by our
   local Kconfig options. This allow us to parse any BASE
   from a remote device, without encounting memory issues.
   We are still memory restricted on the devices we
   actually want to sync to.

Signed-off-by: Emil Gydesen <emil.gydesen@nordicsemi.no>
This commit is contained in:
Emil Gydesen 2023-10-13 11:16:50 +02:00 committed by Fabio Baltieri
parent 031c842ecb
commit c9daed9712
18 changed files with 1330 additions and 681 deletions

View file

@ -34,22 +34,6 @@ extern "C" {
#define BT_BAP_SCAN_DELEGATOR_MAX_SUBGROUPS 0
#endif
/** The minimum size of a Broadcast Audio Source Endpoint (BASE)
* 2 octets UUID
* 3 octets presentation delay
* 1 octet number of subgroups (which is minimum 1)
* 1 octet number of BIS (which is minimum 1)
* 5 octets codec_id
* 1 octet codec configuration length (which may be 0)
* 1 octet metadata length (which may be 0)
* 1 octet BIS index
* 1 octet BIS specific codec configuration length (which may be 0)
*/
#define BT_BAP_BASE_MIN_SIZE 16
/** The minimum size of a bt_bap_base_bis_data */
#define BT_BAP_BASE_BIS_DATA_MIN_SIZE 2 /* index and length */
/** Periodic advertising state reported by the Scan Delegator */
enum bt_bap_pa_state {
/** The periodic advertising has not been synchronized */
@ -102,17 +86,6 @@ enum bt_bap_bass_att_err {
*/
#define BT_BAP_BIS_SYNC_NO_PREF 0xFFFFFFFF
#if defined(CONFIG_BT_BAP_BROADCAST_SINK)
/* TODO: Since these are also used for the broadcast assistant,
* they should not be tied to the broadcast sink
*/
#define BROADCAST_SNK_STREAM_CNT CONFIG_BT_BAP_BROADCAST_SNK_STREAM_COUNT
#define BROADCAST_SNK_SUBGROUP_CNT CONFIG_BT_BAP_BROADCAST_SNK_SUBGROUP_COUNT
#else /* !CONFIG_BT_BAP_BROADCAST_SINK */
#define BROADCAST_SNK_STREAM_CNT 0
#define BROADCAST_SNK_SUBGROUP_CNT 0
#endif /* CONFIG_BT_BAP_BROADCAST_SINK*/
/** Endpoint states */
enum bt_bap_ep_state {
/** Audio Stream Endpoint Idle state */
@ -276,7 +249,6 @@ struct bt_bap_unicast_group;
/** @brief Abstract Audio Endpoint structure. */
struct bt_bap_ep;
/* TODO: Replace with struct bt_bap_base_subgroup */
/** Struct to hold subgroup specific information for the receive state */
struct bt_bap_scan_delegator_subgroup {
/** BIS synced bitfield */
@ -1351,55 +1323,178 @@ int bt_bap_unicast_client_discover(struct bt_conn *conn, enum bt_audio_dir dir);
* @{
*/
struct bt_bap_base_bis_data {
/** @brief Abstract Broadcast Audio Source Endpoint (BASE) subgroup structure. */
struct bt_bap_base_subgroup;
/** @brief Abstract Broadcast Audio Source Endpoint (BASE) structure. */
struct bt_bap_base;
/** Codec ID structure for a Broadcast Audio Source Endpoint (BASE) */
struct bt_bap_base_codec_id {
/** Codec ID */
uint8_t id;
/** Codec Company ID */
uint16_t cid;
/** Codec Company Vendor ID */
uint16_t vid;
};
/** BIS structure for each BIS in a Broadcast Audio Source Endpoint (BASE) subgroup */
struct bt_bap_base_subgroup_bis {
/* Unique index of the BIS */
uint8_t index;
#if CONFIG_BT_AUDIO_CODEC_CFG_MAX_DATA_SIZE > 0
/** Codec Specific Data length. */
size_t data_len;
uint8_t data_len;
/** Codec Specific Data */
uint8_t data[CONFIG_BT_AUDIO_CODEC_CFG_MAX_DATA_SIZE];
#endif /* CONFIG_BT_AUDIO_CODEC_CFG_MAX_DATA_SIZE > 0 */
uint8_t *data;
};
struct bt_bap_base_subgroup {
/* Number of BIS in the subgroup */
size_t bis_count;
/** Codec information for the subgroup
*
* If the data_len of the codec is 0, then codec specific data may be
* found for each BIS in the bis_data.
*/
struct bt_audio_codec_cfg codec_cfg;
/* Array of BIS specific data for each BIS in the subgroup */
struct bt_bap_base_bis_data bis_data[BROADCAST_SNK_STREAM_CNT];
};
struct bt_bap_base {
/** @brief QoS Presentation Delay in microseconds
*
* Value range 0 to @ref BT_AUDIO_PD_MAX.
*/
uint32_t pd;
/* Number of subgroups in the BASE */
size_t subgroup_count;
/* Array of subgroups in the BASE */
struct bt_bap_base_subgroup subgroups[BROADCAST_SNK_SUBGROUP_CNT];
};
/** @brief Decode a Broadcast Audio Source Endpoint (BASE) from advertising data
/**
* @brief Generate a pointer to a BASE from periodic advertising data
*
* The BASE is sent via periodic advertising, and can be decoded into a
* bt_bap_base using this function.
* @param ad The periodic advertising data
*
* @param data The periodic advertising data
* @param base The output struct to put the decode BASE in
*
* @return 0 in case of success or negative errno value in case of error.
* @retval NULL if the data does not contain a BASE
* @retval Pointer to a bt_bap_base structure
*/
int bt_bap_decode_base(struct bt_data *data, struct bt_bap_base *base);
const struct bt_bap_base *bt_bap_base_get_base_from_ad(const struct bt_data *ad);
/**
* @brief Get the presentation delay value of a BASE
*
* @param base The BASE pointer
*
* @retval -EINVAL if arguments are invalid
* @retval The 24-bit presentation delay value
*/
int bt_bap_base_get_pres_delay(const struct bt_bap_base *base);
/**
* @brief Get the subgroup count of a BASE
*
* @param base The BASE pointer
*
* @retval -EINVAL if arguments are invalid
* @retval The 8-bit subgroup count value
*/
int bt_bap_base_get_subgroup_count(const struct bt_bap_base *base);
/**
* @brief Get all BIS indexes of a BASE
*
* @param[in] base The BASE pointer
* @param[out] bis_indexes 32-bit BIS index bitfield that will be populated
*
* @retval -EINVAL if arguments are invalid
* @retval 0 on success
*/
int bt_bap_base_get_bis_indexes(const struct bt_bap_base *base, uint32_t *bis_indexes);
/**
* @brief Iterate on all subgroups in the BASE
*
* @param base The BASE pointer
* @param func Callback function. Return true to continue iterating, or false to stop.
* @param user_data Userdata supplied to @p func
*
* @retval -EINVAL if arguments are invalid
* @retval -ECANCELED if iterating over the subgroups stopped prematurely by @p func
* @retval 0 if all subgroups were iterated
*/
int bt_bap_base_foreach_subgroup(const struct bt_bap_base *base,
bool (*func)(const struct bt_bap_base_subgroup *subgroup,
void *user_data),
void *user_data);
/**
* @brief Get the codec ID of a subgroup
*
* @param[in] subgroup The subgroup pointer
* @param[out] codec_id Pointer to the struct where the results are placed
*
* @retval -EINVAL if arguments are invalid
* @retval 0 on success
*/
int bt_bap_base_get_subgroup_codec_id(const struct bt_bap_base_subgroup *subgroup,
struct bt_bap_base_codec_id *codec_id);
/**
* @brief Get the codec configuration data of a subgroup
*
* @param[in] subgroup The subgroup pointer
* @param[out] data Pointer that will point to the resulting codec configuration data
*
* @retval -EINVAL if arguments are invalid
* @retval 0 on success
*/
int bt_bap_base_get_subgroup_codec_data(const struct bt_bap_base_subgroup *subgroup,
uint8_t **data);
/**
* @brief Get the codec metadata of a subgroup
*
* @param[in] subgroup The subgroup pointer
* @param[out] meta Pointer that will point to the resulting codec metadata
*
* @retval -EINVAL if arguments are invalid
* @retval 0 on success
*/
int bt_bap_base_get_subgroup_codec_meta(const struct bt_bap_base_subgroup *subgroup,
uint8_t **meta);
/**
* @brief Store subgroup codec data in a @ref bt_audio_codec_cfg
*
* @param[in] subgroup The subgroup pointer
* @param[out] codec_cfg Pointer to the struct where the results are placed
*
* @retval -EINVAL if arguments are invalid
* @retval -ENOMEM if the @p codec_cfg cannot store the @p subgroup codec data
* @retval 0 on success
*/
int bt_bap_base_subgroup_codec_to_codec_cfg(const struct bt_bap_base_subgroup *subgroup,
struct bt_audio_codec_cfg *codec_cfg);
/**
* @brief Get the BIS count of a subgroup
*
* @param subgroup The subgroup pointer
*
* @retval -EINVAL if arguments are invalid
* @retval The 8-bit BIS count value
*/
int bt_bap_base_get_subgroup_bis_count(const struct bt_bap_base_subgroup *subgroup);
/**
* @brief Iterate on all BIS in the subgroup
*
* @param subgroup The subgroup pointer
* @param func Callback function. Return true to continue iterating, or false to stop.
* @param user_data Userdata supplied to @p func
*
* @retval -EINVAL if arguments are invalid
* @retval -ECANCELED if iterating over the subgroups stopped prematurely by @p func
* @retval 0 if all BIS were iterated
*/
int bt_bap_base_subgroup_foreach_bis(const struct bt_bap_base_subgroup *subgroup,
bool (*func)(const struct bt_bap_base_subgroup_bis *bis,
void *user_data),
void *user_data);
/**
* @brief Store BIS codec configuration data in a @ref bt_audio_codec_cfg
*
* This only sets the @ref bt_audio_codec_cfg data and @ref bt_audio_codec_cfg data_len, but is
* useful to use the BIS codec configuration data with the bt_audio_codec_cfg_* functions.
*
* @param[in] bis The BIS pointer
* @param[out] codec_cfg Pointer to the struct where the results are placed
*
* @retval -EINVAL if arguments are invalid
* @retval -ENOMEM if the @p codec_cfg cannot store the @p subgroup codec data
* @retval 0 on success
*/
int bt_bap_base_subgroup_bis_codec_to_codec_cfg(const struct bt_bap_base_subgroup_bis *bis,
struct bt_audio_codec_cfg *codec_cfg);
/** @} */ /* End of group bt_bap_broadcast */
@ -1649,8 +1744,10 @@ struct bt_bap_broadcast_sink_cb {
*
* @param sink Pointer to the sink structure.
* @param base Broadcast Audio Source Endpoint (BASE).
* @param base_size Size of the @p base
*/
void (*base_recv)(struct bt_bap_broadcast_sink *sink, const struct bt_bap_base *base);
void (*base_recv)(struct bt_bap_broadcast_sink *sink, const struct bt_bap_base *base,
size_t base_size);
/** @brief Broadcast sink is syncable
*

View file

@ -252,44 +252,71 @@ static struct bt_bap_stream_ops stream_ops = {
.recv = stream_recv_cb,
};
static void base_recv_cb(struct bt_bap_broadcast_sink *sink, const struct bt_bap_base *base)
#if defined(CONFIG_LIBLC3)
static bool base_subgroup_cb(const struct bt_bap_base_subgroup *subgroup, void *user_data)
{
struct bt_audio_codec_cfg *codec_cfg = user_data;
struct bt_bap_base_codec_id codec_id;
int ret;
ret = bt_bap_base_get_subgroup_codec_id(subgroup, &codec_id);
if (ret < 0) {
printk("Could not get codec id for subgroup %p: %d", subgroup, ret);
return true;
}
if (codec_id.id != BT_HCI_CODING_FORMAT_LC3) {
printk("Unsupported codec for subgroup %p: 0x%02x", subgroup, codec_id.id);
return true; /* parse next subgroup */
}
ret = bt_bap_base_subgroup_codec_to_codec_cfg(subgroup, codec_cfg);
if (ret < 0) {
printk("Could convert subgroup %p to codec_cfg: %d", subgroup, ret);
return true;
}
return false; /* We only care about the first subgroup with LC3 */
}
#endif /* CONFIG_LIBLC3 */
static void base_recv_cb(struct bt_bap_broadcast_sink *sink, const struct bt_bap_base *base,
size_t base_size)
{
uint32_t base_bis_index_bitfield = 0U;
int err;
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);
printk("Received BASE with %d subgroups from broadcast sink %p\n",
bt_bap_base_get_subgroup_count(base), sink);
for (size_t i = 0U; i < base->subgroup_count; i++) {
const size_t bis_count = base->subgroups[i].bis_count;
printk("Subgroup[%zu] has %zu streams\n", i, bis_count);
for (size_t j = 0U; j < bis_count; j++) {
const uint8_t index = base->subgroups[i].bis_data[j].index;
printk("\tIndex 0x%02x\n", index);
base_bis_index_bitfield |= BIT(index);
}
#if defined(CONFIG_LIBLC3)
int ret;
const struct bt_audio_codec_cfg *codec_cfg = &base->subgroups[i].codec_cfg;
struct bt_audio_codec_cfg codec_cfg = {0};
if (codec_cfg->id != BT_HCI_CODING_FORMAT_LC3) {
printk("unsupported codec 0x%02x", codec_cfg->id);
return;
}
err = bt_bap_base_foreach_subgroup(base, base_subgroup_cb, &codec_cfg);
if (err != 0 && err != -ECANCELED) {
printk("Failed to parse subgroups: %d\n", err);
return;
} else if (codec_cfg.id != BT_HCI_CODING_FORMAT_LC3) {
/* No subgroups with LC3 was found */
printk("Did not parse an LC3 codec\n");
return;
}
ret = lc3_enable(codec_cfg);
if (ret < 0) {
printk("Error: cannot enable LC3 codec: %d", ret);
return;
}
#endif /* defined(CONFIG_LIBLC3) */
err = lc3_enable(&codec_cfg);
if (err < 0) {
printk("Error: cannot enable LC3 codec: %d", err);
return;
}
#endif /* CONFIG_LIBLC3 */
err = bt_bap_base_get_bis_indexes(base, &base_bis_index_bitfield);
if (err != 0) {
printk("Failed to BIS indexes: %d\n", err);
return;
}
bis_index_bitfield = base_bis_index_bitfield & bis_index_mask;

View file

@ -215,29 +215,20 @@ static void broadcast_scan_timeout(void)
static bool pa_decode_base(struct bt_data *data, void *user_data)
{
const struct bt_bap_base *base = bt_bap_base_get_base_from_ad(data);
uint32_t base_bis_index_bitfield = 0U;
struct bt_bap_base base = { 0 };
int err;
if (data->type != BT_DATA_SVC_DATA16) {
/* Base is NULL if the data does not contain a valid BASE */
if (base == NULL) {
return true;
}
if (data->data_len < BT_BAP_BASE_MIN_SIZE) {
return true;
}
if (bt_bap_decode_base(data, &base) != 0) {
err = bt_bap_base_get_bis_indexes(base, &base_bis_index_bitfield);
if (err != 0) {
return false;
}
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;
k_sem_give(&sem_base_received);
@ -256,7 +247,8 @@ static void syncable_cb(struct bt_bap_broadcast_sink *sink, bool encrypted)
k_sem_give(&sem_syncable);
}
static void base_recv_cb(struct bt_bap_broadcast_sink *sink, const struct bt_bap_base *base)
static void base_recv_cb(struct bt_bap_broadcast_sink *sink, const struct bt_bap_base *base,
size_t base_size)
{
k_sem_give(&sem_base_received);
}

View file

@ -659,6 +659,11 @@ legacy-debug-sym = BT_BAP_DEBUG_STREAM
module-str = "Bluetooth Audio Stream"
source "subsys/bluetooth/common/Kconfig.template.log_config_bt"
parent-module = BT
module = BT_BAP_BASE
module-str = "Bluetooth Basic Audio Profile Broadcast Audio Source Endpoint"
source "subsys/logging/Kconfig.template.log_config_inherit"
parent-module = BT
module = BT_AUDIO_CODEC
module-str = "Bluetooth Audio Codec"

View file

@ -49,6 +49,7 @@ zephyr_library_sources_ifdef(CONFIG_MCTL media_proxy.c)
zephyr_library_sources_ifdef(CONFIG_BT_ASCS ascs.c)
zephyr_library_sources_ifdef(CONFIG_BT_PACS pacs.c)
zephyr_library_sources_ifdef(CONFIG_BT_BAP_STREAM bap_stream.c codec.c bap_iso.c)
zephyr_library_sources_ifdef(CONFIG_BT_BAP_BASE bap_base.c)
zephyr_library_sources_ifdef(CONFIG_BT_BAP_UNICAST_SERVER bap_unicast_server.c)
zephyr_library_sources_ifdef(CONFIG_BT_BAP_UNICAST_CLIENT bap_unicast_client.c)
zephyr_library_sources_ifdef(CONFIG_BT_BAP_BROADCAST_SOURCE bap_broadcast_source.c)

View file

@ -286,5 +286,8 @@ config BT_BAP_DEBUG_STREAM_SEQ_NUM
the Bluetooth Audio functionality. This will provide a warning if the application
provides unexpected sequence numbers.
config BT_BAP_BASE
def_bool BT_BAP_BROADCAST_SINK || BT_BAP_BROADCAST_ASSISTANT || BT_BAP_SCAN_DELEGATOR
rsource "Kconfig.pacs"
rsource "Kconfig.ascs"

View file

@ -154,214 +154,3 @@ ssize_t bt_audio_ccc_cfg_write(struct bt_conn *conn, const struct bt_gatt_attr *
return sizeof(value);
}
#endif /* CONFIG_BT_CONN */
/* Broadcast sink depends on Scan Delegator, so we can just guard it with the Scan Delegator */
#if defined(CONFIG_BT_BAP_SCAN_DELEGATOR)
static int decode_bis_data(struct net_buf_simple *buf, struct bt_bap_base_bis_data *bis)
{
uint8_t len;
if (buf->len < BT_BAP_BASE_BIS_DATA_MIN_SIZE) {
LOG_DBG("Not enough bytes (%u) to decode BIS data", buf->len);
return -ENOMEM;
}
bis->index = net_buf_simple_pull_u8(buf);
if (!IN_RANGE(bis->index, BT_ISO_BIS_INDEX_MIN, BT_ISO_BIS_INDEX_MAX)) {
LOG_DBG("Invalid BIS index %u", bis->index);
return -EINVAL;
}
/* codec config data length */
len = net_buf_simple_pull_u8(buf);
if (len > buf->len) {
LOG_DBG("Invalid BIS specific codec config data length: %u (buf is %u)", len,
buf->len);
return -EMSGSIZE;
}
if (len > 0) {
#if CONFIG_BT_AUDIO_CODEC_CFG_MAX_METADATA_SIZE > 0
void *ltv_data;
if (len > sizeof(bis->data)) {
LOG_DBG("Cannot store codec config data of length %u", len);
return -ENOMEM;
}
ltv_data = net_buf_simple_pull_mem(buf, len);
bis->data_len = len;
memcpy(bis->data, ltv_data, len);
#else /* CONFIG_BT_AUDIO_CODEC_CFG_MAX_METADATA_SIZE == 0 */
LOG_DBG("Cannot store codec config data");
return -ENOMEM;
#endif /* CONFIG_BT_AUDIO_CODEC_CFG_MAX_METADATA_SIZE */
}
return 0;
}
static int decode_subgroup(struct net_buf_simple *buf, struct bt_bap_base_subgroup *subgroup)
{
struct bt_audio_codec_cfg *codec_cfg;
uint8_t len;
codec_cfg = &subgroup->codec_cfg;
subgroup->bis_count = net_buf_simple_pull_u8(buf);
if (subgroup->bis_count > ARRAY_SIZE(subgroup->bis_data)) {
LOG_DBG("BASE has more BIS %u than we support %u", subgroup->bis_count,
(uint8_t)ARRAY_SIZE(subgroup->bis_data));
return -ENOMEM;
}
codec_cfg->id = net_buf_simple_pull_u8(buf);
codec_cfg->cid = net_buf_simple_pull_le16(buf);
codec_cfg->vid = net_buf_simple_pull_le16(buf);
/* codec configuration data length */
len = net_buf_simple_pull_u8(buf);
if (len > buf->len) {
LOG_DBG("Invalid codec config data length: %u (buf is %u)", len, buf->len);
return -EINVAL;
}
#if CONFIG_BT_AUDIO_CODEC_CFG_MAX_DATA_SIZE > 0
void *cfg_ltv_data;
if (len > sizeof(subgroup->codec_cfg.data)) {
LOG_DBG("Cannot store codec config data of length %u", len);
return -ENOMEM;
}
cfg_ltv_data = net_buf_simple_pull_mem(buf, len);
subgroup->codec_cfg.data_len = len;
memcpy(subgroup->codec_cfg.data, cfg_ltv_data, len);
#else /* CONFIG_BT_AUDIO_CODEC_CFG_MAX_DATA_SIZE == 0 */
if (len > 0) {
LOG_DBG("Cannot store codec config data of length %u", len);
return -ENOMEM;
}
#endif /* CONFIG_BT_AUDIO_CODEC_CFG_MAX_DATA_SIZE > 0 */
/* codec metadata length */
len = net_buf_simple_pull_u8(buf);
if (len > buf->len) {
LOG_DBG("Invalid codec config data length: %u (buf is %u)", len, buf->len);
return -EMSGSIZE;
}
#if CONFIG_BT_AUDIO_CODEC_CFG_MAX_METADATA_SIZE > 0
void *meta_ltv_data;
if (len > sizeof(subgroup->codec_cfg.meta)) {
LOG_DBG("Cannot store codec config meta of length %u", len);
return -ENOMEM;
}
meta_ltv_data = net_buf_simple_pull_mem(buf, len);
subgroup->codec_cfg.meta_len = len;
memcpy(subgroup->codec_cfg.meta, meta_ltv_data, len);
#else /* CONFIG_BT_AUDIO_CODEC_CFG_MAX_METADATA_SIZE == 0 */
if (len > 0) {
LOG_DBG("Cannot store metadata");
return -ENOMEM;
}
#endif /* CONFIG_BT_AUDIO_CODEC_CFG_MAX_METADATA_SIZE */
for (size_t i = 0U; i < subgroup->bis_count; i++) {
const int err = decode_bis_data(buf, &subgroup->bis_data[i]);
if (err != 0) {
LOG_DBG("Failed to decode BIS data for bis[%zu]: %d", i, err);
return err;
}
}
return 0;
}
int bt_bap_decode_base(struct bt_data *data, struct bt_bap_base *base)
{
struct bt_uuid_16 broadcast_uuid;
struct net_buf_simple net_buf;
void *uuid;
CHECKIF(data == NULL) {
LOG_DBG("data is NULL");
return -EINVAL;
}
CHECKIF(base == NULL) {
LOG_DBG("base is NULL");
return -EINVAL;
}
if (data->type != BT_DATA_SVC_DATA16) {
LOG_DBG("Invalid type: %u", data->type);
return -ENOMSG;
}
if (data->data_len < BT_BAP_BASE_MIN_SIZE) {
return -EMSGSIZE;
}
net_buf_simple_init_with_data(&net_buf, (void *)data->data,
data->data_len);
uuid = net_buf_simple_pull_mem(&net_buf, BT_UUID_SIZE_16);
if (!bt_uuid_create(&broadcast_uuid.uuid, uuid, BT_UUID_SIZE_16)) {
LOG_ERR("bt_uuid_create failed");
return -EINVAL;
}
if (bt_uuid_cmp(&broadcast_uuid.uuid, BT_UUID_BASIC_AUDIO) != 0) {
LOG_DBG("Invalid UUID");
return -ENOMSG;
}
base->pd = net_buf_simple_pull_le24(&net_buf);
base->subgroup_count = net_buf_simple_pull_u8(&net_buf);
if (base->subgroup_count > ARRAY_SIZE(base->subgroups)) {
LOG_DBG("Cannot decode BASE with %u subgroups (max supported is %zu)",
base->subgroup_count, ARRAY_SIZE(base->subgroups));
return -ENOMEM;
}
for (size_t i = 0U; i < base->subgroup_count; i++) {
const int err = decode_subgroup(&net_buf, &base->subgroups[i]);
if (err != 0) {
LOG_DBG("Failed to decode subgroup[%zu]: %d", i, err);
return err;
}
}
return 0;
}
#endif /* CONFIG_BT_BAP_SCAN_DELEGATOR */

View file

@ -0,0 +1,564 @@
/* bap_base.c - BAP BASE handling */
/*
* Copyright (c) 2023 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/bluetooth/iso.h>
#include <zephyr/bluetooth/uuid.h>
#include <zephyr/bluetooth/audio/bap.h>
#include <zephyr/logging/log.h>
#include <zephyr/sys/check.h>
LOG_MODULE_REGISTER(bt_bap_base, CONFIG_BT_BAP_BASE_LOG_LEVEL);
/* The BASE and the following defines are defined by BAP v1.0.1, section 3.7.2.2 Basic Audio
* Announcements
*/
#define BASE_MAX_SIZE (UINT8_MAX - 1 /* type */ - BT_UUID_SIZE_16)
#define BASE_CODEC_ID_SIZE (1 /* id */ + 2 /* cid */ + 2 /* vid */)
#define BASE_PD_SIZE 3
#define BASE_SUBGROUP_COUNT_SIZE 1
#define BASE_NUM_BIS_SIZE 1
#define BASE_CC_LEN_SIZE 1
#define BASE_META_LEN_SIZE 1
#define BASE_BIS_INDEX_SIZE 1
#define BASE_BIS_CC_LEN_SIZE 1
#define BASE_SUBGROUP_MAX_SIZE (BASE_MAX_SIZE - BASE_PD_SIZE - BASE_SUBGROUP_COUNT_SIZE)
#define BASE_SUBGROUP_MIN_SIZE \
(BASE_NUM_BIS_SIZE + BASE_CODEC_ID_SIZE + BASE_CC_LEN_SIZE + BASE_META_LEN_SIZE + \
BASE_BIS_INDEX_SIZE + BASE_BIS_CC_LEN_SIZE)
#define BASE_MIN_SIZE \
(BT_UUID_SIZE_16 + BASE_PD_SIZE + BASE_SUBGROUP_COUNT_SIZE + BASE_SUBGROUP_MIN_SIZE)
#define BASE_SUBGROUP_MAX_COUNT (BASE_MAX_SIZE / BASE_SUBGROUP_MIN_SIZE)
static uint32_t base_pull_pd(struct net_buf_simple *net_buf)
{
return net_buf_simple_pull_le24(net_buf);
}
static uint8_t base_pull_bis_count(struct net_buf_simple *net_buf)
{
return net_buf_simple_pull_u8(net_buf);
}
static void base_pull_codec_id(struct net_buf_simple *net_buf,
struct bt_bap_base_codec_id *codec_id)
{
struct bt_bap_base_codec_id codec;
codec.id = net_buf_simple_pull_u8(net_buf); /* coding format */
codec.cid = net_buf_simple_pull_le16(net_buf); /* company id */
codec.vid = net_buf_simple_pull_le16(net_buf); /* VS codec id */
if (codec_id != NULL) {
*codec_id = codec;
}
}
static uint8_t base_pull_ltv(struct net_buf_simple *net_buf, uint8_t **data)
{
const uint8_t len = net_buf_simple_pull_u8(net_buf);
if (data == NULL) {
net_buf_simple_pull_mem(net_buf, len);
} else {
*data = net_buf_simple_pull_mem(net_buf, len);
}
return len;
}
static bool check_pull_ltv(struct net_buf_simple *net_buf)
{
uint8_t ltv_len;
if (net_buf->len < sizeof(ltv_len)) {
return false;
}
ltv_len = net_buf_simple_pull_u8(net_buf);
if (net_buf->len < ltv_len) {
return false;
}
net_buf_simple_pull_mem(net_buf, ltv_len);
return true;
}
const struct bt_bap_base *bt_bap_base_get_base_from_ad(const struct bt_data *ad)
{
struct bt_uuid_16 broadcast_uuid;
const struct bt_bap_base *base;
struct net_buf_simple net_buf;
uint8_t subgroup_count;
void *uuid;
CHECKIF(ad == NULL) {
LOG_DBG("data is NULL");
return NULL;
}
if (ad->type != BT_DATA_SVC_DATA16) {
LOG_DBG("Invalid type: %u", ad->type);
return NULL;
}
if (ad->data_len < BASE_MIN_SIZE) {
LOG_DBG("Invalid len: %u", ad->data_len);
return NULL;
}
net_buf_simple_init_with_data(&net_buf, (void *)ad->data, ad->data_len);
uuid = net_buf_simple_pull_mem(&net_buf, BT_UUID_SIZE_16);
if (!bt_uuid_create(&broadcast_uuid.uuid, uuid, BT_UUID_SIZE_16)) {
LOG_ERR("bt_uuid_create failed");
return NULL;
}
if (bt_uuid_cmp(&broadcast_uuid.uuid, BT_UUID_BASIC_AUDIO) != 0) {
LOG_DBG("Invalid UUID");
return NULL;
}
/* Store the start of the BASE */
base = (const struct bt_bap_base *)net_buf.data;
/* Pull all data to verify that the result BASE is valid */
base_pull_pd(&net_buf);
subgroup_count = net_buf_simple_pull_u8(&net_buf);
if (subgroup_count == 0 || subgroup_count > BASE_SUBGROUP_MAX_COUNT) {
LOG_DBG("Invalid subgroup count: %u", subgroup_count);
return NULL;
}
for (uint8_t i = 0U; i < subgroup_count; i++) {
uint8_t bis_count;
if (net_buf.len < sizeof(bis_count)) {
LOG_DBG("Invalid BASE length: %u", ad->data_len);
return NULL;
}
bis_count = base_pull_bis_count(&net_buf);
if (bis_count == 0 || bis_count > BT_ISO_MAX_GROUP_ISO_COUNT) {
LOG_DBG("Subgroup[%u]: Invalid BIS count: %u", i, bis_count);
return NULL;
}
if (net_buf.len < BASE_CODEC_ID_SIZE) {
LOG_DBG("Invalid BASE length: %u", ad->data_len);
return NULL;
}
base_pull_codec_id(&net_buf, NULL);
/* Pull CC */
if (!check_pull_ltv(&net_buf)) {
LOG_DBG("Invalid BASE length: %u", ad->data_len);
return NULL;
}
/* Pull meta */
if (!check_pull_ltv(&net_buf)) {
LOG_DBG("Invalid BASE length: %u", ad->data_len);
return NULL;
}
for (uint8_t j = 0U; j < bis_count; j++) {
uint8_t bis_index;
if (net_buf.len < sizeof(bis_index)) {
LOG_DBG("Invalid BASE length: %u", ad->data_len);
return NULL;
}
bis_index = net_buf_simple_pull_u8(&net_buf);
if (bis_index == 0 || bis_index > BT_ISO_BIS_INDEX_MAX) {
LOG_DBG("Subgroup[%u]: Invalid BIS index: %u", i, bis_index);
return NULL;
}
/* Pull BIS CC data */
if (!check_pull_ltv(&net_buf)) {
LOG_DBG("Invalid BASE length: %u", ad->data_len);
return NULL;
}
}
}
return base;
}
int bt_bap_base_get_pres_delay(const struct bt_bap_base *base)
{
struct net_buf_simple net_buf;
uint32_t pd;
CHECKIF(base == NULL) {
LOG_DBG("base is NULL");
return -EINVAL;
}
net_buf_simple_init_with_data(&net_buf, (void *)base, sizeof(pd));
pd = base_pull_pd(&net_buf);
return (int)pd; /* PD is 24-bit so it fits in an int */
}
int bt_bap_base_get_subgroup_count(const struct bt_bap_base *base)
{
struct net_buf_simple net_buf;
uint8_t subgroup_count;
CHECKIF(base == NULL) {
LOG_DBG("base is NULL");
return -EINVAL;
}
net_buf_simple_init_with_data(&net_buf, (void *)base, BASE_MAX_SIZE);
base_pull_pd(&net_buf);
subgroup_count = net_buf_simple_pull_u8(&net_buf);
return (int)subgroup_count; /* subgroup_count is 8-bit so it fits in an int */
}
int bt_bap_base_foreach_subgroup(const struct bt_bap_base *base,
bool (*func)(const struct bt_bap_base_subgroup *data,
void *user_data),
void *user_data)
{
struct bt_bap_base_subgroup *subgroup;
struct net_buf_simple net_buf;
uint8_t subgroup_count;
CHECKIF(base == NULL) {
LOG_DBG("base is NULL");
return -EINVAL;
}
CHECKIF(func == NULL) {
LOG_DBG("func is NULL");
return -EINVAL;
}
net_buf_simple_init_with_data(&net_buf, (void *)base, BASE_MAX_SIZE);
base_pull_pd(&net_buf);
subgroup_count = net_buf_simple_pull_u8(&net_buf);
for (uint8_t i = 0U; i < subgroup_count; i++) {
subgroup = (struct bt_bap_base_subgroup *)net_buf.data;
if (!func(subgroup, user_data)) {
LOG_DBG("user stopped parsing");
return -ECANCELED;
}
/* Parse subgroup data to get next subgroup pointer */
if (subgroup_count > 1) { /* Only parse data if it isn't the last one */
uint8_t bis_count;
bis_count = base_pull_bis_count(&net_buf);
base_pull_codec_id(&net_buf, NULL);
/* Codec config */
base_pull_ltv(&net_buf, NULL);
/* meta */
base_pull_ltv(&net_buf, NULL);
for (uint8_t j = 0U; j < bis_count; j++) {
net_buf_simple_pull_u8(&net_buf); /* index */
/* Codec config */
base_pull_ltv(&net_buf, NULL);
}
}
}
return 0;
}
int bt_bap_base_get_subgroup_codec_id(const struct bt_bap_base_subgroup *subgroup,
struct bt_bap_base_codec_id *codec_id)
{
struct net_buf_simple net_buf;
CHECKIF(subgroup == NULL) {
LOG_DBG("subgroup is NULL");
return -EINVAL;
}
CHECKIF(codec_id == NULL) {
LOG_DBG("codec_id is NULL");
return -EINVAL;
}
net_buf_simple_init_with_data(&net_buf, (void *)subgroup, BASE_SUBGROUP_MAX_SIZE);
base_pull_bis_count(&net_buf);
base_pull_codec_id(&net_buf, codec_id);
return 0;
}
int bt_bap_base_get_subgroup_codec_data(const struct bt_bap_base_subgroup *subgroup, uint8_t **data)
{
struct net_buf_simple net_buf;
CHECKIF(subgroup == NULL) {
LOG_DBG("subgroup is NULL");
return -EINVAL;
}
CHECKIF(data == NULL) {
LOG_DBG("data is NULL");
return -EINVAL;
}
net_buf_simple_init_with_data(&net_buf, (void *)subgroup, BASE_SUBGROUP_MAX_SIZE);
base_pull_bis_count(&net_buf);
base_pull_codec_id(&net_buf, NULL);
/* Codec config */
return base_pull_ltv(&net_buf, data);
}
int bt_bap_base_get_subgroup_codec_meta(const struct bt_bap_base_subgroup *subgroup, uint8_t **meta)
{
struct net_buf_simple net_buf;
CHECKIF(subgroup == NULL) {
LOG_DBG("subgroup is NULL");
return -EINVAL;
}
CHECKIF(meta == NULL) {
LOG_DBG("meta is NULL");
return -EINVAL;
}
net_buf_simple_init_with_data(&net_buf, (void *)subgroup, BASE_SUBGROUP_MAX_SIZE);
base_pull_bis_count(&net_buf);
base_pull_codec_id(&net_buf, NULL);
/* Codec config */
base_pull_ltv(&net_buf, NULL);
/* meta */
return base_pull_ltv(&net_buf, meta);
}
int bt_bap_base_subgroup_codec_to_codec_cfg(const struct bt_bap_base_subgroup *subgroup,
struct bt_audio_codec_cfg *codec_cfg)
{
struct bt_bap_base_codec_id codec_id;
struct net_buf_simple net_buf;
uint8_t *ltv_data;
uint8_t ltv_len;
CHECKIF(subgroup == NULL) {
LOG_DBG("subgroup is NULL");
return -EINVAL;
}
CHECKIF(codec_cfg == NULL) {
LOG_DBG("codec_cfg is NULL");
return -EINVAL;
}
net_buf_simple_init_with_data(&net_buf, (void *)subgroup, BASE_SUBGROUP_MAX_SIZE);
base_pull_bis_count(&net_buf);
base_pull_codec_id(&net_buf, &codec_id);
codec_cfg->id = codec_id.id;
codec_cfg->cid = codec_id.cid;
codec_cfg->vid = codec_id.vid;
/* Codec config */
ltv_len = base_pull_ltv(&net_buf, &ltv_data);
if (ltv_len > ARRAY_SIZE(codec_cfg->data)) {
LOG_DBG("Cannot fit %u octets of codec data (max %zu)", ltv_len,
ARRAY_SIZE(codec_cfg->data));
return -ENOMEM;
}
codec_cfg->data_len = ltv_len;
memcpy(codec_cfg->data, ltv_data, ltv_len);
/* Meta */
ltv_len = base_pull_ltv(&net_buf, &ltv_data);
if (ltv_len > ARRAY_SIZE(codec_cfg->meta)) {
LOG_DBG("Cannot fit %u octets of codec meta (max %zu)", ltv_len,
ARRAY_SIZE(codec_cfg->meta));
return -ENOMEM;
}
codec_cfg->meta_len = ltv_len;
memcpy(codec_cfg->meta, ltv_data, ltv_len);
return 0;
}
int bt_bap_base_get_subgroup_bis_count(const struct bt_bap_base_subgroup *subgroup)
{
struct net_buf_simple net_buf;
CHECKIF(subgroup == NULL) {
LOG_DBG("subgroup is NULL");
return -EINVAL;
}
net_buf_simple_init_with_data(&net_buf, (void *)subgroup, BASE_SUBGROUP_MAX_SIZE);
return base_pull_bis_count(&net_buf);
}
int bt_bap_base_subgroup_foreach_bis(const struct bt_bap_base_subgroup *subgroup,
bool (*func)(const struct bt_bap_base_subgroup_bis *subgroup,
void *user_data),
void *user_data)
{
struct net_buf_simple net_buf;
uint8_t bis_count;
CHECKIF(subgroup == NULL) {
LOG_DBG("subgroup is NULL");
return -EINVAL;
}
CHECKIF(func == NULL) {
LOG_DBG("func is NULL");
return -EINVAL;
}
net_buf_simple_init_with_data(&net_buf, (void *)subgroup, BASE_SUBGROUP_MAX_SIZE);
bis_count = base_pull_bis_count(&net_buf);
base_pull_codec_id(&net_buf, NULL);
/* Codec config */
base_pull_ltv(&net_buf, NULL);
/* meta */
base_pull_ltv(&net_buf, NULL);
for (uint8_t i = 0U; i < bis_count; i++) {
struct bt_bap_base_subgroup_bis bis;
bis.index = net_buf_simple_pull_u8(&net_buf); /* index */
/* Codec config */
bis.data_len = base_pull_ltv(&net_buf, &bis.data);
if (!func(&bis, user_data)) {
LOG_DBG("user stopped parsing");
return -ECANCELED;
}
}
return 0;
}
int bt_bap_base_subgroup_bis_codec_to_codec_cfg(const struct bt_bap_base_subgroup_bis *bis,
struct bt_audio_codec_cfg *codec_cfg)
{
CHECKIF(bis == NULL) {
LOG_DBG("bis is NULL");
return -EINVAL;
}
CHECKIF(codec_cfg == NULL) {
LOG_DBG("codec_cfg is NULL");
return -EINVAL;
}
if (bis->data_len > ARRAY_SIZE(codec_cfg->data)) {
LOG_DBG("Cannot fit %u octets of codec data (max %zu)", bis->data_len,
ARRAY_SIZE(codec_cfg->data));
return -ENOMEM;
}
codec_cfg->data_len = bis->data_len;
memcpy(codec_cfg->data, bis->data, bis->data_len);
return 0;
}
static bool base_subgroup_bis_cb(const struct bt_bap_base_subgroup_bis *bis, void *user_data)
{
uint32_t *base_bis_index_bitfield = user_data;
*base_bis_index_bitfield |= BIT(bis->index);
return true;
}
static bool base_subgroup_cb(const struct bt_bap_base_subgroup *subgroup, void *user_data)
{
const int err = bt_bap_base_subgroup_foreach_bis(subgroup, base_subgroup_bis_cb, user_data);
if (err != 0) {
LOG_DBG("Failed to parse all BIS: %d", err);
return false;
}
return true;
}
int bt_bap_base_get_bis_indexes(const struct bt_bap_base *base, uint32_t *bis_indexes)
{
CHECKIF(base == NULL) {
LOG_DBG("base is NULL");
return -EINVAL;
}
CHECKIF(bis_indexes == NULL) {
LOG_DBG("bis_indexes is NULL");
return -EINVAL;
}
*bis_indexes = 0U;
return bt_bap_base_foreach_subgroup(base, base_subgroup_cb, bis_indexes);
}

View file

@ -41,7 +41,7 @@ LOG_MODULE_REGISTER(bt_bap_broadcast_sink, CONFIG_BT_BAP_BROADCAST_SINK_LOG_LEVE
#define INVALID_BROADCAST_ID 0xFFFFFFFF
static struct bt_bap_ep broadcast_sink_eps[CONFIG_BT_BAP_BROADCAST_SNK_COUNT]
[BROADCAST_SNK_STREAM_CNT];
[CONFIG_BT_BAP_BROADCAST_SNK_STREAM_COUNT];
static struct bt_bap_broadcast_sink broadcast_sinks[CONFIG_BT_BAP_BROADCAST_SNK_COUNT];
struct codec_cap_lookup_id_data {
@ -91,8 +91,7 @@ static bool find_recv_state_by_pa_sync_cb(const struct bt_bap_scan_delegator_rec
static void update_recv_state_big_synced(const struct bt_bap_broadcast_sink *sink)
{
const struct bt_bap_scan_delegator_recv_state *recv_state;
struct bt_bap_scan_delegator_mod_src_param mod_src_param = { 0 };
const struct bt_bap_base *base;
struct bt_bap_scan_delegator_mod_src_param mod_src_param = {0};
int err;
recv_state = bt_bap_scan_delegator_find_state(find_recv_state_by_sink_cb, (void *)sink);
@ -102,24 +101,13 @@ static void update_recv_state_big_synced(const struct bt_bap_broadcast_sink *sin
return;
}
base = &sink->base;
mod_src_param.num_subgroups = base->subgroup_count;
for (uint8_t i = 0U; i < base->subgroup_count; i++) {
mod_src_param.num_subgroups = sink->subgroup_count;
for (uint8_t i = 0U; i < sink->subgroup_count; i++) {
struct bt_bap_scan_delegator_subgroup *subgroup_param = &mod_src_param.subgroups[i];
const struct bt_bap_base_subgroup *subgroup = &base->subgroups[i];
const struct bt_bap_broadcast_sink_subgroup *sink_subgroup = &sink->subgroups[i];
/* Update the BIS sync indexes for the subgroup based on the BASE*/
for (size_t j = 0U; j < subgroup->bis_count; j++) {
const struct bt_bap_base_bis_data *bis_data = &subgroup->bis_data[j];
subgroup_param->bis_sync |= BIT(bis_data->index);
}
/* Update the bis_sync so that the bis_sync value only contains the indexes that we
* are actually synced to
*/
subgroup_param->bis_sync &= sink->indexes_bitfield;
/* Set the bis_sync value to the indexes available per subgroup */
subgroup_param->bis_sync = sink_subgroup->bis_indexes & sink->indexes_bitfield;
}
if (recv_state->encrypt_state == BT_BAP_BIG_ENC_STATE_BCODE_REQ) {
@ -443,33 +431,44 @@ static void broadcast_sink_add_src(struct bt_bap_broadcast_sink *sink)
}
}
static bool base_subgroup_meta_cb(const struct bt_bap_base_subgroup *subgroup, void *user_data)
{
struct bt_bap_scan_delegator_mod_src_param *mod_src_param = user_data;
struct bt_bap_scan_delegator_subgroup *subgroup_param;
uint8_t *meta;
int ret;
ret = bt_bap_base_get_subgroup_codec_meta(subgroup, &meta);
if (ret < 0) {
return false;
}
subgroup_param = &mod_src_param->subgroups[mod_src_param->num_subgroups++];
subgroup_param->metadata_len = (uint8_t)ret;
memcpy(subgroup_param->metadata, meta, subgroup_param->metadata_len);
return true;
}
static int update_recv_state_base_copy_meta(const struct bt_bap_base *base,
struct bt_bap_scan_delegator_mod_src_param *param)
{
if (base->subgroup_count > ARRAY_SIZE(param->subgroups)) {
LOG_DBG("Could not fit %zu subgroups in the mod param (max %zu)",
base->subgroup_count, ARRAY_SIZE(param->subgroups));
}
int err;
param->num_subgroups = base->subgroup_count;
for (uint8_t i = 0U; i < base->subgroup_count; i++) {
struct bt_bap_scan_delegator_subgroup *subgroup_param = &param->subgroups[i];
const struct bt_bap_base_subgroup *subgroup = &base->subgroups[i];
subgroup_param->metadata_len = subgroup->codec_cfg.meta_len;
memcpy(subgroup_param->metadata, subgroup->codec_cfg.meta,
subgroup->codec_cfg.meta_len);
err = bt_bap_base_foreach_subgroup(base, base_subgroup_meta_cb, param);
if (err != 0) {
LOG_DBG("Failed to parse subgroups: %d", err);
return err;
}
return 0;
}
static void update_recv_state_base(const struct bt_bap_broadcast_sink *sink)
static void update_recv_state_base(const struct bt_bap_broadcast_sink *sink,
const struct bt_bap_base *base)
{
struct bt_bap_scan_delegator_mod_src_param mod_src_param = { 0 };
const struct bt_bap_scan_delegator_recv_state *recv_state;
const struct bt_bap_base *base;
int err;
recv_state = bt_bap_scan_delegator_find_state(find_recv_state_by_sink_cb, (void *)sink);
@ -479,8 +478,6 @@ static void update_recv_state_base(const struct bt_bap_broadcast_sink *sink)
return;
}
base = &sink->base;
err = update_recv_state_base_copy_meta(base, &mod_src_param);
if (err != 0) {
LOG_WRN("Failed to modify Receive State for sink %p: %d", sink, err);
@ -498,55 +495,180 @@ static void update_recv_state_base(const struct bt_bap_broadcast_sink *sink)
}
}
static bool pa_decode_base(struct bt_data *data, void *user_data)
static bool codec_lookup_id(const struct bt_pacs_cap *cap, void *user_data)
{
struct bt_bap_broadcast_sink *sink = (struct bt_bap_broadcast_sink *)user_data;
struct bt_bap_broadcast_sink_cb *listener;
struct bt_bap_base base = { 0 };
struct codec_cap_lookup_id_data *data = user_data;
if (data->type != BT_DATA_SVC_DATA16) {
return true;
}
if (cap->codec_cap->id == data->id) {
data->codec_cap = cap->codec_cap;
if (data->data_len < BT_BAP_BASE_MIN_SIZE) {
return true;
}
if (bt_bap_decode_base(data, &base) != 0) {
return false;
}
if (atomic_test_bit(sink->flags,
BT_BAP_BROADCAST_SINK_FLAG_BIGINFO_RECEIVED)) {
uint8_t num_bis = 0;
return true;
}
for (int i = 0; i < base.subgroup_count; i++) {
num_bis += base.subgroups[i].bis_count;
}
static bool base_subgroup_bis_index_cb(const struct bt_bap_base_subgroup_bis *bis, void *user_data)
{
uint32_t *bis_indexes = user_data;
if (num_bis > sink->biginfo_num_bis) {
LOG_WRN("BASE contains more BIS than reported by BIGInfo");
*bis_indexes |= BIT(bis->index);
return true;
}
static bool base_subgroup_cb(const struct bt_bap_base_subgroup *subgroup, void *user_data)
{
struct bt_bap_broadcast_sink *sink = user_data;
struct bt_bap_broadcast_sink_subgroup *sink_subgroup =
&sink->subgroups[sink->subgroup_count];
struct codec_cap_lookup_id_data lookup_data = {0};
int ret;
if (sink->subgroup_count == ARRAY_SIZE(sink->subgroups)) {
/* We've parsed as many subgroups as we support */
LOG_DBG("Could only store %u subgroups", sink->subgroup_count);
return false;
}
ret = bt_bap_base_subgroup_codec_to_codec_cfg(subgroup, &sink_subgroup->codec_cfg);
if (ret < 0) {
LOG_DBG("Could not store codec_cfg: %d", ret);
return false;
}
ret = bt_bap_base_subgroup_foreach_bis(subgroup, base_subgroup_bis_index_cb,
&sink_subgroup->bis_indexes);
if (ret < 0) {
LOG_DBG("Could not parse BISes: %d", ret);
return false;
}
/* Lookup and assign path_id based on capabilities */
lookup_data.id = sink_subgroup->codec_cfg.id;
bt_pacs_cap_foreach(BT_AUDIO_DIR_SINK, codec_lookup_id, &lookup_data);
if (lookup_data.codec_cap == NULL) {
LOG_DBG("Codec with id %u is not supported by our capabilities", lookup_data.id);
} else {
/* Add BIS to bitfield of valid BIS indexes we support */
sink->valid_indexes_bitfield |= sink_subgroup->bis_indexes;
}
sink->subgroup_count++;
return true;
}
static int store_base_info(struct bt_bap_broadcast_sink *sink, const struct bt_bap_base *base)
{
int ret;
sink->valid_indexes_bitfield = 0U;
sink->subgroup_count = 0U;
ret = bt_bap_base_get_pres_delay(base);
if (ret < 0) {
LOG_DBG("Could not get presentation delay: %d", ret);
return ret;
}
sink->codec_qos.pd = (uint32_t)ret;
ret = bt_bap_base_foreach_subgroup(base, base_subgroup_cb, sink);
if (ret != 0) {
LOG_DBG("Failed to parse all subgroups: %d", ret);
return ret;
}
return 0;
}
static bool base_subgroup_bis_count_cb(const struct bt_bap_base_subgroup *subgroup, void *user_data)
{
uint8_t *bis_cnt = user_data;
int ret;
ret = bt_bap_base_get_subgroup_bis_count(subgroup);
if (ret < 0) {
return false;
}
*bis_cnt += (uint8_t)ret;
return true;
}
static int base_get_bis_count(const struct bt_bap_base *base)
{
uint8_t bis_cnt = 0U;
int err;
err = bt_bap_base_foreach_subgroup(base, base_subgroup_bis_count_cb, &bis_cnt);
if (err != 0) {
LOG_DBG("Failed to parse subgroups: %d", err);
return err;
}
return bis_cnt;
}
static bool pa_decode_base(struct bt_data *data, void *user_data)
{
struct bt_bap_broadcast_sink *sink = (struct bt_bap_broadcast_sink *)user_data;
const struct bt_bap_base *base = bt_bap_base_get_base_from_ad(data);
struct bt_bap_broadcast_sink_cb *listener;
size_t base_size;
int ret;
/* Base is NULL if the data does not contain a valid BASE */
if (base == NULL) {
return true;
}
if (atomic_test_bit(sink->flags, BT_BAP_BROADCAST_SINK_FLAG_BIGINFO_RECEIVED)) {
ret = base_get_bis_count(base);
if (ret < 0) {
LOG_DBG("Invalid BASE: %d", ret);
return false;
} else if (ret != sink->biginfo_num_bis) {
LOG_DBG("BASE contains different amount of BIS (%u) than reported by "
"BIGInfo (%u)",
ret, sink->biginfo_num_bis);
return false;
}
}
sink->codec_qos.pd = base.pd;
if (memcmp(&sink->base, &base, sizeof(base)) != 0) {
/* We only overwrite the sink->base data once the base has
* successfully been decoded to avoid overwriting it with
* invalid data
*/
(void)memcpy(&sink->base, &base, sizeof(base));
/* Store newest BASE info until we are BIG synced */
if (sink->big == NULL) {
LOG_DBG("Updating BASE for sink %p with %d subgroups\n", sink,
bt_bap_base_get_subgroup_count(base));
if (atomic_test_bit(sink->flags,
BT_BAP_BROADCAST_SINK_FLAG_SRC_ID_VALID)) {
update_recv_state_base(sink);
ret = store_base_info(sink, base);
if (ret < 0) {
LOG_DBG("Could not store BASE information: %d", ret);
/* If it returns -ECANCELED it means that we stopped parsing ourselves due
* to lack of memory. In this case we can still provide the BASE to the
* application else abort
*/
if (ret != -ECANCELED) {
return false;
}
}
}
if (atomic_test_bit(sink->flags, BT_BAP_BROADCAST_SINK_FLAG_SRC_ID_VALID)) {
update_recv_state_base(sink, base);
}
/* We provide the BASE without the service data UUID */
base_size = data->data_len - BT_UUID_SIZE_16;
SYS_SLIST_FOR_EACH_CONTAINER(&sink_cbs, listener, _node) {
if (listener->base_recv != NULL) {
listener->base_recv(sink, &base);
listener->base_recv(sink, base, base_size);
}
}
@ -815,36 +937,20 @@ static void broadcast_sink_cleanup(struct bt_bap_broadcast_sink *sink)
(void)memset(sink, 0, sizeof(*sink)); /* also clears flags */
}
static struct bt_audio_codec_cfg *codec_cfg_from_base_by_index(struct bt_bap_base *base,
static struct bt_audio_codec_cfg *codec_cfg_from_base_by_index(struct bt_bap_broadcast_sink *sink,
uint8_t index)
{
for (size_t i = 0U; i < base->subgroup_count; i++) {
struct bt_bap_base_subgroup *subgroup = &base->subgroups[i];
for (size_t i = 0U; i < sink->subgroup_count; i++) {
struct bt_bap_broadcast_sink_subgroup *subgroup = &sink->subgroups[i];
for (size_t j = 0U; j < subgroup->bis_count; j++) {
if (subgroup->bis_data[j].index == index) {
return &subgroup->codec_cfg;
}
if ((subgroup->bis_indexes & BIT(index)) != 0) {
return &subgroup->codec_cfg;
}
}
return NULL;
}
static bool codec_lookup_id(const struct bt_pacs_cap *cap, void *user_data)
{
struct codec_cap_lookup_id_data *data = user_data;
if (cap->codec_cap->id == data->id) {
data->codec_cap = cap->codec_cap;
return false;
}
return true;
}
int bt_bap_broadcast_sink_create(struct bt_le_per_adv_sync *pa_sync, uint32_t broadcast_id,
struct bt_bap_broadcast_sink **out_sink)
{
@ -906,7 +1012,7 @@ int bt_bap_broadcast_sink_sync(struct bt_bap_broadcast_sink *sink, uint32_t inde
struct bt_bap_stream *streams[], const uint8_t broadcast_code[16])
{
struct bt_iso_big_sync_param param;
struct bt_audio_codec_cfg *codec_cfgs[BROADCAST_SNK_STREAM_CNT] = {NULL};
struct bt_audio_codec_cfg *codec_cfgs[CONFIG_BT_BAP_BROADCAST_SNK_SUBGROUP_COUNT] = {NULL};
uint8_t stream_count;
int err;
@ -954,37 +1060,26 @@ int bt_bap_broadcast_sink_sync(struct bt_bap_broadcast_sink *sink, uint32_t inde
}
/* Validate that number of bits set is less than number of streams */
if ((indexes_bitfield & sink->valid_indexes_bitfield) != indexes_bitfield) {
LOG_DBG("Request BIS indexes 0x%08X contains bits not support by the Broadcast "
"Sink 0x%08X",
indexes_bitfield, sink->valid_indexes_bitfield);
return -EINVAL;
}
stream_count = 0;
for (int i = 1; i < BT_ISO_MAX_GROUP_ISO_COUNT; i++) {
if ((indexes_bitfield & BIT(i)) != 0) {
struct bt_audio_codec_cfg *codec_cfg =
codec_cfg_from_base_by_index(&sink->base, i);
struct codec_cap_lookup_id_data lookup_data = {};
codec_cfg_from_base_by_index(sink, i);
if (codec_cfg == NULL) {
LOG_DBG("Index %d not found in BASE", i);
return -EINVAL;
}
/* Lookup and assign path_id based on capabilities */
lookup_data.id = codec_cfg->id;
bt_pacs_cap_foreach(BT_AUDIO_DIR_SINK, codec_lookup_id,
&lookup_data);
if (lookup_data.codec_cap == NULL) {
LOG_DBG("Codec with id %u is not supported by our capabilities",
codec_cfg->id);
return -ENOENT;
}
codec_cfg->path_id = lookup_data.codec_cap->path_id;
__ASSERT(codec_cfg != NULL, "Index %d not found in sink", i);
codec_cfgs[stream_count++] = codec_cfg;
if (stream_count > BROADCAST_SNK_STREAM_CNT) {
LOG_DBG("Cannot sync to more than %d streams",
BROADCAST_SNK_STREAM_CNT);
if (stream_count > CONFIG_BT_BAP_BROADCAST_SNK_STREAM_COUNT) {
LOG_DBG("Cannot sync to more than %d streams (%u was requested)",
CONFIG_BT_BAP_BROADCAST_SNK_STREAM_COUNT, stream_count);
return -EINVAL;
}
}

View file

@ -124,19 +124,27 @@ enum bt_bap_broadcast_sink_flag {
BT_BAP_BROADCAST_SINK_FLAG_NUM_FLAGS,
};
struct bt_bap_broadcast_sink_subgroup {
uint32_t bis_indexes;
struct bt_audio_codec_cfg codec_cfg;
};
#if defined(CONFIG_BT_BAP_BROADCAST_SINK)
struct bt_bap_broadcast_sink {
uint8_t index; /* index of broadcast_snks array */
uint8_t stream_count;
uint8_t bass_src_id;
uint8_t subgroup_count;
uint16_t iso_interval;
uint16_t biginfo_num_bis;
uint32_t broadcast_id; /* 24 bit */
uint32_t indexes_bitfield;
struct bt_bap_base base;
uint32_t valid_indexes_bitfield; /* based on codec support */
struct bt_audio_codec_qos codec_qos;
struct bt_le_per_adv_sync *pa_sync;
struct bt_iso_big *big;
struct bt_iso_chan *bis[BROADCAST_SNK_STREAM_CNT];
struct bt_iso_chan *bis[CONFIG_BT_BAP_BROADCAST_SNK_STREAM_COUNT];
struct bt_bap_broadcast_sink_subgroup subgroups[CONFIG_BT_BAP_BROADCAST_SNK_SUBGROUP_COUNT];
const struct bt_bap_scan_delegator_recv_state *recv_state;
/* The streams used to create the broadcast sink */
sys_slist_t streams;
@ -144,6 +152,7 @@ struct bt_bap_broadcast_sink {
/** Flags */
ATOMIC_DEFINE(flags, BT_BAP_BROADCAST_SINK_FLAG_NUM_FLAGS);
};
#endif /* CONFIG_BT_BAP_BROADCAST_SINK */
static inline const char *bt_bap_ep_state_str(uint8_t state)
{

View file

@ -99,7 +99,8 @@ struct broadcast_source {
struct broadcast_sink {
struct bt_bap_broadcast_sink *bap_sink;
struct bt_le_per_adv_sync *pa_sync;
struct bt_bap_base received_base;
uint8_t received_base[UINT8_MAX];
uint8_t base_size;
uint32_t broadcast_id;
size_t stream_cnt;
bool syncable;
@ -227,51 +228,84 @@ extern struct shell_stream broadcast_source_streams[CONFIG_BT_BAP_BROADCAST_SRC_
extern struct broadcast_source default_source;
#endif /* CONFIG_BT_BAP_BROADCAST_SOURCE */
#if BROADCAST_SNK_SUBGROUP_CNT > 0
static inline void print_base(const struct shell *sh, const struct bt_bap_base *base)
static inline bool print_base_subgroup_bis_cb(const struct bt_bap_base_subgroup_bis *bis,
void *user_data)
{
uint8_t bis_indexes[BT_ISO_MAX_GROUP_ISO_COUNT] = {0};
/* "0xXX " requires 5 characters */
char bis_indexes_str[5 * ARRAY_SIZE(bis_indexes) + 1];
size_t index_count = 0;
struct bt_bap_base_codec_id *codec_id = user_data;
for (size_t i = 0U; i < base->subgroup_count; i++) {
const struct bt_bap_base_subgroup *subgroup;
subgroup = &base->subgroups[i];
shell_print(sh, "Subgroup[%d]:", i);
print_codec_cfg(sh, &subgroup->codec_cfg);
for (size_t j = 0U; j < subgroup->bis_count; j++) {
const struct bt_bap_base_bis_data *bis_data;
bis_data = &subgroup->bis_data[j];
shell_print(sh, "BIS[%d] index 0x%02x", j, bis_data->index);
bis_indexes[index_count++] = bis_data->index;
#if CONFIG_BT_AUDIO_CODEC_CFG_MAX_DATA_SIZE > 0
shell_hexdump(sh, bis_data->data, bis_data->data_len);
#endif /* CONFIG_BT_AUDIO_CODEC_CFG_MAX_DATA_SIZE > 0 */
}
shell_print(ctx_shell, "\t\tBIS index: 0x%02X", bis->index);
/* Print CC data */
if (codec_id->id == BT_HCI_CODING_FORMAT_LC3) {
print_ltv_array(ctx_shell, "\t\tdata", bis->data, bis->data_len);
} else { /* If not LC3, we cannot assume it's LTV */
shell_hexdump(ctx_shell, bis->data, bis->data_len);
}
(void)memset(bis_indexes_str, 0, sizeof(bis_indexes_str));
/* Create space separated list of indexes as hex values */
for (size_t i = 0U; i < index_count; i++) {
char bis_index_str[6];
sprintf(bis_index_str, "0x%02x ", bis_indexes[i]);
strcat(bis_indexes_str, bis_index_str);
shell_print(sh, "[%d]: %s", i, bis_index_str);
}
shell_print(sh, "Possible indexes: %s", bis_indexes_str);
return true;
}
static inline bool print_base_subgroup_cb(const struct bt_bap_base_subgroup *subgroup,
void *user_data)
{
struct bt_bap_base_codec_id codec_id;
uint8_t *data;
int ret;
shell_print(ctx_shell, "Subgroup %p:", subgroup);
ret = bt_bap_base_get_subgroup_codec_id(subgroup, &codec_id);
if (ret < 0) {
return false;
}
shell_print(ctx_shell, "\tCodec Format: 0x%02X", codec_id.id);
shell_print(ctx_shell, "\tCompany ID : 0x%04X", codec_id.cid);
shell_print(ctx_shell, "\tVendor ID : 0x%04X", codec_id.vid);
ret = bt_bap_base_get_subgroup_codec_data(subgroup, &data);
if (ret < 0) {
return false;
}
/* Print CC data */
if (codec_id.id == BT_HCI_CODING_FORMAT_LC3) {
print_ltv_array(ctx_shell, "\tdata", data, (uint8_t)ret);
} else { /* If not LC3, we cannot assume it's LTV */
shell_hexdump(ctx_shell, data, (uint8_t)ret);
}
ret = bt_bap_base_get_subgroup_codec_meta(subgroup, &data);
if (ret < 0) {
return false;
}
/* Print metadata */
if (codec_id.id == BT_HCI_CODING_FORMAT_LC3) {
print_ltv_array(ctx_shell, "\tdata", data, (uint8_t)ret);
} else { /* If not LC3, we cannot assume it's LTV */
shell_hexdump(ctx_shell, data, (uint8_t)ret);
}
ret = bt_bap_base_subgroup_foreach_bis(subgroup, print_base_subgroup_bis_cb, &codec_id);
if (ret < 0) {
return false;
}
return true;
}
static inline void print_base(const struct bt_bap_base *base)
{
int err;
shell_print(ctx_shell, "Presentation delay: %d", bt_bap_base_get_pres_delay(base));
shell_print(ctx_shell, "Subgroup count: %d", bt_bap_base_get_subgroup_count(base));
err = bt_bap_base_foreach_subgroup(base, print_base_subgroup_cb, NULL);
if (err < 0) {
shell_info(ctx_shell, "Invalid BASE: %d", err);
}
}
#endif /* BROADCAST_SNK_SUBGROUP_CNT > 0 */
static inline void copy_unicast_stream_preset(struct shell_stream *stream,
const struct named_lc3_preset *named_preset)

View file

@ -57,7 +57,7 @@ struct shell_stream broadcast_source_streams[CONFIG_BT_BAP_BROADCAST_SRC_STREAM_
struct broadcast_source default_source;
#endif /* CONFIG_BT_BAP_BROADCAST_SOURCE */
#if defined(CONFIG_BT_BAP_BROADCAST_SINK)
static struct bt_bap_stream broadcast_sink_streams[BROADCAST_SNK_STREAM_CNT];
static struct bt_bap_stream broadcast_sink_streams[CONFIG_BT_BAP_BROADCAST_SNK_STREAM_COUNT];
static struct broadcast_sink default_broadcast_sink;
#endif /* CONFIG_BT_BAP_BROADCAST_SINK */
static struct bt_bap_stream *default_stream;
@ -1701,77 +1701,18 @@ static void broadcast_scan_recv(const struct bt_le_scan_recv_info *info, struct
}
}
static bool print_data_func_cb(struct bt_data *data, void *user_data)
static void base_recv(struct bt_bap_broadcast_sink *sink, const struct bt_bap_base *base,
size_t base_size)
{
shell_print(ctx_shell, "type 0x%02x len %u", data->type, data->data_len);
shell_hexdump(ctx_shell, data->data, data->data_len);
/* Don't print duplicates */
if (base_size != default_broadcast_sink.base_size ||
memcmp(base, &default_broadcast_sink.received_base, base_size) != 0) {
shell_print(ctx_shell, "Received BASE from sink %p:", sink);
(void)memcpy(&default_broadcast_sink.received_base, base, base_size);
default_broadcast_sink.base_size = base_size;
return true;
}
static void base_recv(struct bt_bap_broadcast_sink *sink, const struct bt_bap_base *base)
{
uint8_t bis_indexes[BROADCAST_SNK_STREAM_CNT] = { 0 };
/* "0xXX " requires 5 characters */
char bis_indexes_str[5 * ARRAY_SIZE(bis_indexes) + 1];
size_t index_count = 0;
if (memcmp(base, &default_broadcast_sink.received_base,
sizeof(default_broadcast_sink.received_base)) == 0) {
/* Don't print duplicates */
return;
print_base(base);
}
shell_print(ctx_shell, "Received BASE from sink %p:", sink);
shell_print(ctx_shell, "Presentation delay: %u", base->pd);
shell_print(ctx_shell, "Subgroup count: %u", base->subgroup_count);
for (int i = 0; i < base->subgroup_count; i++) {
const struct bt_bap_base_subgroup *subgroup;
subgroup = &base->subgroups[i];
shell_print(ctx_shell, "%2sSubgroup[%d]:", "", i);
print_codec_cfg(ctx_shell, &subgroup->codec_cfg);
for (int j = 0; j < subgroup->bis_count; j++) {
const struct bt_bap_base_bis_data *bis_data;
bis_data = &subgroup->bis_data[j];
shell_print(ctx_shell, "%4sBIS[%d] index 0x%02x", "", i, bis_data->index);
bis_indexes[index_count++] = bis_data->index;
if (subgroup->codec_cfg.id == BT_HCI_CODING_FORMAT_LC3) {
const int err =
bt_audio_data_parse(bis_data->data, bis_data->data_len,
print_data_func_cb, NULL);
if (err != 0) {
shell_error(ctx_shell,
"Failed to parse BIS codec config: %d", err);
}
} else {
shell_hexdump(ctx_shell, bis_data->data, bis_data->data_len);
}
}
}
memset(bis_indexes_str, 0, sizeof(bis_indexes_str));
/* Create space separated list of indexes as hex values */
for (int i = 0; i < index_count; i++) {
char bis_index_str[6];
sprintf(bis_index_str, "0x%02x ", bis_indexes[i]);
strcat(bis_indexes_str, bis_index_str);
shell_print(ctx_shell, "[%d]: %s", i, bis_index_str);
}
shell_print(ctx_shell, "Possible indexes: %s", bis_indexes_str);
(void)memcpy(&default_broadcast_sink.received_base, base,
sizeof(default_broadcast_sink.received_base));
}
static void syncable(struct bt_bap_broadcast_sink *sink, bool encrypted)

View file

@ -28,7 +28,8 @@
/* BIS sync is a 32-bit bitfield where BIT(0) is not allowed */
#define VALID_BIS_SYNC(_bis_sync) ((bis_sync & BIT(0)) == 0U && bis_sync < UINT32_MAX)
static struct bt_bap_base received_base;
static uint8_t received_base[UINT8_MAX];
static uint8_t received_base_size;
static struct bt_auto_scan {
uint32_t broadcast_id;
@ -40,31 +41,20 @@ static struct bt_auto_scan {
static bool pa_decode_base(struct bt_data *data, void *user_data)
{
struct bt_bap_base base = { 0 };
int err;
const struct bt_bap_base *base = bt_bap_base_get_base_from_ad(data);
if (data->type != BT_DATA_SVC_DATA16) {
/* Base is NULL if the data does not contain a valid BASE */
if (base == NULL) {
return true;
}
if (data->data_len < BT_BAP_BASE_MIN_SIZE) {
return true;
}
err = bt_bap_decode_base(data, &base);
if (err != 0 && err != -ENOMSG) {
shell_error(ctx_shell, "Failed to decode BASE: %d", err);
return false;
}
/* Compare BASE and print if different */
if (memcmp(&base, &received_base, sizeof(base)) != 0) {
(void)memcpy(&received_base, &base, sizeof(base));
if (data->data_len != received_base_size ||
memcmp(data->data, received_base, data->data_len) != 0) {
(void)memcpy(&received_base, data->data, data->data_len);
received_base_size = data->data_len;
#if BROADCAST_SNK_SUBGROUP_CNT > 0
print_base(ctx_shell, &received_base);
#endif /* BROADCAST_SNK_SUBGROUP_CNT > 0 */
print_base(base);
}
return false;
@ -779,6 +769,48 @@ static int cmd_bap_broadcast_assistant_mod_src(const struct shell *sh,
return result;
}
static inline bool add_pa_sync_base_subgroup_bis_cb(const struct bt_bap_base_subgroup_bis *bis,
void *user_data)
{
struct bt_bap_scan_delegator_subgroup *subgroup_param = user_data;
subgroup_param->bis_sync |= BIT(bis->index);
return true;
}
static inline bool add_pa_sync_base_subgroup_cb(const struct bt_bap_base_subgroup *subgroup,
void *user_data)
{
struct bt_bap_broadcast_assistant_add_src_param *param = user_data;
struct bt_bap_scan_delegator_subgroup *subgroup_param;
uint8_t *data;
int ret;
ret = bt_bap_base_get_subgroup_codec_meta(subgroup, &data);
if (ret < 0) {
return false;
}
subgroup_param = &param->subgroups[param->num_subgroups];
if (ret > ARRAY_SIZE(subgroup_param->metadata)) {
shell_info(ctx_shell, "Cannot fit %d octets into subgroup param with size %zu", ret,
ARRAY_SIZE(subgroup_param->metadata));
return false;
}
ret = bt_bap_base_subgroup_foreach_bis(subgroup, add_pa_sync_base_subgroup_bis_cb,
subgroup_param);
if (ret < 0) {
return false;
}
param->num_subgroups++;
return true;
}
static int cmd_bap_broadcast_assistant_add_pa_sync(const struct shell *sh,
size_t argc, char **argv)
{
@ -855,45 +887,12 @@ static int cmd_bap_broadcast_assistant_add_pa_sync(const struct shell *sh,
bis_bitfield_req |= BIT(index);
}
/* The MIN is used to handle `array-bounds` error on some compilers */
param.num_subgroups = MIN(received_base.subgroup_count, BROADCAST_SNK_SUBGROUP_CNT);
#if BROADCAST_SNK_SUBGROUP_CNT > 0
struct bt_bap_scan_delegator_subgroup subgroup_params[BROADCAST_SNK_SUBGROUP_CNT] = {0};
param.subgroups = subgroup_params;
for (size_t i = 0; i < param.num_subgroups; i++) {
struct bt_bap_scan_delegator_subgroup *subgroup_param = &subgroup_params[i];
const struct bt_bap_base_subgroup *subgroup = &received_base.subgroups[i];
uint32_t subgroup_bis_indexes = 0U;
ssize_t metadata_len;
for (size_t j = 0U; j < MIN(subgroup->bis_count, ARRAY_SIZE(subgroup->bis_data));
j++) {
const struct bt_bap_base_bis_data *bis_data = &subgroup->bis_data[j];
subgroup_bis_indexes |= BIT(bis_data->index);
}
subgroup_param->bis_sync = subgroup_bis_indexes & bis_bitfield_req;
#if CONFIG_BT_AUDIO_CODEC_CFG_MAX_METADATA_SIZE > 0
metadata_len = subgroup->codec_cfg.meta_len;
if (metadata_len > sizeof(subgroup_param->metadata)) {
shell_error(sh,
"Could not set %zu octets of metadata for subgroup_param of "
"size %zu",
metadata_len, sizeof(subgroup_param->metadata));
return -ENOEXEC;
}
memcpy(subgroup_param->metadata, subgroup->codec_cfg.meta, metadata_len);
#else
metadata_len = 0U;
#endif /* CONFIG_BT_AUDIO_CODEC_CFG_MAX_METADATA_SIZE > 0 */
subgroup_param->metadata_len = metadata_len;
err = bt_bap_base_foreach_subgroup((const struct bt_bap_base *)received_base,
add_pa_sync_base_subgroup_cb, &param);
if (err < 0) {
shell_error(ctx_shell, "Could not add BASE to params %d", err);
return -ENOEXEC;
}
#endif /* BROADCAST_SNK_SUBGROUP_CNT > 0 */
err = bt_bap_broadcast_assistant_add_src(default_conn, &param);
if (err != 0) {

View file

@ -1517,11 +1517,57 @@ static void btp_send_bis_found_ev(const bt_addr_le_t *address, uint32_t broadcas
tester_rsp_buffer_unlock();
}
static void base_recv_cb(struct bt_bap_broadcast_sink *sink, const struct bt_bap_base *base)
struct base_parse_data {
uint32_t broadcast_id;
uint32_t pd;
struct bt_audio_codec_cfg codec_cfg;
uint8_t subgroup_cnt;
uint32_t bis_bitfield;
size_t stream_cnt;
};
static bool base_subgroup_bis_cb(const struct bt_bap_base_subgroup_bis *bis, void *user_data)
{
size_t stream_count = 0U;
uint32_t base_bis_index_bitfield = 0U;
const struct bt_audio_codec_cfg *codec_cfg;
struct base_parse_data *parse_data = user_data;
struct bt_audio_codec_cfg *codec_cfg = &parse_data->codec_cfg;
parse_data->bis_bitfield |= BIT(bis->index);
if (parse_data->stream_cnt < MAX_STREAMS_COUNT) {
broadcaster->streams[parse_data->stream_cnt++].bis_id = bis->index;
}
btp_send_bis_found_ev(&broadcaster_addr, parse_data->broadcast_id, parse_data->pd,
parse_data->subgroup_cnt, bis->index, codec_cfg);
return true;
}
static bool base_subgroup_cb(const struct bt_bap_base_subgroup *subgroup, void *user_data)
{
struct base_parse_data *parse_data = user_data;
int err;
err = bt_bap_base_subgroup_codec_to_codec_cfg(subgroup, &parse_data->codec_cfg);
if (err != 0) {
LOG_DBG("Failed to retrieve codec config: %d", err);
return false;
}
err = bt_bap_base_subgroup_foreach_bis(subgroup, base_subgroup_bis_cb, user_data);
if (err != 0) {
LOG_DBG("Failed to parse all BIS: %d", err);
return false;
}
return true;
}
static void base_recv_cb(struct bt_bap_broadcast_sink *sink, const struct bt_bap_base *base,
size_t base_size)
{
struct base_parse_data parse_data = {0};
int ret;
LOG_DBG("");
@ -1529,25 +1575,25 @@ static void base_recv_cb(struct bt_bap_broadcast_sink *sink, const struct bt_bap
return;
}
LOG_DBG("Received BASE: broadcast sink %p subgroups %u", sink, base->subgroup_count);
LOG_DBG("Received BASE: broadcast sink %p subgroups %u",
sink, bt_bap_base_get_subgroup_count(base));
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;
codec_cfg = &base->subgroups[i].codec_cfg;
base_bis_index_bitfield |= BIT(index);
if (stream_count < MAX_STREAMS_COUNT) {
broadcaster->streams[stream_count++].bis_id = index;
}
btp_send_bis_found_ev(&broadcaster_addr, sink->broadcast_id,
sink->base.pd, i, index, codec_cfg);
}
ret = bt_bap_base_get_pres_delay(base);
if (ret < 0) {
LOG_ERR("Failed to get presentation delay: %d", ret);
return;
}
bis_index_bitfield = base_bis_index_bitfield & bis_index_mask;
parse_data.broadcast_id = sink->broadcast_id;
parse_data.pd = (uint32_t)ret;
ret = bt_bap_base_foreach_subgroup(base, base_subgroup_cb, &parse_data);
if (ret != 0) {
LOG_ERR("Failed to parse subgroups: %d", ret);
return;
}
bis_index_bitfield = parse_data.bis_bitfield & bis_index_mask;
LOG_DBG("bis_index_bitfield 0x%08x", bis_index_bitfield);
}

View file

@ -527,6 +527,7 @@ static void test_main_client_sync(void)
test_bass_mod_source();
test_bass_broadcast_code();
printk("Waiting for receive state with BIS sync\n");
WAIT_FOR_FLAG(flag_recv_state_updated_with_bis_sync);
test_bass_remove_source();
@ -548,6 +549,7 @@ static void test_main_server_sync_client_rem(void)
test_bass_broadcast_code();
printk("Waiting for receive state with BIS sync\n");
WAIT_FOR_FLAG(flag_recv_state_updated_with_bis_sync);
test_bass_remove_source();
@ -569,6 +571,7 @@ static void test_main_server_sync_server_rem(void)
test_bass_broadcast_code();
printk("Waiting for receive state with BIS sync\n");
WAIT_FOR_FLAG(flag_recv_state_updated_with_bis_sync);
WAIT_FOR_FLAG(flag_recv_state_removed);

View file

@ -45,8 +45,6 @@ static const struct bt_audio_codec_cap codec_cap = BT_AUDIO_CODEC_CAP_LC3(
static K_SEM_DEFINE(sem_started, 0U, ARRAY_SIZE(streams));
static K_SEM_DEFINE(sem_stopped, 0U, ARRAY_SIZE(streams));
static uint8_t metadata[CONFIG_BT_AUDIO_CODEC_CFG_MAX_METADATA_SIZE];
/* 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.
@ -54,36 +52,50 @@ static uint8_t metadata[CONFIG_BT_AUDIO_CODEC_CFG_MAX_METADATA_SIZE];
static const uint32_t bis_index_mask = BIT_MASK(ARRAY_SIZE(streams) + 1U);
static uint32_t bis_index_bitfield;
static void base_recv_cb(struct bt_bap_broadcast_sink *sink, const struct bt_bap_base *base)
static bool base_subgroup_cb(const struct bt_bap_base_subgroup *subgroup, void *user_data)
{
static uint8_t metadata[CONFIG_BT_AUDIO_CODEC_CFG_MAX_METADATA_SIZE];
static size_t metadata_size;
uint8_t *meta;
int ret;
ret = bt_bap_base_get_subgroup_codec_meta(subgroup, &meta);
if (ret < 0) {
FAIL("Could not get subgroup meta: %d\n", ret);
return false;
}
if (TEST_FLAG(flag_base_received) &&
((size_t)ret != metadata_size || memcmp(meta, metadata, metadata_size) != 0)) {
printk("Metadata updated\n");
SET_FLAG(flag_base_metadata_updated);
}
metadata_size = (size_t)ret;
(void)memcpy(metadata, meta, metadata_size);
return true;
}
static void base_recv_cb(struct bt_bap_broadcast_sink *sink, const struct bt_bap_base *base,
size_t base_size)
{
uint32_t base_bis_index_bitfield = 0U;
int ret;
if (TEST_FLAG(flag_base_received)) {
if (base->subgroup_count > 0 &&
memcmp(metadata, base->subgroups[0].codec_cfg.meta,
sizeof(base->subgroups[0].codec_cfg.meta)) != 0) {
(void)memcpy(metadata, base->subgroups[0].codec_cfg.meta,
sizeof(base->subgroups[0].codec_cfg.meta));
printk("Metadata updated\n");
SET_FLAG(flag_base_metadata_updated);
}
printk("Received BASE with %d subgroups from broadcast sink %p\n",
bt_bap_base_get_subgroup_count(base), sink);
ret = bt_bap_base_foreach_subgroup(base, base_subgroup_cb, NULL);
if (ret != 0) {
FAIL("Failed to parse subgroups: %d\n", ret);
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);
}
ret = bt_bap_base_get_bis_indexes(base, &base_bis_index_bitfield);
if (ret != 0) {
FAIL("Failed to BIS indexes: %d\n", ret);
return;
}
bis_index_bitfield = base_bis_index_bitfield & bis_index_mask;
@ -667,6 +679,7 @@ static void test_common(void)
/* Ensure that we also see the metadata update */
printk("Waiting for metadata update\n");
WAIT_FOR_FLAG(flag_base_metadata_updated)
backchannel_sync_send_all(); /* let other devices know we have received what we wanted */
}

View file

@ -306,6 +306,36 @@ static void test_broadcast_source_start(struct bt_bap_broadcast_source *source,
}
}
static void test_broadcast_source_update_metadata(struct bt_bap_broadcast_source *source,
struct bt_le_ext_adv *adv)
{
uint8_t new_metadata[] = BT_AUDIO_CODEC_CFG_LC3_META(BT_AUDIO_CONTEXT_TYPE_ALERTS);
struct bt_data per_ad;
int err;
NET_BUF_SIMPLE_DEFINE(base_buf, 128);
printk("Updating metadata\n");
err = bt_bap_broadcast_source_update_metadata(source, new_metadata,
ARRAY_SIZE(new_metadata));
if (err != 0) {
FAIL("Failed to update metadata broadcast source: %d\n", err);
return;
}
/* Get the new BASE */
test_broadcast_source_get_base(source, &base_buf);
/* Update the periodic advertising data with the new BASE */
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) {
FAIL("Failed to set periodic advertising data: %d\n", err);
}
}
static void test_broadcast_source_stop(struct bt_bap_broadcast_source *source)
{
int err;
@ -369,7 +399,6 @@ static int stop_extended_adv(struct bt_le_ext_adv *adv)
static void test_main(void)
{
uint8_t new_metadata[] = BT_AUDIO_CODEC_CFG_LC3_META(BT_AUDIO_CONTEXT_TYPE_ALERTS);
struct bt_bap_broadcast_source *source;
struct bt_le_ext_adv *adv;
int err;
@ -415,13 +444,7 @@ static void test_main(void)
backchannel_sync_wait_any();
/* Update metadata while streaming */
printk("Updating metadata\n");
err = bt_bap_broadcast_source_update_metadata(source, new_metadata,
ARRAY_SIZE(new_metadata));
if (err != 0) {
FAIL("Failed to update metadata broadcast source: %d\n", err);
return;
}
test_broadcast_source_update_metadata(source, adv);
/* Wait for other devices to have received what they wanted */
backchannel_sync_wait_any();

View file

@ -122,16 +122,20 @@ static bool subgroup_data_func_cb(struct bt_data *data, void *user_data)
return true;
}
static bool valid_subgroup_metadata(const struct bt_bap_base_subgroup *subgroup)
static bool valid_subgroup_metadata_cb(const struct bt_bap_base_subgroup *subgroup, void *user_data)
{
bool stream_context_found = false;
int err;
uint8_t *meta;
int ret;
printk("meta %p len %zu", subgroup->codec_cfg.meta, subgroup->codec_cfg.meta_len);
ret = bt_bap_base_get_subgroup_codec_meta(subgroup, &meta);
if (ret < 0) {
FAIL("Could not get subgroup meta: %d\n", ret);
return false;
}
err = bt_audio_data_parse(subgroup->codec_cfg.meta, subgroup->codec_cfg.meta_len,
subgroup_data_func_cb, &stream_context_found);
if (err != 0 && err != -ECANCELED) {
ret = bt_audio_data_parse(meta, (size_t)ret, subgroup_data_func_cb, &stream_context_found);
if (ret != 0 && ret != -ECANCELED) {
return false;
}
@ -139,39 +143,43 @@ static bool valid_subgroup_metadata(const struct bt_bap_base_subgroup *subgroup)
printk("Subgroup did not have streaming context\n");
}
/* if this is false, the iterater will return early with an error */
return stream_context_found;
}
static void base_recv_cb(struct bt_bap_broadcast_sink *sink, const struct bt_bap_base *base)
static void base_recv_cb(struct bt_bap_broadcast_sink *sink, const struct bt_bap_base *base,
size_t base_size)
{
uint32_t base_bis_index_bitfield = 0U;
int ret;
if (TEST_FLAG(flag_base_received)) {
return;
}
printk("Received BASE with %u subgroups from broadcast sink %p\n",
base->subgroup_count, sink);
if (base->subgroup_count == 0) {
FAIL("base->subgroup_count was 0");
ret = bt_bap_base_get_subgroup_count(base);
if (ret < 0) {
FAIL("Failed to get subgroup count: %d\n", ret);
return;
}
printk("Received BASE with %d subgroups from broadcast sink %p\n", ret, sink);
for (size_t i = 0U; i < base->subgroup_count; i++) {
const struct bt_bap_base_subgroup *subgroup = &base->subgroups[i];
if (ret == 0) {
FAIL("subgroup_count was 0");
return;
}
for (size_t j = 0U; j < subgroup->bis_count; j++) {
const uint8_t index = subgroup->bis_data[j].index;
ret = bt_bap_base_foreach_subgroup(base, valid_subgroup_metadata_cb, NULL);
if (ret != 0) {
FAIL("Failed to parse subgroups: %d\n", ret);
return;
}
base_bis_index_bitfield |= BIT(index);
}
if (!valid_subgroup_metadata(subgroup)) {
FAIL("Subgroup[%zu] has invalid metadata\n", i);
return;
}
ret = bt_bap_base_get_bis_indexes(base, &base_bis_index_bitfield);
if (ret != 0) {
FAIL("Failed to BIS indexes: %d\n", ret);
return;
}
bis_index_bitfield = base_bis_index_bitfield & bis_index_mask;