diff mbox series

[BlueZ,v2,2/6] shared/bap: Add API to convert parsed BASE into pac data

Message ID 20240209164630.92208-3-andrei.istodorescu@nxp.com
State Superseded
Headers show
Series Update Sink BASE management | expand

Commit Message

Andrei Istodorescu Feb. 9, 2024, 4:46 p.m. UTC
Create one remote endpoint for each BIS present in the BASE which matches
the local sink capabilities. Add API to get the PAC metadata and
capabilities.
---
 src/shared/bap.c | 497 ++++++++++++++++++++++++++++++++++-------------
 src/shared/bap.h |  11 +-
 2 files changed, 375 insertions(+), 133 deletions(-)
diff mbox series

Patch

diff --git a/src/shared/bap.c b/src/shared/bap.c
index b481e4655c2d..d205632b3c80 100644
--- a/src/shared/bap.c
+++ b/src/shared/bap.c
@@ -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);
+}
diff --git a/src/shared/bap.h b/src/shared/bap.h
index b13fef688da3..aed3bf52b8d9 100644
--- a/src/shared/bap.h
+++ b/src/shared/bap.h
@@ -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);