Subject: [PATCH v3 0/2] Add code to support dynamically generated BASE

Added code that will be used to generate the BASE dynamically in Bluez.
This code will help when will remove the base_lc3_16_2_1 from player.c
so we can use the preset from the endpoint.config command.

Added fix for ScanBuild error: Dereference of null pointer

Silviu Florian Barbulescu (2):
shared/bap: Fix ScanBuild error: Dereference of null pointer
shared/bap: Add code to support dynamically generated BASE from
presets

src/shared/bap.c | 494 ++++++++++++++++++++++++++++++++++++++++++++++-
src/shared/bap.h | 2 +
2 files changed, 495 insertions(+), 1 deletion(-)


base-commit: 770ad5614e7e8074133e6f563495ce4822f63fe4
--
2.39.2



Subject: [PATCH v3 1/2] shared/bap: Fix dereference of null pointer

shared/bap: Fix ScanBuild error: Dereference of null pointer

---
src/shared/bap.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/shared/bap.c b/src/shared/bap.c
index 078d308dc..49eb8d057 100644
--- a/src/shared/bap.c
+++ b/src/shared/bap.c
@@ -4763,7 +4763,7 @@ struct bt_bap_stream *bt_bap_stream_new(struct bt_bap *bap,
return NULL;

bt_bap_foreach_pac(bap, type, match_pac, &match);
- if (!match.lpac)
+ if ((!match.lpac) || (!lpac))
return NULL;
if (!match.rpac && (lpac->type != BT_BAP_BCAST_SOURCE))
return NULL;
--
2.39.2


Subject: [PATCH v3 2/2] shared/bap: Code for dynamically generated BASE

Add code to support dynamically generated BASE from presets

---
src/shared/bap.c | 492 +++++++++++++++++++++++++++++++++++++++++++++++
src/shared/bap.h | 2 +
2 files changed, 494 insertions(+)

diff --git a/src/shared/bap.c b/src/shared/bap.c
index 49eb8d057..fca6ab9cf 100644
--- a/src/shared/bap.c
+++ b/src/shared/bap.c
@@ -226,6 +226,7 @@ struct bt_bap_stream {
struct bt_bap_stream_io *io;
bool client;
void *user_data;
+ struct queue *bcast_links; /* Linked streams from the same BIG */
};

/* TODO: Figure out the capabilities types */
@@ -255,6 +256,30 @@ struct bt_pacs_context {
uint16_t src;
} __packed;

+struct bt_base_data {
+ uint32_t pres_delay;
+ struct queue *base_data_subgroup;
+};
+
+struct bt_stream_base_data {
+ struct queue *ltv_caps;
+ struct queue *ltv_meta;
+ struct bt_bap_stream *stream;
+};
+
+struct bt_base_data_subgroup {
+ uint8_t subgroup_index;
+ struct bt_bap_codec codec;
+ struct queue *ltv_caps;
+ struct queue *ltv_meta;
+ struct queue *bises;
+};
+
+struct bt_base_data_bis {
+ uint8_t bis_index;
+ struct queue *ltv_caps;
+};
+
/* Contains local bt_bap_db */
static struct queue *bap_db;
static struct queue *bap_cbs;
@@ -826,6 +851,7 @@ static struct bt_bap_stream *bap_stream_new(struct bt_bap *bap,
stream->rpac = rpac;
stream->cc = util_iov_dup(data, 1);
stream->client = client;
+ stream->bcast_links = queue_new();

queue_push_tail(bap->streams, stream);

@@ -1010,6 +1036,14 @@ static void stream_io_unref(struct bt_bap_stream_io *io)
stream_io_free(io);
}

+static void bap_stream_unlink(void *data, void *user_data)
+{
+ struct bt_bap_stream *link = data;
+ struct bt_bap_stream *stream = user_data;
+
+ queue_remove(link->bcast_links, stream);
+}
+
static void bap_stream_free(void *data)
{
struct bt_bap_stream *stream = data;
@@ -1020,6 +1054,9 @@ static void bap_stream_free(void *data)
if (stream->link)
stream->link->link = NULL;

+ queue_foreach(stream->bcast_links, bap_stream_unlink, stream);
+ queue_destroy(stream->bcast_links, NULL);
+
stream_io_unref(stream->io);
util_iov_free(stream->cc, 1);
util_iov_free(stream->meta, 1);
@@ -5492,3 +5529,458 @@ void bt_bap_update_bcast_source(struct bt_bap_pac *pac,
bap_pac_merge(pac, data, metadata);
pac->codec = *codec;
}
+
+static void destroy_ltv(void *data)
+{
+ struct bt_ltv *ltv = data;
+
+ if (!ltv)
+ return;
+
+ free(ltv);
+}
+
+static void destroy_base_data_bis(void *data)
+{
+ struct bt_base_data_bis *bis = data;
+
+ if (!bis)
+ return;
+
+ queue_destroy(bis->ltv_caps, destroy_ltv);
+ free(bis);
+}
+
+static void destroy_base_data_subgroup(void *data)
+{
+ struct bt_base_data_subgroup *subgroup = data;
+
+ if (!subgroup)
+ return;
+
+ queue_destroy(subgroup->ltv_caps, destroy_ltv);
+ queue_destroy(subgroup->ltv_meta, destroy_ltv);
+ queue_destroy(subgroup->bises, destroy_base_data_bis);
+
+ free(subgroup);
+}
+
+static void destroy_stream_base_data(void *data)
+{
+ struct bt_stream_base_data *sbd = data;
+
+ if (!sbd)
+ return;
+
+ queue_destroy(sbd->ltv_caps, destroy_ltv);
+ queue_destroy(sbd->ltv_meta, destroy_ltv);
+ sbd->stream = NULL;
+ free(sbd);
+}
+
+static void append_ltv_to_base(void *data, void *user_data)
+{
+ struct bt_ltv *ltv = data;
+ struct iovec *base_iov = user_data;
+
+ if (!util_iov_push_u8(base_iov, ltv->len))
+ return;
+
+ if (!util_iov_push_u8(base_iov, ltv->type))
+ return;
+
+ if (!util_iov_push_mem(base_iov, ltv->len - 1, ltv->value))
+ return;
+}
+
+static void get_ltv_size(void *data, void *user_data)
+{
+ struct bt_ltv *ltv = data;
+ uint8_t *length = user_data;
+
+ *length = *length + ltv->len + 1;
+}
+
+static uint8_t get_size_from_ltv_queue(struct queue *ltv_queue)
+{
+ uint8_t length = 0;
+
+ queue_foreach(ltv_queue, get_ltv_size, &length);
+ return length;
+}
+
+static void generate_bis_base(void *data, void *user_data)
+{
+ struct bt_base_data_bis *bis = data;
+ struct iovec *base_iov = user_data;
+ uint8_t cc_length = get_size_from_ltv_queue(bis->ltv_caps);
+
+ if (!util_iov_push_u8(base_iov, bis->bis_index))
+ return;
+
+ if (!util_iov_push_u8(base_iov, cc_length))
+ return;
+
+ queue_foreach(bis->ltv_caps, append_ltv_to_base, base_iov);
+}
+
+static void generate_subgroup_base(void *data, void *user_data)
+{
+ struct bt_base_data_subgroup *bds = data;
+ struct iovec *base_iov = user_data;
+ uint8_t cc_length = get_size_from_ltv_queue(bds->ltv_caps);
+ uint8_t metadata_length = get_size_from_ltv_queue(bds->ltv_meta);
+
+ if (!util_iov_push_u8(base_iov, queue_length(bds->bises)))
+ return;
+
+ if (!util_iov_push_u8(base_iov, bds->codec.id))
+ return;
+
+ if (!util_iov_push_le16(base_iov, bds->codec.cid))
+ return;
+
+ if (!util_iov_push_le16(base_iov, bds->codec.vid))
+ return;
+
+ if (!util_iov_push_u8(base_iov, cc_length))
+ return;
+
+ queue_foreach(bds->ltv_caps, append_ltv_to_base, base_iov);
+
+ if (!util_iov_push_u8(base_iov, metadata_length))
+ return;
+
+ queue_foreach(bds->ltv_meta, append_ltv_to_base, base_iov);
+
+ queue_foreach(bds->bises, generate_bis_base, base_iov);
+}
+
+static struct iovec *generate_base(struct bt_base_data *base)
+{
+ struct iovec *base_iov = new0(struct iovec, 0x1);
+
+ base_iov->iov_base = util_malloc(BASE_MAX_LENGTH);
+
+ if (!util_iov_push_le24(base_iov, base->pres_delay))
+ return NULL;
+
+ if (!util_iov_push_u8(base_iov,
+ queue_length(base->base_data_subgroup)))
+ return NULL;
+
+ queue_foreach(base->base_data_subgroup, generate_subgroup_base,
+ base_iov);
+
+ return base_iov;
+}
+
+static void get_max_bises_index(void *data, void *user_data)
+{
+ struct bt_base_data_bis *bdb = data;
+ uint8_t *bis_index = user_data;
+
+ if (bdb->bis_index > *bis_index)
+ *bis_index = bdb->bis_index + 1;
+ else if (bdb->bis_index == *bis_index)
+ *bis_index = *bis_index + 1;
+}
+
+static void get_bises_index(void *data, void *user_data)
+{
+ struct bt_base_data_subgroup *bds = data;
+ uint8_t *bis_index = user_data;
+
+ queue_foreach(bds->bises, get_max_bises_index, bis_index);
+}
+
+static uint8_t get_bis_index(struct queue *subgroups)
+{
+ uint8_t bis_index = 1;
+
+ queue_foreach(subgroups, get_bises_index, &bis_index);
+ return bis_index;
+}
+
+static void add_new_bis(struct bt_base_data_subgroup *subgroup,
+ uint8_t bis_index, struct queue *ltv_bis_caps)
+{
+ struct bt_base_data_bis *bdb = new0(struct bt_base_data_bis, 1);
+
+ bdb->bis_index = bis_index;
+ bdb->ltv_caps = ltv_bis_caps;
+ queue_push_tail(subgroup->bises, bdb);
+}
+
+static void add_new_subgroup(struct queue *subgroups,
+ struct bt_stream_base_data *base_data)
+{
+ struct bt_bap_pac *lpac = base_data->stream->lpac;
+ struct bt_base_data_subgroup *bds = new0(
+ struct bt_base_data_subgroup, 1);
+ uint16_t cid = 0;
+ uint16_t vid = 0;
+
+ bt_bap_pac_get_vendor_codec(lpac, &bds->codec.id, &cid,
+ &vid, NULL, NULL);
+ bds->codec.cid = cid;
+ bds->codec.vid = vid;
+ bds->ltv_caps = base_data->ltv_caps;
+ bds->ltv_meta = base_data->ltv_meta;
+ base_data->ltv_caps = NULL;
+ base_data->ltv_meta = NULL;
+ bds->bises = queue_new();
+ base_data->stream->qos.bcast.bis = get_bis_index(subgroups);
+ add_new_bis(bds, base_data->stream->qos.bcast.bis,
+ queue_new());
+ queue_push_tail(subgroups, bds);
+}
+
+static bool ltv_match(const void *data, const void *match_data)
+{
+ const struct bt_ltv *ltv1 = data;
+ const struct bt_ltv *ltv2 = match_data;
+
+ if (ltv1->len == ltv2->len)
+ if (ltv1->type == ltv2->type)
+ if (memcmp(ltv1->value, ltv2->value, ltv1->len - 1)
+ == 0)
+ return true;
+ return false;
+}
+
+
+static bool compare_ltv_lists(struct queue *ltv_list1, struct queue *ltv_list2)
+{
+ const struct queue_entry *entry;
+ /* Compare metadata length */
+ if (queue_length(ltv_list1) != queue_length(ltv_list2))
+ return false;
+
+ /* Compare metadata ltvs */
+ for (entry = queue_get_entries(ltv_list1); entry; entry = entry->next) {
+ struct bt_ltv *ltv = entry->data;
+
+ if (!queue_find(ltv_list2, ltv_match, ltv))
+ return false;
+ }
+
+ return true;
+}
+
+static struct queue *compare_caps_ltv_lists(
+ struct queue *subgroup_caps, struct queue *bis_caps)
+{
+ struct queue *ltv_caps = queue_new();
+ const struct queue_entry *entry;
+
+ /* Compare metadata ltvs */
+ for (entry = queue_get_entries(bis_caps); entry;
+ entry = entry->next) {
+ struct bt_ltv *ltv = entry->data;
+
+ if (!queue_find(subgroup_caps, ltv_match, ltv))
+ queue_push_tail(ltv_caps, ltv);
+ }
+
+ if (queue_isempty(ltv_caps)) {
+ queue_destroy(ltv_caps, NULL);
+ return NULL;
+ } else
+ return ltv_caps;
+}
+
+static void remove_ltv_form_list(void *data, void *user_data)
+{
+ struct bt_ltv *ltv = data;
+ struct queue *ltv_caps = user_data;
+
+ queue_remove(ltv_caps, ltv);
+}
+
+static void set_base_subgroup(void *data, void *user_data)
+{
+ struct bt_stream_base_data *stream_base = data;
+ struct bt_base_data *base = user_data;
+ struct queue *ltv_caps;
+
+ if (queue_isempty(base->base_data_subgroup)) {
+ add_new_subgroup(base->base_data_subgroup, stream_base);
+ } else {
+ /* Verify if a subgroup has the same metadata */
+ const struct queue_entry *entry;
+ struct bt_base_data_subgroup *subgroup_base = NULL;
+ bool same_meta = false;
+
+ for (entry = queue_get_entries(base->base_data_subgroup);
+ entry; entry = entry->next) {
+ subgroup_base = entry->data;
+ if (compare_ltv_lists(subgroup_base->ltv_meta,
+ stream_base->ltv_meta)) {
+ same_meta = true;
+ break;
+ }
+ }
+
+ if (!same_meta) {
+ /* No subgroup with the same metadata found.
+ * Create a new one.
+ */
+ add_new_subgroup(base->base_data_subgroup,
+ stream_base);
+ } else {
+ /* Subgroup found with the same metadata
+ * get different capabilities
+ */
+ ltv_caps = compare_caps_ltv_lists(
+ subgroup_base->ltv_caps,
+ stream_base->ltv_caps);
+
+ queue_foreach(ltv_caps, remove_ltv_form_list,
+ stream_base->ltv_caps);
+ stream_base->stream->qos.bcast.bis = get_bis_index(
+ base->base_data_subgroup);
+ add_new_bis(subgroup_base,
+ stream_base->stream->qos.bcast.bis,
+ ltv_caps);
+ }
+ }
+}
+
+static void set_device_presentation_delay(void *data, void *user_data)
+{
+ struct bt_stream_base_data *sbd = data;
+ struct bt_base_data *base = user_data;
+ struct bt_bap_qos *qos = &sbd->stream->qos;
+
+ if (base->pres_delay < qos->bcast.delay)
+ base->pres_delay = qos->bcast.delay;
+}
+
+static void parse_ltv(size_t i, uint8_t l, uint8_t t, uint8_t *v,
+ void *user_data)
+{
+ struct queue **ltv_queue = user_data;
+ struct bt_ltv *q_ltv_elem = malloc(sizeof(struct bt_ltv) + l);
+
+ q_ltv_elem->len = l + 1;
+ q_ltv_elem->type = t;
+
+ memcpy(q_ltv_elem->value, v, l);
+
+ if (!*ltv_queue)
+ *ltv_queue = queue_new();
+ queue_push_tail(*ltv_queue, q_ltv_elem);
+}
+
+/*
+ * Extract Codec Specific configurations and Metadata information
+ * that will be use in the BASE creation
+ */
+static struct bt_stream_base_data *get_stream_base_info(
+ struct bt_bap_stream *stream)
+{
+ struct bt_stream_base_data *sbd = new0(struct bt_stream_base_data, 1);
+ struct iovec *stream_caps_iov = util_iov_dup(
+ stream->cc, 1);
+ struct iovec *stream_meta_iov = util_iov_dup(
+ stream->meta, 1);
+
+ /*
+ * Copy the Codec Specific configurations from stream
+ */
+ if (stream_caps_iov != NULL) {
+ if (!util_ltv_foreach(stream_caps_iov->iov_base,
+ stream_caps_iov->iov_len, NULL,
+ parse_ltv, &sbd->ltv_caps)) {
+ DBG(stream->bap,
+ "Unable to parse Codec Specific configurations");
+ goto fail;
+ }
+ }
+
+ /*
+ * Copy the Metadata from stream
+ */
+ if (stream_meta_iov != NULL) {
+ if (!util_ltv_foreach(stream_meta_iov->iov_base,
+ stream_meta_iov->iov_len, NULL,
+ parse_ltv, &sbd->ltv_meta)) {
+ DBG(stream->bap,
+ "Unable to parse metadata");
+ goto fail;
+ }
+ }
+
+ sbd->stream = stream;
+
+ util_iov_free(stream_caps_iov, 1);
+ util_iov_free(stream_meta_iov, 1);
+
+ return sbd;
+
+fail:
+ util_iov_free(stream_caps_iov, 1);
+ util_iov_free(stream_meta_iov, 1);
+
+ if (sbd->ltv_caps)
+ queue_destroy(sbd->ltv_caps, destroy_ltv);
+
+ if (sbd->ltv_meta)
+ queue_destroy(sbd->ltv_meta, destroy_ltv);
+
+ free(sbd);
+
+ return NULL;
+}
+
+static void get_stream_base_data(void *data, void *user_data)
+{
+ struct bt_bap_stream *stream = data;
+ struct queue *streams_base_data = user_data;
+ struct bt_stream_base_data *sbd = get_stream_base_info(stream);
+
+ if (sbd)
+ queue_push_tail(streams_base_data, sbd);
+}
+
+/*
+ * Function to update the BASE using configuration data
+ * from each BIS in an BIG
+ */
+struct iovec *bt_bap_update_base(struct bt_bap_stream *stream)
+{
+ struct bt_base_data *base;
+ struct iovec *base_iov;
+ struct queue *streams_base_data = queue_new();
+ struct bt_stream_base_data *sbd = get_stream_base_info(stream);
+
+ /*
+ * Extract Codec Specific configurations and Metadata information
+ * that will be use in the BASE creation from all linked BISes
+ */
+ queue_foreach(stream->bcast_links, get_stream_base_data,
+ streams_base_data);
+
+ queue_push_tail(streams_base_data, sbd);
+
+ base = new0(struct bt_base_data, 1);
+ base->base_data_subgroup = queue_new();
+
+ queue_foreach(streams_base_data, set_device_presentation_delay, base);
+
+ /*
+ * Create subgroups and BISes in a BASE
+ */
+ queue_foreach(streams_base_data, set_base_subgroup, base);
+
+ base_iov = generate_base(base);
+
+ queue_destroy(streams_base_data, destroy_stream_base_data);
+
+ queue_destroy(base->base_data_subgroup, destroy_base_data_subgroup);
+
+ free(base);
+
+ return base_iov;
+}
diff --git a/src/shared/bap.h b/src/shared/bap.h
index 51edc08ab..725151fa5 100644
--- a/src/shared/bap.h
+++ b/src/shared/bap.h
@@ -88,6 +88,7 @@ struct bt_bap_bcast_qos {
uint16_t timeout;
uint8_t pa_sync;
struct bt_bap_io_qos io_qos;
+ uint32_t delay; /* Presentation Delay */
};

struct bt_bap_qos {
@@ -321,3 +322,4 @@ void bt_bap_update_bcast_source(struct bt_bap_pac *pac,

bool bt_bap_pac_bcast_is_local(struct bt_bap *bap, struct bt_bap_pac *pac);

+struct iovec *bt_bap_update_base(struct bt_bap_stream *stream);
--
2.39.2


2024-01-15 00:19:23

by bluez.test.bot

[permalink] [raw]
Subject: RE: Add code to support dynamically generated BASE

This is automated email and please do not reply to this email!

Dear submitter,

Thank you for submitting the patches to the linux bluetooth mailing list.
This is a CI test results with your patch series:
PW Link:https://patchwork.kernel.org/project/bluetooth/list/?series=816773

---Test result---

Test Summary:
CheckPatch PASS 1.20 seconds
GitLint PASS 0.70 seconds
BuildEll PASS 24.23 seconds
BluezMake PASS 718.94 seconds
MakeCheck PASS 11.92 seconds
MakeDistcheck PASS 161.77 seconds
CheckValgrind PASS 224.61 seconds
CheckSmatch PASS 330.37 seconds
bluezmakeextell PASS 107.24 seconds
IncrementalBuild PASS 1328.07 seconds
ScanBuild PASS 960.58 seconds



---
Regards,
Linux Bluetooth

2024-01-16 17:20:32

by patchwork-bot+bluetooth

[permalink] [raw]
Subject: Re: [PATCH v3 0/2] Add code to support dynamically generated BASE

Hello:

This series was applied to bluetooth/bluez.git (master)
by Luiz Augusto von Dentz <[email protected]>:

On Mon, 15 Jan 2024 00:41:02 +0200 you wrote:
> Added code that will be used to generate the BASE dynamically in Bluez.
> This code will help when will remove the base_lc3_16_2_1 from player.c
> so we can use the preset from the endpoint.config command.
>
> Added fix for ScanBuild error: Dereference of null pointer
>
> Silviu Florian Barbulescu (2):
> shared/bap: Fix ScanBuild error: Dereference of null pointer
> shared/bap: Add code to support dynamically generated BASE from
> presets
>
> [...]

Here is the summary with links:
- [v3,1/2] shared/bap: Fix dereference of null pointer
https://git.kernel.org/pub/scm/bluetooth/bluez.git/?id=2a3e935f3f65
- [v3,2/2] shared/bap: Code for dynamically generated BASE
(no matching commit)

You are awesome, thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html