2024-02-07 12:23:28

by Andrei Istodorescu

[permalink] [raw]
Subject: [PATCH BlueZ 0/3] Update Sink BASE management

This patch refactors the flow for the BAP Broadcast Sink.
After discovering a new Broadcast Source a short PA Sync will be tried.
Depending on the BASE information present in the PA and the local PACS
capabilities of the Sink, one remote media endpoint will be created for
each compatible BIS.
Configuring one of these endpoints results in creating a stream and a
media transport for the corresponding BIS.


Andrei Istodorescu (3):
shared/bap: Add API to convert BASE into bt_bap_base queue
shared/bap: Add API to convert parsed BASE into pac data
bap: Do PA Sync for each BAP Broadcast source discovered

profiles/audio/bap.c | 324 ++++++++++------------------
src/shared/bap.c | 502 +++++++++++++++++++++++++++++++++++++++----
src/shared/bap.h | 34 ++-
3 files changed, 602 insertions(+), 258 deletions(-)

--
2.40.1



2024-02-07 12:23:28

by Andrei Istodorescu

[permalink] [raw]
Subject: [PATCH BlueZ 1/3] shared/bap: Add API to convert BASE into bt_bap_base queue

Create public function to read the BASE bytes and populate a bt_bap_base
structure for further processing.
---
src/shared/bap.c | 250 ++++++++++++++++++++++++++++++++++++++++-------
src/shared/bap.h | 27 +++++
2 files changed, 244 insertions(+), 33 deletions(-)

diff --git a/src/shared/bap.c b/src/shared/bap.c
index 00d211c15ddf..aa78b1b74fbb 100644
--- a/src/shared/bap.c
+++ b/src/shared/bap.c
@@ -255,26 +255,6 @@ struct bt_pacs_context {
uint16_t src;
} __packed;

-struct bt_base {
- uint8_t big_id;
- uint32_t pres_delay;
- uint8_t next_bis_index;
- struct queue *subgroups;
-};
-
-struct bt_subgroup {
- uint8_t index;
- struct bt_bap_codec codec;
- struct iovec *caps;
- struct iovec *meta;
- struct queue *bises;
-};
-
-struct bt_bis {
- uint8_t index;
- struct iovec *caps;
-};
-
/* Contains local bt_bap_db */
static struct queue *bap_db;
static struct queue *bap_cbs;
@@ -5536,7 +5516,7 @@ void bt_bap_update_bcast_source(struct bt_bap_pac *pac,

static void destroy_base_bis(void *data)
{
- struct bt_bis *bis = data;
+ struct bt_bap_bis *bis = data;

if (!bis)
return;
@@ -5549,7 +5529,7 @@ static void destroy_base_bis(void *data)

static void generate_bis_base(void *data, void *user_data)
{
- struct bt_bis *bis = data;
+ struct bt_bap_bis *bis = data;
struct iovec *base_iov = user_data;
uint8_t cc_length = bis->caps->iov_len;

@@ -5566,7 +5546,7 @@ static void generate_bis_base(void *data, void *user_data)

static void generate_subgroup_base(void *data, void *user_data)
{
- struct bt_subgroup *sgrp = data;
+ struct bt_bap_subgroup *sgrp = data;
struct iovec *base_iov = user_data;

if (!util_iov_push_u8(base_iov, queue_length(sgrp->bises)))
@@ -5604,7 +5584,7 @@ static void generate_subgroup_base(void *data, void *user_data)
queue_foreach(sgrp->bises, generate_bis_base, base_iov);
}

-static struct iovec *generate_base(struct bt_base *base)
+static struct iovec *generate_base(struct bt_bap_base *base)
{
struct iovec *base_iov = new0(struct iovec, 0x1);

@@ -5623,10 +5603,10 @@ static struct iovec *generate_base(struct bt_base *base)
return base_iov;
}

-static void add_new_bis(struct bt_subgroup *subgroup,
+static void add_new_bis(struct bt_bap_subgroup *subgroup,
uint8_t bis_index, struct iovec *caps)
{
- struct bt_bis *bis = new0(struct bt_bis, 1);
+ struct bt_bap_bis *bis = new0(struct bt_bap_bis, 1);

bis->index = bis_index;

@@ -5638,12 +5618,12 @@ static void add_new_bis(struct bt_subgroup *subgroup,
queue_push_tail(subgroup->bises, bis);
}

-static void add_new_subgroup(struct bt_base *base,
+static void add_new_subgroup(struct bt_bap_base *base,
struct bt_bap_stream *stream)
{
struct bt_bap_pac *lpac = stream->lpac;
- struct bt_subgroup *sgrp = new0(
- struct bt_subgroup, 1);
+ struct bt_bap_subgroup *sgrp = new0(
+ struct bt_bap_subgroup, 1);
uint16_t cid = 0;
uint16_t vid = 0;

@@ -5784,7 +5764,7 @@ static struct iovec *extract_diff_caps(
static void set_base_subgroup(void *data, void *user_data)
{
struct bt_bap_stream *stream = data;
- struct bt_base *base = user_data;
+ struct bt_bap_base *base = user_data;
/* BIS specific codec capabilities */
struct iovec *bis_caps;

@@ -5802,7 +5782,7 @@ static void set_base_subgroup(void *data, void *user_data)
} else {
/* Verify if a subgroup has the same metadata */
const struct queue_entry *entry;
- struct bt_subgroup *subgroup = NULL;
+ struct bt_bap_subgroup *subgroup = NULL;
bool same_meta = false;

for (entry = queue_get_entries(base->subgroups);
@@ -5836,7 +5816,7 @@ static void set_base_subgroup(void *data, void *user_data)

static void destroy_base_subgroup(void *data)
{
- struct bt_subgroup *subgroup = data;
+ struct bt_bap_subgroup *subgroup = data;

if (!subgroup)
return;
@@ -5858,7 +5838,7 @@ static void destroy_base_subgroup(void *data)
*/
struct iovec *bt_bap_stream_get_base(struct bt_bap_stream *stream)
{
- struct bt_base base;
+ struct bt_bap_base base;
struct iovec *base_iov;

base.subgroups = queue_new();
@@ -5877,3 +5857,207 @@ struct iovec *bt_bap_stream_get_base(struct bt_bap_stream *stream)

return base_iov;
}
+
+static void cleanup_bis(void *data)
+{
+ struct bt_bap_bis *bis = data;
+
+ if (bis->caps)
+ util_iov_free(bis->caps, 1);
+}
+
+static void cleanup_subgroup(struct bt_bap_subgroup *subgroup)
+{
+ if (!subgroup)
+ return;
+
+ if (subgroup->meta)
+ util_iov_free(subgroup->meta, 1);
+
+ if (subgroup->caps)
+ util_iov_free(subgroup->caps, 1);
+
+ queue_destroy(subgroup->bises, cleanup_bis);
+
+ if (subgroup)
+ free(subgroup);
+}
+
+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,
+ };
+ util_debug(func, NULL, "BASE len %ld", len);
+ if (!base)
+ return false;
+
+ if (!util_iov_pull_le24(&iov, &base->pres_delay))
+ return false;
+ util_debug(func, NULL, "PresentationDelay %d", base->pres_delay);
+
+ 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;
+
+ for (int sg = 0; sg < num_subgroups; sg++) {
+ struct bt_bap_subgroup *sub_group = new0(
+ struct bt_bap_subgroup, 1);
+ uint8_t caps_len, metaLen;
+ uint8_t *hexstream;
+
+ sub_group->subgroup_index = sg;
+
+ util_debug(func, NULL, "Subgroup #%d", sg);
+ sub_group->bap = bap;
+ sub_group->bises = queue_new();
+
+ if (!util_iov_pull_u8(&iov, &num_bis)) {
+ cleanup_subgroup(sub_group);
+ 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);
+
+ /*
+ * 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;
+ }
+
+ 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);
+ }
+
+ if (!util_iov_pull_u8(&iov, &metaLen)) {
+ cleanup_subgroup(sub_group);
+ 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;
+
+ hexstream = util_iov_pull_mem(&iov, metaLen);
+ if (!hexstream) {
+ cleanup_subgroup(sub_group);
+ goto fail;
+ }
+ util_hexdump(' ', hexstream, metaLen, func, NULL);
+
+ for (int bis_sg = 0; bis_sg < sub_group->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);
+ goto fail;
+ }
+ util_debug(func, NULL, "BIS #%d", crt_bis);
+
+ bis = new0(struct bt_bap_bis, 1);
+ bis->index = crt_bis;
+
+ if (!util_iov_pull_u8(&iov, &caps_len)) {
+ cleanup_subgroup(sub_group);
+ 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",
+ 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);
+ }
+
+ queue_push_tail(sub_group->bises, bis);
+ }
+
+ queue_push_tail(base->subgroups, sub_group);
+ }
+ 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;
+}
diff --git a/src/shared/bap.h b/src/shared/bap.h
index 2c3550921f07..b13fef688da3 100644
--- a/src/shared/bap.h
+++ b/src/shared/bap.h
@@ -98,6 +98,29 @@ 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;
+ struct queue *subgroups;
+};
+
+struct bt_bap_subgroup {
+ uint8_t subgroup_index;
+ struct bt_bap *bap;
+ uint8_t num_bises;
+ struct bt_bap_codec codec;
+ struct iovec *caps;
+ struct iovec *meta;
+ struct queue *bises;
+};
+
+struct bt_bap_bis {
+ uint8_t index;
+ struct iovec *caps;
+};
+
typedef void (*bt_bap_ready_func_t)(struct bt_bap *bap, void *user_data);
typedef void (*bt_bap_destroy_func_t)(void *user_data);
typedef void (*bt_bap_debug_func_t)(const char *str, void *user_data);
@@ -323,3 +346,7 @@ 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_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);
+
--
2.40.1


2024-02-07 12:23:35

by Andrei Istodorescu

[permalink] [raw]
Subject: [PATCH BlueZ 3/3] bap: Do PA Sync for each BAP Broadcast source discovered

After discovering a BAP Broadcast Source do a short PA sync first to learn
the BASE. After discovering the BASE check how many BISes are matching
the sink capabilities and create endpoints for them. Allow user to
configure one endpoint using "SetConfiguration" causing BIG
synchronization to the corresponding BIS; also this results in creating a
stream and the corresponding transport.
---
profiles/audio/bap.c | 324 +++++++++++++++----------------------------
1 file changed, 110 insertions(+), 214 deletions(-)

diff --git a/profiles/audio/bap.c b/profiles/audio/bap.c
index 88c93127bea0..8646eae2ed20 100644
--- a/profiles/audio/bap.c
+++ b/profiles/audio/bap.c
@@ -105,6 +105,44 @@ struct bap_data {
void *user_data;
};

+/* Structure holding the parameters for periodic train and BIG
+ * synchronization
+ */
+static struct bt_iso_qos bap_sink_sync_parameters = {
+ .bcast = {
+ .big = BT_ISO_QOS_BIG_UNSET,
+ .bis = BT_ISO_QOS_BIS_UNSET,
+ /* HCI_LE_Periodic_Advertising_Create_Sync */
+ .options = 0x00,
+ .skip = 0x0000,
+ .sync_timeout = 0x4000,
+ .sync_cte_type = 0x00,
+ /* HCI_LE_BIG_Create_Sync */
+ .encryption = 0x00,
+ .bcode = {0x00},
+ .mse = 0x00,
+ .timeout = 0x4000,
+ /* to remove from kernel check */
+ .sync_factor = 0x07,
+ .packing = 0x00,
+ .framing = 0x00,
+ .in = {
+ .interval = 10000,
+ .latency = 10,
+ .sdu = 40,
+ .phy = 0x02,
+ .rtn = 2,
+ },
+ .out = {
+ .interval = 10000,
+ .latency = 10,
+ .sdu = 40,
+ .phy = 0x02,
+ .rtn = 2,
+ }
+ }
+};
+
static struct queue *sessions;

static bool bap_data_set_user_data(struct bap_data *data, void *user_data)
@@ -422,113 +460,6 @@ static int parse_array(DBusMessageIter *iter, struct iovec *iov)
return 0;
}

-static bool parse_base(void *data, size_t len, util_debug_func_t func,
- uint32_t *presDelay, uint8_t *numSubgroups, uint8_t *numBis,
- struct bt_bap_codec *codec, struct iovec **caps,
- struct iovec **meta)
-{
- struct iovec iov = {
- .iov_base = data,
- .iov_len = len,
- };
-
- uint8_t capsLen, metaLen;
- struct iovec cc;
- struct iovec metadata;
-
- if (presDelay) {
- if (!util_iov_pull_le24(&iov, presDelay))
- return false;
- util_debug(func, NULL, "PresentationDelay %d", *presDelay);
- }
-
- if (numSubgroups) {
- if (!util_iov_pull_u8(&iov, numSubgroups))
- return false;
- util_debug(func, NULL, "NumSubgroups %d", *numSubgroups);
- }
-
- if (numBis) {
- if (!util_iov_pull_u8(&iov, numBis))
- return false;
- util_debug(func, NULL, "NumBis %d", *numBis);
- }
-
- if (codec) {
- codec = util_iov_pull_mem(&iov, sizeof(*codec));
- if (!codec)
- return false;
- util_debug(func, NULL, "%s: ID %d CID 0x%2.2x VID 0x%2.2x",
- "Codec", codec->id, codec->cid, codec->vid);
- }
-
- if (!util_iov_pull_u8(&iov, &capsLen))
- return false;
- util_debug(func, NULL, "CC Len %d", capsLen);
-
- if (!capsLen)
- return false;
-
- cc.iov_len = capsLen;
- cc.iov_base = util_iov_pull_mem(&iov, capsLen);
- if (!cc.iov_base)
- return false;
-
- if (caps) {
- if (*caps)
- util_iov_free(*caps, 1);
-
- *caps = util_iov_dup(&cc, 1);
- }
-
- for (int i = 0; capsLen > 1; i++) {
- struct bt_ltv *ltv = util_iov_pull_mem(&cc, sizeof(*ltv));
- uint8_t *caps;
-
- if (!ltv) {
- util_debug(func, NULL, "Unable to parse %s",
- "Capabilities");
- return false;
- }
-
- util_debug(func, NULL, "%s #%u: len %u type %u",
- "CC", i, ltv->len, ltv->type);
-
- caps = util_iov_pull_mem(&cc, ltv->len - 1);
- if (!caps) {
- util_debug(func, NULL, "Unable to parse %s",
- "CC");
- return false;
- }
- util_hexdump(' ', caps, ltv->len - 1, func, NULL);
-
- capsLen -= (ltv->len + 1);
- }
-
- if (!util_iov_pull_u8(&iov, &metaLen))
- return false;
- util_debug(func, NULL, "Metadata Len %d", metaLen);
-
- if (!metaLen)
- return false;
-
- metadata.iov_len = metaLen;
- metadata.iov_base = util_iov_pull_mem(&iov, metaLen);
- if (!metadata.iov_base)
- return false;
-
- if (meta) {
- if (*meta)
- util_iov_free(*meta, 1);
-
- *meta = util_iov_dup(&metadata, 1);
- }
-
- util_hexdump(' ', metadata.iov_base, metaLen, func, NULL);
-
- return true;
-}
-
static int parse_io_qos(const char *key, int var, DBusMessageIter *iter,
struct bt_bap_io_qos *qos)
{
@@ -954,6 +885,17 @@ static DBusMessage *set_configuration(DBusConnection *conn, DBusMessage *msg,
return btd_error_invalid_args(msg);
}

+ /* For BAP Broadcast Sink, the capabilities and metadata are coming
+ * from the source's BIS, which are present in the remote PAC
+ */
+ if (bt_bap_pac_get_type(ep->lpac) == BT_BAP_BCAST_SINK) {
+ util_iov_free(setup->caps, 1);
+ setup->caps = util_iov_dup(bt_bap_pac_get_data(ep->rpac), 1);
+ util_iov_free(setup->metadata, 1);
+ setup->metadata = util_iov_dup(
+ bt_bap_pac_get_metadata(ep->rpac), 1);
+ }
+
setup->stream = bt_bap_stream_new(ep->data->bap, ep->lpac, ep->rpac,
&setup->qos, setup->caps);

@@ -977,95 +919,27 @@ static DBusMessage *set_configuration(DBusConnection *conn, DBusMessage *msg,
break;
case BT_BAP_STREAM_TYPE_BCAST:
/* No message sent over the air for broadcast */
- if (bt_bap_pac_get_type(ep->lpac) == BT_BAP_BCAST_SINK)
- setup->msg = dbus_message_ref(msg);
- else {
+ if (bt_bap_pac_get_type(ep->lpac) == BT_BAP_BCAST_SOURCE)
setup->base = bt_bap_stream_get_base(setup->stream);
- setup->id = 0;
}
+ setup->id = 0;

if (ep->data->service)
service_set_connecting(ep->data->service);

return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
- }

return NULL;
}

-static void update_bcast_qos(struct bt_iso_qos *qos,
- struct bt_bap_qos *bap_qos)
-{
- bap_qos->bcast.big = qos->bcast.big;
- bap_qos->bcast.bis = qos->bcast.bis;
- bap_qos->bcast.sync_factor = qos->bcast.sync_factor;
- bap_qos->bcast.packing = qos->bcast.packing;
- bap_qos->bcast.framing = qos->bcast.framing;
- bap_qos->bcast.encryption = qos->bcast.encryption;
- bap_qos->bcast.options = qos->bcast.options;
- bap_qos->bcast.skip = qos->bcast.skip;
- bap_qos->bcast.sync_timeout = qos->bcast.sync_timeout;
- bap_qos->bcast.sync_cte_type = qos->bcast.sync_cte_type;
- bap_qos->bcast.mse = qos->bcast.mse;
- bap_qos->bcast.timeout = qos->bcast.timeout;
- bap_qos->bcast.io_qos.interval = qos->bcast.in.interval;
- bap_qos->bcast.io_qos.latency = qos->bcast.in.latency;
- bap_qos->bcast.io_qos.phy = qos->bcast.in.phy;
- bap_qos->bcast.io_qos.sdu = qos->bcast.in.sdu;
- bap_qos->bcast.io_qos.rtn = qos->bcast.in.rtn;
- if (!bap_qos->bcast.bcode)
- bap_qos->bcast.bcode = new0(struct iovec, 1);
- util_iov_memcpy(bap_qos->bcast.bcode, qos->bcast.bcode,
- sizeof(qos->bcast.bcode));
-}
-
static void iso_bcast_confirm_cb(GIOChannel *io, GError *err, void *user_data)
{
struct bap_setup *setup = user_data;
- struct bap_ep *ep = setup->ep;
- struct bap_data *data = ep->data;
- struct bt_iso_qos qos;
- struct bt_iso_base base;
- char address[18];
int fd;
- struct iovec *base_io;
- uint32_t presDelay;
- uint8_t numSubgroups;
- uint8_t numBis;
- struct bt_bap_codec codec;
-
- bt_io_get(io, &err,
- BT_IO_OPT_DEST, address,
- BT_IO_OPT_QOS, &qos,
- BT_IO_OPT_BASE, &base,
- BT_IO_OPT_INVALID);
- if (err) {
- error("%s", err->message);
- g_error_free(err);
- goto drop;
- }

- g_io_channel_ref(io);
- btd_service_connecting_complete(data->service, 0);
- DBG("BCAST ISO: sync with %s (BIG 0x%02x BIS 0x%02x)",
- address, qos.bcast.big, qos.bcast.bis);
-
- update_bcast_qos(&qos, &setup->qos);
-
- base_io = new0(struct iovec, 1);
- util_iov_memcpy(base_io, base.base, base.base_len);
-
- parse_base(base_io->iov_base, base_io->iov_len, bap_debug,
- &presDelay, &numSubgroups, &numBis,
- &codec, &setup->caps, &setup->metadata);
-
- /* Update pac with BASE information */
- bt_bap_update_bcast_source(ep->rpac, &codec, setup->caps,
- setup->metadata);
- setup->id = bt_bap_stream_config(setup->stream, &setup->qos,
- setup->caps, NULL, NULL);
-
- bt_bap_stream_set_user_data(setup->stream, ep->path);
+ /* listen channel is not needed anymore */
+ g_io_channel_unref(setup->io);
+ setup->io = NULL;

fd = g_io_channel_unix_get_fd(io);

@@ -1074,26 +948,43 @@ static void iso_bcast_confirm_cb(GIOChannel *io, GError *err, void *user_data)
g_io_channel_set_close_on_unref(io, FALSE);
return;
}
-
-
- return;
-
-drop:
- g_io_channel_shutdown(io, TRUE, NULL);
-
}

static void iso_pa_sync_confirm_cb(GIOChannel *io, void *user_data)
{
GError *err = NULL;
+ struct bap_data *data = user_data;
+ struct bt_iso_base base;
+ struct bt_bap_base base_s;
+ struct bt_iso_qos qos;

- if (!bt_io_bcast_accept(io, iso_bcast_confirm_cb,
- user_data, NULL, &err, BT_IO_OPT_INVALID)) {
- error("bt_io_bcast_accept: %s", err->message);
+ btd_service_connecting_complete(data->service, 0);
+
+ bt_io_get(io, &err,
+ BT_IO_OPT_BASE, &base,
+ BT_IO_OPT_QOS, &qos,
+ BT_IO_OPT_INVALID);
+ if (err) {
+ error("%s", err->message);
g_error_free(err);
g_io_channel_shutdown(io, TRUE, NULL);
+ return;
}

+ /* The PA Sync channel becomes the new listen_io.
+ * It will be later used to listen for a BIS io.
+ */
+ g_io_channel_unref(data->listen_io);
+ data->listen_io = io;
+ g_io_channel_ref(io);
+
+ /* Analyze received BASE data and create remote media endpoints for each
+ * matching BIS
+ */
+ base_s.subgroups = queue_new();
+ bt_bap_parse_base(data->bap, base.base, base.base_len, bap_debug,
+ &base_s);
+ queue_foreach(base_s.subgroups, bt_bap_parse_bis, NULL);
}

static bool match_data_bap_data(const void *data, const void *match_data)
@@ -1934,12 +1825,11 @@ static void setup_listen_io(struct bap_data *data, struct bt_bap_stream *stream,
data->listen_io = io;
}

-static void setup_listen_io_broadcast(struct bap_data *data,
+static void setup_accept_io_broadcast(struct bap_data *data,
struct bap_setup *setup,
struct bt_bap_stream *stream,
struct bt_iso_qos *qos)
{
- GIOChannel *io;
GError *err = NULL;
struct sockaddr_iso_bc iso_bc_addr;

@@ -1951,29 +1841,18 @@ static void setup_listen_io_broadcast(struct bap_data *data,

DBG("stream %p", stream);

- /* If IO already set skip creating it again */
- if (bt_bap_stream_get_io(stream) || data->listen_io)
- return;
-
- io = bt_io_listen(NULL, iso_pa_sync_confirm_cb, setup, NULL, &err,
- BT_IO_OPT_SOURCE_BDADDR,
- btd_adapter_get_address(data->adapter),
- BT_IO_OPT_DEST_BDADDR,
- device_get_address(data->device),
- BT_IO_OPT_DEST_TYPE,
- btd_device_get_bdaddr_type(data->device),
- BT_IO_OPT_MODE, BT_IO_MODE_ISO,
- BT_IO_OPT_QOS, &qos->bcast,
- BT_IO_OPT_ISO_BC_NUM_BIS, iso_bc_addr.bc_num_bis,
- BT_IO_OPT_ISO_BC_BIS, iso_bc_addr.bc_bis,
- BT_IO_OPT_INVALID);
- if (!io) {
- error("%s", err->message);
+ if (!bt_io_bcast_accept(data->listen_io,
+ iso_bcast_confirm_cb,
+ setup, NULL, &err,
+ BT_IO_OPT_ISO_BC_NUM_BIS,
+ iso_bc_addr.bc_num_bis, BT_IO_OPT_ISO_BC_BIS,
+ iso_bc_addr.bc_bis, BT_IO_OPT_INVALID)) {
+ error("bt_io_bcast_accept: %s", err->message);
g_error_free(err);
}
- setup->io = io;
- data->listen_io = io;

+ setup->io = data->listen_io;
+ data->listen_io = NULL;
}
static void setup_create_ucast_io(struct bap_data *data,
struct bap_setup *setup,
@@ -2037,7 +1916,7 @@ done:
if (bt_bap_pac_get_type(setup->ep->lpac) == BT_BAP_BCAST_SOURCE)
setup_connect_io_broadcast(data, setup, stream, &iso_qos);
else
- setup_listen_io_broadcast(data, setup, stream, &iso_qos);
+ setup_accept_io_broadcast(data, setup, stream, &iso_qos);
}

static void setup_create_io(struct bap_data *data, struct bap_setup *setup,
@@ -2422,6 +2301,7 @@ static int bap_bcast_probe(struct btd_service *service)
struct btd_gatt_database *database = btd_adapter_get_database(adapter);
struct bap_data *data = btd_service_get_user_data(service);
char addr[18];
+ GError *err = NULL;

ba2str(device_get_address(device), addr);

@@ -2465,7 +2345,23 @@ static int bap_bcast_probe(struct btd_service *service)

bt_bap_set_user_data(data->bap, service);

- bt_bap_new_bcast_source(data->bap, device_get_path(device));
+ DBG("Create PA sync with this source");
+ data->listen_io = bt_io_listen(NULL, iso_pa_sync_confirm_cb, data,
+ NULL, &err,
+ BT_IO_OPT_SOURCE_BDADDR,
+ btd_adapter_get_address(data->adapter),
+ BT_IO_OPT_DEST_BDADDR,
+ device_get_address(data->device),
+ BT_IO_OPT_DEST_TYPE,
+ btd_device_get_bdaddr_type(data->device),
+ BT_IO_OPT_MODE, BT_IO_MODE_ISO,
+ BT_IO_OPT_QOS, &bap_sink_sync_parameters,
+ BT_IO_OPT_INVALID);
+ if (!data->listen_io) {
+ error("%s", err->message);
+ g_error_free(err);
+ }
+
return 0;
}

--
2.40.1


2024-02-07 12:23:36

by Andrei Istodorescu

[permalink] [raw]
Subject: [PATCH BlueZ 2/3] shared/bap: Add API to convert parsed BASE into pac data

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 | 502 ++++++++++++++++++++++++++++++++++-------------
src/shared/bap.h | 11 +-
2 files changed, 375 insertions(+), 138 deletions(-)

diff --git a/src/shared/bap.c b/src/shared/bap.c
index aa78b1b74fbb..09f681688cc7 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;
@@ -2655,6 +2660,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)
@@ -2806,6 +2818,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)
@@ -4631,11 +4653,6 @@ unsigned int bt_bap_stream_config(struct bt_bap_stream *stream,
return req->id;
case BT_BAP_STREAM_TYPE_BCAST:
stream->qos = *qos;
- if (stream->lpac->type == BT_BAP_BCAST_SINK) {
- if (data)
- stream_config(stream, data, NULL);
- stream_set_state(stream, BT_BAP_STREAM_STATE_CONFIG);
- }
return 1;
}

@@ -5643,8 +5660,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 {
@@ -5663,7 +5681,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;
}

@@ -5675,7 +5693,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,
@@ -5716,8 +5734,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,
@@ -5729,7 +5749,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
@@ -5883,12 +5903,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,
@@ -5903,161 +5927,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);
- 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);
+ if (!util_iov_pull_u8(&iov, &subgroup->num_bises))
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);

--
2.40.1


2024-02-07 14:03:55

by bluez.test.bot

[permalink] [raw]
Subject: RE: Update Sink BASE management

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=823945

---Test result---

Test Summary:
CheckPatch PASS 1.94 seconds
GitLint PASS 0.98 seconds
BuildEll PASS 24.45 seconds
BluezMake PASS 738.59 seconds
MakeCheck PASS 12.13 seconds
MakeDistcheck PASS 164.55 seconds
CheckValgrind PASS 227.95 seconds
CheckSmatch PASS 330.35 seconds
bluezmakeextell PASS 107.35 seconds
IncrementalBuild PASS 2026.10 seconds
ScanBuild PASS 936.05 seconds



---
Regards,
Linux Bluetooth

2024-02-07 14:42:36

by Luiz Augusto von Dentz

[permalink] [raw]
Subject: Re: [PATCH BlueZ 3/3] bap: Do PA Sync for each BAP Broadcast source discovered

Hi Andrei,

On Wed, Feb 7, 2024 at 7:23 AM Andrei Istodorescu
<[email protected]> wrote:
>
> After discovering a BAP Broadcast Source do a short PA sync first to learn
> the BASE. After discovering the BASE check how many BISes are matching
> the sink capabilities and create endpoints for them. Allow user to
> configure one endpoint using "SetConfiguration" causing BIG
> synchronization to the corresponding BIS; also this results in creating a
> stream and the corresponding transport.
> ---
> profiles/audio/bap.c | 324 +++++++++++++++----------------------------
> 1 file changed, 110 insertions(+), 214 deletions(-)
>
> diff --git a/profiles/audio/bap.c b/profiles/audio/bap.c
> index 88c93127bea0..8646eae2ed20 100644
> --- a/profiles/audio/bap.c
> +++ b/profiles/audio/bap.c
> @@ -105,6 +105,44 @@ struct bap_data {
> void *user_data;
> };
>
> +/* Structure holding the parameters for periodic train and BIG
> + * synchronization
> + */
> +static struct bt_iso_qos bap_sink_sync_parameters = {
> + .bcast = {
> + .big = BT_ISO_QOS_BIG_UNSET,
> + .bis = BT_ISO_QOS_BIS_UNSET,
> + /* HCI_LE_Periodic_Advertising_Create_Sync */
> + .options = 0x00,
> + .skip = 0x0000,
> + .sync_timeout = 0x4000,
> + .sync_cte_type = 0x00,
> + /* HCI_LE_BIG_Create_Sync */
> + .encryption = 0x00,
> + .bcode = {0x00},
> + .mse = 0x00,
> + .timeout = 0x4000,
> + /* to remove from kernel check */
> + .sync_factor = 0x07,
> + .packing = 0x00,
> + .framing = 0x00,
> + .in = {
> + .interval = 10000,
> + .latency = 10,
> + .sdu = 40,
> + .phy = 0x02,
> + .rtn = 2,
> + },
> + .out = {
> + .interval = 10000,
> + .latency = 10,
> + .sdu = 40,
> + .phy = 0x02,
> + .rtn = 2,
> + }
> + }
> +};

This cannot be global, it needs to be stored on a per device basis so
it doesn't get overwritten.

> static struct queue *sessions;
>
> static bool bap_data_set_user_data(struct bap_data *data, void *user_data)
> @@ -422,113 +460,6 @@ static int parse_array(DBusMessageIter *iter, struct iovec *iov)
> return 0;
> }
>
> -static bool parse_base(void *data, size_t len, util_debug_func_t func,
> - uint32_t *presDelay, uint8_t *numSubgroups, uint8_t *numBis,
> - struct bt_bap_codec *codec, struct iovec **caps,
> - struct iovec **meta)
> -{
> - struct iovec iov = {
> - .iov_base = data,
> - .iov_len = len,
> - };
> -
> - uint8_t capsLen, metaLen;
> - struct iovec cc;
> - struct iovec metadata;
> -
> - if (presDelay) {
> - if (!util_iov_pull_le24(&iov, presDelay))
> - return false;
> - util_debug(func, NULL, "PresentationDelay %d", *presDelay);
> - }
> -
> - if (numSubgroups) {
> - if (!util_iov_pull_u8(&iov, numSubgroups))
> - return false;
> - util_debug(func, NULL, "NumSubgroups %d", *numSubgroups);
> - }
> -
> - if (numBis) {
> - if (!util_iov_pull_u8(&iov, numBis))
> - return false;
> - util_debug(func, NULL, "NumBis %d", *numBis);
> - }
> -
> - if (codec) {
> - codec = util_iov_pull_mem(&iov, sizeof(*codec));
> - if (!codec)
> - return false;
> - util_debug(func, NULL, "%s: ID %d CID 0x%2.2x VID 0x%2.2x",
> - "Codec", codec->id, codec->cid, codec->vid);
> - }
> -
> - if (!util_iov_pull_u8(&iov, &capsLen))
> - return false;
> - util_debug(func, NULL, "CC Len %d", capsLen);
> -
> - if (!capsLen)
> - return false;
> -
> - cc.iov_len = capsLen;
> - cc.iov_base = util_iov_pull_mem(&iov, capsLen);
> - if (!cc.iov_base)
> - return false;
> -
> - if (caps) {
> - if (*caps)
> - util_iov_free(*caps, 1);
> -
> - *caps = util_iov_dup(&cc, 1);
> - }
> -
> - for (int i = 0; capsLen > 1; i++) {
> - struct bt_ltv *ltv = util_iov_pull_mem(&cc, sizeof(*ltv));
> - uint8_t *caps;
> -
> - if (!ltv) {
> - util_debug(func, NULL, "Unable to parse %s",
> - "Capabilities");
> - return false;
> - }
> -
> - util_debug(func, NULL, "%s #%u: len %u type %u",
> - "CC", i, ltv->len, ltv->type);
> -
> - caps = util_iov_pull_mem(&cc, ltv->len - 1);
> - if (!caps) {
> - util_debug(func, NULL, "Unable to parse %s",
> - "CC");
> - return false;
> - }
> - util_hexdump(' ', caps, ltv->len - 1, func, NULL);
> -
> - capsLen -= (ltv->len + 1);
> - }
> -
> - if (!util_iov_pull_u8(&iov, &metaLen))
> - return false;
> - util_debug(func, NULL, "Metadata Len %d", metaLen);
> -
> - if (!metaLen)
> - return false;
> -
> - metadata.iov_len = metaLen;
> - metadata.iov_base = util_iov_pull_mem(&iov, metaLen);
> - if (!metadata.iov_base)
> - return false;
> -
> - if (meta) {
> - if (*meta)
> - util_iov_free(*meta, 1);
> -
> - *meta = util_iov_dup(&metadata, 1);
> - }
> -
> - util_hexdump(' ', metadata.iov_base, metaLen, func, NULL);
> -
> - return true;
> -}
> -
> static int parse_io_qos(const char *key, int var, DBusMessageIter *iter,
> struct bt_bap_io_qos *qos)
> {
> @@ -954,6 +885,17 @@ static DBusMessage *set_configuration(DBusConnection *conn, DBusMessage *msg,
> return btd_error_invalid_args(msg);
> }
>
> + /* For BAP Broadcast Sink, the capabilities and metadata are coming
> + * from the source's BIS, which are present in the remote PAC
> + */
> + if (bt_bap_pac_get_type(ep->lpac) == BT_BAP_BCAST_SINK) {
> + util_iov_free(setup->caps, 1);
> + setup->caps = util_iov_dup(bt_bap_pac_get_data(ep->rpac), 1);
> + util_iov_free(setup->metadata, 1);
> + setup->metadata = util_iov_dup(
> + bt_bap_pac_get_metadata(ep->rpac), 1);
> + }
> +
> setup->stream = bt_bap_stream_new(ep->data->bap, ep->lpac, ep->rpac,
> &setup->qos, setup->caps);
>
> @@ -977,95 +919,27 @@ static DBusMessage *set_configuration(DBusConnection *conn, DBusMessage *msg,
> break;
> case BT_BAP_STREAM_TYPE_BCAST:
> /* No message sent over the air for broadcast */
> - if (bt_bap_pac_get_type(ep->lpac) == BT_BAP_BCAST_SINK)
> - setup->msg = dbus_message_ref(msg);
> - else {
> + if (bt_bap_pac_get_type(ep->lpac) == BT_BAP_BCAST_SOURCE)
> setup->base = bt_bap_stream_get_base(setup->stream);
> - setup->id = 0;
> }
> + setup->id = 0;
>
> if (ep->data->service)
> service_set_connecting(ep->data->service);
>
> return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
> - }
>
> return NULL;
> }
>
> -static void update_bcast_qos(struct bt_iso_qos *qos,
> - struct bt_bap_qos *bap_qos)
> -{
> - bap_qos->bcast.big = qos->bcast.big;
> - bap_qos->bcast.bis = qos->bcast.bis;
> - bap_qos->bcast.sync_factor = qos->bcast.sync_factor;
> - bap_qos->bcast.packing = qos->bcast.packing;
> - bap_qos->bcast.framing = qos->bcast.framing;
> - bap_qos->bcast.encryption = qos->bcast.encryption;
> - bap_qos->bcast.options = qos->bcast.options;
> - bap_qos->bcast.skip = qos->bcast.skip;
> - bap_qos->bcast.sync_timeout = qos->bcast.sync_timeout;
> - bap_qos->bcast.sync_cte_type = qos->bcast.sync_cte_type;
> - bap_qos->bcast.mse = qos->bcast.mse;
> - bap_qos->bcast.timeout = qos->bcast.timeout;
> - bap_qos->bcast.io_qos.interval = qos->bcast.in.interval;
> - bap_qos->bcast.io_qos.latency = qos->bcast.in.latency;
> - bap_qos->bcast.io_qos.phy = qos->bcast.in.phy;
> - bap_qos->bcast.io_qos.sdu = qos->bcast.in.sdu;
> - bap_qos->bcast.io_qos.rtn = qos->bcast.in.rtn;
> - if (!bap_qos->bcast.bcode)
> - bap_qos->bcast.bcode = new0(struct iovec, 1);
> - util_iov_memcpy(bap_qos->bcast.bcode, qos->bcast.bcode,
> - sizeof(qos->bcast.bcode));
> -}
> -
> static void iso_bcast_confirm_cb(GIOChannel *io, GError *err, void *user_data)
> {
> struct bap_setup *setup = user_data;
> - struct bap_ep *ep = setup->ep;
> - struct bap_data *data = ep->data;
> - struct bt_iso_qos qos;
> - struct bt_iso_base base;
> - char address[18];
> int fd;
> - struct iovec *base_io;
> - uint32_t presDelay;
> - uint8_t numSubgroups;
> - uint8_t numBis;
> - struct bt_bap_codec codec;
> -
> - bt_io_get(io, &err,
> - BT_IO_OPT_DEST, address,
> - BT_IO_OPT_QOS, &qos,
> - BT_IO_OPT_BASE, &base,
> - BT_IO_OPT_INVALID);
> - if (err) {
> - error("%s", err->message);
> - g_error_free(err);
> - goto drop;
> - }
>
> - g_io_channel_ref(io);
> - btd_service_connecting_complete(data->service, 0);
> - DBG("BCAST ISO: sync with %s (BIG 0x%02x BIS 0x%02x)",
> - address, qos.bcast.big, qos.bcast.bis);
> -
> - update_bcast_qos(&qos, &setup->qos);
> -
> - base_io = new0(struct iovec, 1);
> - util_iov_memcpy(base_io, base.base, base.base_len);
> -
> - parse_base(base_io->iov_base, base_io->iov_len, bap_debug,
> - &presDelay, &numSubgroups, &numBis,
> - &codec, &setup->caps, &setup->metadata);
> -
> - /* Update pac with BASE information */
> - bt_bap_update_bcast_source(ep->rpac, &codec, setup->caps,
> - setup->metadata);
> - setup->id = bt_bap_stream_config(setup->stream, &setup->qos,
> - setup->caps, NULL, NULL);
> -
> - bt_bap_stream_set_user_data(setup->stream, ep->path);
> + /* listen channel is not needed anymore */
> + g_io_channel_unref(setup->io);
> + setup->io = NULL;
>
> fd = g_io_channel_unix_get_fd(io);
>
> @@ -1074,26 +948,43 @@ static void iso_bcast_confirm_cb(GIOChannel *io, GError *err, void *user_data)
> g_io_channel_set_close_on_unref(io, FALSE);
> return;
> }
> -
> -
> - return;
> -
> -drop:
> - g_io_channel_shutdown(io, TRUE, NULL);
> -
> }
>
> static void iso_pa_sync_confirm_cb(GIOChannel *io, void *user_data)
> {
> GError *err = NULL;
> + struct bap_data *data = user_data;
> + struct bt_iso_base base;
> + struct bt_bap_base base_s;
> + struct bt_iso_qos qos;
>
> - if (!bt_io_bcast_accept(io, iso_bcast_confirm_cb,
> - user_data, NULL, &err, BT_IO_OPT_INVALID)) {
> - error("bt_io_bcast_accept: %s", err->message);
> + btd_service_connecting_complete(data->service, 0);
> +
> + bt_io_get(io, &err,
> + BT_IO_OPT_BASE, &base,
> + BT_IO_OPT_QOS, &qos,
> + BT_IO_OPT_INVALID);
> + if (err) {
> + error("%s", err->message);
> g_error_free(err);
> g_io_channel_shutdown(io, TRUE, NULL);
> + return;
> }
>
> + /* The PA Sync channel becomes the new listen_io.
> + * It will be later used to listen for a BIS io.
> + */
> + g_io_channel_unref(data->listen_io);
> + data->listen_io = io;
> + g_io_channel_ref(io);
> +
> + /* Analyze received BASE data and create remote media endpoints for each
> + * matching BIS
> + */
> + base_s.subgroups = queue_new();
> + bt_bap_parse_base(data->bap, base.base, base.base_len, bap_debug,
> + &base_s);
> + queue_foreach(base_s.subgroups, bt_bap_parse_bis, NULL);
> }
>
> static bool match_data_bap_data(const void *data, const void *match_data)
> @@ -1934,12 +1825,11 @@ static void setup_listen_io(struct bap_data *data, struct bt_bap_stream *stream,
> data->listen_io = io;
> }
>
> -static void setup_listen_io_broadcast(struct bap_data *data,
> +static void setup_accept_io_broadcast(struct bap_data *data,
> struct bap_setup *setup,
> struct bt_bap_stream *stream,
> struct bt_iso_qos *qos)
> {
> - GIOChannel *io;
> GError *err = NULL;
> struct sockaddr_iso_bc iso_bc_addr;
>
> @@ -1951,29 +1841,18 @@ static void setup_listen_io_broadcast(struct bap_data *data,
>
> DBG("stream %p", stream);
>
> - /* If IO already set skip creating it again */
> - if (bt_bap_stream_get_io(stream) || data->listen_io)
> - return;
> -
> - io = bt_io_listen(NULL, iso_pa_sync_confirm_cb, setup, NULL, &err,
> - BT_IO_OPT_SOURCE_BDADDR,
> - btd_adapter_get_address(data->adapter),
> - BT_IO_OPT_DEST_BDADDR,
> - device_get_address(data->device),
> - BT_IO_OPT_DEST_TYPE,
> - btd_device_get_bdaddr_type(data->device),
> - BT_IO_OPT_MODE, BT_IO_MODE_ISO,
> - BT_IO_OPT_QOS, &qos->bcast,
> - BT_IO_OPT_ISO_BC_NUM_BIS, iso_bc_addr.bc_num_bis,
> - BT_IO_OPT_ISO_BC_BIS, iso_bc_addr.bc_bis,
> - BT_IO_OPT_INVALID);
> - if (!io) {
> - error("%s", err->message);
> + if (!bt_io_bcast_accept(data->listen_io,
> + iso_bcast_confirm_cb,
> + setup, NULL, &err,
> + BT_IO_OPT_ISO_BC_NUM_BIS,
> + iso_bc_addr.bc_num_bis, BT_IO_OPT_ISO_BC_BIS,
> + iso_bc_addr.bc_bis, BT_IO_OPT_INVALID)) {
> + error("bt_io_bcast_accept: %s", err->message);
> g_error_free(err);
> }
> - setup->io = io;
> - data->listen_io = io;
>
> + setup->io = data->listen_io;
> + data->listen_io = NULL;
> }
> static void setup_create_ucast_io(struct bap_data *data,
> struct bap_setup *setup,
> @@ -2037,7 +1916,7 @@ done:
> if (bt_bap_pac_get_type(setup->ep->lpac) == BT_BAP_BCAST_SOURCE)
> setup_connect_io_broadcast(data, setup, stream, &iso_qos);
> else
> - setup_listen_io_broadcast(data, setup, stream, &iso_qos);
> + setup_accept_io_broadcast(data, setup, stream, &iso_qos);
> }
>
> static void setup_create_io(struct bap_data *data, struct bap_setup *setup,
> @@ -2422,6 +2301,7 @@ static int bap_bcast_probe(struct btd_service *service)
> struct btd_gatt_database *database = btd_adapter_get_database(adapter);
> struct bap_data *data = btd_service_get_user_data(service);
> char addr[18];
> + GError *err = NULL;
>
> ba2str(device_get_address(device), addr);
>
> @@ -2465,7 +2345,23 @@ static int bap_bcast_probe(struct btd_service *service)
>
> bt_bap_set_user_data(data->bap, service);
>
> - bt_bap_new_bcast_source(data->bap, device_get_path(device));
> + DBG("Create PA sync with this source");
> + data->listen_io = bt_io_listen(NULL, iso_pa_sync_confirm_cb, data,
> + NULL, &err,
> + BT_IO_OPT_SOURCE_BDADDR,
> + btd_adapter_get_address(data->adapter),
> + BT_IO_OPT_DEST_BDADDR,
> + device_get_address(data->device),
> + BT_IO_OPT_DEST_TYPE,
> + btd_device_get_bdaddr_type(data->device),
> + BT_IO_OPT_MODE, BT_IO_MODE_ISO,
> + BT_IO_OPT_QOS, &bap_sink_sync_parameters,
> + BT_IO_OPT_INVALID);
> + if (!data->listen_io) {
> + error("%s", err->message);
> + g_error_free(err);
> + }

I really doubt this will work in a crowded environment, it seems we
would be doing several PA Sync in parallel, one for each announcement
found, which not only would overwrite the QOS but also I don't think
controller are capable of doing multiple PA Sync like that so we might
need to serialize the process of doing short lived PA Syncs to
enumerate the BASE.

Usually we have dealt with the serialization using an idle timer which
can then check services that need to be resolved, once a service is
being resolved then the timer shall be stopped, we restart the time
everytime something needs to be resolved.

> +
> return 0;
> }
>
> --
> 2.40.1
>


--
Luiz Augusto von Dentz

2024-02-08 11:26:05

by Andrei Istodorescu

[permalink] [raw]
Subject: Re: [PATCH BlueZ 3/3] bap: Do PA Sync for each BAP Broadcast source discovered

Hi Luiz,

> -----Original Message-----
> From: Luiz Augusto von Dentz <[email protected]>
> Sent: Wednesday, February 7, 2024 4:42 PM
> To: Andrei Istodorescu <[email protected]>
> Cc: [email protected]; Mihai-Octavian Urzica <mihai-
> [email protected]>; Silviu Florian Barbulescu
> <[email protected]>; Vlad Pruteanu <[email protected]>;
> Iulia Tanasescu <[email protected]>
> Subject: [EXT] Re: [PATCH BlueZ 3/3] bap: Do PA Sync for each BAP Broadcast
> source discovered
>
> Caution: This is an external email. Please take care when clicking links or
> opening attachments. When in doubt, report the message using the 'Report
> this email' button
>
>
> Hi Andrei,
>
> On Wed, Feb 7, 2024 at 7:23 AM Andrei Istodorescu
> <[email protected]> wrote:
> >
> > After discovering a BAP Broadcast Source do a short PA sync first to
> > learn the BASE. After discovering the BASE check how many BISes are
> > matching the sink capabilities and create endpoints for them. Allow
> > user to configure one endpoint using "SetConfiguration" causing BIG
> > synchronization to the corresponding BIS; also this results in
> > creating a stream and the corresponding transport.
> > ---
> > profiles/audio/bap.c | 324
> > +++++++++++++++----------------------------
> > 1 file changed, 110 insertions(+), 214 deletions(-)
> >
> > diff --git a/profiles/audio/bap.c b/profiles/audio/bap.c index
> > 88c93127bea0..8646eae2ed20 100644
> > --- a/profiles/audio/bap.c
> > +++ b/profiles/audio/bap.c
> > @@ -105,6 +105,44 @@ struct bap_data {
> > void *user_data;
> > };
> >
> > +/* Structure holding the parameters for periodic train and BIG
> > + * synchronization
> > + */
> > +static struct bt_iso_qos bap_sink_sync_parameters = {
> > + .bcast = {
> > + .big = BT_ISO_QOS_BIG_UNSET,
> > + .bis = BT_ISO_QOS_BIS_UNSET,
> > + /* HCI_LE_Periodic_Advertising_Create_Sync */
> > + .options = 0x00,
> > + .skip = 0x0000,
> > + .sync_timeout = 0x4000,
> > + .sync_cte_type = 0x00,
> > + /* HCI_LE_BIG_Create_Sync */
> > + .encryption = 0x00,
> > + .bcode = {0x00},
> > + .mse = 0x00,
> > + .timeout = 0x4000,
> > + /* to remove from kernel check */
> > + .sync_factor = 0x07,
> > + .packing = 0x00,
> > + .framing = 0x00,
> > + .in = {
> > + .interval = 10000,
> > + .latency = 10,
> > + .sdu = 40,
> > + .phy = 0x02,
> > + .rtn = 2,
> > + },
> > + .out = {
> > + .interval = 10000,
> > + .latency = 10,
> > + .sdu = 40,
> > + .phy = 0x02,
> > + .rtn = 2,
> > + }
> > + }
> > +};
>
> This cannot be global, it needs to be stored on a per device basis so it doesn't
> get overwritten.

I will submit an update for this.

>
> > static struct queue *sessions;
> >
> > static bool bap_data_set_user_data(struct bap_data *data, void
> > *user_data) @@ -422,113 +460,6 @@ static int
> parse_array(DBusMessageIter *iter, struct iovec *iov)
> > return 0;
> > }
> >
> > -static bool parse_base(void *data, size_t len, util_debug_func_t func,
> > - uint32_t *presDelay, uint8_t *numSubgroups, uint8_t *numBis,
> > - struct bt_bap_codec *codec, struct iovec **caps,
> > - struct iovec **meta)
> > -{
> > - struct iovec iov = {
> > - .iov_base = data,
> > - .iov_len = len,
> > - };
> > -
> > - uint8_t capsLen, metaLen;
> > - struct iovec cc;
> > - struct iovec metadata;
> > -
> > - if (presDelay) {
> > - if (!util_iov_pull_le24(&iov, presDelay))
> > - return false;
> > - util_debug(func, NULL, "PresentationDelay %d", *presDelay);
> > - }
> > -
> > - if (numSubgroups) {
> > - if (!util_iov_pull_u8(&iov, numSubgroups))
> > - return false;
> > - util_debug(func, NULL, "NumSubgroups %d", *numSubgroups);
> > - }
> > -
> > - if (numBis) {
> > - if (!util_iov_pull_u8(&iov, numBis))
> > - return false;
> > - util_debug(func, NULL, "NumBis %d", *numBis);
> > - }
> > -
> > - if (codec) {
> > - codec = util_iov_pull_mem(&iov, sizeof(*codec));
> > - if (!codec)
> > - return false;
> > - util_debug(func, NULL, "%s: ID %d CID 0x%2.2x VID 0x%2.2x",
> > - "Codec", codec->id, codec->cid, codec->vid);
> > - }
> > -
> > - if (!util_iov_pull_u8(&iov, &capsLen))
> > - return false;
> > - util_debug(func, NULL, "CC Len %d", capsLen);
> > -
> > - if (!capsLen)
> > - return false;
> > -
> > - cc.iov_len = capsLen;
> > - cc.iov_base = util_iov_pull_mem(&iov, capsLen);
> > - if (!cc.iov_base)
> > - return false;
> > -
> > - if (caps) {
> > - if (*caps)
> > - util_iov_free(*caps, 1);
> > -
> > - *caps = util_iov_dup(&cc, 1);
> > - }
> > -
> > - for (int i = 0; capsLen > 1; i++) {
> > - struct bt_ltv *ltv = util_iov_pull_mem(&cc, sizeof(*ltv));
> > - uint8_t *caps;
> > -
> > - if (!ltv) {
> > - util_debug(func, NULL, "Unable to parse %s",
> > - "Capabilities");
> > - return false;
> > - }
> > -
> > - util_debug(func, NULL, "%s #%u: len %u type %u",
> > - "CC", i, ltv->len, ltv->type);
> > -
> > - caps = util_iov_pull_mem(&cc, ltv->len - 1);
> > - if (!caps) {
> > - util_debug(func, NULL, "Unable to parse %s",
> > - "CC");
> > - return false;
> > - }
> > - util_hexdump(' ', caps, ltv->len - 1, func, NULL);
> > -
> > - capsLen -= (ltv->len + 1);
> > - }
> > -
> > - if (!util_iov_pull_u8(&iov, &metaLen))
> > - return false;
> > - util_debug(func, NULL, "Metadata Len %d", metaLen);
> > -
> > - if (!metaLen)
> > - return false;
> > -
> > - metadata.iov_len = metaLen;
> > - metadata.iov_base = util_iov_pull_mem(&iov, metaLen);
> > - if (!metadata.iov_base)
> > - return false;
> > -
> > - if (meta) {
> > - if (*meta)
> > - util_iov_free(*meta, 1);
> > -
> > - *meta = util_iov_dup(&metadata, 1);
> > - }
> > -
> > - util_hexdump(' ', metadata.iov_base, metaLen, func, NULL);
> > -
> > - return true;
> > -}
> > -
> > static int parse_io_qos(const char *key, int var, DBusMessageIter *iter,
> > struct bt_bap_io_qos *qos) { @@
> > -954,6 +885,17 @@ static DBusMessage
> *set_configuration(DBusConnection *conn, DBusMessage *msg,
> > return btd_error_invalid_args(msg);
> > }
> >
> > + /* For BAP Broadcast Sink, the capabilities and metadata are coming
> > + * from the source's BIS, which are present in the remote PAC
> > + */
> > + if (bt_bap_pac_get_type(ep->lpac) == BT_BAP_BCAST_SINK) {
> > + util_iov_free(setup->caps, 1);
> > + setup->caps = util_iov_dup(bt_bap_pac_get_data(ep->rpac), 1);
> > + util_iov_free(setup->metadata, 1);
> > + setup->metadata = util_iov_dup(
> > + bt_bap_pac_get_metadata(ep->rpac), 1);
> > + }
> > +
> > setup->stream = bt_bap_stream_new(ep->data->bap, ep->lpac, ep-
> >rpac,
> > &setup->qos,
> > setup->caps);
> >
> > @@ -977,95 +919,27 @@ static DBusMessage
> *set_configuration(DBusConnection *conn, DBusMessage *msg,
> > break;
> > case BT_BAP_STREAM_TYPE_BCAST:
> > /* No message sent over the air for broadcast */
> > - if (bt_bap_pac_get_type(ep->lpac) == BT_BAP_BCAST_SINK)
> > - setup->msg = dbus_message_ref(msg);
> > - else {
> > + if (bt_bap_pac_get_type(ep->lpac) ==
> > + BT_BAP_BCAST_SOURCE)
> > setup->base = bt_bap_stream_get_base(setup->stream);
> > - setup->id = 0;
> > }
> > + setup->id = 0;
> >
> > if (ep->data->service)
> > service_set_connecting(ep->data->service);
> >
> > return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
> > - }
> >
> > return NULL;
> > }
> >
> > -static void update_bcast_qos(struct bt_iso_qos *qos,
> > - struct bt_bap_qos *bap_qos)
> > -{
> > - bap_qos->bcast.big = qos->bcast.big;
> > - bap_qos->bcast.bis = qos->bcast.bis;
> > - bap_qos->bcast.sync_factor = qos->bcast.sync_factor;
> > - bap_qos->bcast.packing = qos->bcast.packing;
> > - bap_qos->bcast.framing = qos->bcast.framing;
> > - bap_qos->bcast.encryption = qos->bcast.encryption;
> > - bap_qos->bcast.options = qos->bcast.options;
> > - bap_qos->bcast.skip = qos->bcast.skip;
> > - bap_qos->bcast.sync_timeout = qos->bcast.sync_timeout;
> > - bap_qos->bcast.sync_cte_type = qos->bcast.sync_cte_type;
> > - bap_qos->bcast.mse = qos->bcast.mse;
> > - bap_qos->bcast.timeout = qos->bcast.timeout;
> > - bap_qos->bcast.io_qos.interval = qos->bcast.in.interval;
> > - bap_qos->bcast.io_qos.latency = qos->bcast.in.latency;
> > - bap_qos->bcast.io_qos.phy = qos->bcast.in.phy;
> > - bap_qos->bcast.io_qos.sdu = qos->bcast.in.sdu;
> > - bap_qos->bcast.io_qos.rtn = qos->bcast.in.rtn;
> > - if (!bap_qos->bcast.bcode)
> > - bap_qos->bcast.bcode = new0(struct iovec, 1);
> > - util_iov_memcpy(bap_qos->bcast.bcode, qos->bcast.bcode,
> > - sizeof(qos->bcast.bcode));
> > -}
> > -
> > static void iso_bcast_confirm_cb(GIOChannel *io, GError *err, void
> > *user_data) {
> > struct bap_setup *setup = user_data;
> > - struct bap_ep *ep = setup->ep;
> > - struct bap_data *data = ep->data;
> > - struct bt_iso_qos qos;
> > - struct bt_iso_base base;
> > - char address[18];
> > int fd;
> > - struct iovec *base_io;
> > - uint32_t presDelay;
> > - uint8_t numSubgroups;
> > - uint8_t numBis;
> > - struct bt_bap_codec codec;
> > -
> > - bt_io_get(io, &err,
> > - BT_IO_OPT_DEST, address,
> > - BT_IO_OPT_QOS, &qos,
> > - BT_IO_OPT_BASE, &base,
> > - BT_IO_OPT_INVALID);
> > - if (err) {
> > - error("%s", err->message);
> > - g_error_free(err);
> > - goto drop;
> > - }
> >
> > - g_io_channel_ref(io);
> > - btd_service_connecting_complete(data->service, 0);
> > - DBG("BCAST ISO: sync with %s (BIG 0x%02x BIS 0x%02x)",
> > - address, qos.bcast.big, qos.bcast.bis);
> > -
> > - update_bcast_qos(&qos, &setup->qos);
> > -
> > - base_io = new0(struct iovec, 1);
> > - util_iov_memcpy(base_io, base.base, base.base_len);
> > -
> > - parse_base(base_io->iov_base, base_io->iov_len, bap_debug,
> > - &presDelay, &numSubgroups, &numBis,
> > - &codec, &setup->caps, &setup->metadata);
> > -
> > - /* Update pac with BASE information */
> > - bt_bap_update_bcast_source(ep->rpac, &codec, setup->caps,
> > - setup->metadata);
> > - setup->id = bt_bap_stream_config(setup->stream, &setup->qos,
> > - setup->caps, NULL, NULL);
> > -
> > - bt_bap_stream_set_user_data(setup->stream, ep->path);
> > + /* listen channel is not needed anymore */
> > + g_io_channel_unref(setup->io);
> > + setup->io = NULL;
> >
> > fd = g_io_channel_unix_get_fd(io);
> >
> > @@ -1074,26 +948,43 @@ static void iso_bcast_confirm_cb(GIOChannel
> *io, GError *err, void *user_data)
> > g_io_channel_set_close_on_unref(io, FALSE);
> > return;
> > }
> > -
> > -
> > - return;
> > -
> > -drop:
> > - g_io_channel_shutdown(io, TRUE, NULL);
> > -
> > }
> >
> > static void iso_pa_sync_confirm_cb(GIOChannel *io, void *user_data)
> > {
> > GError *err = NULL;
> > + struct bap_data *data = user_data;
> > + struct bt_iso_base base;
> > + struct bt_bap_base base_s;
> > + struct bt_iso_qos qos;
> >
> > - if (!bt_io_bcast_accept(io, iso_bcast_confirm_cb,
> > - user_data, NULL, &err, BT_IO_OPT_INVALID)) {
> > - error("bt_io_bcast_accept: %s", err->message);
> > + btd_service_connecting_complete(data->service, 0);
> > +
> > + bt_io_get(io, &err,
> > + BT_IO_OPT_BASE, &base,
> > + BT_IO_OPT_QOS, &qos,
> > + BT_IO_OPT_INVALID);
> > + if (err) {
> > + error("%s", err->message);
> > g_error_free(err);
> > g_io_channel_shutdown(io, TRUE, NULL);
> > + return;
> > }
> >
> > + /* The PA Sync channel becomes the new listen_io.
> > + * It will be later used to listen for a BIS io.
> > + */
> > + g_io_channel_unref(data->listen_io);
> > + data->listen_io = io;
> > + g_io_channel_ref(io);
> > +
> > + /* Analyze received BASE data and create remote media endpoints for
> each
> > + * matching BIS
> > + */
> > + base_s.subgroups = queue_new();
> > + bt_bap_parse_base(data->bap, base.base, base.base_len,
> bap_debug,
> > + &base_s);
> > + queue_foreach(base_s.subgroups, bt_bap_parse_bis, NULL);
> > }
> >
> > static bool match_data_bap_data(const void *data, const void
> > *match_data) @@ -1934,12 +1825,11 @@ static void setup_listen_io(struct
> bap_data *data, struct bt_bap_stream *stream,
> > data->listen_io = io;
> > }
> >
> > -static void setup_listen_io_broadcast(struct bap_data *data,
> > +static void setup_accept_io_broadcast(struct bap_data *data,
> > struct bap_setup *setup,
> > struct bt_bap_stream *stream,
> > struct bt_iso_qos *qos) {
> > - GIOChannel *io;
> > GError *err = NULL;
> > struct sockaddr_iso_bc iso_bc_addr;
> >
> > @@ -1951,29 +1841,18 @@ static void setup_listen_io_broadcast(struct
> > bap_data *data,
> >
> > DBG("stream %p", stream);
> >
> > - /* If IO already set skip creating it again */
> > - if (bt_bap_stream_get_io(stream) || data->listen_io)
> > - return;
> > -
> > - io = bt_io_listen(NULL, iso_pa_sync_confirm_cb, setup, NULL, &err,
> > - BT_IO_OPT_SOURCE_BDADDR,
> > - btd_adapter_get_address(data->adapter),
> > - BT_IO_OPT_DEST_BDADDR,
> > - device_get_address(data->device),
> > - BT_IO_OPT_DEST_TYPE,
> > - btd_device_get_bdaddr_type(data->device),
> > - BT_IO_OPT_MODE, BT_IO_MODE_ISO,
> > - BT_IO_OPT_QOS, &qos->bcast,
> > - BT_IO_OPT_ISO_BC_NUM_BIS, iso_bc_addr.bc_num_bis,
> > - BT_IO_OPT_ISO_BC_BIS, iso_bc_addr.bc_bis,
> > - BT_IO_OPT_INVALID);
> > - if (!io) {
> > - error("%s", err->message);
> > + if (!bt_io_bcast_accept(data->listen_io,
> > + iso_bcast_confirm_cb,
> > + setup, NULL, &err,
> > + BT_IO_OPT_ISO_BC_NUM_BIS,
> > + iso_bc_addr.bc_num_bis, BT_IO_OPT_ISO_BC_BIS,
> > + iso_bc_addr.bc_bis, BT_IO_OPT_INVALID)) {
> > + error("bt_io_bcast_accept: %s", err->message);
> > g_error_free(err);
> > }
> > - setup->io = io;
> > - data->listen_io = io;
> >
> > + setup->io = data->listen_io;
> > + data->listen_io = NULL;
> > }
> > static void setup_create_ucast_io(struct bap_data *data,
> > struct bap_setup *setup, @@
> > -2037,7 +1916,7 @@ done:
> > if (bt_bap_pac_get_type(setup->ep->lpac) ==
> BT_BAP_BCAST_SOURCE)
> > setup_connect_io_broadcast(data, setup, stream, &iso_qos);
> > else
> > - setup_listen_io_broadcast(data, setup, stream, &iso_qos);
> > + setup_accept_io_broadcast(data, setup, stream,
> > + &iso_qos);
> > }
> >
> > static void setup_create_io(struct bap_data *data, struct bap_setup
> > *setup, @@ -2422,6 +2301,7 @@ static int bap_bcast_probe(struct
> btd_service *service)
> > struct btd_gatt_database *database =
> btd_adapter_get_database(adapter);
> > struct bap_data *data = btd_service_get_user_data(service);
> > char addr[18];
> > + GError *err = NULL;
> >
> > ba2str(device_get_address(device), addr);
> >
> > @@ -2465,7 +2345,23 @@ static int bap_bcast_probe(struct btd_service
> > *service)
> >
> > bt_bap_set_user_data(data->bap, service);
> >
> > - bt_bap_new_bcast_source(data->bap, device_get_path(device));
> > + DBG("Create PA sync with this source");
> > + data->listen_io = bt_io_listen(NULL, iso_pa_sync_confirm_cb, data,
> > + NULL, &err,
> > + BT_IO_OPT_SOURCE_BDADDR,
> > + btd_adapter_get_address(data->adapter),
> > + BT_IO_OPT_DEST_BDADDR,
> > + device_get_address(data->device),
> > + BT_IO_OPT_DEST_TYPE,
> > + btd_device_get_bdaddr_type(data->device),
> > + BT_IO_OPT_MODE, BT_IO_MODE_ISO,
> > + BT_IO_OPT_QOS, &bap_sink_sync_parameters,
> > + BT_IO_OPT_INVALID);
> > + if (!data->listen_io) {
> > + error("%s", err->message);
> > + g_error_free(err);
> > + }
>
> I really doubt this will work in a crowded environment, it seems we would be
> doing several PA Sync in parallel, one for each announcement found, which
> not only would overwrite the QOS but also I don't think controller are capable
> of doing multiple PA Sync like that so we might need to serialize the process
> of doing short lived PA Syncs to enumerate the BASE.
>
> Usually we have dealt with the serialization using an idle timer which can
> then check services that need to be resolved, once a service is being
> resolved then the timer shall be stopped, we restart the time everytime
> something needs to be resolved.
>

bap_bcast_probe starts a PA Sync controller procedure for each new Broadcast
source seen.
This can take several milliseconds with physical controller and runs in an instant
with the emulator. The problem that can arrive is that another Broadcast source
gets probed and another bt_io_listen is executed before the previous one is
completed. In this scenario the controller will return an error for no resources
and the second PA sync will fail, but the one in progress will complete successfully.
Given the timings for this to happen, can I submit a different patch for this
issue, or shall I fix it in this one?

> > +
> > return 0;
> > }
> >
> > --
> > 2.40.1
> >
>
>
> --
> Luiz Augusto von Dentz

2024-02-08 13:19:42

by Luiz Augusto von Dentz

[permalink] [raw]
Subject: Re: [PATCH BlueZ 3/3] bap: Do PA Sync for each BAP Broadcast source discovered

Hi Andrei,

On Thu, Feb 8, 2024 at 6:25 AM Andrei Istodorescu
<[email protected]> wrote:
>
> Hi Luiz,
>
> > -----Original Message-----
> > From: Luiz Augusto von Dentz <[email protected]>
> > Sent: Wednesday, February 7, 2024 4:42 PM
> > To: Andrei Istodorescu <[email protected]>
> > Cc: [email protected]; Mihai-Octavian Urzica <mihai-
> > [email protected]>; Silviu Florian Barbulescu
> > <[email protected]>; Vlad Pruteanu <[email protected]>;
> > Iulia Tanasescu <[email protected]>
> > Subject: [EXT] Re: [PATCH BlueZ 3/3] bap: Do PA Sync for each BAP Broadcast
> > source discovered
> >
> > Caution: This is an external email. Please take care when clicking links or
> > opening attachments. When in doubt, report the message using the 'Report
> > this email' button
> >
> >
> > Hi Andrei,
> >
> > On Wed, Feb 7, 2024 at 7:23 AM Andrei Istodorescu
> > <[email protected]> wrote:
> > >
> > > After discovering a BAP Broadcast Source do a short PA sync first to
> > > learn the BASE. After discovering the BASE check how many BISes are
> > > matching the sink capabilities and create endpoints for them. Allow
> > > user to configure one endpoint using "SetConfiguration" causing BIG
> > > synchronization to the corresponding BIS; also this results in
> > > creating a stream and the corresponding transport.
> > > ---
> > > profiles/audio/bap.c | 324
> > > +++++++++++++++----------------------------
> > > 1 file changed, 110 insertions(+), 214 deletions(-)
> > >
> > > diff --git a/profiles/audio/bap.c b/profiles/audio/bap.c index
> > > 88c93127bea0..8646eae2ed20 100644
> > > --- a/profiles/audio/bap.c
> > > +++ b/profiles/audio/bap.c
> > > @@ -105,6 +105,44 @@ struct bap_data {
> > > void *user_data;
> > > };
> > >
> > > +/* Structure holding the parameters for periodic train and BIG
> > > + * synchronization
> > > + */
> > > +static struct bt_iso_qos bap_sink_sync_parameters = {
> > > + .bcast = {
> > > + .big = BT_ISO_QOS_BIG_UNSET,
> > > + .bis = BT_ISO_QOS_BIS_UNSET,
> > > + /* HCI_LE_Periodic_Advertising_Create_Sync */
> > > + .options = 0x00,
> > > + .skip = 0x0000,
> > > + .sync_timeout = 0x4000,
> > > + .sync_cte_type = 0x00,
> > > + /* HCI_LE_BIG_Create_Sync */
> > > + .encryption = 0x00,
> > > + .bcode = {0x00},
> > > + .mse = 0x00,
> > > + .timeout = 0x4000,
> > > + /* to remove from kernel check */
> > > + .sync_factor = 0x07,
> > > + .packing = 0x00,
> > > + .framing = 0x00,
> > > + .in = {
> > > + .interval = 10000,
> > > + .latency = 10,
> > > + .sdu = 40,
> > > + .phy = 0x02,
> > > + .rtn = 2,
> > > + },
> > > + .out = {
> > > + .interval = 10000,
> > > + .latency = 10,
> > > + .sdu = 40,
> > > + .phy = 0x02,
> > > + .rtn = 2,
> > > + }
> > > + }
> > > +};
> >
> > This cannot be global, it needs to be stored on a per device basis so it doesn't
> > get overwritten.
>
> I will submit an update for this.
>
> >
> > > static struct queue *sessions;
> > >
> > > static bool bap_data_set_user_data(struct bap_data *data, void
> > > *user_data) @@ -422,113 +460,6 @@ static int
> > parse_array(DBusMessageIter *iter, struct iovec *iov)
> > > return 0;
> > > }
> > >
> > > -static bool parse_base(void *data, size_t len, util_debug_func_t func,
> > > - uint32_t *presDelay, uint8_t *numSubgroups, uint8_t *numBis,
> > > - struct bt_bap_codec *codec, struct iovec **caps,
> > > - struct iovec **meta)
> > > -{
> > > - struct iovec iov = {
> > > - .iov_base = data,
> > > - .iov_len = len,
> > > - };
> > > -
> > > - uint8_t capsLen, metaLen;
> > > - struct iovec cc;
> > > - struct iovec metadata;
> > > -
> > > - if (presDelay) {
> > > - if (!util_iov_pull_le24(&iov, presDelay))
> > > - return false;
> > > - util_debug(func, NULL, "PresentationDelay %d", *presDelay);
> > > - }
> > > -
> > > - if (numSubgroups) {
> > > - if (!util_iov_pull_u8(&iov, numSubgroups))
> > > - return false;
> > > - util_debug(func, NULL, "NumSubgroups %d", *numSubgroups);
> > > - }
> > > -
> > > - if (numBis) {
> > > - if (!util_iov_pull_u8(&iov, numBis))
> > > - return false;
> > > - util_debug(func, NULL, "NumBis %d", *numBis);
> > > - }
> > > -
> > > - if (codec) {
> > > - codec = util_iov_pull_mem(&iov, sizeof(*codec));
> > > - if (!codec)
> > > - return false;
> > > - util_debug(func, NULL, "%s: ID %d CID 0x%2.2x VID 0x%2.2x",
> > > - "Codec", codec->id, codec->cid, codec->vid);
> > > - }
> > > -
> > > - if (!util_iov_pull_u8(&iov, &capsLen))
> > > - return false;
> > > - util_debug(func, NULL, "CC Len %d", capsLen);
> > > -
> > > - if (!capsLen)
> > > - return false;
> > > -
> > > - cc.iov_len = capsLen;
> > > - cc.iov_base = util_iov_pull_mem(&iov, capsLen);
> > > - if (!cc.iov_base)
> > > - return false;
> > > -
> > > - if (caps) {
> > > - if (*caps)
> > > - util_iov_free(*caps, 1);
> > > -
> > > - *caps = util_iov_dup(&cc, 1);
> > > - }
> > > -
> > > - for (int i = 0; capsLen > 1; i++) {
> > > - struct bt_ltv *ltv = util_iov_pull_mem(&cc, sizeof(*ltv));
> > > - uint8_t *caps;
> > > -
> > > - if (!ltv) {
> > > - util_debug(func, NULL, "Unable to parse %s",
> > > - "Capabilities");
> > > - return false;
> > > - }
> > > -
> > > - util_debug(func, NULL, "%s #%u: len %u type %u",
> > > - "CC", i, ltv->len, ltv->type);
> > > -
> > > - caps = util_iov_pull_mem(&cc, ltv->len - 1);
> > > - if (!caps) {
> > > - util_debug(func, NULL, "Unable to parse %s",
> > > - "CC");
> > > - return false;
> > > - }
> > > - util_hexdump(' ', caps, ltv->len - 1, func, NULL);
> > > -
> > > - capsLen -= (ltv->len + 1);
> > > - }
> > > -
> > > - if (!util_iov_pull_u8(&iov, &metaLen))
> > > - return false;
> > > - util_debug(func, NULL, "Metadata Len %d", metaLen);
> > > -
> > > - if (!metaLen)
> > > - return false;
> > > -
> > > - metadata.iov_len = metaLen;
> > > - metadata.iov_base = util_iov_pull_mem(&iov, metaLen);
> > > - if (!metadata.iov_base)
> > > - return false;
> > > -
> > > - if (meta) {
> > > - if (*meta)
> > > - util_iov_free(*meta, 1);
> > > -
> > > - *meta = util_iov_dup(&metadata, 1);
> > > - }
> > > -
> > > - util_hexdump(' ', metadata.iov_base, metaLen, func, NULL);
> > > -
> > > - return true;
> > > -}
> > > -
> > > static int parse_io_qos(const char *key, int var, DBusMessageIter *iter,
> > > struct bt_bap_io_qos *qos) { @@
> > > -954,6 +885,17 @@ static DBusMessage
> > *set_configuration(DBusConnection *conn, DBusMessage *msg,
> > > return btd_error_invalid_args(msg);
> > > }
> > >
> > > + /* For BAP Broadcast Sink, the capabilities and metadata are coming
> > > + * from the source's BIS, which are present in the remote PAC
> > > + */
> > > + if (bt_bap_pac_get_type(ep->lpac) == BT_BAP_BCAST_SINK) {
> > > + util_iov_free(setup->caps, 1);
> > > + setup->caps = util_iov_dup(bt_bap_pac_get_data(ep->rpac), 1);
> > > + util_iov_free(setup->metadata, 1);
> > > + setup->metadata = util_iov_dup(
> > > + bt_bap_pac_get_metadata(ep->rpac), 1);
> > > + }
> > > +
> > > setup->stream = bt_bap_stream_new(ep->data->bap, ep->lpac, ep-
> > >rpac,
> > > &setup->qos,
> > > setup->caps);
> > >
> > > @@ -977,95 +919,27 @@ static DBusMessage
> > *set_configuration(DBusConnection *conn, DBusMessage *msg,
> > > break;
> > > case BT_BAP_STREAM_TYPE_BCAST:
> > > /* No message sent over the air for broadcast */
> > > - if (bt_bap_pac_get_type(ep->lpac) == BT_BAP_BCAST_SINK)
> > > - setup->msg = dbus_message_ref(msg);
> > > - else {
> > > + if (bt_bap_pac_get_type(ep->lpac) ==
> > > + BT_BAP_BCAST_SOURCE)
> > > setup->base = bt_bap_stream_get_base(setup->stream);
> > > - setup->id = 0;
> > > }
> > > + setup->id = 0;
> > >
> > > if (ep->data->service)
> > > service_set_connecting(ep->data->service);
> > >
> > > return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
> > > - }
> > >
> > > return NULL;
> > > }
> > >
> > > -static void update_bcast_qos(struct bt_iso_qos *qos,
> > > - struct bt_bap_qos *bap_qos)
> > > -{
> > > - bap_qos->bcast.big = qos->bcast.big;
> > > - bap_qos->bcast.bis = qos->bcast.bis;
> > > - bap_qos->bcast.sync_factor = qos->bcast.sync_factor;
> > > - bap_qos->bcast.packing = qos->bcast.packing;
> > > - bap_qos->bcast.framing = qos->bcast.framing;
> > > - bap_qos->bcast.encryption = qos->bcast.encryption;
> > > - bap_qos->bcast.options = qos->bcast.options;
> > > - bap_qos->bcast.skip = qos->bcast.skip;
> > > - bap_qos->bcast.sync_timeout = qos->bcast.sync_timeout;
> > > - bap_qos->bcast.sync_cte_type = qos->bcast.sync_cte_type;
> > > - bap_qos->bcast.mse = qos->bcast.mse;
> > > - bap_qos->bcast.timeout = qos->bcast.timeout;
> > > - bap_qos->bcast.io_qos.interval = qos->bcast.in.interval;
> > > - bap_qos->bcast.io_qos.latency = qos->bcast.in.latency;
> > > - bap_qos->bcast.io_qos.phy = qos->bcast.in.phy;
> > > - bap_qos->bcast.io_qos.sdu = qos->bcast.in.sdu;
> > > - bap_qos->bcast.io_qos.rtn = qos->bcast.in.rtn;
> > > - if (!bap_qos->bcast.bcode)
> > > - bap_qos->bcast.bcode = new0(struct iovec, 1);
> > > - util_iov_memcpy(bap_qos->bcast.bcode, qos->bcast.bcode,
> > > - sizeof(qos->bcast.bcode));
> > > -}
> > > -
> > > static void iso_bcast_confirm_cb(GIOChannel *io, GError *err, void
> > > *user_data) {
> > > struct bap_setup *setup = user_data;
> > > - struct bap_ep *ep = setup->ep;
> > > - struct bap_data *data = ep->data;
> > > - struct bt_iso_qos qos;
> > > - struct bt_iso_base base;
> > > - char address[18];
> > > int fd;
> > > - struct iovec *base_io;
> > > - uint32_t presDelay;
> > > - uint8_t numSubgroups;
> > > - uint8_t numBis;
> > > - struct bt_bap_codec codec;
> > > -
> > > - bt_io_get(io, &err,
> > > - BT_IO_OPT_DEST, address,
> > > - BT_IO_OPT_QOS, &qos,
> > > - BT_IO_OPT_BASE, &base,
> > > - BT_IO_OPT_INVALID);
> > > - if (err) {
> > > - error("%s", err->message);
> > > - g_error_free(err);
> > > - goto drop;
> > > - }
> > >
> > > - g_io_channel_ref(io);
> > > - btd_service_connecting_complete(data->service, 0);
> > > - DBG("BCAST ISO: sync with %s (BIG 0x%02x BIS 0x%02x)",
> > > - address, qos.bcast.big, qos.bcast.bis);
> > > -
> > > - update_bcast_qos(&qos, &setup->qos);
> > > -
> > > - base_io = new0(struct iovec, 1);
> > > - util_iov_memcpy(base_io, base.base, base.base_len);
> > > -
> > > - parse_base(base_io->iov_base, base_io->iov_len, bap_debug,
> > > - &presDelay, &numSubgroups, &numBis,
> > > - &codec, &setup->caps, &setup->metadata);
> > > -
> > > - /* Update pac with BASE information */
> > > - bt_bap_update_bcast_source(ep->rpac, &codec, setup->caps,
> > > - setup->metadata);
> > > - setup->id = bt_bap_stream_config(setup->stream, &setup->qos,
> > > - setup->caps, NULL, NULL);
> > > -
> > > - bt_bap_stream_set_user_data(setup->stream, ep->path);
> > > + /* listen channel is not needed anymore */
> > > + g_io_channel_unref(setup->io);
> > > + setup->io = NULL;
> > >
> > > fd = g_io_channel_unix_get_fd(io);
> > >
> > > @@ -1074,26 +948,43 @@ static void iso_bcast_confirm_cb(GIOChannel
> > *io, GError *err, void *user_data)
> > > g_io_channel_set_close_on_unref(io, FALSE);
> > > return;
> > > }
> > > -
> > > -
> > > - return;
> > > -
> > > -drop:
> > > - g_io_channel_shutdown(io, TRUE, NULL);
> > > -
> > > }
> > >
> > > static void iso_pa_sync_confirm_cb(GIOChannel *io, void *user_data)
> > > {
> > > GError *err = NULL;
> > > + struct bap_data *data = user_data;
> > > + struct bt_iso_base base;
> > > + struct bt_bap_base base_s;
> > > + struct bt_iso_qos qos;
> > >
> > > - if (!bt_io_bcast_accept(io, iso_bcast_confirm_cb,
> > > - user_data, NULL, &err, BT_IO_OPT_INVALID)) {
> > > - error("bt_io_bcast_accept: %s", err->message);
> > > + btd_service_connecting_complete(data->service, 0);
> > > +
> > > + bt_io_get(io, &err,
> > > + BT_IO_OPT_BASE, &base,
> > > + BT_IO_OPT_QOS, &qos,
> > > + BT_IO_OPT_INVALID);
> > > + if (err) {
> > > + error("%s", err->message);
> > > g_error_free(err);
> > > g_io_channel_shutdown(io, TRUE, NULL);
> > > + return;
> > > }
> > >
> > > + /* The PA Sync channel becomes the new listen_io.
> > > + * It will be later used to listen for a BIS io.
> > > + */
> > > + g_io_channel_unref(data->listen_io);
> > > + data->listen_io = io;
> > > + g_io_channel_ref(io);
> > > +
> > > + /* Analyze received BASE data and create remote media endpoints for
> > each
> > > + * matching BIS
> > > + */
> > > + base_s.subgroups = queue_new();
> > > + bt_bap_parse_base(data->bap, base.base, base.base_len,
> > bap_debug,
> > > + &base_s);
> > > + queue_foreach(base_s.subgroups, bt_bap_parse_bis, NULL);
> > > }
> > >
> > > static bool match_data_bap_data(const void *data, const void
> > > *match_data) @@ -1934,12 +1825,11 @@ static void setup_listen_io(struct
> > bap_data *data, struct bt_bap_stream *stream,
> > > data->listen_io = io;
> > > }
> > >
> > > -static void setup_listen_io_broadcast(struct bap_data *data,
> > > +static void setup_accept_io_broadcast(struct bap_data *data,
> > > struct bap_setup *setup,
> > > struct bt_bap_stream *stream,
> > > struct bt_iso_qos *qos) {
> > > - GIOChannel *io;
> > > GError *err = NULL;
> > > struct sockaddr_iso_bc iso_bc_addr;
> > >
> > > @@ -1951,29 +1841,18 @@ static void setup_listen_io_broadcast(struct
> > > bap_data *data,
> > >
> > > DBG("stream %p", stream);
> > >
> > > - /* If IO already set skip creating it again */
> > > - if (bt_bap_stream_get_io(stream) || data->listen_io)
> > > - return;
> > > -
> > > - io = bt_io_listen(NULL, iso_pa_sync_confirm_cb, setup, NULL, &err,
> > > - BT_IO_OPT_SOURCE_BDADDR,
> > > - btd_adapter_get_address(data->adapter),
> > > - BT_IO_OPT_DEST_BDADDR,
> > > - device_get_address(data->device),
> > > - BT_IO_OPT_DEST_TYPE,
> > > - btd_device_get_bdaddr_type(data->device),
> > > - BT_IO_OPT_MODE, BT_IO_MODE_ISO,
> > > - BT_IO_OPT_QOS, &qos->bcast,
> > > - BT_IO_OPT_ISO_BC_NUM_BIS, iso_bc_addr.bc_num_bis,
> > > - BT_IO_OPT_ISO_BC_BIS, iso_bc_addr.bc_bis,
> > > - BT_IO_OPT_INVALID);
> > > - if (!io) {
> > > - error("%s", err->message);
> > > + if (!bt_io_bcast_accept(data->listen_io,
> > > + iso_bcast_confirm_cb,
> > > + setup, NULL, &err,
> > > + BT_IO_OPT_ISO_BC_NUM_BIS,
> > > + iso_bc_addr.bc_num_bis, BT_IO_OPT_ISO_BC_BIS,
> > > + iso_bc_addr.bc_bis, BT_IO_OPT_INVALID)) {
> > > + error("bt_io_bcast_accept: %s", err->message);
> > > g_error_free(err);
> > > }
> > > - setup->io = io;
> > > - data->listen_io = io;
> > >
> > > + setup->io = data->listen_io;
> > > + data->listen_io = NULL;
> > > }
> > > static void setup_create_ucast_io(struct bap_data *data,
> > > struct bap_setup *setup, @@
> > > -2037,7 +1916,7 @@ done:
> > > if (bt_bap_pac_get_type(setup->ep->lpac) ==
> > BT_BAP_BCAST_SOURCE)
> > > setup_connect_io_broadcast(data, setup, stream, &iso_qos);
> > > else
> > > - setup_listen_io_broadcast(data, setup, stream, &iso_qos);
> > > + setup_accept_io_broadcast(data, setup, stream,
> > > + &iso_qos);
> > > }
> > >
> > > static void setup_create_io(struct bap_data *data, struct bap_setup
> > > *setup, @@ -2422,6 +2301,7 @@ static int bap_bcast_probe(struct
> > btd_service *service)
> > > struct btd_gatt_database *database =
> > btd_adapter_get_database(adapter);
> > > struct bap_data *data = btd_service_get_user_data(service);
> > > char addr[18];
> > > + GError *err = NULL;
> > >
> > > ba2str(device_get_address(device), addr);
> > >
> > > @@ -2465,7 +2345,23 @@ static int bap_bcast_probe(struct btd_service
> > > *service)
> > >
> > > bt_bap_set_user_data(data->bap, service);
> > >
> > > - bt_bap_new_bcast_source(data->bap, device_get_path(device));
> > > + DBG("Create PA sync with this source");
> > > + data->listen_io = bt_io_listen(NULL, iso_pa_sync_confirm_cb, data,
> > > + NULL, &err,
> > > + BT_IO_OPT_SOURCE_BDADDR,
> > > + btd_adapter_get_address(data->adapter),
> > > + BT_IO_OPT_DEST_BDADDR,
> > > + device_get_address(data->device),
> > > + BT_IO_OPT_DEST_TYPE,
> > > + btd_device_get_bdaddr_type(data->device),
> > > + BT_IO_OPT_MODE, BT_IO_MODE_ISO,
> > > + BT_IO_OPT_QOS, &bap_sink_sync_parameters,
> > > + BT_IO_OPT_INVALID);
> > > + if (!data->listen_io) {
> > > + error("%s", err->message);
> > > + g_error_free(err);
> > > + }
> >
> > I really doubt this will work in a crowded environment, it seems we would be
> > doing several PA Sync in parallel, one for each announcement found, which
> > not only would overwrite the QOS but also I don't think controller are capable
> > of doing multiple PA Sync like that so we might need to serialize the process
> > of doing short lived PA Syncs to enumerate the BASE.
> >
> > Usually we have dealt with the serialization using an idle timer which can
> > then check services that need to be resolved, once a service is being
> > resolved then the timer shall be stopped, we restart the time everytime
> > something needs to be resolved.
> >
>
> bap_bcast_probe starts a PA Sync controller procedure for each new Broadcast
> source seen.
> This can take several milliseconds with physical controller and runs in an instant
> with the emulator. The problem that can arrive is that another Broadcast source
> gets probed and another bt_io_listen is executed before the previous one is
> completed. In this scenario the controller will return an error for no resources
> and the second PA sync will fail, but the one in progress will complete successfully.
> Given the timings for this to happen, can I submit a different patch for this
> issue, or shall I fix it in this one?

Yes, you can choose to fix it later by adding a TODO comment, that
said I'd fix the QoS being a static global.

> > > +
> > > return 0;
> > > }
> > >
> > > --
> > > 2.40.1
> > >
> >
> >
> > --
> > Luiz Augusto von Dentz



--
Luiz Augusto von Dentz