Subject: [PATCH 0/6] Add support for multiple BISes

Add support for multiple BIS-es
How to configure multiple BIS-es:
In main.conf set the number of required BISes in NumberOfBISes field of
the newly added Bcast section.
In the base_lc3_48_4_1 struct set the configuration for the base
Continue with the registering endpoint for the broadcast source
endpoint.register 00001852-0000-1000-8000-00805f9b34fb 0x06
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 48_4_1
[/local/endpoint/ep0] BIG (value): 0
[/local/endpoint/ep0] BIS (value): 1
Use this endpoint config command to configure all the BISes
After all BISes are configured, use the transports to send data


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

Silviu Florian Barbulescu (4):
src: Add support for defining the number of BISes in the main.conf
Add support for multiple BISes
profiles/audio/transport.c: Add support for multiple BISes
client/player.c: Add support for multiple BISes

btio/btio.c | 4 +-
client/player.c | 57 ++++-
profiles/audio/bap.c | 483 +++++++++++++++++++++++++------------
profiles/audio/media.c | 3 +-
profiles/audio/transport.c | 49 ++--
src/btd.h | 6 +
src/main.c | 10 +
src/main.conf | 8 +
src/shared/bap.c | 254 ++++++++++++++-----
src/shared/bap.h | 39 ++-
unit/test-bap.c | 12 +-
11 files changed, 678 insertions(+), 247 deletions(-)


base-commit: 00fdb61d56161f523e975b4c044030f4b40abb6d
--
2.39.2


Subject: [PATCH 5/6] profiles/audio/transport.c: Add support for multiple BISes

Add support for multiple BISes in broadcast source and sink

---
profiles/audio/transport.c | 49 ++++++++++++++++++++++++++------------
1 file changed, 34 insertions(+), 15 deletions(-)

diff --git a/profiles/audio/transport.c b/profiles/audio/transport.c
index 23ea267f6..dff73e0f7 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,9 +314,11 @@ 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),
- linked_transport_remove_owner, owner);
+ if (bt_bap_stream_get_type(bap->stream) ==
+ BT_BAP_STREAM_TYPE_UCAST)
+ if (bap->linked)
+ queue_foreach(bt_bap_stream_io_get_links(bap->stream),
+ linked_transport_remove_owner, owner);

if (owner->watch)
g_dbus_remove_watch(btd_get_dbus_connection(), owner->watch);
@@ -496,9 +500,11 @@ static void media_transport_set_owner(struct media_transport *transport,
DBG("Transport %s Owner %s", transport->path, owner->name);
transport->owner = owner;

- if (bap->linked)
- queue_foreach(bt_bap_stream_io_get_links(bap->stream),
- linked_transport_set_owner, owner);
+ if (bt_bap_stream_get_type(bap->stream) ==
+ BT_BAP_STREAM_TYPE_UCAST)
+ if (bap->linked)
+ queue_foreach(bt_bap_stream_io_get_links(bap->stream),
+ linked_transport_set_owner, owner);

owner->transport = transport;
owner->watch = g_dbus_add_disconnect_watch(btd_get_dbus_connection(),
@@ -686,7 +692,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);

@@ -1272,7 +1282,9 @@ static bool match_link_transport(const void *data, const void *user_data)
if (!transport)
return false;

- bap_update_links(transport);
+ if (bt_bap_stream_get_type((struct bt_bap_stream *)stream) ==
+ BT_BAP_STREAM_TYPE_UCAST)
+ bap_update_links(transport);

return true;
}
@@ -1378,7 +1390,9 @@ static guint resume_bap(struct media_transport *transport,
if (bap->resume_id)
return 0;

- bap_update_links(transport);
+ if (bt_bap_stream_get_type(bap->stream) ==
+ BT_BAP_STREAM_TYPE_UCAST)
+ bap_update_links(transport);

switch (bt_bap_stream_get_state(bap->stream)) {
case BT_BAP_STREAM_STATE_ENABLING:
@@ -1416,7 +1430,9 @@ static guint suspend_bap(struct media_transport *transport,
else
transport_set_state(transport, TRANSPORT_STATE_IDLE);

- bap_update_links(transport);
+ if (bt_bap_stream_get_type(bap->stream) ==
+ BT_BAP_STREAM_TYPE_UCAST)
+ bap_update_links(transport);

return bt_bap_stream_disable(bap->stream, bap->linked, func, owner);
}
@@ -1491,9 +1507,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 +1527,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;
}
@@ -1555,7 +1572,9 @@ static void bap_connecting(struct bt_bap_stream *stream, bool state, int fd,
if (bap->stream != stream)
return;

- bap_update_links(transport);
+ if (bt_bap_stream_get_type(bap->stream) ==
+ BT_BAP_STREAM_TYPE_UCAST)
+ bap_update_links(transport);
}

static void free_bap(void *data)
--
2.39.2

Subject: [PATCH 4/6] Add support for multiple BISes

Add support for multiple BISes in broadcast source and sink

---
btio/btio.c | 4 +-
profiles/audio/bap.c | 458 ++++++++++++++++++++++++++++-------------
profiles/audio/media.c | 3 +-
src/shared/bap.c | 191 ++++++++++++++---
src/shared/bap.h | 36 +++-
unit/test-bap.c | 12 +-
6 files changed, 521 insertions(+), 183 deletions(-)

diff --git a/btio/btio.c b/btio/btio.c
index d30cfcac7..ec6b6c4ab 100644
--- a/btio/btio.c
+++ b/btio/btio.c
@@ -247,7 +247,8 @@ static gboolean server_cb(GIOChannel *io, GIOCondition cond,
GIOChannel *cli_io;

/* If the user closed the server */
- if ((cond & G_IO_NVAL) || check_nval(io))
+ if ((cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) ||
+ check_nval(io))
return FALSE;

srv_sock = g_io_channel_unix_get_fd(io);
@@ -1654,6 +1655,7 @@ static gboolean iso_get(int sock, GError **err, BtIOOption opt1, va_list args)
return FALSE;
}

+ len = BASE_MAX_LENGTH;
if (getsockopt(sock, SOL_BLUETOOTH, BT_ISO_BASE,
&base.base, &len) < 0) {
ERROR_FAILED(err, "getsockopt(BT_ISO_BASE)", errno);
diff --git a/profiles/audio/bap.c b/profiles/audio/bap.c
index a84181e1a..8dabd06b6 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,100 +412,150 @@ 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 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_subgroup *subGroup = new0(
+ struct bt_bap_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)
+ util_debug(func, NULL, "Subgroup #%d", sg);
+ subGroup->bap = bap;
+ subGroup->bises = queue_new();
+
+ if (!util_iov_pull_u8(&iov, &numBis))
return false;
+ 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))
+ return false;
+ util_debug(func, NULL, "CC Len %d", capsLen);
+
+ subGroup->caps = new0(struct iovec, 1);
+ subGroup->caps->iov_len = capsLen;
+ 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;
+
+ if (!ltv) {
+ util_debug(func, NULL, "Unable to parse %s",
+ "Capabilities");
+ return false;
+ }

- if (!util_iov_pull_u8(&iov, &capsLen))
- return false;
- util_debug(func, NULL, "CC Len %d", capsLen);
+ util_debug(func, NULL, "%s #%u: len %u type %u",
+ "CC", i, ltv->len, ltv->type);

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

- for (int i = 0; capsLen > 1; i++) {
- struct bt_ltv *ltv = util_iov_pull_mem(&iov, sizeof(*ltv));
- uint8_t *caps;
+ capsLen -= (ltv->len + 1);
+ }

- if (!ltv) {
- util_debug(func, NULL, "Unable to parse %s",
- "Capabilities");
+ if (!util_iov_pull_u8(&iov, &metaLen))
return false;
- }
+ util_debug(func, NULL, "Metadata Len %d", metaLen);

- util_debug(func, NULL, "%s #%u: len %u type %u",
- "CC", i, ltv->len, ltv->type);
+ subGroup->meta = new0(struct iovec, 1);
+ subGroup->meta->iov_len = metaLen;
+ subGroup->meta->iov_base = iov.iov_base;

- caps = util_iov_pull_mem(&iov, ltv->len - 1);
- if (!caps) {
- util_debug(func, NULL, "Unable to parse %s",
- "CC");
+ hexstream = util_iov_pull_mem(&iov, metaLen);
+ if (!hexstream)
return false;
- }
- 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))
+ return false;
+ 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))
+ return false;
+ 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");
+ return false;
+ }
+
+ 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");
+ return false;
+ }
+ 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;
}

@@ -556,6 +639,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,7 +746,8 @@ 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;
@@ -702,14 +796,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 +886,23 @@ 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 DBusMessage *set_configuration(DBusConnection *conn, DBusMessage *msg,
void *data)
{
struct bap_ep *ep = data;
const char *path;
DBusMessageIter args, props;
+ struct bt_bap_base *base_s = new0(struct bt_bap_base, 1);
+ struct bt_bap_bis *curent_bis;
+ struct bt_bap_subgroup *subgroup;

if (ep->msg)
return btd_error_busy(msg);
@@ -832,11 +933,36 @@ 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, base_s) < 0) {
DBG("Unable to parse configuration");
return btd_error_invalid_args(msg);
}

+ if (bt_bap_pac_get_type(ep->lpac) == BT_BAP_BCAST_SOURCE) {
+ subgroup = queue_peek_head(base_s->subgroups);
+ curent_bis = queue_find(subgroup->bises, match_bis_by_id,
+ INT_TO_PTR(ep->qos.bcast.bis));
+ 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);
+ ltv_merge(ep->caps, subgroup->caps, false);
+ ltv_merge(ep->caps, curent_bis->caps, false);
+ } 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);
+ 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.
*/
@@ -916,11 +1042,6 @@ static void iso_bcast_confirm_cb(GIOChannel *io, GError *err, void *user_data)
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,
@@ -945,15 +1066,6 @@ static void iso_bcast_confirm_cb(GIOChannel *io, GError *err, void *user_data)

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;
@@ -968,25 +1080,42 @@ 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);
+
+ DBG("BCAST ISO: PA sync with %s", address);
+ DBG("BASE len received from source %d", base.base_len);
+ base_s = new0(struct bt_bap_base, 1);
+ 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 +1211,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;
}
@@ -1582,9 +1712,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;
}
@@ -1675,7 +1808,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;
@@ -1711,7 +1844,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) {
@@ -1763,42 +1896,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,
+ data, 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)
{
@@ -1833,9 +1952,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;
@@ -1852,9 +1968,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);
}
@@ -1879,6 +1995,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)
@@ -1906,15 +2047,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);
@@ -2222,6 +2374,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);

@@ -2260,7 +2418,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;
}

@@ -2406,6 +2581,9 @@ static int bap_adapter_probe(struct btd_profile *p,
return -EINVAL;
}

+ for (uint8_t i = 0; i < btd_opts.bcast.nb_bises-1; i++)
+ bt_bap_new_bcast_sink(data->bap);
+
data->state_id = bt_bap_state_register(data->bap, bap_state,
bap_connecting, data, NULL);
data->pac_id = bt_bap_pac_register(data->bap, pac_added_broadcast,
diff --git a/profiles/audio/media.c b/profiles/audio/media.c
index 4d9a6aa03..30658ebaf 100644
--- a/profiles/audio/media.c
+++ b/profiles/audio/media.c
@@ -37,6 +37,7 @@
#include "src/uuid-helper.h"
#include "src/log.h"
#include "src/error.h"
+#include "src/btd.h"
#include "src/gatt-database.h"
#include "src/shared/util.h"
#include "src/shared/queue.h"
@@ -1250,7 +1251,7 @@ static bool endpoint_init_pac(struct media_endpoint *endpoint, uint8_t type,

endpoint->pac = bt_bap_add_vendor_pac(db, name, type, endpoint->codec,
endpoint->cid, endpoint->vid, &endpoint->qos,
- &data, metadata);
+ &data, metadata, btd_opts.bcast.nb_bises);
if (!endpoint->pac) {
error("Unable to create PAC");
free(metadata);
diff --git a/src/shared/bap.c b/src/shared/bap.c
index 54ad713f2..45fd6df59 100644
--- a/src/shared/bap.c
+++ b/src/shared/bap.c
@@ -1111,7 +1111,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 +1324,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,7 +2364,7 @@ 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)
+void *ltv_merge(struct iovec *data, struct iovec *cont, bool add_delimiter)
{
uint8_t delimiter = 0;

@@ -2367,7 +2374,8 @@ static void *ltv_merge(struct iovec *data, struct iovec *cont)
if (!cont || !cont->iov_len || !cont->iov_base)
return data->iov_base;

- iov_append(data, sizeof(delimiter), &delimiter);
+ if (add_delimiter)
+ iov_append(data, sizeof(delimiter), &delimiter);

return iov_append(data, cont->iov_len, cont->iov_base);
}
@@ -2377,13 +2385,13 @@ static void bap_pac_merge(struct bt_bap_pac *pac, struct iovec *data,
{
/* Merge data into existing record */
if (pac->data)
- ltv_merge(pac->data, data);
+ 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);
+ ltv_merge(pac->metadata, metadata, true);
else
pac->metadata = util_iov_dup(metadata, 1);
}
@@ -2619,7 +2627,8 @@ struct bt_bap_pac *bt_bap_add_vendor_pac(struct gatt_db *db,
uint8_t id, uint16_t cid, uint16_t vid,
struct bt_bap_pac_qos *qos,
struct iovec *data,
- struct iovec *metadata)
+ struct iovec *metadata,
+ uint8_t nb_bises)
{
struct bt_bap_db *bdb;
struct bt_bap_pac *pac;
@@ -2650,7 +2659,17 @@ struct bt_bap_pac *bt_bap_add_vendor_pac(struct gatt_db *db,
break;
case BT_BAP_BCAST_SOURCE:
bap_add_broadcast_source(pac);
- break;
+ queue_foreach(sessions, notify_session_pac_added, pac);
+ /*
+ * Add a new broadcast source pac for each for each new BIS
+ */
+ for (uint8_t i = 0; i < nb_bises-1; i++) {
+ pac = bap_pac_new(bdb, name, type, &codec, qos,
+ data, metadata);
+ bap_add_broadcast_source(pac);
+ queue_foreach(sessions, notify_session_pac_added, pac);
+ }
+ return pac;
case BT_BAP_BCAST_SINK:
bap_add_broadcast_sink(pac);
break;
@@ -2668,10 +2687,11 @@ struct bt_bap_pac *bt_bap_add_pac(struct gatt_db *db, const char *name,
uint8_t type, uint8_t id,
struct bt_bap_pac_qos *qos,
struct iovec *data,
- struct iovec *metadata)
+ struct iovec *metadata,
+ uint8_t nb_bises)
{
return bt_bap_add_vendor_pac(db, name, type, id, 0x0000, 0x0000, qos,
- data, metadata);
+ data, metadata, nb_bises);
}

uint8_t bt_bap_pac_get_type(struct bt_bap_pac *pac)
@@ -2701,6 +2721,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 +2811,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;
}

@@ -2807,8 +2849,13 @@ static void remove_streams(void *data, void *user_data)
struct bt_bap_stream *stream;

stream = queue_remove_if(bap->streams, match_stream_lpac, pac);
- if (stream)
+ if (stream) {
+ /* Mark stream->lpac to NULL,
+ * as it will be freed in bt_bap_remove_pac
+ */
+ stream->lpac = NULL;
bt_bap_stream_release(stream, NULL, NULL);
+ }
}

static void bap_pac_sink_removed(void *data, void *user_data)
@@ -4471,6 +4518,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;
@@ -4682,7 +4734,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 && (match.lpac->type != BT_BAP_BCAST_SOURCE))
return NULL;
@@ -4839,7 +4891,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,
@@ -4866,13 +4927,14 @@ 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))
+ if (!bt_bap_stream_io_dir(stream)) {
stream_set_state_broadcast(stream,
- BT_BAP_STREAM_STATE_CONFIG);
- else if (bt_bap_stream_io_dir(stream) == BT_BAP_BCAST_SOURCE)
+ BT_BAP_STREAM_STATE_CONFIG);
+ queue_foreach(stream->links, bap_stream_enable_link,
+ NULL);
+ } 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 +4995,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 +5261,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;
}
@@ -5245,10 +5318,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 +5455,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 +5513,58 @@ bool bt_bap_new_bcast_source(struct bt_bap *bap, const char *name)
return true;
}

+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_subgroup *subgroup = user_data;
+ struct bt_bap_pac *pac_source_bis;
+ struct bt_bap_endpoint *ep;
+ int err = 0;
+
+ 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_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..74f0effb0 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_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);
@@ -141,13 +162,15 @@ struct bt_bap_pac *bt_bap_add_vendor_pac(struct gatt_db *db,
uint8_t id, uint16_t cid, uint16_t vid,
struct bt_bap_pac_qos *qos,
struct iovec *data,
- struct iovec *metadata);
+ struct iovec *metadata,
+ uint8_t nb_bises);

struct bt_bap_pac *bt_bap_add_pac(struct gatt_db *db, const char *name,
uint8_t type, uint8_t id,
struct bt_bap_pac_qos *qos,
struct iovec *data,
- struct iovec *metadata);
+ struct iovec *metadata,
+ uint8_t nb_bises);

struct bt_bap_pac_ops {
int (*select)(struct bt_bap_pac *lpac, struct bt_bap_pac *rpac,
@@ -168,6 +191,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);
@@ -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,
@@ -312,11 +338,11 @@ uint8_t bt_bap_stream_io_dir(struct bt_bap_stream *stream);
int bt_bap_stream_io_connecting(struct bt_bap_stream *stream, int fd);
bool bt_bap_stream_io_is_connecting(struct bt_bap_stream *stream, int *fd);

+bool bt_bap_new_bcast_sink(struct bt_bap *bap);
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 *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..80838d813 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, 0x0);
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, 0x0);
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, 0x0);
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, 0x0);
g_assert(data->src);
}
}
--
2.39.2

Subject: [PATCH 1/6] 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..54ad713f2 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 && (match.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 6/6] client/player.c: Add support for multiple BISes

Add support for multiple BISes in broadcast source and sink

---
client/player.c | 57 +++++++++++++++++++++++++++++++++++++++++++------
1 file changed, 50 insertions(+), 7 deletions(-)

diff --git a/client/player.c b/client/player.c
index 715598aa9..bf0338dfb 100644
--- a/client/player.c
+++ b/client/player.c
@@ -118,16 +118,16 @@ struct transport {
struct io *timer_io;
};

-static const uint8_t base_lc3_16_2_1[] = {
+static const uint8_t base_lc3_48_4_1[] = {
0x28, 0x00, 0x00, /* Presentation Delay */
0x01, /* Number of Subgroups */
0x01, /* Number of BIS */
0x06, 0x00, 0x00, 0x00, 0x00, /* Code ID = LC3 (0x06) */
0x10, /* Codec Specific Configuration */
- 0x02, 0x01, 0x03, /* 16 KHZ */
+ 0x02, 0x01, 0x08, /* 48 KHZ */
0x02, 0x02, 0x01, /* 10 ms */
- 0x05, 0x03, 0x01, 0x00, 0x00, 0x00, /* Front Left */
- 0x03, 0x04, 0x28, 0x00, /* Frame Length 40 bytes */
+ 0x05, 0x03, 0x03, 0x00, 0x00, 0x00, /* Front Left, Front Right */
+ 0x03, 0x04, 0x78, 0x00, /* Frame Length 120 bytes */
0x04, /* Metadata */
0x03, 0x02, 0x02, 0x00, /* Audio Context: Convertional */
0x01, /* BIS */
@@ -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");
@@ -3057,8 +3096,8 @@ static void cmd_config_endpoint(int argc, char *argv[])
iov_append(&cfg->ep->bcode, bcast_qos.bcast.bcode,
sizeof(bcast_qos.bcast.bcode));
/* Copy capabilities for broadcast*/
- iov_append(&cfg->caps, base_lc3_16_2_1,
- sizeof(base_lc3_16_2_1));
+ iov_append(&cfg->caps, base_lc3_48_4_1,
+ sizeof(base_lc3_48_4_1));
} else {
/* Copy capabilities */
iov_append(&cfg->caps, preset->data.iov_base,
@@ -3068,7 +3107,11 @@ static void cmd_config_endpoint(int argc, char *argv[])
/* Set QoS parameters */
cfg->qos = &preset->qos;

- endpoint_set_config(cfg);
+ if (cfg->ep->broadcast)
+ bt_shell_prompt_input(cfg->ep->path, "BIG (value):",
+ config_endpoint_iso_group, cfg);
+ else
+ endpoint_set_config(cfg);
return;
}

--
2.39.2

Subject: [PATCH 3/6] src: Add support for the number of BISes in the main.conf

Add support for the number of BISes in the main.conf

---
src/btd.h | 6 ++++++
src/main.c | 10 ++++++++++
src/main.conf | 8 ++++++++
3 files changed, 24 insertions(+)

diff --git a/src/btd.h b/src/btd.h
index b7e7ebd61..2ad494008 100644
--- a/src/btd.h
+++ b/src/btd.h
@@ -108,6 +108,10 @@ struct btd_advmon_opts {
uint8_t rssi_sampling_period;
};

+struct btd_bcast_opts {
+ uint8_t nb_bises;
+};
+
struct btd_opts {
char *name;
uint32_t class;
@@ -151,6 +155,8 @@ struct btd_opts {
struct btd_advmon_opts advmon;

struct btd_csis csis;
+
+ struct btd_bcast_opts bcast;
};

extern struct btd_opts btd_opts;
diff --git a/src/main.c b/src/main.c
index b1339c230..364f899c8 100644
--- a/src/main.c
+++ b/src/main.c
@@ -1137,6 +1137,13 @@ static void parse_advmon(GKeyFile *config)
0, UINT8_MAX);
}

+static void parse_bcast(GKeyFile *config)
+{
+ parse_config_u8(config, "Bcast", "NumberOfBISes",
+ &btd_opts.bcast.nb_bises,
+ 0, UINT8_MAX);
+}
+
static void parse_config(GKeyFile *config)
{
if (!config)
@@ -1154,6 +1161,7 @@ static void parse_config(GKeyFile *config)
parse_csis(config);
parse_avdtp(config);
parse_advmon(config);
+ parse_bcast(config);
}

static void init_defaults(void)
@@ -1195,6 +1203,8 @@ static void init_defaults(void)
btd_opts.avdtp.stream_mode = BT_IO_MODE_BASIC;

btd_opts.advmon.rssi_sampling_period = 0xFF;
+
+ btd_opts.bcast.nb_bises = 0x01;
btd_opts.csis.encrypt = true;
}

diff --git a/src/main.conf b/src/main.conf
index 085c81a46..119d27a7b 100644
--- a/src/main.conf
+++ b/src/main.conf
@@ -337,3 +337,11 @@
# 0xFF Report only one advertisement per device during monitoring period
# Default: 0xFF
#RSSISamplingPeriod=0xFF
+
+[Bcast]
+# The number of BISes. This is used, when an application registers a
+# broadcast source endpoint, as the number of BISes in a BIG.
+# Possible values:
+# N = 0xXX Number of BISes (range: 0x01 to nb of maximum Controller supported BISes)
+# Default: 0x01
+#NumberOfBISes=0x01
--
2.39.2

2023-10-26 17:46:18

by bluez.test.bot

[permalink] [raw]
Subject: RE: Add support for 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=796846

---Test result---

Test Summary:
CheckPatch PASS 3.65 seconds
GitLint PASS 1.79 seconds
BuildEll PASS 33.84 seconds
BluezMake PASS 1085.99 seconds
MakeCheck PASS 13.29 seconds
MakeDistcheck PASS 215.11 seconds
CheckValgrind PASS 325.67 seconds
CheckSmatch PASS 449.76 seconds
bluezmakeextell PASS 143.73 seconds
IncrementalBuild PASS 5540.16 seconds
ScanBuild PASS 1243.12 seconds



---
Regards,
Linux Bluetooth

2023-10-26 21:32:24

by Luiz Augusto von Dentz

[permalink] [raw]
Subject: Re: [PATCH 0/6] Add support for multiple BISes

Hi Silviu,

On Thu, Oct 26, 2023 at 7:51 AM Silviu Florian Barbulescu
<[email protected]> wrote:
>
> Add support for multiple BIS-es
> How to configure multiple BIS-es:
> In main.conf set the number of required BISes in NumberOfBISes field of
> the newly added Bcast section.
> In the base_lc3_48_4_1 struct set the configuration for the base
> Continue with the registering endpoint for the broadcast source
> endpoint.register 00001852-0000-1000-8000-00805f9b34fb 0x06
> 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 48_4_1
> [/local/endpoint/ep0] BIG (value): 0
> [/local/endpoint/ep0] BIS (value): 1
> Use this endpoint config command to configure all the BISes
> After all BISes are configured, use the transports to send data

Don't think it is a good idea to use main.conf for entering this, in
fact I think we should get rid of BIS and instead have it as number of
BIS because we no longer use the BIS ID as Adv Set ID, or perhaps we
can use the max transport as number of BIS since each BIS shall have a
correspondent transport/socket, either way it shall be the upper layer
that defines how many BIS to configure.

>
> Claudia Draghicescu (2):
> bap: Fix source+sink endpoint registration
> bap: Fix source+sink endpoint registration
>
> Silviu Florian Barbulescu (4):
> src: Add support for defining the number of BISes in the main.conf
> Add support for multiple BISes
> profiles/audio/transport.c: Add support for multiple BISes
> client/player.c: Add support for multiple BISes
>
> btio/btio.c | 4 +-
> client/player.c | 57 ++++-
> profiles/audio/bap.c | 483 +++++++++++++++++++++++++------------
> profiles/audio/media.c | 3 +-
> profiles/audio/transport.c | 49 ++--
> src/btd.h | 6 +
> src/main.c | 10 +
> src/main.conf | 8 +
> src/shared/bap.c | 254 ++++++++++++++-----
> src/shared/bap.h | 39 ++-
> unit/test-bap.c | 12 +-
> 11 files changed, 678 insertions(+), 247 deletions(-)
>
>
> base-commit: 00fdb61d56161f523e975b4c044030f4b40abb6d
> --
> 2.39.2
>


--
Luiz Augusto von Dentz