@@ -48,6 +48,11 @@
#define BAP_PROCESS_TIMEOUT 10
+#define BAP_FREQ_LTV_TYPE 1
+#define BAP_DURATION_LTV_TYPE 2
+#define BAP_CHANNEL_ALLOCATION_LTV_TYPE 3
+#define BAP_FRAME_LEN_LTV_TYPE 4
+
struct bt_bap_pac_changed {
unsigned int id;
bt_bap_pac_func_t added;
@@ -3282,6 +3287,13 @@ static void bap_add_broadcast_source(struct bt_bap_pac *pac)
static void bap_add_broadcast_sink(struct bt_bap_pac *pac)
{
queue_push_tail(pac->bdb->broadcast_sinks, pac);
+
+ /* Update local PACS for broadcast sink also, when registering an
+ * endpoint
+ */
+ pacs_add_sink_location(pac->bdb->pacs, pac->qos.location);
+ pacs_add_sink_supported_context(pac->bdb->pacs,
+ pac->qos.supported_context);
}
static void notify_pac_added(void *data, void *user_data)
@@ -3433,6 +3445,16 @@ struct bt_bap_pac_qos *bt_bap_pac_get_qos(struct bt_bap_pac *pac)
return &pac->qos;
}
+struct iovec *bt_bap_pac_get_data(struct bt_bap_pac *pac)
+{
+ return pac->data;
+}
+
+struct iovec *bt_bap_pac_get_metadata(struct bt_bap_pac *pac)
+{
+ return pac->metadata;
+}
+
uint8_t bt_bap_stream_get_type(struct bt_bap_stream *stream)
{
if (!stream)
@@ -5872,8 +5894,9 @@ static void add_new_subgroup(struct bt_bap_base *base,
struct bt_ltv_match {
uint8_t l;
- uint8_t *v;
+ void *data;
bool found;
+ uint32_t data32;
};
struct bt_ltv_search {
@@ -5892,7 +5915,7 @@ static void match_ltv(size_t i, uint8_t l, uint8_t t, uint8_t *v,
if (ltv_match->l != l)
return;
- if (!memcmp(v, ltv_match->v, l))
+ if (!memcmp(v, ltv_match->data, l))
ltv_match->found = true;
}
@@ -5904,7 +5927,7 @@ static void search_ltv(size_t i, uint8_t l, uint8_t t, uint8_t *v,
ltv_match.found = false;
ltv_match.l = l;
- ltv_match.v = v;
+ ltv_match.data = v;
util_ltv_foreach(ltv_search->iov->iov_base,
ltv_search->iov->iov_len, &t,
@@ -5945,8 +5968,10 @@ static bool compare_ltv(struct iovec *iov1,
}
struct bt_ltv_extract {
- struct iovec *result;
struct iovec *src;
+ void *value;
+ uint8_t len;
+ struct iovec *result;
};
static void extract_ltv(size_t i, uint8_t l, uint8_t t, uint8_t *v,
@@ -5958,7 +5983,7 @@ static void extract_ltv(size_t i, uint8_t l, uint8_t t, uint8_t *v,
ltv_match.found = false;
ltv_match.l = l;
- ltv_match.v = v;
+ ltv_match.data = v;
/* Search each BIS caps ltv in subgroup caps
* to extract the one that are BIS specific
@@ -6112,12 +6137,16 @@ static void cleanup_subgroup(struct bt_bap_subgroup *subgroup)
free(subgroup);
}
+static void print_ltv(size_t i, uint8_t l, uint8_t t, uint8_t *v,
+ void *user_data)
+{
+ util_debug(user_data, NULL, "CC #%zu: l:%u t:%u", i, l, t);
+ util_hexdump(' ', v, l, user_data, NULL);
+}
+
bool bt_bap_parse_base(struct bt_bap *bap, void *data, size_t len,
util_debug_func_t func, struct bt_bap_base *base)
{
- uint8_t num_subgroups;
- uint8_t num_bis;
-
struct iovec iov = {
.iov_base = data,
.iov_len = len,
@@ -6132,161 +6161,369 @@ bool bt_bap_parse_base(struct bt_bap *bap, void *data, size_t len,
if (!util_iov_pull_u8(&iov, &base->num_subgroups))
return false;
- util_debug(func, NULL, "NumSubgroups %d", base->num_subgroups);
- num_subgroups = base->num_subgroups;
+ util_debug(func, NULL, "Number of Subgroups: %d", base->num_subgroups);
- for (int sg = 0; sg < num_subgroups; sg++) {
- struct bt_bap_subgroup *sub_group = new0(
+ for (int idx = 0; idx < base->num_subgroups; idx++) {
+ struct bt_bap_subgroup *subgroup = new0(
struct bt_bap_subgroup, 1);
- uint8_t caps_len, metaLen;
- uint8_t *hexstream;
- sub_group->subgroup_index = sg;
+ subgroup->index = idx;
- util_debug(func, NULL, "Subgroup #%d", sg);
- sub_group->bap = bap;
- sub_group->bises = queue_new();
+ util_debug(func, NULL, "Subgroup #%d", idx);
+ subgroup->bap = bap;
+ subgroup->bises = queue_new();
- if (!util_iov_pull_u8(&iov, &num_bis)) {
- cleanup_subgroup(sub_group);
+ if (!util_iov_pull_u8(&iov, &subgroup->num_bises))
goto fail;
- }
- util_debug(func, NULL, "NumBis %d", num_bis);
- sub_group->num_bises = num_bis;
-
- memcpy(&sub_group->codec, util_iov_pull_mem(&iov,
- sizeof(struct bt_bap_codec)), sizeof(struct bt_bap_codec));
- util_debug(func, NULL, "%s: ID %d CID 0x%2.2x VID 0x%2.2x",
- "Codec", sub_group->codec.id, sub_group->codec.cid,
- sub_group->codec.vid);
- if (!util_iov_pull_u8(&iov, &caps_len)) {
- cleanup_subgroup(sub_group);
- goto fail;
- }
- util_debug(func, NULL, "CC Len %d", caps_len);
+ util_debug(func, NULL, "Number of BISes: %d",
+ subgroup->num_bises);
- /*
- * Copy the Codec Specific configurations from base
- */
- sub_group->caps = new0(struct iovec, 1);
- util_iov_memcpy(sub_group->caps, iov.iov_base, caps_len);
- util_debug(func, NULL, "subgroup caps len %ld",
- sub_group->caps->iov_len);
-
- for (int i = 0; caps_len > 1; i++) {
- struct bt_ltv *ltv = util_iov_pull_mem(&iov,
- sizeof(*ltv));
- uint8_t *caps;
-
- if (!ltv) {
- util_debug(func, NULL, "Unable to parse %s",
- "Capabilities");
- cleanup_subgroup(sub_group);
- goto fail;
- }
+ memcpy(&subgroup->codec, util_iov_pull_mem(&iov,
+ sizeof(struct bt_bap_codec)),
+ sizeof(struct bt_bap_codec));
+ util_debug(func, NULL, "Codec: ID %d CID 0x%2.2x VID 0x%2.2x",
+ subgroup->codec.id, subgroup->codec.cid,
+ subgroup->codec.vid);
- util_debug(func, NULL, "%s #%u: len %u type %u",
- "CC", i, ltv->len, ltv->type);
+ /* BASE Level 2 */
+ /* Read Codec Specific Configuration */
+ subgroup->caps = new0(struct iovec, 1);
+ if (!util_iov_pull_u8(&iov, (void *)&subgroup->caps->iov_len))
+ goto fail;
- caps = util_iov_pull_mem(&iov, ltv->len - 1);
- if (!caps) {
- util_debug(func, NULL, "Unable to parse %s",
- "CC");
- cleanup_subgroup(sub_group);
- goto fail;
- }
- util_hexdump(' ', caps, ltv->len - 1, func, NULL);
+ util_iov_memcpy(subgroup->caps,
+ util_iov_pull_mem(&iov,
+ subgroup->caps->iov_len),
+ subgroup->caps->iov_len);
- caps_len -= (ltv->len + 1);
- }
+ /* Print Codec Specific Configuration */
+ util_debug(func, NULL, "CC len: %ld",
+ subgroup->caps->iov_len);
+ util_ltv_foreach(subgroup->caps->iov_base,
+ subgroup->caps->iov_len, NULL, print_ltv, func);
- if (!util_iov_pull_u8(&iov, &metaLen)) {
- cleanup_subgroup(sub_group);
+ /* Read Metadata */
+ subgroup->meta = new0(struct iovec, 1);
+ if (!util_iov_pull_u8(&iov, (void *)&subgroup->meta->iov_len))
goto fail;
- }
- util_debug(func, NULL, "Metadata Len %d", metaLen);
- sub_group->meta = new0(struct iovec, 1);
- sub_group->meta->iov_len = metaLen;
- sub_group->meta->iov_base = iov.iov_base;
+ util_iov_memcpy(subgroup->meta,
+ util_iov_pull_mem(&iov,
+ subgroup->meta->iov_len),
+ subgroup->meta->iov_len);
- hexstream = util_iov_pull_mem(&iov, metaLen);
- if (!hexstream) {
- cleanup_subgroup(sub_group);
- goto fail;
- }
- util_hexdump(' ', hexstream, metaLen, func, NULL);
+ /* Print Metadata */
+ util_debug(func, NULL, "Metadata len: %i",
+ (uint8_t)subgroup->meta->iov_len);
+ util_hexdump(' ', subgroup->meta->iov_base,
+ subgroup->meta->iov_len, func, NULL);
- for (int bis_sg = 0; bis_sg < sub_group->num_bises; bis_sg++) {
+ /* BASE Level 3 */
+ for (int bis_sg = 0; bis_sg < subgroup->num_bises; bis_sg++) {
struct bt_bap_bis *bis;
- uint8_t caps_len;
- uint8_t crt_bis;
- if (!util_iov_pull_u8(&iov, &crt_bis)) {
- cleanup_subgroup(sub_group);
+ bis = new0(struct bt_bap_bis, 1);
+ if (!util_iov_pull_u8(&iov, &bis->index))
goto fail;
- }
- util_debug(func, NULL, "BIS #%d", crt_bis);
- bis = new0(struct bt_bap_bis, 1);
- bis->index = crt_bis;
+ util_debug(func, NULL, "BIS #%d", bis->index);
- if (!util_iov_pull_u8(&iov, &caps_len)) {
- cleanup_subgroup(sub_group);
+ /* Read Codec Specific Configuration */
+ bis->caps = new0(struct iovec, 1);
+ if (!util_iov_pull_u8(&iov,
+ (void *)&bis->caps->iov_len))
goto fail;
- }
- util_debug(func, NULL, "CC Len %d", caps_len);
- bis->caps = new0(struct iovec, 1);
- bis->caps->iov_len = caps_len;
- util_iov_memcpy(bis->caps, iov.iov_base, caps_len);
- util_debug(func, NULL, "bis caps len %ld",
+ util_iov_memcpy(bis->caps,
+ util_iov_pull_mem(&iov,
+ bis->caps->iov_len),
bis->caps->iov_len);
- for (int i = 0; caps_len > 1; i++) {
- struct bt_ltv *ltv = util_iov_pull_mem(&iov,
- sizeof(*ltv));
- uint8_t *caps;
-
- if (!ltv) {
- util_debug(func, NULL, "Unable to parse %s",
- "Capabilities");
- cleanup_subgroup(sub_group);
- goto fail;
- }
-
- util_debug(func, NULL, "%s #%u: len %u type %u",
- "CC", i, ltv->len, ltv->type);
-
- caps = util_iov_pull_mem(&iov, ltv->len - 1);
- if (!caps) {
- util_debug(func, NULL,
- "Unable to parse %s", "CC");
- cleanup_subgroup(sub_group);
- goto fail;
- }
- util_hexdump(' ', caps, ltv->len - 1, func,
- NULL);
-
- caps_len -= (ltv->len + 1);
- }
+ /* Print Codec Specific Configuration */
+ util_debug(func, NULL, "CC Len: %d",
+ (uint8_t)bis->caps->iov_len);
+ util_ltv_foreach(bis->caps->iov_base,
+ bis->caps->iov_len, NULL, print_ltv,
+ func);
- queue_push_tail(sub_group->bises, bis);
+ queue_push_tail(subgroup->bises, bis);
}
- queue_push_tail(base->subgroups, sub_group);
+ queue_push_tail(base->subgroups, subgroup);
}
return true;
fail:
- while (!queue_isempty(base->subgroups)) {
- struct bt_bap_subgroup *subGroup =
- queue_peek_head(base->subgroups);
- cleanup_subgroup(subGroup);
- base->num_subgroups--;
- }
util_debug(func, NULL, "Unable to parse %s", "Base");
return false;
}
+
+static void bap_sink_get_allocation(size_t i, uint8_t l, uint8_t t,
+ uint8_t *v, void *user_data)
+{
+ uint32_t location32;
+
+ if (!v)
+ return;
+
+ memcpy(&location32, v, l);
+ *((uint32_t *)user_data) = le32_to_cpu(location32);
+}
+
+/*
+ * This function compares PAC Codec Specific Capabilities, with the Codec
+ * Specific Configuration LTVs received in the BASE of the BAP Source. The
+ * result is accumulated in data32 which is a bitmask of types.
+ */
+static void check_pac_caps_ltv(size_t i, uint8_t l, uint8_t t, uint8_t *v,
+ void *user_data)
+{
+ struct bt_ltv_match *compare_data = user_data;
+ uint8_t *bis_v = compare_data->data;
+
+ switch (t) {
+ case BAP_FREQ_LTV_TYPE:
+ {
+ uint16_t mask = *((uint16_t *)v);
+
+ mask = le16_to_cpu(mask);
+ if (mask & (1 << (bis_v[0] - 1)))
+ compare_data->data32 |= 1<<t;
+ }
+ break;
+ case BAP_DURATION_LTV_TYPE:
+ if ((v[0]) & (1 << bis_v[0]))
+ compare_data->data32 |= 1<<t;
+ break;
+ case BAP_FRAME_LEN_LTV_TYPE:
+ {
+ uint16_t min = *((uint16_t *)v);
+ uint16_t max = *((uint16_t *)(&v[2]));
+ uint16_t frame_len = *((uint16_t *)bis_v);
+
+ min = le16_to_cpu(min);
+ max = le16_to_cpu(max);
+ frame_len = le16_to_cpu(frame_len);
+ if ((frame_len >= min) &&
+ (frame_len <= max))
+ compare_data->data32 |= 1<<t;
+ }
+ break;
+ }
+}
+
+static void check_source_ltv(size_t i, uint8_t l, uint8_t t, uint8_t *v,
+ void *user_data)
+{
+ struct bt_ltv_match *local_data = user_data;
+ struct iovec *pac_caps = local_data->data;
+ struct bt_ltv_match compare_data;
+
+ compare_data.data = v;
+
+ /* Search inside local PAC's caps for LTV of type t */
+ util_ltv_foreach(pac_caps->iov_base, pac_caps->iov_len, &t,
+ check_pac_caps_ltv, &compare_data);
+
+ local_data->data32 |= compare_data.data32;
+}
+
+static void bap_sink_check_level3_ltv(size_t i, uint8_t l, uint8_t t,
+ uint8_t *v, void *user_data)
+{
+ struct bt_ltv_extract *merge_data = user_data;
+
+ merge_data->value = v;
+ merge_data->len = l;
+}
+
+static void bap_push_ltv(struct iovec *output, uint8_t l, uint8_t t, void *v)
+{
+ l++;
+ iov_append(output, 1, &l);
+ iov_append(output, 1, &t);
+ iov_append(output, l - 1, v);
+}
+
+static void bap_sink_check_level2_ltv(size_t i, uint8_t l, uint8_t t,
+ uint8_t *v, void *user_data)
+{
+ struct bt_ltv_extract *merge_data = user_data;
+
+ merge_data->value = NULL;
+ util_ltv_foreach(merge_data->src->iov_base,
+ merge_data->src->iov_len,
+ &t,
+ bap_sink_check_level3_ltv, user_data);
+
+ /* If the LTV at level 2 was found at level 3 add the one from level 3,
+ * otherwise add the one at level 2
+ */
+ if (merge_data->value)
+ bap_push_ltv(merge_data->result, merge_data->len,
+ t, merge_data->value);
+ else
+ bap_push_ltv(merge_data->result, l, t, v);
+}
+
+static void check_local_pac(void *data, void *user_data)
+{
+#define Codec_Specific_Configuration_Check_Mask (\
+ (1<<BAP_FREQ_LTV_TYPE)|\
+ (1<<BAP_DURATION_LTV_TYPE)|\
+ (1<<BAP_FRAME_LEN_LTV_TYPE))
+ struct bt_ltv_match *compare_data = user_data;
+ struct iovec *bis_data = (struct iovec *)compare_data->data;
+ const struct bt_bap_pac *pac = data;
+
+ /* Keep searching for a matching PAC if one wasn't found
+ * in previous PAC element
+ */
+ if (compare_data->found == false) {
+ struct bt_ltv_match bis_compare_data = {
+ .data = pac->data,
+ .data32 = 0, /* LTVs bitmask result */
+ .found = false
+ };
+
+ /* loop each BIS LTV */
+ util_ltv_foreach(bis_data->iov_base, bis_data->iov_len, NULL,
+ check_source_ltv, &bis_compare_data);
+
+ /* We have a match if all selected LTVs have a match */
+ if ((bis_compare_data.data32 &
+ Codec_Specific_Configuration_Check_Mask) ==
+ Codec_Specific_Configuration_Check_Mask)
+ compare_data->found = true;
+ }
+}
+
+static void bap_sink_match_allocation(size_t i, uint8_t l, uint8_t t,
+ uint8_t *v, void *user_data)
+{
+ struct bt_ltv_match *data = user_data;
+ uint32_t location32;
+
+ if (!v)
+ return;
+
+ memcpy(&location32, v, l);
+
+ /* If all the bits in the received bitmask are found in
+ * the local bitmask then we have a match
+ */
+ if ((le32_to_cpu(location32) & data->data32) ==
+ le32_to_cpu(location32))
+ data->found = true;
+ else
+ data->found = false;
+}
+
+static bool bap_check_bis(struct bt_bap_db *ldb, struct iovec *bis_data)
+{
+ struct bt_ltv_match compare_data = {};
+
+ /* Check channel allocation against the PACS location.
+ * If we don't have a location set we can accept any BIS location.
+ * If the BIS doesn't have a location set we also accept it
+ */
+ compare_data.found = true;
+
+ if (ldb->pacs->sink_loc_value) {
+ uint8_t type = BAP_CHANNEL_ALLOCATION_LTV_TYPE;
+
+ compare_data.data32 = ldb->pacs->sink_loc_value;
+ util_ltv_foreach(bis_data->iov_base, bis_data->iov_len, &type,
+ bap_sink_match_allocation, &compare_data);
+ }
+
+ /* Check remaining LTVs against the PACs list */
+ if (compare_data.found) {
+ compare_data.data = bis_data;
+ compare_data.found = false;
+ queue_foreach(ldb->broadcast_sinks, check_local_pac,
+ &compare_data);
+ }
+
+ return compare_data.found;
+}
+
+static void bis_to_pac(void *data, void *user_data)
+{
+ struct bt_bap_bis *bis = data;
+ struct bt_bap_subgroup *subgroup = user_data;
+ struct bt_bap_pac *pac_source_bis;
+ struct bt_bap_endpoint *ep;
+ int err = 0;
+ struct bt_bap_pac_qos bis_qos = {0};
+ uint8_t type = 0;
+ struct bt_ltv_extract merge_data = {0};
+
+ merge_data.src = bis->caps;
+ merge_data.result = new0(struct iovec, 1);
+
+ /* Create a Codec Specific Configuration with LTVs at level 2 (subgroup)
+ * overwritten by LTVs at level 3 (BIS)
+ */
+ util_ltv_foreach(subgroup->caps->iov_base,
+ subgroup->caps->iov_len,
+ NULL,
+ bap_sink_check_level2_ltv, &merge_data);
+
+ /* Check each BIS Codec Specific Configuration LTVs against our Codec
+ * Specific Capabilities and if the BIS matches create a PAC with it
+ */
+ if (bap_check_bis(subgroup->bap->ldb, merge_data.result) == false)
+ goto cleanup;
+
+ DBG(subgroup->bap, "Matching BIS %i", bis->index);
+
+ /* Create a QoS structure based on the received BIS information to
+ * specify the desired channel for this BIS/PAC
+ */
+ type = BAP_CHANNEL_ALLOCATION_LTV_TYPE;
+ util_ltv_foreach(merge_data.result->iov_base,
+ merge_data.result->iov_len, &type,
+ bap_sink_get_allocation, &bis_qos.location);
+
+ /* Create a remote PAC */
+ pac_source_bis = bap_pac_new(subgroup->bap->rdb, NULL,
+ BT_BAP_BCAST_SOURCE, &subgroup->codec, &bis_qos,
+ merge_data.result, subgroup->meta);
+
+ err = asprintf(&pac_source_bis->name, "%d", bis->index);
+
+ if (err < 0) {
+ DBG(subgroup->bap, "error in asprintf");
+ goto cleanup;
+ }
+
+ /* Add remote source endpoint */
+ if (!subgroup->bap->rdb->broadcast_sources)
+ subgroup->bap->rdb->broadcast_sources = queue_new();
+ queue_push_tail(subgroup->bap->rdb->broadcast_sources, pac_source_bis);
+
+ queue_foreach(subgroup->bap->pac_cbs, notify_pac_added, pac_source_bis);
+ /* Push remote endpoint with direction sink */
+ ep = bap_endpoint_new_broadcast(subgroup->bap->rdb, BT_BAP_BCAST_SINK);
+
+ if (ep)
+ queue_push_tail(subgroup->bap->remote_eps, ep);
+
+cleanup:
+ util_iov_free(merge_data.result, 1);
+}
+
+/*
+ * Parse each subgroup, check if we can create PACs from its BISes and then
+ * clear the subgroup data.
+ */
+void bt_bap_parse_bis(void *data, void *user_data)
+{
+ struct bt_bap_subgroup *subgroup = data;
+
+ queue_foreach(subgroup->bises, bis_to_pac, subgroup);
+ cleanup_subgroup(subgroup);
+}
@@ -4,7 +4,7 @@
* BlueZ - Bluetooth protocol stack for Linux
*
* Copyright (C) 2022 Intel Corporation. All rights reserved.
- * Copyright 2023 NXP
+ * Copyright 2023-2024 NXP
*
*/
@@ -101,13 +101,13 @@ struct bt_bap_qos {
struct bt_bap_base {
uint32_t pres_delay;
uint8_t big_id;
- uint8_t num_subgroups;
uint8_t next_bis_index;
+ uint8_t num_subgroups;
struct queue *subgroups;
};
struct bt_bap_subgroup {
- uint8_t subgroup_index;
+ uint8_t index;
struct bt_bap *bap;
uint8_t num_bises;
struct bt_bap_codec codec;
@@ -198,6 +198,10 @@ uint16_t bt_bap_pac_get_context(struct bt_bap_pac *pac);
struct bt_bap_pac_qos *bt_bap_pac_get_qos(struct bt_bap_pac *pac);
+struct iovec *bt_bap_pac_get_data(struct bt_bap_pac *pac);
+
+struct iovec *bt_bap_pac_get_metadata(struct bt_bap_pac *pac);
+
uint8_t bt_bap_stream_get_type(struct bt_bap_stream *stream);
struct bt_bap_stream *bt_bap_pac_get_stream(struct bt_bap_pac *pac);
@@ -349,4 +353,5 @@ struct iovec *bt_bap_stream_get_base(struct bt_bap_stream *stream);
bool bt_bap_parse_base(struct bt_bap *bap, void *data, size_t len,
util_debug_func_t func, struct bt_bap_base *base);
+void bt_bap_parse_bis(void *data, void *user_data);