Subject: [PATCH BlueZ v6 0/5] Add support for bcast multiple BISes

Add support for multiple BISes
Register the broadcast source endpoint
endpoint.register 00001852-0000-1000-8000-00805f9b34fb 0x06
After registration a new remote endpoint is created
Use the endpoint.config command to set the BIS codec configuration by
specifying the BIS when prompted by the command
endpoint.config /org/bluez/hci0/pac_bcast0 /local/endpoint/ep0 16_2_1
[/local/endpoint/ep0] BIG (value): 0
[/local/endpoint/ep0] BIS (value): 1
Based on the number of BISes seted in the base_lc3_16_2_1 more
remote endoints wil be created (If more then one bis is setted)
Use the endpoint.config command to set the BIS codec configuration by
specifying the BIS when prompted by the command
endpoint.config /org/bluez/hci0/pac_bcast0 /local/endpoint/ep0 16_2_1
[/local/endpoint/ep0] BIG (value): 0
[/local/endpoint/ep0] BIS (value): 2
Use the endpoint config command to configure all the BISes
After all BISes are configured, use the transports to send data

Claudia Draghicescu (2):
shared/bap: Fix source+sink endpoint registration
audio/bap: Fix source+sink endpoint registration

Silviu Florian Barbulescu (3):
Add support for multiple BISes
profiles/audio/transport.c: Add support for multiple BISes
client/player.c: Add support for multiple BISes

client/player.c | 46 ++-
profiles/audio/bap.c | 617 +++++++++++++++++++++++++++----------
profiles/audio/transport.c | 36 ++-
src/shared/bap.c | 290 ++++++++++++-----
src/shared/bap.h | 38 ++-
unit/test-bap.c | 12 +-
6 files changed, 776 insertions(+), 263 deletions(-)


base-commit: 8b035b70f379e45a32c94579ec8d00e48070c21e
--
2.39.2



Subject: [PATCH BlueZ v6 1/5] shared/bap: Fix source+sink endpoint registration

From: Claudia Draghicescu <[email protected]>

When registering both sink and source endpoints,
the register endpoint method call results in 2 remote endpoints for each
scanned broadcast source

---
src/shared/bap.c | 73 ++++++++++++++++++++++++------------------------
src/shared/bap.h | 3 ++
2 files changed, 40 insertions(+), 36 deletions(-)

diff --git a/src/shared/bap.c b/src/shared/bap.c
index 13bbcf793..d8a3adde6 100644
--- a/src/shared/bap.c
+++ b/src/shared/bap.c
@@ -629,7 +629,7 @@ static struct bt_bap_endpoint *bap_endpoint_new_broadcast(struct bt_bap_db *bdb,
if (type == BT_BAP_BCAST_SINK)
ep->dir = BT_BAP_BCAST_SOURCE;
else
- ep->dir = BT_BAP_BCAST_SINK;
+ ep->dir = 0;

return ep;
}
@@ -1485,12 +1485,12 @@ static void ep_config_cb(struct bt_bap_stream *stream, int err)
return;

if (bt_bap_stream_get_type(stream) == BT_BAP_STREAM_TYPE_BCAST) {
- if (bt_bap_stream_io_dir(stream) == BT_BAP_BCAST_SINK)
+ if (!bt_bap_stream_io_dir(stream))
stream_set_state_broadcast(stream,
- BT_BAP_STREAM_STATE_QOS);
+ BT_BAP_STREAM_STATE_QOS);
else if (bt_bap_stream_io_dir(stream) == BT_BAP_BCAST_SOURCE)
stream_set_state_broadcast(stream,
- BT_BAP_STREAM_STATE_CONFIG);
+ BT_BAP_STREAM_STATE_CONFIG);
return;
}

@@ -2622,7 +2622,7 @@ struct bt_bap_pac *bt_bap_add_vendor_pac(struct gatt_db *db,
struct iovec *metadata)
{
struct bt_bap_db *bdb;
- struct bt_bap_pac *pac, *pac_broadcast_sink;
+ struct bt_bap_pac *pac;
struct bt_bap_codec codec;

if (!db)
@@ -2650,15 +2650,6 @@ struct bt_bap_pac *bt_bap_add_vendor_pac(struct gatt_db *db,
break;
case BT_BAP_BCAST_SOURCE:
bap_add_broadcast_source(pac);
- if (queue_isempty(bdb->broadcast_sinks)) {
- /* When adding a local broadcast source, add also a
- * local broadcast sink
- */
- pac_broadcast_sink = bap_pac_new(bdb, name,
- BT_BAP_BCAST_SINK, &codec, qos,
- data, metadata);
- bap_add_broadcast_sink(pac_broadcast_sink);
- }
break;
case BT_BAP_BCAST_SINK:
bap_add_broadcast_sink(pac);
@@ -4416,21 +4407,12 @@ void bt_bap_foreach_pac(struct bt_bap *bap, uint8_t type,
func, user_data);
case BT_BAP_SOURCE:
return bap_foreach_pac(bap->ldb->sinks, bap->rdb->sources,
- func, user_data);
+ func, user_data);
case BT_BAP_BCAST_SOURCE:
- if (queue_isempty(bap->rdb->broadcast_sources)
- && queue_isempty(bap->rdb->broadcast_sinks))
- return bap_foreach_pac(bap->ldb->broadcast_sources,
- bap->ldb->broadcast_sinks,
- func, user_data);
-
- return bap_foreach_pac(bap->ldb->broadcast_sinks,
- bap->rdb->broadcast_sources,
- func, user_data);
case BT_BAP_BCAST_SINK:
return bap_foreach_pac(bap->ldb->broadcast_sinks,
- bap->rdb->broadcast_sources,
- func, user_data);
+ bap->rdb->broadcast_sources,
+ func, user_data);
}
}

@@ -4475,6 +4457,20 @@ void *bt_bap_pac_get_user_data(struct bt_bap_pac *pac)
return pac->user_data;
}

+bool bt_bap_pac_bcast_is_local(struct bt_bap *bap, struct bt_bap_pac *pac)
+{
+ if (!bap->ldb)
+ return false;
+
+ if (queue_find(bap->ldb->broadcast_sinks, NULL, pac))
+ return true;
+
+ if (queue_find(bap->ldb->broadcast_sources, NULL, pac))
+ return true;
+
+ return false;
+}
+
static bool find_ep_unused(const void *data, const void *user_data)
{
const struct bt_bap_endpoint *ep = data;
@@ -4483,7 +4479,10 @@ static bool find_ep_unused(const void *data, const void *user_data)
if (ep->stream)
return false;

- return ep->dir == match->rpac->type;
+ if (match->rpac)
+ return ep->dir == match->rpac->type;
+ else
+ return true;
}

static bool find_ep_pacs(const void *data, const void *user_data)
@@ -4642,7 +4641,11 @@ struct bt_bap_stream *bt_bap_stream_new(struct bt_bap *bap,
struct bt_bap_endpoint *ep;
struct match_pac match;

- if (!bap || !bap->rdb || queue_isempty(bap->remote_eps))
+ if (!bap)
+ return NULL;
+
+ if (!rpac && (lpac->type != BT_BAP_BCAST_SOURCE)
+ && queue_isempty(bap->remote_eps))
return NULL;

if (lpac && rpac) {
@@ -4679,7 +4682,9 @@ 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 || !match.rpac)
+ if (!match.lpac)
+ return NULL;
+ if (!match.rpac && (lpac->type != BT_BAP_BCAST_SOURCE))
return NULL;

lpac = match.lpac;
@@ -4861,12 +4866,12 @@ unsigned int bt_bap_stream_enable(struct bt_bap_stream *stream,
queue_foreach(stream->links, bap_stream_enable_link, metadata);
break;
case BT_BAP_STREAM_TYPE_BCAST:
- if (bt_bap_stream_io_dir(stream) == BT_BAP_BCAST_SINK)
+ if (!bt_bap_stream_io_dir(stream))
stream_set_state_broadcast(stream,
- BT_BAP_STREAM_STATE_CONFIG);
+ BT_BAP_STREAM_STATE_CONFIG);
else if (bt_bap_stream_io_dir(stream) == BT_BAP_BCAST_SOURCE)
stream_set_state_broadcast(stream,
- BT_BAP_STREAM_STATE_STREAMING);
+ BT_BAP_STREAM_STATE_STREAMING);

return 1;
}
@@ -5399,10 +5404,6 @@ bool bt_bap_new_bcast_source(struct bt_bap *bap, const char *name)
struct bt_bap_endpoint *ep;
struct bt_bap_pac *pac_broadcast_source;

- /* Add the remote source only if a local sink endpoint was registered */
- if (queue_isempty(bap->ldb->broadcast_sinks))
- return false;
-
/* Add the remote source only if a local sink endpoint was registered */
if (queue_isempty(bap->ldb->broadcast_sinks))
return false;
diff --git a/src/shared/bap.h b/src/shared/bap.h
index 23edbf4c6..2c8f9208e 100644
--- a/src/shared/bap.h
+++ b/src/shared/bap.h
@@ -317,3 +317,6 @@ void bt_bap_update_bcast_source(struct bt_bap_pac *pac,
struct bt_bap_codec *codec,
struct iovec *data,
struct iovec *metadata);
+
+bool bt_bap_pac_bcast_is_local(struct bt_bap *bap, struct bt_bap_pac *pac);
+
--
2.39.2


Subject: [PATCH BlueZ v6 2/5] audio/bap: Fix source+sink endpoint registration

From: Claudia Draghicescu <[email protected]>

When registering both sink and source endpoints,
the register endpoint method call results in 2 remote endpoints for each
scanned broadcast source

---
profiles/audio/bap.c | 38 ++++++++++++++++++++++++++------------
1 file changed, 26 insertions(+), 12 deletions(-)

diff --git a/profiles/audio/bap.c b/profiles/audio/bap.c
index 780dff412..c279b5b0e 100644
--- a/profiles/audio/bap.c
+++ b/profiles/audio/bap.c
@@ -1050,7 +1050,7 @@ static struct bap_ep *ep_register_bcast(struct bap_data *data,
const char *suffix;
struct match_ep match = { lpac, rpac };

- switch (bt_bap_pac_get_type(rpac)) {
+ switch (bt_bap_pac_get_type(lpac)) {
case BT_BAP_BCAST_SOURCE:
case BT_BAP_BCAST_SINK:
queue = data->bcast;
@@ -1073,13 +1073,13 @@ static struct bap_ep *ep_register_bcast(struct bap_data *data,
if (device)
ep->data->device = device;

- switch (bt_bap_pac_get_type(rpac)) {
- case BT_BAP_BCAST_SINK:
+ switch (bt_bap_pac_get_type(lpac)) {
+ case BT_BAP_BCAST_SOURCE:
err = asprintf(&ep->path, "%s/pac_%s%d",
adapter_get_path(adapter), suffix, i);
ep->base = new0(struct iovec, 1);
break;
- case BT_BAP_BCAST_SOURCE:
+ case BT_BAP_BCAST_SINK:
err = asprintf(&ep->path, "%s/pac_%s%d",
device_get_path(device), suffix, i);
ep->base = new0(struct iovec, 1);
@@ -1101,7 +1101,15 @@ static struct bap_ep *ep_register_bcast(struct bap_data *data,
ep_free(ep);
return NULL;
}
- bt_bap_pac_set_user_data(rpac, ep->path);
+
+ /*
+ * The broadcast source local endpoint has only lpac and broadcast
+ * sink local endpoint has a rpac and a lpac
+ */
+ if (rpac)
+ bt_bap_pac_set_user_data(rpac, ep->path);
+ else
+ bt_bap_pac_set_user_data(lpac, ep->path);

DBG("ep %p lpac %p rpac %p path %s", ep, ep->lpac, ep->rpac, ep->path);

@@ -1792,7 +1800,7 @@ static void bap_listen_io_broadcast(struct bap_data *data, struct bap_ep *ep,
error("%s", err->message);
g_error_free(err);
}
-
+ ep->io = io;
ep->data->listen_io = io;

}
@@ -1962,12 +1970,18 @@ static void pac_added_broadcast(struct bt_bap_pac *pac, void *user_data)
{
struct bap_data *data = user_data;

- if (bt_bap_pac_get_type(pac) == BT_BAP_BCAST_SOURCE)
- bt_bap_foreach_pac(data->bap, BT_BAP_BCAST_SOURCE,
- pac_found_bcast, data);
- else if (bt_bap_pac_get_type(pac) == BT_BAP_BCAST_SINK)
- bt_bap_foreach_pac(data->bap, BT_BAP_BCAST_SINK,
- pac_found_bcast, data);
+ /*
+ * If pac type is BT_BAP_BCAST_SOURCE locally create an endpoint
+ * without a remote pac.
+ * If pac type is BT_BAP_BCAST_SOURCE and remote then look for a
+ * local broadcast sink pac locally before creating an endpoint.
+ */
+ if (bt_bap_pac_bcast_is_local(data->bap, pac) &&
+ (bt_bap_pac_get_type(pac) == BT_BAP_BCAST_SOURCE))
+ pac_found_bcast(pac, NULL, user_data);
+ else
+ bt_bap_foreach_pac(data->bap, bt_bap_pac_get_type(pac),
+ pac_found_bcast, data);
}

static bool ep_match_pac(const void *data, const void *match_data)
--
2.39.2


Subject: [PATCH BlueZ v6 4/5] audio/transport.c: Add support for multiple BISes

Add support for bcast multiple BISes in profiles/audio/transport.c

---
profiles/audio/transport.c | 36 ++++++++++++++++++++++++++----------
1 file changed, 26 insertions(+), 10 deletions(-)

diff --git a/profiles/audio/transport.c b/profiles/audio/transport.c
index 23ea267f6..6f3dd68be 100644
--- a/profiles/audio/transport.c
+++ b/profiles/audio/transport.c
@@ -163,7 +163,9 @@ find_transport_by_bap_stream(const struct bt_bap_stream *stream)
struct bap_transport *bap;

if (strcasecmp(uuid, PAC_SINK_UUID) &&
- strcasecmp(uuid, PAC_SOURCE_UUID))
+ strcasecmp(uuid, PAC_SOURCE_UUID) &&
+ strcasecmp(uuid, BCAA_SERVICE_UUID) &&
+ strcasecmp(uuid, BAA_SERVICE_UUID))
continue;

bap = transport->data;
@@ -312,8 +314,9 @@ static void media_transport_remove_owner(struct media_transport *transport)
media_request_reply(owner->pending, EIO);

transport->owner = NULL;
+
if (bap->linked)
- queue_foreach(bt_bap_stream_io_get_links(bap->stream),
+ bt_bap_stream_foreach_link(bap->stream,
linked_transport_remove_owner, owner);

if (owner->watch)
@@ -497,7 +500,7 @@ static void media_transport_set_owner(struct media_transport *transport,
transport->owner = owner;

if (bap->linked)
- queue_foreach(bt_bap_stream_io_get_links(bap->stream),
+ bt_bap_stream_foreach_link(bap->stream,
linked_transport_set_owner, owner);

owner->transport = transport;
@@ -641,6 +644,7 @@ static DBusMessage *release(DBusConnection *conn, DBusMessage *msg,
const char *sender;
struct media_request *req;
guint id;
+ const char *uuid;

sender = dbus_message_get_sender(msg);

@@ -669,9 +673,12 @@ static DBusMessage *release(DBusConnection *conn, DBusMessage *msg,
req = media_request_create(msg, id);
media_owner_add(owner, req);

- if (bt_bap_stream_get_type(bap->stream) ==
- BT_BAP_STREAM_TYPE_BCAST) {
- bap_disable_complete(bap->stream, 0x00, 0x00, owner);
+ uuid = media_endpoint_get_uuid(transport->endpoint);
+ if (!strcasecmp(uuid, BCAA_SERVICE_UUID) ||
+ !strcasecmp(uuid, BAA_SERVICE_UUID)) {
+ if (bt_bap_stream_get_type(bap->stream) ==
+ BT_BAP_STREAM_TYPE_BCAST)
+ bap_disable_complete(bap->stream, 0x00, 0x00, owner);
}

return NULL;
@@ -686,7 +693,11 @@ static gboolean get_device(const GDBusPropertyTable *property,
if (transport->device)
path = device_get_path(transport->device);
else
- path = adapter_get_path(transport->adapter);
+ /*
+ *Use remote endpoint path as fake device path
+ *for broadcast source transport
+ */
+ path = transport->remote_endpoint;

dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &path);

@@ -1282,6 +1293,10 @@ static void bap_update_links(const struct media_transport *transport)
struct bap_transport *bap = transport->data;
struct queue *links = bt_bap_stream_io_get_links(bap->stream);

+ if (bt_bap_stream_get_type(bap->stream) !=
+ BT_BAP_STREAM_TYPE_UCAST)
+ return;
+
if (bap->linked == !queue_isempty(links))
return;

@@ -1491,9 +1506,10 @@ static void bap_state_changed(struct bt_bap_stream *stream, uint8_t old_state,
/* If a request is pending wait it to complete */
if (owner && owner->pending)
return;
- bap_update_links(transport);
- if (!media_endpoint_is_broadcast(transport->endpoint))
+ if (!media_endpoint_is_broadcast(transport->endpoint)) {
+ bap_update_links(transport);
bap_update_qos(transport);
+ }
else if (bt_bap_stream_io_dir(stream) != BT_BAP_BCAST_SOURCE)
bap_update_bcast_qos(transport);
transport_update_playing(transport, FALSE);
@@ -1510,7 +1526,7 @@ static void bap_state_changed(struct bt_bap_stream *stream, uint8_t old_state,
bap_update_bcast_qos(transport);
break;
case BT_BAP_STREAM_STATE_RELEASING:
- if (bt_bap_stream_io_dir(stream) == BT_BAP_BCAST_SINK)
+ if (!bt_bap_stream_io_dir(stream))
return;
break;
}
--
2.39.2


Subject: [PATCH BlueZ v6 3/5] Add support for multiple BISes

Add support for bcast multiple BISes

---
profiles/audio/bap.c | 581 +++++++++++++++++++++++++++++++------------
src/shared/bap.c | 223 ++++++++++++++---
src/shared/bap.h | 35 ++-
unit/test-bap.c | 12 +-
4 files changed, 643 insertions(+), 208 deletions(-)

diff --git a/profiles/audio/bap.c b/profiles/audio/bap.c
index c279b5b0e..14efa97d8 100644
--- a/profiles/audio/bap.c
+++ b/profiles/audio/bap.c
@@ -97,6 +97,35 @@ struct bap_data {
void *user_data;
};

+#define DEFAULT_IO_QOS \
+{ \
+ .interval = 10000, \
+ .latency = 10, \
+ .sdu = 40, \
+ .phy = 0x02, \
+ .rtn = 2, \
+}
+
+static struct bt_iso_qos default_qos = {
+ .bcast = {
+ .big = BT_ISO_QOS_BIG_UNSET,
+ .bis = BT_ISO_QOS_BIS_UNSET,
+ .sync_factor = 0x07,
+ .packing = 0x00,
+ .framing = 0x00,
+ .in = DEFAULT_IO_QOS,
+ .out = DEFAULT_IO_QOS,
+ .encryption = 0x00,
+ .bcode = {0x00},
+ .options = 0x00,
+ .skip = 0x0000,
+ .sync_timeout = 0x4000,
+ .sync_cte_type = 0x00,
+ .mse = 0x00,
+ .timeout = 0x4000,
+ }
+};
+
static struct queue *sessions;

static bool bap_data_set_user_data(struct bap_data *data, void *user_data)
@@ -254,7 +283,11 @@ static gboolean get_device(const GDBusPropertyTable *property,
const char *path;

if (bt_bap_pac_get_type(ep->lpac) == BT_BAP_BCAST_SOURCE)
- path = adapter_get_path(ep->data->adapter);
+ /*
+ *Use remote endpoint path as fake device path
+ *for the remote broadcast source endpoint
+ */
+ path = ep->path;
else
path = device_get_path(ep->data->device);

@@ -379,101 +412,203 @@ 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)
+static void cleanup_subgroup(struct bt_bap_base_subgroup *subGroup)
+{
+ if (!subGroup)
+ return;
+
+ if (subGroup->meta)
+ util_iov_free(subGroup->meta, 1);
+
+ if (subGroup->caps)
+ util_iov_free(subGroup->caps, 1);
+
+ while (!queue_isempty(subGroup->bises)) {
+ struct bt_bap_bis *bis = queue_peek_head(subGroup->bises);
+
+ if (bis->caps)
+ util_iov_free(bis->caps, 1);
+ queue_remove(subGroup->bises, bis);
+ subGroup->numBises--;
+ }
+ queue_destroy(subGroup->bises, NULL);
+
+ if (subGroup)
+ free(subGroup);
+}
+
+static bool parse_base(struct bt_bap *bap, void *data, size_t len,
+ util_debug_func_t func, struct bt_bap_base *base)
{
+ uint8_t numSubgroups;
+ uint8_t numBis;
+
struct iovec iov = {
.iov_base = data,
.iov_len = len,
};
+ util_debug(func, NULL, "BASE len %ld", len);
+ if (!base)
+ return false;

- uint8_t capsLen, metaLen;
- uint8_t *hexstream;
+ if (!util_iov_pull_le24(&iov, &base->presDelay))
+ return false;
+ util_debug(func, NULL, "PresentationDelay %d", base->presDelay);

- if (presDelay) {
- if (!util_iov_pull_le24(&iov, presDelay))
- return false;
- util_debug(func, NULL, "PresentationDelay %d", *presDelay);
- }
+ if (!util_iov_pull_u8(&iov, &base->numSubgroups))
+ return false;
+ util_debug(func, NULL, "NumSubgroups %d", base->numSubgroups);
+ numSubgroups = base->numSubgroups;

- if (numSubgroups) {
- if (!util_iov_pull_u8(&iov, numSubgroups))
- return false;
- util_debug(func, NULL, "NumSubgroups %d", *numSubgroups);
- }
+ for (int sg = 0; sg < numSubgroups; sg++) {
+ struct bt_bap_base_subgroup *subGroup = new0(
+ struct bt_bap_base_subgroup, 1);
+ uint8_t capsLen, metaLen;
+ uint8_t *hexstream;

- if (numBis) {
- if (!util_iov_pull_u8(&iov, numBis))
- return false;
- util_debug(func, NULL, "NumBis %d", *numBis);
- }
+ subGroup->subGroupIndex = sg;

- if (codec) {
- codec = util_iov_pull_mem(&iov, sizeof(*codec));
- if (!codec)
- return false;
+ util_debug(func, NULL, "Subgroup #%d", sg);
+ subGroup->bap = bap;
+ subGroup->bises = queue_new();
+
+ if (!util_iov_pull_u8(&iov, &numBis)) {
+ cleanup_subgroup(subGroup);
+ goto fail;
+ }
+ util_debug(func, NULL, "NumBis %d", numBis);
+ subGroup->numBises = numBis;
+
+ memcpy(&subGroup->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", codec->id, codec->cid, codec->vid);
- }
+ "Codec", subGroup->codec.id, subGroup->codec.cid,
+ subGroup->codec.vid);
+ if (!util_iov_pull_u8(&iov, &capsLen)) {
+ cleanup_subgroup(subGroup);
+ goto fail;
+ }

- if (!util_iov_pull_u8(&iov, &capsLen))
- return false;
- util_debug(func, NULL, "CC Len %d", capsLen);
+ util_debug(func, NULL, "CC Len %d", capsLen);

- if (!capsLen)
- return false;
- if (caps) {
- if (!(*caps))
- *caps = new0(struct iovec, 1);
- (*caps)->iov_len = capsLen;
- (*caps)->iov_base = iov.iov_base;
- }
+ subGroup->caps = new0(struct iovec, 1);
+ util_iov_memcpy(subGroup->caps, iov.iov_base, capsLen);
+ DBG("subgroup caps len %ld", subGroup->caps->iov_len);

- for (int i = 0; capsLen > 1; i++) {
- struct bt_ltv *ltv = util_iov_pull_mem(&iov, sizeof(*ltv));
- uint8_t *caps;
+ for (int i = 0; capsLen > 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");
- return false;
+ if (!ltv) {
+ util_debug(func, NULL, "Unable to parse %s",
+ "Capabilities");
+ cleanup_subgroup(subGroup);
+ 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(subGroup);
+ goto fail;
+ }
+ util_hexdump(' ', caps, ltv->len - 1, func, NULL);
+
+ capsLen -= (ltv->len + 1);
}

- util_debug(func, NULL, "%s #%u: len %u type %u",
- "CC", i, ltv->len, ltv->type);
+ if (!util_iov_pull_u8(&iov, &metaLen)) {
+ cleanup_subgroup(subGroup);
+ goto fail;
+ }
+ util_debug(func, NULL, "Metadata Len %d", metaLen);

- caps = util_iov_pull_mem(&iov, ltv->len - 1);
- if (!caps) {
- util_debug(func, NULL, "Unable to parse %s",
- "CC");
- return false;
+ subGroup->meta = new0(struct iovec, 1);
+ subGroup->meta->iov_len = metaLen;
+ subGroup->meta->iov_base = iov.iov_base;
+
+ hexstream = util_iov_pull_mem(&iov, metaLen);
+ if (!hexstream) {
+ cleanup_subgroup(subGroup);
+ goto fail;
}
- util_hexdump(' ', caps, ltv->len - 1, func, NULL);
+ util_hexdump(' ', hexstream, metaLen, func, NULL);

- capsLen -= (ltv->len + 1);
- }
+ for (int bis_sg = 0; bis_sg < subGroup->numBises; bis_sg++) {
+ struct bt_bap_bis *bis;
+ uint8_t capsLen;
+ uint8_t crtBis;

- if (!util_iov_pull_u8(&iov, &metaLen))
- return false;
- util_debug(func, NULL, "Metadata Len %d", metaLen);
+ if (!util_iov_pull_u8(&iov, &crtBis)) {
+ cleanup_subgroup(subGroup);
+ goto fail;
+ }
+ util_debug(func, NULL, "BIS #%d", crtBis);

- if (!metaLen)
- return false;
- if (meta) {
- if (!(*meta))
- *meta = new0(struct iovec, 1);
- (*meta)->iov_len = metaLen;
- (*meta)->iov_base = iov.iov_base;
- }
+ bis = new0(struct bt_bap_bis, 1);
+ bis->bisIndex = crtBis;

- hexstream = util_iov_pull_mem(&iov, metaLen);
- if (!hexstream)
- return false;
- util_hexdump(' ', hexstream, metaLen, func, NULL);
+ if (!util_iov_pull_u8(&iov, &capsLen)) {
+ cleanup_subgroup(subGroup);
+ goto fail;
+ }
+ util_debug(func, NULL, "CC Len %d", capsLen);
+
+ bis->caps = new0(struct iovec, 1);
+ bis->caps->iov_len = capsLen;
+ util_iov_memcpy(bis->caps, iov.iov_base, capsLen);
+ DBG("bis caps len %ld", bis->caps->iov_len);
+
+ for (int i = 0; capsLen > 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(subGroup);
+ 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(subGroup);
+ goto fail;
+ }
+ util_hexdump(' ', caps, ltv->len - 1, func,
+ NULL);
+
+ capsLen -= (ltv->len + 1);
+ }
+
+ queue_push_tail(subGroup->bises, bis);
+ }

+ queue_push_tail(base->subgroups, subGroup);
+ }
return true;
+
+fail:
+ while (!queue_isempty(base->subgroups)) {
+ struct bt_bap_base_subgroup *subGroup =
+ queue_peek_head(base->subgroups);
+ cleanup_subgroup(subGroup);
+ base->numSubgroups--;
+ }
+ util_debug(func, NULL, "Unable to parse %s", "Base");
+
+ return false;
}

static int parse_io_qos(const char *key, int var, DBusMessageIter *iter,
@@ -556,6 +691,16 @@ static int parse_bcast_qos(const char *key, int var, DBusMessageIter *iter,
return -EINVAL;

dbus_message_iter_get_basic(iter, &qos->bcast.encryption);
+ } else if (!strcasecmp(key, "BIG")) {
+ if (var != DBUS_TYPE_BYTE)
+ return -EINVAL;
+
+ dbus_message_iter_get_basic(iter, &qos->bcast.big);
+ } else if (!strcasecmp(key, "BIS")) {
+ if (var != DBUS_TYPE_BYTE)
+ return -EINVAL;
+
+ dbus_message_iter_get_basic(iter, &qos->bcast.bis);
} else if (!strcasecmp(key, "Options")) {
if (var != DBUS_TYPE_BYTE)
return -EINVAL;
@@ -653,10 +798,12 @@ static int parse_qos(DBusMessageIter *iter, struct bt_bap_qos *qos,

static int parse_configuration(DBusMessageIter *props, struct iovec **caps,
struct iovec **metadata, struct iovec **base,
- struct bt_bap_qos *qos)
+ struct bt_bap_qos *qos,
+ struct bt_bap_base *base_s)
{
const char *key;
struct iovec iov;
+ base_s->subgroups = NULL;

memset(&iov, 0, sizeof(iov));

@@ -702,14 +849,10 @@ static int parse_configuration(DBusMessageIter *props, struct iovec **caps,
}

if (*base) {
- uint32_t presDelay;
- uint8_t numSubgroups, numBis;
- struct bt_bap_codec codec;
-
util_iov_memcpy(*base, (*caps)->iov_base, (*caps)->iov_len);
- parse_base((*caps)->iov_base, (*caps)->iov_len, bap_debug,
- &presDelay, &numSubgroups, &numBis, &codec,
- caps, NULL);
+ base_s->subgroups = queue_new();
+ parse_base(NULL, (*caps)->iov_base, (*caps)->iov_len,
+ bap_debug, base_s);
}

return 0;
@@ -796,12 +939,53 @@ static void bap_io_close(struct bap_ep *ep)
ep->cig_active = false;
}

+static bool match_bis_by_id(const void *data, const void *user_data)
+{
+ int32_t bis_id = PTR_TO_INT(user_data);
+ const struct bt_bap_bis *bis = data;
+
+ return bis->bisIndex == bis_id;
+}
+
+static int get_number_bises_in_base(struct queue *subgroups)
+{
+ const struct queue_entry *entry;
+ struct bt_bap_base_subgroup *subgroup;
+ int nr_bis = 0;
+
+ for (entry = queue_get_entries(subgroups); entry;
+ entry = entry->next) {
+ subgroup = entry->data;
+ nr_bis += subgroup->numBises;
+ }
+
+ return nr_bis;
+}
+
+static int get_number_bcast_source_ep(struct bap_ep *ep)
+{
+ const struct queue_entry *entry;
+ struct bap_ep *current_ep;
+ int nr_ep = 0;
+
+ for (entry = queue_get_entries(ep->data->bcast); entry;
+ entry = entry->next) {
+ current_ep = entry->data;
+ if (bt_bap_pac_get_type(current_ep->lpac) ==
+ BT_BAP_BCAST_SOURCE)
+ nr_ep++;
+ }
+
+ return nr_ep;
+}
+
static DBusMessage *set_configuration(DBusConnection *conn, DBusMessage *msg,
void *data)
{
struct bap_ep *ep = data;
const char *path;
DBusMessageIter args, props;
+ struct bt_bap_base parse_bap_base;

if (ep->msg)
return btd_error_busy(msg);
@@ -832,11 +1016,57 @@ static DBusMessage *set_configuration(DBusConnection *conn, DBusMessage *msg,
}

if (parse_configuration(&props, &ep->caps, &ep->metadata,
- &ep->base, &ep->qos) < 0) {
+ &ep->base, &ep->qos, &parse_bap_base) < 0) {
DBG("Unable to parse configuration");
return btd_error_invalid_args(msg);
}

+ if (bt_bap_pac_get_type(ep->lpac) == BT_BAP_BCAST_SOURCE) {
+ unsigned int nr_ep = 1;
+ unsigned int nr_bises;
+ const struct queue_entry *entry;
+ struct bt_bap_bis *curent_bis = NULL;
+ struct bt_bap_base_subgroup *subgroup;
+
+ for (entry = queue_get_entries(parse_bap_base.subgroups);
+ entry; entry = entry->next) {
+ subgroup = entry->data;
+ curent_bis = queue_find(subgroup->bises,
+ match_bis_by_id,
+ INT_TO_PTR(ep->qos.bcast.bis));
+ if (curent_bis != NULL)
+ break;
+ }
+
+ if (curent_bis == NULL) {
+ DBG("Unable to find BIS");
+ return btd_error_invalid_args(msg);
+ }
+ util_iov_free(ep->caps, 1);
+ ep->caps = new0(struct iovec, 1);
+ bt_bap_ltv_merge(ep->caps, subgroup->caps, false);
+ bt_bap_ltv_merge(ep->caps, curent_bis->caps, false);
+
+ nr_ep = get_number_bcast_source_ep(ep);
+ nr_bises = get_number_bises_in_base(parse_bap_base.subgroups);
+ if (nr_ep < nr_bises) {
+ for (uint8_t i = 0; i < nr_bises-1; i++)
+ bt_bap_add_vendor_pac_from_pac(ep->lpac);
+ }
+ } else if ((bt_bap_pac_get_type(ep->rpac) == BT_BAP_BCAST_SOURCE) &&
+ (bt_bap_pac_get_type(ep->lpac) == BT_BAP_BCAST_SINK)) {
+ util_iov_free(ep->caps, 1);
+ ep->caps = new0(struct iovec, 1);
+ bt_bap_ltv_merge(ep->caps, bt_bap_pac_get_data(ep->rpac),
+ false);
+ }
+ /*
+ * Kernel uses bis id as advertising handler. To open all BISes from
+ * a big on the same advertisng handler set the bis
+ * to BT_ISO_QOS_BIS_UNSET
+ */
+ ep->qos.bcast.bis = BT_ISO_QOS_BIS_UNSET;
+
/* TODO: Check if stream capabilities match add support for Latency
* and PHY.
*/
@@ -848,7 +1078,7 @@ static DBusMessage *set_configuration(DBusConnection *conn, DBusMessage *msg,
config_cb, ep);
if (!ep->id) {
DBG("Unable to config stream");
- free(ep->caps);
+ util_iov_free(ep->caps, 1);
ep->caps = NULL;
return btd_error_invalid_args(msg);
}
@@ -901,31 +1131,16 @@ static void update_bcast_qos(struct bt_iso_qos *qos,
sizeof(qos->bcast.bcode));
}

-static bool match_ep_type(const void *data, const void *user_data)
-{
- const struct bap_ep *ep = data;
-
- return (bt_bap_pac_get_type(ep->lpac) == PTR_TO_INT(user_data));
-}
-
static void iso_bcast_confirm_cb(GIOChannel *io, GError *err, void *user_data)
{
- struct bap_data *data = user_data;
+ struct bap_ep *ep = user_data;
struct bt_iso_qos qos;
- struct bt_iso_base base;
char address[18];
- struct bap_ep *ep;
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);
@@ -934,29 +1149,15 @@ static void iso_bcast_confirm_cb(GIOChannel *io, GError *err, void *user_data)
}

g_io_channel_ref(io);
- btd_service_connecting_complete(data->service, 0);
+ btd_service_connecting_complete(ep->data->service, 0);
DBG("BCAST ISO: sync with %s (BIG 0x%02x BIS 0x%02x)",
address, qos.bcast.big, qos.bcast.bis);

- ep = queue_find(data->bcast, match_ep_type,
- INT_TO_PTR(BT_BAP_BCAST_SINK));
- if (!ep)
- return;
-
update_bcast_qos(&qos, &ep->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, &ep->caps, &ep->metadata);
-
- /* Update pac with BASE information */
- bt_bap_update_bcast_source(ep->rpac, &codec, ep->caps, ep->metadata);
ep->id = bt_bap_stream_config(ep->stream, &ep->qos,
ep->caps, NULL, NULL);
- data->listen_io = io;
+ ep->data->listen_io = io;

bt_bap_stream_set_user_data(ep->stream, ep->path);

@@ -968,25 +1169,39 @@ static void iso_bcast_confirm_cb(GIOChannel *io, GError *err, void *user_data)
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 bt_iso_base base;
+ char address[18];
+ struct bt_bap_base base_s;
+ struct bap_data *data = user_data;
+ 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);
+ 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);
- g_io_channel_shutdown(io, TRUE, NULL);
+ return;
}
+ g_io_channel_ref(io);
+ data->listen_io = io;
+ btd_service_connecting_complete(data->service, 0);

+ base_s.subgroups = queue_new();
+ parse_base(data->bap, base.base, base.base_len, bap_debug, &base_s);
+
+ bt_bap_update_bcast_source(NULL, &base_s);
}

static bool match_data_bap_data(const void *data, const void *match_data)
@@ -1082,6 +1297,7 @@ static struct bap_ep *ep_register_bcast(struct bap_data *data,
case BT_BAP_BCAST_SINK:
err = asprintf(&ep->path, "%s/pac_%s%d",
device_get_path(device), suffix, i);
+ ep->io = data->listen_io;
ep->base = new0(struct iovec, 1);
break;
}
@@ -1587,9 +1803,12 @@ static gboolean bap_io_disconnected(GIOChannel *io, GIOCondition cond,

bap_io_close(ep);

- /* Check if connecting recreate IO */
- if (!is_cig_busy(ep->data, ep->qos.ucast.cig_id))
- recreate_cig(ep);
+ if (bt_bap_stream_get_type(ep->stream) ==
+ BT_BAP_STREAM_TYPE_UCAST) {
+ /* Check if connecting recreate IO */
+ if (!is_cig_busy(ep->data, ep->qos.ucast.cig_id))
+ recreate_cig(ep);
+ }

return FALSE;
}
@@ -1680,7 +1899,7 @@ static void bap_connect_io(struct bap_data *data, struct bap_ep *ep,

static void bap_connect_io_broadcast(struct bap_data *data, struct bap_ep *ep,
struct bt_bap_stream *stream,
- struct bt_iso_qos *qos)
+ struct bt_iso_qos *qos, int defer)
{
struct btd_adapter *adapter = data->user_data;
GIOChannel *io = NULL;
@@ -1716,7 +1935,7 @@ static void bap_connect_io_broadcast(struct bap_data *data, struct bap_ep *ep,
BT_IO_OPT_MODE, BT_IO_MODE_ISO,
BT_IO_OPT_QOS, qos,
BT_IO_OPT_BASE, &base,
- BT_IO_OPT_DEFER_TIMEOUT, false,
+ BT_IO_OPT_DEFER_TIMEOUT, defer,
BT_IO_OPT_INVALID);

if (!io) {
@@ -1768,42 +1987,28 @@ static void bap_listen_io(struct bap_data *data, struct bt_bap_stream *stream,
static void bap_listen_io_broadcast(struct bap_data *data, struct bap_ep *ep,
struct bt_bap_stream *stream, struct bt_iso_qos *qos)
{
- GIOChannel *io;
GError *err = NULL;
struct sockaddr_iso_bc iso_bc_addr;

iso_bc_addr.bc_bdaddr_type = btd_device_get_bdaddr_type(data->device);
memcpy(&iso_bc_addr.bc_bdaddr, device_get_address(data->device),
sizeof(bdaddr_t));
- iso_bc_addr.bc_bis[0] = 1;
iso_bc_addr.bc_num_bis = 1;
+ iso_bc_addr.bc_bis[0] = strtol(bt_bap_pac_get_name(ep->rpac), NULL, 10);

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, ep->data, NULL, &err,
- BT_IO_OPT_SOURCE_BDADDR,
- btd_adapter_get_address(ep->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(ep->data->listen_io, iso_bcast_confirm_cb,
+ ep, 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);
+ g_io_channel_shutdown(ep->data->listen_io, TRUE, NULL);
}
- ep->io = io;
- ep->data->listen_io = io;
-
}
+
static void bap_create_ucast_io(struct bap_data *data, struct bap_ep *ep,
struct bt_bap_stream *stream, int defer)
{
@@ -1838,9 +2043,6 @@ static void bap_create_bcast_io(struct bap_data *data, struct bap_ep *ep,

memset(&iso_qos, 0, sizeof(iso_qos));

- if (!defer)
- goto done;
-
iso_qos.bcast.big = ep->qos.bcast.big;
iso_qos.bcast.bis = ep->qos.bcast.bis;
iso_qos.bcast.sync_factor = ep->qos.bcast.sync_factor;
@@ -1857,9 +2059,9 @@ static void bap_create_bcast_io(struct bap_data *data, struct bap_ep *ep,
iso_qos.bcast.timeout = ep->qos.bcast.timeout;
memcpy(&iso_qos.bcast.out, &ep->qos.bcast.io_qos,
sizeof(struct bt_iso_io_qos));
-done:
+
if (bt_bap_pac_get_type(ep->lpac) == BT_BAP_BCAST_SOURCE)
- bap_connect_io_broadcast(data, ep, stream, &iso_qos);
+ bap_connect_io_broadcast(data, ep, stream, &iso_qos, defer);
else
bap_listen_io_broadcast(data, ep, stream, &iso_qos);
}
@@ -1884,6 +2086,31 @@ static void bap_create_io(struct bap_data *data, struct bap_ep *ep,
break;
}
}
+/*
+ * Function test_linked_streams_state will check if all linked streams
+ * have the test state.
+ * If all streams are in test_state returns true; Else returns false
+ */
+static bool test_linked_streams_state(struct bt_bap_stream *stream,
+ uint8_t test_state)
+{
+ const struct queue_entry *entry;
+ struct bt_bap_stream *linked_stream;
+ struct queue *links;
+
+ links = bt_bap_stream_io_get_links(stream);
+
+ if (!links)
+ return true;
+
+ for (entry = queue_get_entries(links); entry; entry = entry->next) {
+ linked_stream = entry->data;
+ if (bt_bap_stream_get_state(linked_stream) != test_state)
+ return false;
+ }
+
+ return true;
+}

static void bap_state(struct bt_bap_stream *stream, uint8_t old_state,
uint8_t new_state, void *user_data)
@@ -1915,15 +2142,26 @@ static void bap_state(struct bt_bap_stream *stream, uint8_t old_state,
break;
case BT_BAP_STREAM_STATE_CONFIG:
if (ep && !ep->id) {
- bap_create_io(data, ep, stream, true);
- if (!ep->io) {
- error("Unable to create io");
- bt_bap_stream_release(stream, NULL, NULL);
- return;
- }
-
if (bt_bap_stream_get_type(stream) ==
+ BT_BAP_STREAM_TYPE_BCAST) {
+ bool defer = !test_linked_streams_state(stream,
+ BT_BAP_STREAM_STATE_CONFIG);
+ bap_create_io(data, ep, stream, defer);
+ if (!ep->io) {
+ error("Unable to create io");
+ bt_bap_stream_release(stream,
+ NULL, NULL);
+ return;
+ }
+ } else if (bt_bap_stream_get_type(stream) ==
BT_BAP_STREAM_TYPE_UCAST) {
+ bap_create_io(data, ep, stream, true);
+ if (!ep->io) {
+ error("Unable to create io");
+ bt_bap_stream_release(stream,
+ NULL, NULL);
+ return;
+ }
/* Wait QoS response to respond */
ep->id = bt_bap_stream_qos(stream, &ep->qos,
qos_cb, ep);
@@ -2237,6 +2475,12 @@ 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];
+ GIOChannel *io;
+ GError *err = NULL;
+ struct sockaddr_iso_bc iso_bc_addr;
+
+ iso_bc_addr.bc_bis[0] = 1;
+ iso_bc_addr.bc_num_bis = 1;

ba2str(device_get_address(device), addr);

@@ -2275,7 +2519,24 @@ 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 source");
+ 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, &default_qos,
+ 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);
+ g_error_free(err);
+ }
+
return 0;
}

diff --git a/src/shared/bap.c b/src/shared/bap.c
index d8a3adde6..9593c77cf 100644
--- a/src/shared/bap.c
+++ b/src/shared/bap.c
@@ -254,6 +254,8 @@ static struct queue *bap_db;
static struct queue *bap_cbs;
static struct queue *sessions;

+static bool bt_bap_new_bcast_sink(struct bt_bap *bap);
+
static bool bap_db_match(const void *data, const void *match_data)
{
const struct bt_bap_db *bdb = data;
@@ -1111,7 +1113,8 @@ static struct bt_bap_stream_io *stream_get_io(struct bt_bap_stream *stream)
return stream->io;

io = NULL;
- queue_foreach(stream->links, stream_find_io, &io);
+ if (bt_bap_stream_get_type(stream) == BT_BAP_STREAM_TYPE_UCAST)
+ queue_foreach(stream->links, stream_find_io, &io);

return io;
}
@@ -1323,6 +1326,12 @@ static void stream_set_state_broadcast(struct bt_bap_stream *stream,

bt_bap_ref(bap);

+ switch (stream->ep->state) {
+ case BT_ASCS_ASE_STATE_QOS:
+ bap_stream_update_io_links(stream);
+ break;
+ }
+
for (entry = queue_get_entries(bap->state_cbs); entry;
entry = entry->next) {
struct bt_bap_state *state = entry->data;
@@ -2357,33 +2366,18 @@ static struct bt_bap_pac *bap_pac_find(struct bt_bap_db *bdb, uint8_t type,
return NULL;
}

-static void *ltv_merge(struct iovec *data, struct iovec *cont)
-{
- uint8_t delimiter = 0;
-
- if (!data)
- return NULL;
-
- if (!cont || !cont->iov_len || !cont->iov_base)
- return data->iov_base;
-
- iov_append(data, sizeof(delimiter), &delimiter);
-
- return iov_append(data, cont->iov_len, cont->iov_base);
-}
-
static void bap_pac_merge(struct bt_bap_pac *pac, struct iovec *data,
struct iovec *metadata)
{
/* Merge data into existing record */
if (pac->data)
- ltv_merge(pac->data, data);
+ bt_bap_ltv_merge(pac->data, data, true);
else
pac->data = util_iov_dup(data, 1);

/* Merge metadata into existing record */
if (pac->metadata)
- ltv_merge(pac->metadata, metadata);
+ bt_bap_ltv_merge(pac->metadata, metadata, true);
else
pac->metadata = util_iov_dup(metadata, 1);
}
@@ -2614,6 +2608,20 @@ static void notify_session_pac_added(void *data, void *user_data)
queue_foreach(bap->pac_cbs, notify_pac_added, user_data);
}

+struct bt_bap_pac *bt_bap_add_vendor_pac_from_pac(struct bt_bap_pac *pac)
+{
+ struct bt_bap_pac *new_pac;
+
+ new_pac = bt_bap_add_vendor_pac(pac->bdb->db, pac->name, pac->type,
+ pac->codec.id,
+ pac->codec.cid,
+ pac->codec.cid,
+ &pac->qos, pac->data,
+ pac->metadata);
+ bt_bap_pac_set_ops(new_pac, pac->ops, pac->user_data);
+ return new_pac;
+}
+
struct bt_bap_pac *bt_bap_add_vendor_pac(struct gatt_db *db,
const char *name, uint8_t type,
uint8_t id, uint16_t cid, uint16_t vid,
@@ -2671,7 +2679,7 @@ struct bt_bap_pac *bt_bap_add_pac(struct gatt_db *db, const char *name,
struct iovec *metadata)
{
return bt_bap_add_vendor_pac(db, name, type, id, 0x0000, 0x0000, qos,
- data, metadata);
+ data, metadata);
}

uint8_t bt_bap_pac_get_type(struct bt_bap_pac *pac)
@@ -2701,6 +2709,14 @@ uint32_t bt_bap_pac_get_locations(struct bt_bap_pac *pac)
}
}

+struct iovec *bt_bap_pac_get_data(struct bt_bap_pac *pac)
+{
+ if (!pac)
+ return 0x00;
+
+ return pac->data;
+}
+
uint16_t bt_bap_pac_get_supported_context(struct bt_bap_pac *pac)
{
struct bt_pacs *pacs;
@@ -2783,12 +2799,26 @@ static void notify_session_pac_removed(void *data, void *user_data)
bool bt_bap_pac_set_ops(struct bt_bap_pac *pac, struct bt_bap_pac_ops *ops,
void *user_data)
{
+ const struct queue_entry *entry = NULL;
+ struct bt_bap_pac *pac_entry;
+
if (!pac)
return false;

pac->ops = ops;
pac->user_data = user_data;

+ if (pac->type == BT_BAP_BCAST_SOURCE) {
+ for (entry = queue_get_entries(pac->bdb->broadcast_sources);
+ entry != NULL; entry = entry->next) {
+ pac_entry = entry->data;
+ if (pac_entry != pac) {
+ pac_entry->ops = ops;
+ pac_entry->user_data = user_data;
+ }
+ }
+ }
+
return true;
}

@@ -4471,6 +4501,11 @@ bool bt_bap_pac_bcast_is_local(struct bt_bap *bap, struct bt_bap_pac *pac)
return false;
}

+char *bt_bap_pac_get_name(struct bt_bap_pac *pac)
+{
+ return pac->name;
+}
+
static bool find_ep_unused(const void *data, const void *user_data)
{
const struct bt_bap_endpoint *ep = data;
@@ -4591,7 +4626,6 @@ unsigned int bt_bap_stream_config(struct bt_bap_stream *stream,
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;
}
@@ -4682,7 +4716,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;
@@ -4699,7 +4733,15 @@ struct bt_bap_stream *bt_bap_stream_new(struct bt_bap *bap,
if (!ep) {
/* Check for unused ASE */
ep = queue_find(bap->remote_eps, find_ep_unused, &match);
- if (!ep) {
+ if (!ep && lpac->type == BT_BAP_BCAST_SOURCE) {
+ bt_bap_new_bcast_sink(bap);
+ ep = queue_find(bap->remote_eps, find_ep_unused,
+ &match);
+ if (!ep) {
+ DBG(bap, "Unable to find unused ASE");
+ return NULL;
+ }
+ } else if (!ep) {
DBG(bap, "Unable to find unused ASE");
return NULL;
}
@@ -4712,6 +4754,15 @@ struct bt_bap_stream *bt_bap_stream_new(struct bt_bap *bap,
return stream;
}

+void bt_bap_stream_foreach_link(struct bt_bap_stream *stream,
+ queue_foreach_func_t function, void *user_data)
+{
+ if (bt_bap_stream_get_type(stream) ==
+ BT_BAP_STREAM_TYPE_UCAST)
+ queue_foreach(bt_bap_stream_io_get_links(stream),
+ function, user_data);
+}
+
struct bt_bap *bt_bap_stream_get_session(struct bt_bap_stream *stream)
{
if (!stream)
@@ -4839,7 +4890,16 @@ static void bap_stream_enable_link(void *data, void *user_data)
struct bt_bap_stream *stream = data;
struct iovec *metadata = user_data;

- bap_stream_metadata(stream, BT_ASCS_ENABLE, metadata, NULL, NULL);
+ switch (bt_bap_stream_get_type(stream)) {
+ case BT_BAP_STREAM_TYPE_UCAST:
+ bap_stream_metadata(stream, BT_ASCS_ENABLE, metadata,
+ NULL, NULL);
+ break;
+ case BT_BAP_STREAM_TYPE_BCAST:
+ stream_set_state_broadcast(stream,
+ BT_BAP_STREAM_STATE_CONFIG);
+ break;
+ }
}

unsigned int bt_bap_stream_enable(struct bt_bap_stream *stream,
@@ -4868,11 +4928,10 @@ unsigned int bt_bap_stream_enable(struct bt_bap_stream *stream,
case BT_BAP_STREAM_TYPE_BCAST:
if (!bt_bap_stream_io_dir(stream))
stream_set_state_broadcast(stream,
- BT_BAP_STREAM_STATE_CONFIG);
+ BT_BAP_STREAM_STATE_CONFIG);
else if (bt_bap_stream_io_dir(stream) == BT_BAP_BCAST_SOURCE)
stream_set_state_broadcast(stream,
BT_BAP_STREAM_STATE_STREAMING);
-
return 1;
}

@@ -4933,17 +4992,27 @@ static void bap_stream_disable_link(void *data, void *user_data)
struct iovec iov;
struct bt_ascs_disable disable;

- memset(&disable, 0, sizeof(disable));
+ switch (bt_bap_stream_get_type(stream)) {
+ case BT_BAP_STREAM_TYPE_UCAST:
+ memset(&disable, 0, sizeof(disable));
+
+ disable.ase = stream->ep->id;

- disable.ase = stream->ep->id;
+ iov.iov_base = &disable;
+ iov.iov_len = sizeof(disable);

- iov.iov_base = &disable;
- iov.iov_len = sizeof(disable);
+ req = bap_req_new(stream, BT_ASCS_DISABLE, &iov,
+ 1, NULL, NULL);

- req = bap_req_new(stream, BT_ASCS_DISABLE, &iov, 1, NULL, NULL);
+ if (!bap_queue_req(stream->bap, req))
+ bap_req_free(req);
+ break;

- if (!bap_queue_req(stream->bap, req))
- bap_req_free(req);
+ case BT_BAP_STREAM_TYPE_BCAST:
+ stream_set_state_broadcast(stream,
+ BT_BAP_STREAM_STATE_RELEASING);
+ break;
+ }
}

unsigned int bt_bap_stream_disable(struct bt_bap_stream *stream,
@@ -5189,7 +5258,8 @@ bool bt_bap_stream_set_io(struct bt_bap_stream *stream, int fd)

bap_stream_set_io(stream, INT_TO_PTR(fd));

- queue_foreach(stream->links, bap_stream_set_io, INT_TO_PTR(fd));
+ if (bt_bap_stream_get_type(stream) == BT_BAP_STREAM_TYPE_UCAST)
+ queue_foreach(stream->links, bap_stream_set_io, INT_TO_PTR(fd));

return true;
}
@@ -5210,6 +5280,23 @@ static bool match_name(const void *data, const void *match_data)
return (!strcmp(pac->name, name));
}

+void *bt_bap_ltv_merge(struct iovec *data, struct iovec *cont,
+ bool add_delimiter)
+{
+ uint8_t delimiter = 0;
+
+ if (!data)
+ return NULL;
+
+ if (!cont || !cont->iov_len || !cont->iov_base)
+ return data->iov_base;
+
+ if (add_delimiter)
+ iov_append(data, sizeof(delimiter), &delimiter);
+
+ return iov_append(data, cont->iov_len, cont->iov_base);
+}
+
int bt_bap_stream_cancel(struct bt_bap_stream *stream, unsigned int id)
{
struct bt_bap_req *req;
@@ -5245,10 +5332,15 @@ int bt_bap_stream_io_link(struct bt_bap_stream *stream,
if (queue_find(stream->links, NULL, link))
return -EALREADY;

- if (stream->client != link->client ||
+ if (bt_bap_stream_get_type(stream) == BT_BAP_STREAM_TYPE_BCAST) {
+ if (stream->qos.bcast.big != link->qos.bcast.big)
+ return -EINVAL;
+ } else {
+ if (stream->client != link->client ||
stream->qos.ucast.cig_id != link->qos.ucast.cig_id ||
stream->qos.ucast.cis_id != link->qos.ucast.cis_id)
- return -EINVAL;
+ return -EINVAL;
+ }

if (!stream->links)
stream->links = queue_new();
@@ -5377,7 +5469,9 @@ int bt_bap_stream_io_connecting(struct bt_bap_stream *stream, int fd)

bap_stream_io_connecting(stream, INT_TO_PTR(fd));

- queue_foreach(stream->links, bap_stream_io_connecting, INT_TO_PTR(fd));
+ if (bt_bap_stream_get_type(stream) == BT_BAP_STREAM_TYPE_UCAST)
+ queue_foreach(stream->links, bap_stream_io_connecting,
+ INT_TO_PTR(fd));

return 0;
}
@@ -5433,11 +5527,58 @@ bool bt_bap_new_bcast_source(struct bt_bap *bap, const char *name)
return true;
}

+static bool bt_bap_new_bcast_sink(struct bt_bap *bap)
+{
+ struct bt_bap_endpoint *ep;
+
+ /* Push remote endpoint with direction source */
+ ep = bap_endpoint_new_broadcast(bap->ldb, BT_BAP_BCAST_SOURCE);
+
+ if (ep)
+ queue_push_tail(bap->remote_eps, ep);
+
+ ep->bap = bap;
+ return true;
+}
+
+static void bis_to_pac(void *data, void *user_data)
+{
+ struct bt_bap_bis *bis = data;
+ struct bt_bap_base_subgroup *subgroup = user_data;
+ struct bt_bap_pac *pac_source_bis;
+ struct bt_bap_endpoint *ep;
+ int err = 0;
+
+ bt_bap_ltv_merge(bis->caps, subgroup->caps, false);
+ pac_source_bis = bap_pac_new(subgroup->bap->rdb, NULL,
+ BT_BAP_BCAST_SOURCE, &subgroup->codec, NULL,
+ bis->caps, subgroup->meta);
+
+ err = asprintf(&pac_source_bis->name, "%d", bis->bisIndex);
+
+ if (err < 0)
+ DBG(subgroup->bap, "error in asprintf");
+
+ /* 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);
+}
+static void parse_bis(void *data, void *user_data)
+{
+ struct bt_bap_base_subgroup *subgroup = data;
+
+ queue_foreach(subgroup->bises, bis_to_pac, subgroup);
+}
void bt_bap_update_bcast_source(struct bt_bap_pac *pac,
- struct bt_bap_codec *codec,
- struct iovec *data,
- struct iovec *metadata)
+ struct bt_bap_base *base)
{
- bap_pac_merge(pac, data, metadata);
- pac->codec = *codec;
+ queue_foreach(base->subgroups, parse_bis, NULL);
}
diff --git a/src/shared/bap.h b/src/shared/bap.h
index 2c8f9208e..411655801 100644
--- a/src/shared/bap.h
+++ b/src/shared/bap.h
@@ -97,6 +97,27 @@ struct bt_bap_qos {
};
};

+struct bt_bap_base {
+ uint32_t presDelay;
+ uint8_t numSubgroups;
+ struct queue *subgroups;
+};
+
+struct bt_bap_base_subgroup {
+ uint8_t subGroupIndex;
+ struct bt_bap *bap;
+ uint8_t numBises;
+ struct bt_bap_codec codec;
+ struct iovec *caps;
+ struct iovec *meta;
+ struct queue *bises;
+};
+
+struct bt_bap_bis {
+ uint8_t bisIndex;
+ 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);
@@ -168,6 +189,8 @@ uint8_t bt_bap_pac_get_type(struct bt_bap_pac *pac);

uint32_t bt_bap_pac_get_locations(struct bt_bap_pac *pac);

+struct iovec *bt_bap_pac_get_data(struct bt_bap_pac *pac);
+
uint16_t bt_bap_pac_get_supported_context(struct bt_bap_pac *pac);

uint16_t bt_bap_pac_get_context(struct bt_bap_pac *pac);
@@ -176,6 +199,8 @@ struct bt_bap_pac_qos *bt_bap_pac_get_qos(struct bt_bap_pac *pac);

uint8_t bt_bap_stream_get_type(struct bt_bap_stream *stream);

+struct bt_bap_pac *bt_bap_add_vendor_pac_from_pac(struct bt_bap_pac *pac);
+
struct bt_bap_stream *bt_bap_pac_get_stream(struct bt_bap_pac *pac);

/* Session related function */
@@ -231,6 +256,7 @@ int bt_bap_pac_get_codec(struct bt_bap_pac *pac, uint8_t *id,

void bt_bap_pac_set_user_data(struct bt_bap_pac *pac, void *user_data);
void *bt_bap_pac_get_user_data(struct bt_bap_pac *pac);
+char *bt_bap_pac_get_name(struct bt_bap_pac *pac);

/* Stream related functions */
int bt_bap_select(struct bt_bap_pac *lpac, struct bt_bap_pac *rpac,
@@ -242,6 +268,9 @@ struct bt_bap_stream *bt_bap_stream_new(struct bt_bap *bap,
struct bt_bap_qos *pqos,
struct iovec *data);

+void bt_bap_stream_foreach_link(struct bt_bap_stream *stream,
+ queue_foreach_func_t function, void *user_data);
+
struct bt_bap *bt_bap_stream_get_session(struct bt_bap_stream *stream);
uint8_t bt_bap_stream_get_state(struct bt_bap_stream *stream);

@@ -314,9 +343,9 @@ bool bt_bap_stream_io_is_connecting(struct bt_bap_stream *stream, int *fd);

bool bt_bap_new_bcast_source(struct bt_bap *bap, const char *name);
void bt_bap_update_bcast_source(struct bt_bap_pac *pac,
- struct bt_bap_codec *codec,
- struct iovec *data,
- struct iovec *metadata);
+ struct bt_bap_base *base);
+void *bt_bap_ltv_merge(struct iovec *data, struct iovec *cont,
+ bool add_delimiter);

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

diff --git a/unit/test-bap.c b/unit/test-bap.c
index adade07db..d308d42f9 100644
--- a/unit/test-bap.c
+++ b/unit/test-bap.c
@@ -377,11 +377,13 @@ static void test_client_config(struct test_data *data)
"test-bap-snk",
BT_BAP_SINK, 0x0ff,
0x0001, 0x0001,
- NULL, data->caps, NULL);
+ NULL, data->caps,
+ NULL);
else
data->snk = bt_bap_add_pac(data->db, "test-bap-snk",
BT_BAP_SINK, LC3_ID,
- NULL, data->caps, NULL);
+ NULL, data->caps,
+ NULL);
g_assert(data->snk);
}

@@ -391,11 +393,13 @@ static void test_client_config(struct test_data *data)
"test-bap-src",
BT_BAP_SOURCE, 0x0ff,
0x0001, 0x0001,
- NULL, data->caps, NULL);
+ NULL, data->caps,
+ NULL);
else
data->src = bt_bap_add_pac(data->db, "test-bap-src",
BT_BAP_SOURCE, LC3_ID,
- NULL, data->caps, NULL);
+ NULL, data->caps,
+ NULL);
g_assert(data->src);
}
}
--
2.39.2


Subject: [PATCH BlueZ v6 5/5] client/player.c: Add support for multiple BISes

Add support for multiple BISes
Register the broadcast source endpoint
endpoint.register 00001852-0000-1000-8000-00805f9b34fb 0x06
After registration a new remote endpoint is created
Use the endpoint.config command to set the BIS codec configuration by
specifying the BIS when prompted by the command
endpoint.config /org/bluez/hci0/pac_bcast0 /local/endpoint/ep0 16_2_1
[/local/endpoint/ep0] BIG (value): 0
[/local/endpoint/ep0] BIS (value): 1
Based on the number of BISes seted in the base_lc3_16_2_1 more
remote endoints wil be created (If more then one bis is setted)
Use the endpoint.config command to set the BIS codec configuration by
specifying the BIS when prompted by the command
endpoint.config /org/bluez/hci0/pac_bcast0 /local/endpoint/ep0 16_2_1
[/local/endpoint/ep0] BIG (value): 0
[/local/endpoint/ep0] BIS (value): 2
Use the endpoint config command to configure all the BISes
After all BISes are configured, use the transports to send data

---
client/player.c | 46 +++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 45 insertions(+), 1 deletion(-)

diff --git a/client/player.c b/client/player.c
index 715598aa9..e060e6a5f 100644
--- a/client/player.c
+++ b/client/player.c
@@ -2999,7 +2999,45 @@ static void endpoint_config(const char *input, void *user_data)
endpoint_set_config(cfg);
}

+static void config_endpoint_iso_stream(const char *input, void *user_data)
+{
+ struct endpoint_config *cfg = user_data;
+ char *endptr = NULL;
+ int value;
+
+ value = strtol(input, &endptr, 0);
+
+ if (!endptr || *endptr != '\0' || value > UINT8_MAX) {
+ bt_shell_printf("Invalid argument: %s\n", input);
+ return bt_shell_noninteractive_quit(EXIT_FAILURE);
+ }
+
+ bcast_qos.bcast.bis = value;
+
+ endpoint_set_config(cfg);
+}
+
+static void config_endpoint_iso_group(const char *input, void *user_data)
+{
+ struct endpoint_config *cfg = user_data;
+ char *endptr = NULL;
+ int value;
+
+ value = strtol(input, &endptr, 0);
+
+ if (!endptr || *endptr != '\0' || value > UINT8_MAX) {
+ bt_shell_printf("Invalid argument: %s\n", input);
+ return bt_shell_noninteractive_quit(EXIT_FAILURE);
+ }
+
+ bcast_qos.bcast.big = value;
+
+ bt_shell_prompt_input(cfg->ep->path, "BIS (value):",
+ config_endpoint_iso_stream, cfg);
+}
+
static struct endpoint *endpoint_new(const struct capabilities *cap);
+static void endpoint_init_defaults(struct endpoint *ep);

static void cmd_config_endpoint(int argc, char *argv[])
{
@@ -3033,6 +3071,7 @@ static void cmd_config_endpoint(int argc, char *argv[])
if (cap) {
broadcast = true;
cfg->ep = endpoint_new(cap);
+ endpoint_init_defaults(cfg->ep);
cfg->ep->preset = find_presets_name(uuid, argv[3]);
if (!cfg->ep->preset)
bt_shell_printf("Preset not found\n");
@@ -3068,7 +3107,12 @@ static void cmd_config_endpoint(int argc, char *argv[])
/* Set QoS parameters */
cfg->qos = &preset->qos;

- endpoint_set_config(cfg);
+ if ((cfg->ep->broadcast) && (strcmp(cfg->ep->uuid,
+ BCAA_SERVICE_UUID) == 0))
+ bt_shell_prompt_input(cfg->ep->path, "BIG (value):",
+ config_endpoint_iso_group, cfg);
+ else
+ endpoint_set_config(cfg);
return;
}

--
2.39.2


2023-11-23 23:30:06

by bluez.test.bot

[permalink] [raw]
Subject: RE: Add support for bcast multiple BISes

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

---Test result---

Test Summary:
CheckPatch PASS 2.13 seconds
GitLint PASS 1.08 seconds
BuildEll PASS 23.60 seconds
BluezMake PASS 530.26 seconds
MakeCheck PASS 10.76 seconds
MakeDistcheck PASS 143.50 seconds
CheckValgrind PASS 204.29 seconds
CheckSmatch PASS 306.07 seconds
bluezmakeextell PASS 92.68 seconds
IncrementalBuild PASS 2423.59 seconds
ScanBuild PASS 856.29 seconds



---
Regards,
Linux Bluetooth

2023-11-27 14:33:49

by Luiz Augusto von Dentz

[permalink] [raw]
Subject: Re: [PATCH BlueZ v6 3/5] Add support for multiple BISes

Hi Silviu,

On Thu, Nov 23, 2023 at 5:00 PM Silviu Florian Barbulescu
<[email protected]> wrote:
>
> Add support for bcast multiple BISes
>
> ---
> profiles/audio/bap.c | 581 +++++++++++++++++++++++++++++++------------
> src/shared/bap.c | 223 ++++++++++++++---
> src/shared/bap.h | 35 ++-
> unit/test-bap.c | 12 +-
> 4 files changed, 643 insertions(+), 208 deletions(-)
>
> diff --git a/profiles/audio/bap.c b/profiles/audio/bap.c
> index c279b5b0e..14efa97d8 100644
> --- a/profiles/audio/bap.c
> +++ b/profiles/audio/bap.c
> @@ -97,6 +97,35 @@ struct bap_data {
> void *user_data;
> };
>
> +#define DEFAULT_IO_QOS \
> +{ \
> + .interval = 10000, \
> + .latency = 10, \
> + .sdu = 40, \
> + .phy = 0x02, \
> + .rtn = 2, \
> +}
> +
> +static struct bt_iso_qos default_qos = {
> + .bcast = {
> + .big = BT_ISO_QOS_BIG_UNSET,
> + .bis = BT_ISO_QOS_BIS_UNSET,
> + .sync_factor = 0x07,
> + .packing = 0x00,
> + .framing = 0x00,
> + .in = DEFAULT_IO_QOS,
> + .out = DEFAULT_IO_QOS,
> + .encryption = 0x00,
> + .bcode = {0x00},
> + .options = 0x00,
> + .skip = 0x0000,
> + .sync_timeout = 0x4000,
> + .sync_cte_type = 0x00,
> + .mse = 0x00,
> + .timeout = 0x4000,
> + }
> +};
> +
> static struct queue *sessions;
>
> static bool bap_data_set_user_data(struct bap_data *data, void *user_data)
> @@ -254,7 +283,11 @@ static gboolean get_device(const GDBusPropertyTable *property,
> const char *path;
>
> if (bt_bap_pac_get_type(ep->lpac) == BT_BAP_BCAST_SOURCE)
> - path = adapter_get_path(ep->data->adapter);
> + /*
> + *Use remote endpoint path as fake device path
> + *for the remote broadcast source endpoint
> + */
> + path = ep->path;

Actually we should omit the Device property if that doesn't really exist.

> else
> path = device_get_path(ep->data->device);
>
> @@ -379,101 +412,203 @@ 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)
> +static void cleanup_subgroup(struct bt_bap_base_subgroup *subGroup)
> +{
> + if (!subGroup)
> + return;
> +
> + if (subGroup->meta)
> + util_iov_free(subGroup->meta, 1);
> +
> + if (subGroup->caps)
> + util_iov_free(subGroup->caps, 1);
> +
> + while (!queue_isempty(subGroup->bises)) {
> + struct bt_bap_bis *bis = queue_peek_head(subGroup->bises);
> +
> + if (bis->caps)
> + util_iov_free(bis->caps, 1);
> + queue_remove(subGroup->bises, bis);
> + subGroup->numBises--;
> + }
> + queue_destroy(subGroup->bises, NULL);
> +
> + if (subGroup)
> + free(subGroup);

We don't really use camel casing for variables, so let's please use
subgroup or subgrp for shorter.

> +}
> +
> +static bool parse_base(struct bt_bap *bap, void *data, size_t len,
> + util_debug_func_t func, struct bt_bap_base *base)
> {
> + uint8_t numSubgroups;
> + uint8_t numBis;

Ditto.

> struct iovec iov = {
> .iov_base = data,
> .iov_len = len,
> };
> + util_debug(func, NULL, "BASE len %ld", len);
> + if (!base)
> + return false;
>
> - uint8_t capsLen, metaLen;
> - uint8_t *hexstream;
> + if (!util_iov_pull_le24(&iov, &base->presDelay))
> + return false;
> + util_debug(func, NULL, "PresentationDelay %d", base->presDelay);
>
> - if (presDelay) {
> - if (!util_iov_pull_le24(&iov, presDelay))
> - return false;
> - util_debug(func, NULL, "PresentationDelay %d", *presDelay);
> - }
> + if (!util_iov_pull_u8(&iov, &base->numSubgroups))
> + return false;
> + util_debug(func, NULL, "NumSubgroups %d", base->numSubgroups);
> + numSubgroups = base->numSubgroups;
>
> - if (numSubgroups) {
> - if (!util_iov_pull_u8(&iov, numSubgroups))
> - return false;
> - util_debug(func, NULL, "NumSubgroups %d", *numSubgroups);
> - }
> + for (int sg = 0; sg < numSubgroups; sg++) {
> + struct bt_bap_base_subgroup *subGroup = new0(
> + struct bt_bap_base_subgroup, 1);
> + uint8_t capsLen, metaLen;
> + uint8_t *hexstream;
>
> - if (numBis) {
> - if (!util_iov_pull_u8(&iov, numBis))
> - return false;
> - util_debug(func, NULL, "NumBis %d", *numBis);
> - }
> + subGroup->subGroupIndex = sg;
>
> - if (codec) {
> - codec = util_iov_pull_mem(&iov, sizeof(*codec));
> - if (!codec)
> - return false;
> + util_debug(func, NULL, "Subgroup #%d", sg);
> + subGroup->bap = bap;
> + subGroup->bises = queue_new();
> +
> + if (!util_iov_pull_u8(&iov, &numBis)) {
> + cleanup_subgroup(subGroup);
> + goto fail;
> + }
> + util_debug(func, NULL, "NumBis %d", numBis);
> + subGroup->numBises = numBis;
> +
> + memcpy(&subGroup->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", codec->id, codec->cid, codec->vid);
> - }
> + "Codec", subGroup->codec.id, subGroup->codec.cid,
> + subGroup->codec.vid);
> + if (!util_iov_pull_u8(&iov, &capsLen)) {
> + cleanup_subgroup(subGroup);
> + goto fail;
> + }
>
> - if (!util_iov_pull_u8(&iov, &capsLen))
> - return false;
> - util_debug(func, NULL, "CC Len %d", capsLen);
> + util_debug(func, NULL, "CC Len %d", capsLen);
>
> - if (!capsLen)
> - return false;
> - if (caps) {
> - if (!(*caps))
> - *caps = new0(struct iovec, 1);
> - (*caps)->iov_len = capsLen;
> - (*caps)->iov_base = iov.iov_base;
> - }
> + subGroup->caps = new0(struct iovec, 1);
> + util_iov_memcpy(subGroup->caps, iov.iov_base, capsLen);

util_iov_dup is probably more efficient so you don't have to do new0, etc.

> + DBG("subgroup caps len %ld", subGroup->caps->iov_len);
>
> - for (int i = 0; capsLen > 1; i++) {
> - struct bt_ltv *ltv = util_iov_pull_mem(&iov, sizeof(*ltv));
> - uint8_t *caps;
> + for (int i = 0; capsLen > 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");
> - return false;
> + if (!ltv) {
> + util_debug(func, NULL, "Unable to parse %s",
> + "Capabilities");
> + cleanup_subgroup(subGroup);
> + 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(subGroup);
> + goto fail;
> + }
> + util_hexdump(' ', caps, ltv->len - 1, func, NULL);
> +
> + capsLen -= (ltv->len + 1);
> }
>
> - util_debug(func, NULL, "%s #%u: len %u type %u",
> - "CC", i, ltv->len, ltv->type);
> + if (!util_iov_pull_u8(&iov, &metaLen)) {
> + cleanup_subgroup(subGroup);
> + goto fail;
> + }
> + util_debug(func, NULL, "Metadata Len %d", metaLen);
>
> - caps = util_iov_pull_mem(&iov, ltv->len - 1);
> - if (!caps) {
> - util_debug(func, NULL, "Unable to parse %s",
> - "CC");
> - return false;
> + subGroup->meta = new0(struct iovec, 1);
> + subGroup->meta->iov_len = metaLen;
> + subGroup->meta->iov_base = iov.iov_base;
> +
> + hexstream = util_iov_pull_mem(&iov, metaLen);
> + if (!hexstream) {
> + cleanup_subgroup(subGroup);
> + goto fail;
> }
> - util_hexdump(' ', caps, ltv->len - 1, func, NULL);
> + util_hexdump(' ', hexstream, metaLen, func, NULL);
>
> - capsLen -= (ltv->len + 1);
> - }
> + for (int bis_sg = 0; bis_sg < subGroup->numBises; bis_sg++) {
> + struct bt_bap_bis *bis;
> + uint8_t capsLen;
> + uint8_t crtBis;
>
> - if (!util_iov_pull_u8(&iov, &metaLen))
> - return false;
> - util_debug(func, NULL, "Metadata Len %d", metaLen);
> + if (!util_iov_pull_u8(&iov, &crtBis)) {
> + cleanup_subgroup(subGroup);
> + goto fail;
> + }
> + util_debug(func, NULL, "BIS #%d", crtBis);
>
> - if (!metaLen)
> - return false;
> - if (meta) {
> - if (!(*meta))
> - *meta = new0(struct iovec, 1);
> - (*meta)->iov_len = metaLen;
> - (*meta)->iov_base = iov.iov_base;
> - }
> + bis = new0(struct bt_bap_bis, 1);
> + bis->bisIndex = crtBis;
>
> - hexstream = util_iov_pull_mem(&iov, metaLen);
> - if (!hexstream)
> - return false;
> - util_hexdump(' ', hexstream, metaLen, func, NULL);
> + if (!util_iov_pull_u8(&iov, &capsLen)) {
> + cleanup_subgroup(subGroup);
> + goto fail;
> + }
> + util_debug(func, NULL, "CC Len %d", capsLen);
> +
> + bis->caps = new0(struct iovec, 1);
> + bis->caps->iov_len = capsLen;
> + util_iov_memcpy(bis->caps, iov.iov_base, capsLen);
> + DBG("bis caps len %ld", bis->caps->iov_len);
> +
> + for (int i = 0; capsLen > 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(subGroup);
> + 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(subGroup);
> + goto fail;
> + }
> + util_hexdump(' ', caps, ltv->len - 1, func,
> + NULL);
> +
> + capsLen -= (ltv->len + 1);
> + }
> +
> + queue_push_tail(subGroup->bises, bis);
> + }
>
> + queue_push_tail(base->subgroups, subGroup);
> + }
> return true;
> +
> +fail:
> + while (!queue_isempty(base->subgroups)) {
> + struct bt_bap_base_subgroup *subGroup =
> + queue_peek_head(base->subgroups);
> + cleanup_subgroup(subGroup);
> + base->numSubgroups--;
> + }
> + util_debug(func, NULL, "Unable to parse %s", "Base");
> +
> + return false;
> }
>
> static int parse_io_qos(const char *key, int var, DBusMessageIter *iter,
> @@ -556,6 +691,16 @@ static int parse_bcast_qos(const char *key, int var, DBusMessageIter *iter,
> return -EINVAL;
>
> dbus_message_iter_get_basic(iter, &qos->bcast.encryption);
> + } else if (!strcasecmp(key, "BIG")) {
> + if (var != DBUS_TYPE_BYTE)
> + return -EINVAL;
> +
> + dbus_message_iter_get_basic(iter, &qos->bcast.big);
> + } else if (!strcasecmp(key, "BIS")) {
> + if (var != DBUS_TYPE_BYTE)
> + return -EINVAL;
> +
> + dbus_message_iter_get_basic(iter, &qos->bcast.bis);
> } else if (!strcasecmp(key, "Options")) {
> if (var != DBUS_TYPE_BYTE)
> return -EINVAL;
> @@ -653,10 +798,12 @@ static int parse_qos(DBusMessageIter *iter, struct bt_bap_qos *qos,
>
> static int parse_configuration(DBusMessageIter *props, struct iovec **caps,
> struct iovec **metadata, struct iovec **base,
> - struct bt_bap_qos *qos)
> + struct bt_bap_qos *qos,
> + struct bt_bap_base *base_s)
> {
> const char *key;
> struct iovec iov;
> + base_s->subgroups = NULL;
>
> memset(&iov, 0, sizeof(iov));
>
> @@ -702,14 +849,10 @@ static int parse_configuration(DBusMessageIter *props, struct iovec **caps,
> }
>
> if (*base) {
> - uint32_t presDelay;
> - uint8_t numSubgroups, numBis;
> - struct bt_bap_codec codec;
> -
> util_iov_memcpy(*base, (*caps)->iov_base, (*caps)->iov_len);
> - parse_base((*caps)->iov_base, (*caps)->iov_len, bap_debug,
> - &presDelay, &numSubgroups, &numBis, &codec,
> - caps, NULL);
> + base_s->subgroups = queue_new();
> + parse_base(NULL, (*caps)->iov_base, (*caps)->iov_len,
> + bap_debug, base_s);
> }
>
> return 0;
> @@ -796,12 +939,53 @@ static void bap_io_close(struct bap_ep *ep)
> ep->cig_active = false;
> }
>
> +static bool match_bis_by_id(const void *data, const void *user_data)
> +{
> + int32_t bis_id = PTR_TO_INT(user_data);
> + const struct bt_bap_bis *bis = data;
> +
> + return bis->bisIndex == bis_id;
> +}
> +
> +static int get_number_bises_in_base(struct queue *subgroups)
> +{
> + const struct queue_entry *entry;
> + struct bt_bap_base_subgroup *subgroup;
> + int nr_bis = 0;
> +
> + for (entry = queue_get_entries(subgroups); entry;
> + entry = entry->next) {
> + subgroup = entry->data;
> + nr_bis += subgroup->numBises;
> + }
> +
> + return nr_bis;
> +}
> +
> +static int get_number_bcast_source_ep(struct bap_ep *ep)
> +{
> + const struct queue_entry *entry;
> + struct bap_ep *current_ep;
> + int nr_ep = 0;
> +
> + for (entry = queue_get_entries(ep->data->bcast); entry;
> + entry = entry->next) {
> + current_ep = entry->data;
> + if (bt_bap_pac_get_type(current_ep->lpac) ==
> + BT_BAP_BCAST_SOURCE)
> + nr_ep++;
> + }
> +
> + return nr_ep;
> +}
> +
> static DBusMessage *set_configuration(DBusConnection *conn, DBusMessage *msg,
> void *data)
> {
> struct bap_ep *ep = data;
> const char *path;
> DBusMessageIter args, props;
> + struct bt_bap_base parse_bap_base;
>
> if (ep->msg)
> return btd_error_busy(msg);
> @@ -832,11 +1016,57 @@ static DBusMessage *set_configuration(DBusConnection *conn, DBusMessage *msg,
> }
>
> if (parse_configuration(&props, &ep->caps, &ep->metadata,
> - &ep->base, &ep->qos) < 0) {
> + &ep->base, &ep->qos, &parse_bap_base) < 0) {
> DBG("Unable to parse configuration");
> return btd_error_invalid_args(msg);
> }
>
> + if (bt_bap_pac_get_type(ep->lpac) == BT_BAP_BCAST_SOURCE) {
> + unsigned int nr_ep = 1;
> + unsigned int nr_bises;
> + const struct queue_entry *entry;
> + struct bt_bap_bis *curent_bis = NULL;
> + struct bt_bap_base_subgroup *subgroup;
> +
> + for (entry = queue_get_entries(parse_bap_base.subgroups);
> + entry; entry = entry->next) {
> + subgroup = entry->data;
> + curent_bis = queue_find(subgroup->bises,
> + match_bis_by_id,
> + INT_TO_PTR(ep->qos.bcast.bis));
> + if (curent_bis != NULL)
> + break;
> + }
> +
> + if (curent_bis == NULL) {
> + DBG("Unable to find BIS");
> + return btd_error_invalid_args(msg);
> + }
> + util_iov_free(ep->caps, 1);
> + ep->caps = new0(struct iovec, 1);
> + bt_bap_ltv_merge(ep->caps, subgroup->caps, false);
> + bt_bap_ltv_merge(ep->caps, curent_bis->caps, false);
> +
> + nr_ep = get_number_bcast_source_ep(ep);
> + nr_bises = get_number_bises_in_base(parse_bap_base.subgroups);
> + if (nr_ep < nr_bises) {
> + for (uint8_t i = 0; i < nr_bises-1; i++)
> + bt_bap_add_vendor_pac_from_pac(ep->lpac);
> + }
> + } else if ((bt_bap_pac_get_type(ep->rpac) == BT_BAP_BCAST_SOURCE) &&
> + (bt_bap_pac_get_type(ep->lpac) == BT_BAP_BCAST_SINK)) {
> + util_iov_free(ep->caps, 1);
> + ep->caps = new0(struct iovec, 1);
> + bt_bap_ltv_merge(ep->caps, bt_bap_pac_get_data(ep->rpac),
> + false);
> + }
> + /*
> + * Kernel uses bis id as advertising handler. To open all BISes from
> + * a big on the same advertisng handler set the bis
> + * to BT_ISO_QOS_BIS_UNSET
> + */
> + ep->qos.bcast.bis = BT_ISO_QOS_BIS_UNSET;
> +
> /* TODO: Check if stream capabilities match add support for Latency
> * and PHY.
> */
> @@ -848,7 +1078,7 @@ static DBusMessage *set_configuration(DBusConnection *conn, DBusMessage *msg,
> config_cb, ep);
> if (!ep->id) {
> DBG("Unable to config stream");
> - free(ep->caps);
> + util_iov_free(ep->caps, 1);
> ep->caps = NULL;
> return btd_error_invalid_args(msg);
> }
> @@ -901,31 +1131,16 @@ static void update_bcast_qos(struct bt_iso_qos *qos,
> sizeof(qos->bcast.bcode));
> }
>
> -static bool match_ep_type(const void *data, const void *user_data)
> -{
> - const struct bap_ep *ep = data;
> -
> - return (bt_bap_pac_get_type(ep->lpac) == PTR_TO_INT(user_data));
> -}
> -
> static void iso_bcast_confirm_cb(GIOChannel *io, GError *err, void *user_data)
> {
> - struct bap_data *data = user_data;
> + struct bap_ep *ep = user_data;
> struct bt_iso_qos qos;
> - struct bt_iso_base base;
> char address[18];
> - struct bap_ep *ep;
> 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);
> @@ -934,29 +1149,15 @@ static void iso_bcast_confirm_cb(GIOChannel *io, GError *err, void *user_data)
> }
>
> g_io_channel_ref(io);
> - btd_service_connecting_complete(data->service, 0);
> + btd_service_connecting_complete(ep->data->service, 0);
> DBG("BCAST ISO: sync with %s (BIG 0x%02x BIS 0x%02x)",
> address, qos.bcast.big, qos.bcast.bis);
>
> - ep = queue_find(data->bcast, match_ep_type,
> - INT_TO_PTR(BT_BAP_BCAST_SINK));
> - if (!ep)
> - return;
> -
> update_bcast_qos(&qos, &ep->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, &ep->caps, &ep->metadata);
> -
> - /* Update pac with BASE information */
> - bt_bap_update_bcast_source(ep->rpac, &codec, ep->caps, ep->metadata);
> ep->id = bt_bap_stream_config(ep->stream, &ep->qos,
> ep->caps, NULL, NULL);
> - data->listen_io = io;
> + ep->data->listen_io = io;
>
> bt_bap_stream_set_user_data(ep->stream, ep->path);
>
> @@ -968,25 +1169,39 @@ static void iso_bcast_confirm_cb(GIOChannel *io, GError *err, void *user_data)
> 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 bt_iso_base base;
> + char address[18];
> + struct bt_bap_base base_s;
> + struct bap_data *data = user_data;
> + 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);
> + 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);
> - g_io_channel_shutdown(io, TRUE, NULL);
> + return;
> }
> + g_io_channel_ref(io);
> + data->listen_io = io;
> + btd_service_connecting_complete(data->service, 0);
>
> + base_s.subgroups = queue_new();
> + parse_base(data->bap, base.base, base.base_len, bap_debug, &base_s);
> +
> + bt_bap_update_bcast_source(NULL, &base_s);
> }
>
> static bool match_data_bap_data(const void *data, const void *match_data)
> @@ -1082,6 +1297,7 @@ static struct bap_ep *ep_register_bcast(struct bap_data *data,
> case BT_BAP_BCAST_SINK:
> err = asprintf(&ep->path, "%s/pac_%s%d",
> device_get_path(device), suffix, i);
> + ep->io = data->listen_io;
> ep->base = new0(struct iovec, 1);
> break;
> }
> @@ -1587,9 +1803,12 @@ static gboolean bap_io_disconnected(GIOChannel *io, GIOCondition cond,
>
> bap_io_close(ep);
>
> - /* Check if connecting recreate IO */
> - if (!is_cig_busy(ep->data, ep->qos.ucast.cig_id))
> - recreate_cig(ep);
> + if (bt_bap_stream_get_type(ep->stream) ==
> + BT_BAP_STREAM_TYPE_UCAST) {
> + /* Check if connecting recreate IO */
> + if (!is_cig_busy(ep->data, ep->qos.ucast.cig_id))
> + recreate_cig(ep);
> + }
>
> return FALSE;
> }
> @@ -1680,7 +1899,7 @@ static void bap_connect_io(struct bap_data *data, struct bap_ep *ep,
>
> static void bap_connect_io_broadcast(struct bap_data *data, struct bap_ep *ep,
> struct bt_bap_stream *stream,
> - struct bt_iso_qos *qos)
> + struct bt_iso_qos *qos, int defer)
> {
> struct btd_adapter *adapter = data->user_data;
> GIOChannel *io = NULL;
> @@ -1716,7 +1935,7 @@ static void bap_connect_io_broadcast(struct bap_data *data, struct bap_ep *ep,
> BT_IO_OPT_MODE, BT_IO_MODE_ISO,
> BT_IO_OPT_QOS, qos,
> BT_IO_OPT_BASE, &base,
> - BT_IO_OPT_DEFER_TIMEOUT, false,
> + BT_IO_OPT_DEFER_TIMEOUT, defer,
> BT_IO_OPT_INVALID);
>
> if (!io) {
> @@ -1768,42 +1987,28 @@ static void bap_listen_io(struct bap_data *data, struct bt_bap_stream *stream,
> static void bap_listen_io_broadcast(struct bap_data *data, struct bap_ep *ep,
> struct bt_bap_stream *stream, struct bt_iso_qos *qos)
> {
> - GIOChannel *io;
> GError *err = NULL;
> struct sockaddr_iso_bc iso_bc_addr;
>
> iso_bc_addr.bc_bdaddr_type = btd_device_get_bdaddr_type(data->device);
> memcpy(&iso_bc_addr.bc_bdaddr, device_get_address(data->device),
> sizeof(bdaddr_t));
> - iso_bc_addr.bc_bis[0] = 1;
> iso_bc_addr.bc_num_bis = 1;
> + iso_bc_addr.bc_bis[0] = strtol(bt_bap_pac_get_name(ep->rpac), NULL, 10);
>
> 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, ep->data, NULL, &err,
> - BT_IO_OPT_SOURCE_BDADDR,
> - btd_adapter_get_address(ep->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(ep->data->listen_io, iso_bcast_confirm_cb,
> + ep, 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);
> + g_io_channel_shutdown(ep->data->listen_io, TRUE, NULL);
> }
> - ep->io = io;
> - ep->data->listen_io = io;
> -
> }
> +
> static void bap_create_ucast_io(struct bap_data *data, struct bap_ep *ep,
> struct bt_bap_stream *stream, int defer)
> {
> @@ -1838,9 +2043,6 @@ static void bap_create_bcast_io(struct bap_data *data, struct bap_ep *ep,
>
> memset(&iso_qos, 0, sizeof(iso_qos));
>
> - if (!defer)
> - goto done;
> -
> iso_qos.bcast.big = ep->qos.bcast.big;
> iso_qos.bcast.bis = ep->qos.bcast.bis;
> iso_qos.bcast.sync_factor = ep->qos.bcast.sync_factor;
> @@ -1857,9 +2059,9 @@ static void bap_create_bcast_io(struct bap_data *data, struct bap_ep *ep,
> iso_qos.bcast.timeout = ep->qos.bcast.timeout;
> memcpy(&iso_qos.bcast.out, &ep->qos.bcast.io_qos,
> sizeof(struct bt_iso_io_qos));
> -done:
> +
> if (bt_bap_pac_get_type(ep->lpac) == BT_BAP_BCAST_SOURCE)
> - bap_connect_io_broadcast(data, ep, stream, &iso_qos);
> + bap_connect_io_broadcast(data, ep, stream, &iso_qos, defer);
> else
> bap_listen_io_broadcast(data, ep, stream, &iso_qos);
> }
> @@ -1884,6 +2086,31 @@ static void bap_create_io(struct bap_data *data, struct bap_ep *ep,
> break;
> }
> }
> +/*
> + * Function test_linked_streams_state will check if all linked streams
> + * have the test state.
> + * If all streams are in test_state returns true; Else returns false
> + */
> +static bool test_linked_streams_state(struct bt_bap_stream *stream,
> + uint8_t test_state)
> +{
> + const struct queue_entry *entry;
> + struct bt_bap_stream *linked_stream;
> + struct queue *links;
> +
> + links = bt_bap_stream_io_get_links(stream);
> +
> + if (!links)
> + return true;
> +
> + for (entry = queue_get_entries(links); entry; entry = entry->next) {
> + linked_stream = entry->data;
> + if (bt_bap_stream_get_state(linked_stream) != test_state)
> + return false;
> + }
> +
> + return true;
> +}
>
> static void bap_state(struct bt_bap_stream *stream, uint8_t old_state,
> uint8_t new_state, void *user_data)
> @@ -1915,15 +2142,26 @@ static void bap_state(struct bt_bap_stream *stream, uint8_t old_state,
> break;
> case BT_BAP_STREAM_STATE_CONFIG:
> if (ep && !ep->id) {
> - bap_create_io(data, ep, stream, true);
> - if (!ep->io) {
> - error("Unable to create io");
> - bt_bap_stream_release(stream, NULL, NULL);
> - return;
> - }
> -
> if (bt_bap_stream_get_type(stream) ==
> + BT_BAP_STREAM_TYPE_BCAST) {
> + bool defer = !test_linked_streams_state(stream,
> + BT_BAP_STREAM_STATE_CONFIG);
> + bap_create_io(data, ep, stream, defer);
> + if (!ep->io) {
> + error("Unable to create io");
> + bt_bap_stream_release(stream,
> + NULL, NULL);
> + return;
> + }
> + } else if (bt_bap_stream_get_type(stream) ==
> BT_BAP_STREAM_TYPE_UCAST) {
> + bap_create_io(data, ep, stream, true);
> + if (!ep->io) {
> + error("Unable to create io");
> + bt_bap_stream_release(stream,
> + NULL, NULL);
> + return;
> + }
> /* Wait QoS response to respond */
> ep->id = bt_bap_stream_qos(stream, &ep->qos,
> qos_cb, ep);
> @@ -2237,6 +2475,12 @@ 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];
> + GIOChannel *io;
> + GError *err = NULL;
> + struct sockaddr_iso_bc iso_bc_addr;
> +
> + iso_bc_addr.bc_bis[0] = 1;
> + iso_bc_addr.bc_num_bis = 1;
>
> ba2str(device_get_address(device), addr);
>
> @@ -2275,7 +2519,24 @@ 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 source");
> + 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, &default_qos,
> + 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);
> + g_error_free(err);
> + }
> +
> return 0;
> }
>
> diff --git a/src/shared/bap.c b/src/shared/bap.c
> index d8a3adde6..9593c77cf 100644
> --- a/src/shared/bap.c
> +++ b/src/shared/bap.c
> @@ -254,6 +254,8 @@ static struct queue *bap_db;
> static struct queue *bap_cbs;
> static struct queue *sessions;
>
> +static bool bt_bap_new_bcast_sink(struct bt_bap *bap);
> +
> static bool bap_db_match(const void *data, const void *match_data)
> {
> const struct bt_bap_db *bdb = data;
> @@ -1111,7 +1113,8 @@ static struct bt_bap_stream_io *stream_get_io(struct bt_bap_stream *stream)
> return stream->io;
>
> io = NULL;
> - queue_foreach(stream->links, stream_find_io, &io);
> + if (bt_bap_stream_get_type(stream) == BT_BAP_STREAM_TYPE_UCAST)
> + queue_foreach(stream->links, stream_find_io, &io);
>
> return io;
> }
> @@ -1323,6 +1326,12 @@ static void stream_set_state_broadcast(struct bt_bap_stream *stream,
>
> bt_bap_ref(bap);
>
> + switch (stream->ep->state) {
> + case BT_ASCS_ASE_STATE_QOS:
> + bap_stream_update_io_links(stream);
> + break;
> + }
> +
> for (entry = queue_get_entries(bap->state_cbs); entry;
> entry = entry->next) {
> struct bt_bap_state *state = entry->data;
> @@ -2357,33 +2366,18 @@ static struct bt_bap_pac *bap_pac_find(struct bt_bap_db *bdb, uint8_t type,
> return NULL;
> }
>
> -static void *ltv_merge(struct iovec *data, struct iovec *cont)
> -{
> - uint8_t delimiter = 0;
> -
> - if (!data)
> - return NULL;
> -
> - if (!cont || !cont->iov_len || !cont->iov_base)
> - return data->iov_base;
> -
> - iov_append(data, sizeof(delimiter), &delimiter);
> -
> - return iov_append(data, cont->iov_len, cont->iov_base);
> -}
> -
> static void bap_pac_merge(struct bt_bap_pac *pac, struct iovec *data,
> struct iovec *metadata)
> {
> /* Merge data into existing record */
> if (pac->data)
> - ltv_merge(pac->data, data);
> + bt_bap_ltv_merge(pac->data, data, true);
> else
> pac->data = util_iov_dup(data, 1);
>
> /* Merge metadata into existing record */
> if (pac->metadata)
> - ltv_merge(pac->metadata, metadata);
> + bt_bap_ltv_merge(pac->metadata, metadata, true);
> else
> pac->metadata = util_iov_dup(metadata, 1);
> }
> @@ -2614,6 +2608,20 @@ static void notify_session_pac_added(void *data, void *user_data)
> queue_foreach(bap->pac_cbs, notify_pac_added, user_data);
> }
>
> +struct bt_bap_pac *bt_bap_add_vendor_pac_from_pac(struct bt_bap_pac *pac)
> +{
> + struct bt_bap_pac *new_pac;
> +
> + new_pac = bt_bap_add_vendor_pac(pac->bdb->db, pac->name, pac->type,
> + pac->codec.id,
> + pac->codec.cid,
> + pac->codec.cid,
> + &pac->qos, pac->data,
> + pac->metadata);
> + bt_bap_pac_set_ops(new_pac, pac->ops, pac->user_data);
> + return new_pac;
> +}
> +
> struct bt_bap_pac *bt_bap_add_vendor_pac(struct gatt_db *db,
> const char *name, uint8_t type,
> uint8_t id, uint16_t cid, uint16_t vid,
> @@ -2671,7 +2679,7 @@ struct bt_bap_pac *bt_bap_add_pac(struct gatt_db *db, const char *name,
> struct iovec *metadata)
> {
> return bt_bap_add_vendor_pac(db, name, type, id, 0x0000, 0x0000, qos,
> - data, metadata);
> + data, metadata);
> }
>
> uint8_t bt_bap_pac_get_type(struct bt_bap_pac *pac)
> @@ -2701,6 +2709,14 @@ uint32_t bt_bap_pac_get_locations(struct bt_bap_pac *pac)
> }
> }
>
> +struct iovec *bt_bap_pac_get_data(struct bt_bap_pac *pac)
> +{
> + if (!pac)
> + return 0x00;
> +
> + return pac->data;
> +}
> +
> uint16_t bt_bap_pac_get_supported_context(struct bt_bap_pac *pac)
> {
> struct bt_pacs *pacs;
> @@ -2783,12 +2799,26 @@ static void notify_session_pac_removed(void *data, void *user_data)
> bool bt_bap_pac_set_ops(struct bt_bap_pac *pac, struct bt_bap_pac_ops *ops,
> void *user_data)
> {
> + const struct queue_entry *entry = NULL;
> + struct bt_bap_pac *pac_entry;
> +
> if (!pac)
> return false;
>
> pac->ops = ops;
> pac->user_data = user_data;
>
> + if (pac->type == BT_BAP_BCAST_SOURCE) {
> + for (entry = queue_get_entries(pac->bdb->broadcast_sources);
> + entry != NULL; entry = entry->next) {
> + pac_entry = entry->data;
> + if (pac_entry != pac) {
> + pac_entry->ops = ops;
> + pac_entry->user_data = user_data;
> + }
> + }
> + }
> +
> return true;
> }
>
> @@ -4471,6 +4501,11 @@ bool bt_bap_pac_bcast_is_local(struct bt_bap *bap, struct bt_bap_pac *pac)
> return false;
> }
>
> +char *bt_bap_pac_get_name(struct bt_bap_pac *pac)
> +{
> + return pac->name;
> +}
> +
> static bool find_ep_unused(const void *data, const void *user_data)
> {
> const struct bt_bap_endpoint *ep = data;
> @@ -4591,7 +4626,6 @@ unsigned int bt_bap_stream_config(struct bt_bap_stream *stream,
> 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;
> }
> @@ -4682,7 +4716,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;
> @@ -4699,7 +4733,15 @@ struct bt_bap_stream *bt_bap_stream_new(struct bt_bap *bap,
> if (!ep) {
> /* Check for unused ASE */
> ep = queue_find(bap->remote_eps, find_ep_unused, &match);
> - if (!ep) {
> + if (!ep && lpac->type == BT_BAP_BCAST_SOURCE) {
> + bt_bap_new_bcast_sink(bap);
> + ep = queue_find(bap->remote_eps, find_ep_unused,
> + &match);
> + if (!ep) {
> + DBG(bap, "Unable to find unused ASE");
> + return NULL;
> + }
> + } else if (!ep) {
> DBG(bap, "Unable to find unused ASE");
> return NULL;
> }
> @@ -4712,6 +4754,15 @@ struct bt_bap_stream *bt_bap_stream_new(struct bt_bap *bap,
> return stream;
> }
>
> +void bt_bap_stream_foreach_link(struct bt_bap_stream *stream,
> + queue_foreach_func_t function, void *user_data)
> +{
> + if (bt_bap_stream_get_type(stream) ==
> + BT_BAP_STREAM_TYPE_UCAST)
> + queue_foreach(bt_bap_stream_io_get_links(stream),
> + function, user_data);
> +}
> +
> struct bt_bap *bt_bap_stream_get_session(struct bt_bap_stream *stream)
> {
> if (!stream)
> @@ -4839,7 +4890,16 @@ static void bap_stream_enable_link(void *data, void *user_data)
> struct bt_bap_stream *stream = data;
> struct iovec *metadata = user_data;
>
> - bap_stream_metadata(stream, BT_ASCS_ENABLE, metadata, NULL, NULL);
> + switch (bt_bap_stream_get_type(stream)) {
> + case BT_BAP_STREAM_TYPE_UCAST:
> + bap_stream_metadata(stream, BT_ASCS_ENABLE, metadata,
> + NULL, NULL);
> + break;
> + case BT_BAP_STREAM_TYPE_BCAST:
> + stream_set_state_broadcast(stream,
> + BT_BAP_STREAM_STATE_CONFIG);
> + break;
> + }
> }
>
> unsigned int bt_bap_stream_enable(struct bt_bap_stream *stream,
> @@ -4868,11 +4928,10 @@ unsigned int bt_bap_stream_enable(struct bt_bap_stream *stream,
> case BT_BAP_STREAM_TYPE_BCAST:
> if (!bt_bap_stream_io_dir(stream))
> stream_set_state_broadcast(stream,
> - BT_BAP_STREAM_STATE_CONFIG);
> + BT_BAP_STREAM_STATE_CONFIG);
> else if (bt_bap_stream_io_dir(stream) == BT_BAP_BCAST_SOURCE)
> stream_set_state_broadcast(stream,
> BT_BAP_STREAM_STATE_STREAMING);
> -
> return 1;
> }
>
> @@ -4933,17 +4992,27 @@ static void bap_stream_disable_link(void *data, void *user_data)
> struct iovec iov;
> struct bt_ascs_disable disable;
>
> - memset(&disable, 0, sizeof(disable));
> + switch (bt_bap_stream_get_type(stream)) {
> + case BT_BAP_STREAM_TYPE_UCAST:
> + memset(&disable, 0, sizeof(disable));
> +
> + disable.ase = stream->ep->id;
>
> - disable.ase = stream->ep->id;
> + iov.iov_base = &disable;
> + iov.iov_len = sizeof(disable);
>
> - iov.iov_base = &disable;
> - iov.iov_len = sizeof(disable);
> + req = bap_req_new(stream, BT_ASCS_DISABLE, &iov,
> + 1, NULL, NULL);
>
> - req = bap_req_new(stream, BT_ASCS_DISABLE, &iov, 1, NULL, NULL);
> + if (!bap_queue_req(stream->bap, req))
> + bap_req_free(req);
> + break;
>
> - if (!bap_queue_req(stream->bap, req))
> - bap_req_free(req);
> + case BT_BAP_STREAM_TYPE_BCAST:
> + stream_set_state_broadcast(stream,
> + BT_BAP_STREAM_STATE_RELEASING);
> + break;
> + }
> }
>
> unsigned int bt_bap_stream_disable(struct bt_bap_stream *stream,
> @@ -5189,7 +5258,8 @@ bool bt_bap_stream_set_io(struct bt_bap_stream *stream, int fd)
>
> bap_stream_set_io(stream, INT_TO_PTR(fd));
>
> - queue_foreach(stream->links, bap_stream_set_io, INT_TO_PTR(fd));
> + if (bt_bap_stream_get_type(stream) == BT_BAP_STREAM_TYPE_UCAST)
> + queue_foreach(stream->links, bap_stream_set_io, INT_TO_PTR(fd));
>
> return true;
> }
> @@ -5210,6 +5280,23 @@ static bool match_name(const void *data, const void *match_data)
> return (!strcmp(pac->name, name));
> }
>
> +void *bt_bap_ltv_merge(struct iovec *data, struct iovec *cont,
> + bool add_delimiter)
> +{
> + uint8_t delimiter = 0;
> +
> + if (!data)
> + return NULL;
> +
> + if (!cont || !cont->iov_len || !cont->iov_base)
> + return data->iov_base;
> +
> + if (add_delimiter)
> + iov_append(data, sizeof(delimiter), &delimiter);
> +
> + return iov_append(data, cont->iov_len, cont->iov_base);
> +}
> +
> int bt_bap_stream_cancel(struct bt_bap_stream *stream, unsigned int id)
> {
> struct bt_bap_req *req;
> @@ -5245,10 +5332,15 @@ int bt_bap_stream_io_link(struct bt_bap_stream *stream,
> if (queue_find(stream->links, NULL, link))
> return -EALREADY;
>
> - if (stream->client != link->client ||
> + if (bt_bap_stream_get_type(stream) == BT_BAP_STREAM_TYPE_BCAST) {
> + if (stream->qos.bcast.big != link->qos.bcast.big)
> + return -EINVAL;
> + } else {
> + if (stream->client != link->client ||
> stream->qos.ucast.cig_id != link->qos.ucast.cig_id ||
> stream->qos.ucast.cis_id != link->qos.ucast.cis_id)
> - return -EINVAL;
> + return -EINVAL;
> + }
>
> if (!stream->links)
> stream->links = queue_new();
> @@ -5377,7 +5469,9 @@ int bt_bap_stream_io_connecting(struct bt_bap_stream *stream, int fd)
>
> bap_stream_io_connecting(stream, INT_TO_PTR(fd));
>
> - queue_foreach(stream->links, bap_stream_io_connecting, INT_TO_PTR(fd));
> + if (bt_bap_stream_get_type(stream) == BT_BAP_STREAM_TYPE_UCAST)
> + queue_foreach(stream->links, bap_stream_io_connecting,
> + INT_TO_PTR(fd));
>
> return 0;
> }
> @@ -5433,11 +5527,58 @@ bool bt_bap_new_bcast_source(struct bt_bap *bap, const char *name)
> return true;
> }
>
> +static bool bt_bap_new_bcast_sink(struct bt_bap *bap)
> +{
> + struct bt_bap_endpoint *ep;
> +
> + /* Push remote endpoint with direction source */
> + ep = bap_endpoint_new_broadcast(bap->ldb, BT_BAP_BCAST_SOURCE);
> +
> + if (ep)
> + queue_push_tail(bap->remote_eps, ep);
> +
> + ep->bap = bap;
> + return true;
> +}
> +
> +static void bis_to_pac(void *data, void *user_data)
> +{
> + struct bt_bap_bis *bis = data;
> + struct bt_bap_base_subgroup *subgroup = user_data;
> + struct bt_bap_pac *pac_source_bis;
> + struct bt_bap_endpoint *ep;
> + int err = 0;
> +
> + bt_bap_ltv_merge(bis->caps, subgroup->caps, false);
> + pac_source_bis = bap_pac_new(subgroup->bap->rdb, NULL,
> + BT_BAP_BCAST_SOURCE, &subgroup->codec, NULL,
> + bis->caps, subgroup->meta);
> +
> + err = asprintf(&pac_source_bis->name, "%d", bis->bisIndex);
> +
> + if (err < 0)
> + DBG(subgroup->bap, "error in asprintf");
> +
> + /* 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);
> +}
> +static void parse_bis(void *data, void *user_data)
> +{
> + struct bt_bap_base_subgroup *subgroup = data;
> +
> + queue_foreach(subgroup->bises, bis_to_pac, subgroup);
> +}
> void bt_bap_update_bcast_source(struct bt_bap_pac *pac,
> - struct bt_bap_codec *codec,
> - struct iovec *data,
> - struct iovec *metadata)
> + struct bt_bap_base *base)
> {
> - bap_pac_merge(pac, data, metadata);
> - pac->codec = *codec;
> + queue_foreach(base->subgroups, parse_bis, NULL);
> }
> diff --git a/src/shared/bap.h b/src/shared/bap.h
> index 2c8f9208e..411655801 100644
> --- a/src/shared/bap.h
> +++ b/src/shared/bap.h
> @@ -97,6 +97,27 @@ struct bt_bap_qos {
> };
> };
>
> +struct bt_bap_base {
> + uint32_t presDelay;
> + uint8_t numSubgroups;
> + struct queue *subgroups;
> +};
> +
> +struct bt_bap_base_subgroup {
> + uint8_t subGroupIndex;
> + struct bt_bap *bap;
> + uint8_t numBises;
> + struct bt_bap_codec codec;
> + struct iovec *caps;
> + struct iovec *meta;
> + struct queue *bises;
> +};
> +
> +struct bt_bap_bis {
> + uint8_t bisIndex;
> + 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);
> @@ -168,6 +189,8 @@ uint8_t bt_bap_pac_get_type(struct bt_bap_pac *pac);
>
> uint32_t bt_bap_pac_get_locations(struct bt_bap_pac *pac);
>
> +struct iovec *bt_bap_pac_get_data(struct bt_bap_pac *pac);
> +
> uint16_t bt_bap_pac_get_supported_context(struct bt_bap_pac *pac);
>
> uint16_t bt_bap_pac_get_context(struct bt_bap_pac *pac);
> @@ -176,6 +199,8 @@ struct bt_bap_pac_qos *bt_bap_pac_get_qos(struct bt_bap_pac *pac);
>
> uint8_t bt_bap_stream_get_type(struct bt_bap_stream *stream);
>
> +struct bt_bap_pac *bt_bap_add_vendor_pac_from_pac(struct bt_bap_pac *pac);
> +
> struct bt_bap_stream *bt_bap_pac_get_stream(struct bt_bap_pac *pac);
>
> /* Session related function */
> @@ -231,6 +256,7 @@ int bt_bap_pac_get_codec(struct bt_bap_pac *pac, uint8_t *id,
>
> void bt_bap_pac_set_user_data(struct bt_bap_pac *pac, void *user_data);
> void *bt_bap_pac_get_user_data(struct bt_bap_pac *pac);
> +char *bt_bap_pac_get_name(struct bt_bap_pac *pac);
>
> /* Stream related functions */
> int bt_bap_select(struct bt_bap_pac *lpac, struct bt_bap_pac *rpac,
> @@ -242,6 +268,9 @@ struct bt_bap_stream *bt_bap_stream_new(struct bt_bap *bap,
> struct bt_bap_qos *pqos,
> struct iovec *data);
>
> +void bt_bap_stream_foreach_link(struct bt_bap_stream *stream,
> + queue_foreach_func_t function, void *user_data);
> +
> struct bt_bap *bt_bap_stream_get_session(struct bt_bap_stream *stream);
> uint8_t bt_bap_stream_get_state(struct bt_bap_stream *stream);
>
> @@ -314,9 +343,9 @@ bool bt_bap_stream_io_is_connecting(struct bt_bap_stream *stream, int *fd);
>
> bool bt_bap_new_bcast_source(struct bt_bap *bap, const char *name);
> void bt_bap_update_bcast_source(struct bt_bap_pac *pac,
> - struct bt_bap_codec *codec,
> - struct iovec *data,
> - struct iovec *metadata);
> + struct bt_bap_base *base);
> +void *bt_bap_ltv_merge(struct iovec *data, struct iovec *cont,
> + bool add_delimiter);
>
> bool bt_bap_pac_bcast_is_local(struct bt_bap *bap, struct bt_bap_pac *pac);
>
> diff --git a/unit/test-bap.c b/unit/test-bap.c
> index adade07db..d308d42f9 100644
> --- a/unit/test-bap.c
> +++ b/unit/test-bap.c
> @@ -377,11 +377,13 @@ static void test_client_config(struct test_data *data)
> "test-bap-snk",
> BT_BAP_SINK, 0x0ff,
> 0x0001, 0x0001,
> - NULL, data->caps, NULL);
> + NULL, data->caps,
> + NULL);
> else
> data->snk = bt_bap_add_pac(data->db, "test-bap-snk",
> BT_BAP_SINK, LC3_ID,
> - NULL, data->caps, NULL);
> + NULL, data->caps,
> + NULL);
> g_assert(data->snk);
> }
>
> @@ -391,11 +393,13 @@ static void test_client_config(struct test_data *data)
> "test-bap-src",
> BT_BAP_SOURCE, 0x0ff,
> 0x0001, 0x0001,
> - NULL, data->caps, NULL);
> + NULL, data->caps,
> + NULL);
> else
> data->src = bt_bap_add_pac(data->db, "test-bap-src",
> BT_BAP_SOURCE, LC3_ID,
> - NULL, data->caps, NULL);
> + NULL, data->caps,
> + NULL);
> g_assert(data->src);
> }
> }
> --
> 2.39.2
>
>


--
Luiz Augusto von Dentz

2023-11-28 17:40:38

by patchwork-bot+bluetooth

[permalink] [raw]
Subject: Re: [PATCH BlueZ v6 0/5] Add support for bcast multiple BISes

Hello:

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

On Thu, 23 Nov 2023 23:59:30 +0200 you wrote:
> Add support for multiple BISes
> Register the broadcast source endpoint
> endpoint.register 00001852-0000-1000-8000-00805f9b34fb 0x06
> After registration a new remote endpoint is created
> Use the endpoint.config command to set the BIS codec configuration by
> specifying the BIS when prompted by the command
> endpoint.config /org/bluez/hci0/pac_bcast0 /local/endpoint/ep0 16_2_1
> [/local/endpoint/ep0] BIG (value): 0
> [/local/endpoint/ep0] BIS (value): 1
> Based on the number of BISes seted in the base_lc3_16_2_1 more
> remote endoints wil be created (If more then one bis is setted)
> Use the endpoint.config command to set the BIS codec configuration by
> specifying the BIS when prompted by the command
> endpoint.config /org/bluez/hci0/pac_bcast0 /local/endpoint/ep0 16_2_1
> [/local/endpoint/ep0] BIG (value): 0
> [/local/endpoint/ep0] BIS (value): 2
> Use the endpoint config command to configure all the BISes
> After all BISes are configured, use the transports to send data
>
> [...]

Here is the summary with links:
- [BlueZ,v6,1/5] shared/bap: Fix source+sink endpoint registration
https://git.kernel.org/pub/scm/bluetooth/bluez.git/?id=b93690e72d9e
- [BlueZ,v6,2/5] audio/bap: Fix source+sink endpoint registration
https://git.kernel.org/pub/scm/bluetooth/bluez.git/?id=bd5342fdf46a
- [BlueZ,v6,3/5] Add support for multiple BISes
(no matching commit)
- [BlueZ,v6,4/5] audio/transport.c: Add support for multiple BISes
(no matching commit)
- [BlueZ,v6,5/5] client/player.c: Add support for multiple BISes
(no matching commit)

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