2023-08-02 09:14:27

by Claudia Draghicescu

[permalink] [raw]
Subject: [PATCH BlueZ v3 5/6] bap: Add support for BAP broadcast sink

This adds support for BAP broadcast sink, creates a remote endpoint when a
broadcast source is discovered and synchronizes with the source upon
endpoint configuration.
This feature was tested using bluetoothctl with the following commands:

[bluetooth]# endpoint.register 00001851-0000-1000-8000-00805f9b34fb 0x06
[bluetooth]# scan on
[NEW] Endpoint /org/bluez/hci0/dev_XX_XX_XX_XX_XX_XX/pac_bcast0
[bluetooth]# endpoint.config
/org/bluez/hci0/dev_XX_XX_XX_XX_XX_XX/pac_bcast0
/local/endpoint/ep0 16_2_1
---
profiles/audio/bap.c | 300 ++++++++++++++++++++++++++++++++++++++-----
src/shared/bap.c | 153 +++++++++++++++++++---
src/shared/bap.h | 11 +-
3 files changed, 410 insertions(+), 54 deletions(-)

diff --git a/profiles/audio/bap.c b/profiles/audio/bap.c
index 8cbb238ef..112e0673d 100644
--- a/profiles/audio/bap.c
+++ b/profiles/audio/bap.c
@@ -34,6 +34,7 @@
#include "lib/hci.h"
#include "lib/sdp.h"
#include "lib/uuid.h"
+#include "lib/iso.h"

#include "src/btd.h"
#include "src/dbus-common.h"
@@ -58,6 +59,7 @@
#define ISO_SOCKET_UUID "6fbaf188-05e0-496a-9885-d6ddfdb4e03e"
#define PACS_UUID_STR "00001850-0000-1000-8000-00805f9b34fb"
#define MEDIA_ENDPOINT_INTERFACE "org.bluez.MediaEndpoint1"
+#define MEDIA_INTERFACE "org.bluez.Media1"

struct bap_ep {
char *path;
@@ -186,8 +188,11 @@ static gboolean get_uuid(const GDBusPropertyTable *property,
uuid = PAC_SINK_UUID;
else if (queue_find(ep->data->srcs, NULL, ep))
uuid = PAC_SOURCE_UUID;
- else
+ else if ((queue_find(ep->data->bcast, NULL, ep)
+ && (bt_bap_pac_get_type(ep->lpac) == BT_BAP_BCAST_SINK)))
uuid = BAA_SERVICE_UUID;
+ else
+ uuid = BCAA_SERVICE_UUID;

dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &uuid);

@@ -341,15 +346,18 @@ static int parse_properties(DBusMessageIter *props, struct iovec **caps,
} else if (!strcasecmp(key, "PHY")) {
const char *str;

- if (var != DBUS_TYPE_STRING)
- goto fail;
-
- dbus_message_iter_get_basic(&value, &str);
-
- if (!strcasecmp(str, "1M"))
- io_qos.phy = 0x01;
- else if (!strcasecmp(str, "2M"))
- io_qos.phy = 0x02;
+ if (var == DBUS_TYPE_STRING) {
+ dbus_message_iter_get_basic(&value, &str);
+
+ if (!strcasecmp(str, "1M"))
+ io_qos.phy = 0x01;
+ else if (!strcasecmp(str, "2M"))
+ io_qos.phy = 0x02;
+ else
+ goto fail;
+ } else if (var == DBUS_TYPE_BYTE)
+ dbus_message_iter_get_basic(&value,
+ &io_qos.phy);
else
goto fail;
} else if (!strcasecmp(key, "SDU")) {
@@ -556,7 +564,7 @@ static DBusMessage *set_configuration(DBusConnection *conn, DBusMessage *msg,
}

if (bt_bap_pac_get_type(ep->lpac) == BT_BAP_BCAST_SOURCE) {
- /* Mark CIG and CIS to be auto assigned */
+ /* Mark BIG and BIS to be auto assigned */
ep->qos.bcast.big = BT_ISO_QOS_BIG_UNSET;
ep->qos.bcast.bis = BT_ISO_QOS_BIS_UNSET;
} else {
@@ -577,8 +585,12 @@ static DBusMessage *set_configuration(DBusConnection *conn, DBusMessage *msg,
ep->stream = bt_bap_stream_new(ep->data->bap, ep->lpac,
ep->rpac, &ep->qos, ep->caps);

- ep->id = bt_bap_stream_config(ep->stream, &ep->qos, ep->caps,
- config_cb, ep);
+ if (bt_bap_pac_get_type(ep->lpac) == BT_BAP_BCAST_SINK)
+ ep->id = bt_bap_stream_config(ep->stream, &ep->qos, NULL,
+ config_cb, ep);
+ else
+ ep->id = bt_bap_stream_config(ep->stream, &ep->qos, ep->caps,
+ config_cb, ep);
if (!ep->id) {
DBG("Unable to config stream");
free(ep->caps);
@@ -597,13 +609,120 @@ static DBusMessage *set_configuration(DBusConnection *conn, DBusMessage *msg,
break;
case BT_BAP_STREAM_TYPE_BCAST:
/* No message sent over the air for broadcast */
- ep->id = 0;
+ if (bt_bap_pac_get_type(ep->lpac) == BT_BAP_BCAST_SINK)
+ ep->msg = dbus_message_ref(msg);
+ else
+ ep->id = 0;
+
return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
}

return NULL;
}

+static void update_bcast_qos(struct bt_iso_qos *qos,
+ struct bt_bap_qos *bap_qos)
+{
+ bap_qos->bcast.big = qos->bcast.big;
+ bap_qos->bcast.bis = qos->bcast.bis;
+ bap_qos->bcast.sync_interval = qos->bcast.sync_interval;
+ bap_qos->bcast.packing = qos->bcast.packing;
+ bap_qos->bcast.framing = qos->bcast.framing;
+ bap_qos->bcast.encryption = qos->bcast.encryption;
+ bap_qos->bcast.options = qos->bcast.options;
+ bap_qos->bcast.skip = qos->bcast.skip;
+ bap_qos->bcast.sync_timeout = qos->bcast.sync_timeout;
+ bap_qos->bcast.sync_cte_type = qos->bcast.sync_cte_type;
+ bap_qos->bcast.mse = qos->bcast.mse;
+ bap_qos->bcast.timeout = qos->bcast.timeout;
+ bap_qos->bcast.io_qos.interval = qos->bcast.in.interval;
+ bap_qos->bcast.io_qos.latency = qos->bcast.in.latency;
+ bap_qos->bcast.io_qos.phy = qos->bcast.in.phy;
+ bap_qos->bcast.io_qos.sdu = qos->bcast.in.sdu;
+ bap_qos->bcast.io_qos.rtn = qos->bcast.in.rtn;
+
+ bap_qos->bcast.bcode = new0(struct iovec, 1);
+ util_iov_memcpy(bap_qos->bcast.bcode, qos->bcast.bcode,
+ 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 bt_iso_qos qos;
+ struct bt_iso_base base;
+ char address[18];
+ struct bap_ep *ep;
+ int fd;
+ struct iovec *base_io;
+
+ bt_io_get(io, &err,
+ BT_IO_OPT_DEST, address,
+ BT_IO_OPT_QOS, &qos,
+ BT_IO_OPT_BASE, &base,
+ BT_IO_OPT_INVALID);
+ if (err) {
+ error("%s", err->message);
+ g_error_free(err);
+ goto drop;
+ }
+
+ g_io_channel_ref(io);
+
+ 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) {
+ DBG("ep not found");
+ return;
+ }
+
+ update_bcast_qos(&qos, &ep->qos);
+
+ base_io = new0(struct iovec, 1);
+ util_iov_memcpy(base_io, base.base, base.base_len);
+
+ if (ep->stream == NULL)
+ DBG("stream is null");
+ ep->id = bt_bap_stream_config(ep->stream, &ep->qos,
+ base_io, NULL, NULL);
+ data->listen_io = io;
+
+ bt_bap_stream_set_user_data(ep->stream, ep->path);
+
+ fd = g_io_channel_unix_get_fd(io);
+
+ if (bt_bap_stream_set_io(ep->stream, fd)) {
+ bt_bap_stream_enable(ep->stream, true, NULL, NULL, NULL);
+ g_io_channel_set_close_on_unref(io, FALSE);
+ return;
+ }
+
+
+ return;
+
+drop:
+ g_io_channel_shutdown(io, TRUE, NULL);
+
+}
+
+static bool match_data_bap_data(const void *data, const void *match_data)
+{
+ const struct bap_data *bdata = data;
+ const struct btd_adapter *adapter = match_data;
+
+ return bdata->user_data == adapter;
+}
+
static const GDBusMethodTable ep_methods[] = {
{ GDBUS_EXPERIMENTAL_ASYNC_METHOD("SetConfiguration",
GDBUS_ARGS({ "endpoint", "o" },
@@ -650,14 +769,23 @@ static struct bap_ep *ep_register_bcast(struct bap_data *data,
struct bt_bap_pac *rpac)
{
struct btd_adapter *adapter = data->user_data;
+ struct btd_device *device = data->device;
struct bap_ep *ep;
struct queue *queue;
- int i, err;
+ int i, err = 0;
const char *suffix;
struct match_ep match = { lpac, rpac };

+ if (!adapter)
+ DBG("adapter is null");
+
+ if (!device)
+ device = btd_adapter_find_device_by_path(adapter,
+ bt_bap_pac_get_name(rpac));
+
switch (bt_bap_pac_get_type(rpac)) {
case BT_BAP_BCAST_SOURCE:
+ case BT_BAP_BCAST_SINK:
queue = data->bcast;
i = queue_length(data->bcast);
suffix = "bcast";
@@ -675,8 +803,24 @@ static struct bap_ep *ep_register_bcast(struct bap_data *data,
ep->lpac = lpac;
ep->rpac = rpac;

- err = asprintf(&ep->path, "%s/pac_%s%d", adapter_get_path(adapter),
- suffix, i);
+ if (device)
+ ep->data->device = device;
+
+ switch (bt_bap_pac_get_type(rpac)) {
+ case BT_BAP_BCAST_SINK:
+ DBG("sink");
+ err = asprintf(&ep->path, "%s/pac_%s%d",
+ adapter_get_path(adapter), suffix, i);
+ DBG("sink path %s", ep->path);
+ break;
+ case BT_BAP_BCAST_SOURCE:
+ DBG("source");
+ err = asprintf(&ep->path, "%s/pac_%s%d",
+ device_get_path(device), suffix, i);
+ DBG("source path %s", ep->path);
+ break;
+ }
+
if (err < 0) {
error("Could not allocate path for remote pac %s/pac%d",
adapter_get_path(adapter), i);
@@ -685,14 +829,13 @@ static struct bap_ep *ep_register_bcast(struct bap_data *data,
}

if (g_dbus_register_interface(btd_get_dbus_connection(),
- ep->path, MEDIA_ENDPOINT_INTERFACE,
- ep_methods, NULL, ep_properties,
- ep, ep_free) == FALSE) {
+ ep->path, MEDIA_ENDPOINT_INTERFACE,
+ ep_methods, NULL, ep_properties,
+ ep, ep_free) == FALSE) {
error("Could not register remote ep %s", ep->path);
ep_free(ep);
return NULL;
}
-
bt_bap_pac_set_user_data(rpac, ep->path);

DBG("ep %p lpac %p rpac %p path %s", ep, ep->lpac, ep->rpac, ep->path);
@@ -824,6 +967,7 @@ done:

queue_foreach(ep->data->srcs, bap_config, NULL);
queue_foreach(ep->data->snks, bap_config, NULL);
+ queue_foreach(ep->data->bcast, bap_config, NULL);
}

static bool pac_found(struct bt_bap_pac *lpac, struct bt_bap_pac *rpac,
@@ -1310,6 +1454,46 @@ static void bap_listen_io(struct bap_data *data, struct bt_bap_stream *stream,
data->listen_io = io;
}

+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;
+
+ 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(iso_bcast_confirm_cb, NULL, 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);
+ g_error_free(err);
+ } else
+ DBG("io created");
+
+ 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)
{
@@ -1364,10 +1548,10 @@ static void bap_create_bcast_io(struct bap_data *data, struct bap_ep *ep,
memcpy(&iso_qos.bcast.out, &ep->qos.bcast.io_qos,
sizeof(struct bt_iso_io_qos));
done:
- if (ep)
+ if (bt_bap_pac_get_type(ep->lpac) == BT_BAP_BCAST_SOURCE)
bap_connect_io_broadcast(data, ep, stream, &iso_qos);
else
- bap_listen_io(data, stream, &iso_qos);
+ bap_listen_io_broadcast(data, ep, stream, &iso_qos);
}

static void bap_create_io(struct bap_data *data, struct bap_ep *ep,
@@ -1417,6 +1601,11 @@ static void bap_state(struct bt_bap_stream *stream, uint8_t old_state,
break;
case BT_BAP_STREAM_STATE_CONFIG:
if (ep && !ep->id) {
+ if
+ (bt_bap_pac_get_type(ep->lpac) == BT_BAP_BCAST_SINK) {
+ bap_create_bcast_io(data, ep, stream, true);
+ return;
+ }
bap_create_io(data, ep, stream, true);
if (!ep->io) {
error("Unable to create io");
@@ -1424,7 +1613,6 @@ static void bap_state(struct bt_bap_stream *stream, uint8_t old_state,
return;
}

-
if (bt_bap_stream_get_type(stream) ==
BT_BAP_STREAM_TYPE_UCAST) {
/* Wait QoS response to respond */
@@ -1480,6 +1668,10 @@ static void pac_added_broadcast(struct bt_bap_pac *pac, void *user_data)

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) {
+ DBG("sink pac %p", pac);
+ bt_bap_foreach_pac(data->bap, BT_BAP_BCAST_SINK,
+ pac_found_bcast, data);
}
}

@@ -1596,14 +1788,6 @@ static bool match_data(const void *data, const void *match_data)
return bdata->bap == bap;
}

-static bool match_data_bap_data(const void *data, const void *match_data)
-{
- const struct bap_data *bdata = data;
- const struct btd_adapter *adapter = match_data;
-
- return bdata->user_data == adapter;
-}
-
static bool io_get_qos(GIOChannel *io, struct bt_iso_qos *qos)
{
GError *err = NULL;
@@ -1854,7 +2038,7 @@ static int bap_adapter_probe(struct btd_profile *p,

bap_data_add(data);

- if (!bt_bap_attach_broadcast(data->bap)) {
+ if (!bt_bap_attach_broadcast(data->bap, BT_BAP_BCAST_SOURCE)) {
error("BAP unable to attach");
return -EINVAL;
}
@@ -1901,12 +2085,62 @@ static struct btd_profile bap_profile = {
.experimental = true,
};

+static GDBusProxy *media;
+
+static void proxy_added(GDBusProxy *proxy, void *user_data)
+{
+ const char *interface;
+
+ interface = g_dbus_proxy_get_interface(proxy);
+
+ if (!strcmp(interface, MEDIA_INTERFACE)) {
+ DBG("proxy added %s ", g_dbus_proxy_get_path(proxy));
+ media = proxy;
+ }
+}
+
+static int bcast_server_probe(struct btd_adapter *adapter)
+{
+ static GDBusClient *client;
+
+ client = g_dbus_client_new(btd_get_dbus_connection(),
+ "org.bluez", "/org/bluez");
+
+ g_dbus_client_set_proxy_handlers(client, proxy_added, NULL,
+ NULL, NULL);
+
+ return 0;
+}
+
+static void bcast_server_remove(struct btd_adapter *adapter)
+{
+ DBG("path %s", adapter_get_path(adapter));
+}
+
+static void bcast_new_source(struct btd_adapter *adapter,
+ struct btd_device *device)
+{
+ struct bap_data *data = queue_find(sessions, match_data_bap_data,
+ adapter);
+
+ bt_bap_new_bcast_source(data->bap, device_get_path(device), 0x06);
+}
+
+static struct btd_adapter_driver bcast_driver = {
+ .name = "bcast",
+ .probe = bcast_server_probe,
+ .remove = bcast_server_remove,
+ .device_discovered = bcast_new_source,
+ .experimental = true,
+};
+
static unsigned int bap_id = 0;

static int bap_init(void)
{
int err;

+ btd_register_adapter_driver(&bcast_driver);
err = btd_profile_register(&bap_profile);
if (err)
return err;
diff --git a/src/shared/bap.c b/src/shared/bap.c
index 72ce67c08..ae3f64730 100644
--- a/src/shared/bap.c
+++ b/src/shared/bap.c
@@ -633,14 +633,18 @@ static struct bt_bap_endpoint *bap_endpoint_new(struct bt_bap_db *bdb,
return ep;
}

-static struct bt_bap_endpoint *bap_endpoint_new_broacast(struct bt_bap_db *bdb)
+static struct bt_bap_endpoint *bap_endpoint_new_broadcast(struct bt_bap_db *bdb,
+ uint8_t type)
{
struct bt_bap_endpoint *ep;

ep = new0(struct bt_bap_endpoint, 1);
ep->bdb = bdb;
ep->attr = NULL;
- ep->dir = BT_BAP_BCAST_SOURCE;
+ if (type == BT_BAP_BCAST_SINK)
+ ep->dir = BT_BAP_BCAST_SOURCE;
+ else
+ ep->dir = BT_BAP_BCAST_SINK;

return ep;
}
@@ -667,22 +671,27 @@ static struct bt_bap_endpoint *bap_get_endpoint(struct queue *endpoints,
return ep;
}

+static bool match_ep_type(const void *data, const void *match_data)
+{
+ const struct bt_bap_endpoint *ep = data;
+ const uint8_t type = PTR_TO_INT(match_data);
+
+ return (ep->dir == type);
+}
+
static struct bt_bap_endpoint *bap_get_endpoint_bcast(struct queue *endpoints,
- struct bt_bap_db *db)
+ struct bt_bap_db *db, uint8_t type)
{
struct bt_bap_endpoint *ep;

if (!db)
return NULL;
- /*
- * We have support for only one stream so we will have
- * only one endpoint.
- * TO DO add support for more then one stream
- */
- if (queue_length(endpoints) > 0)
- return queue_peek_head(endpoints);

- ep = bap_endpoint_new_broacast(db);
+ ep = queue_find(endpoints, match_ep_type, INT_TO_PTR(type));
+ if (ep)
+ return ep;
+
+ ep = bap_endpoint_new_broadcast(db, type);
if (!ep)
return NULL;

@@ -1317,6 +1326,8 @@ static void stream_set_state_broadcast(struct bt_bap_stream *stream,
struct bt_bap *bap = stream->bap;
const struct queue_entry *entry;

+ if (ep->old_state == state)
+ return;
ep->old_state = ep->state;
ep->state = state;

@@ -1348,6 +1359,9 @@ static void stream_set_state(struct bt_bap_stream *stream, uint8_t state)
ep->old_state = ep->state;
ep->state = state;

+ if (stream->lpac->type == BT_BAP_BCAST_SINK)
+ goto done;
+
if (stream->client)
goto done;

@@ -2379,6 +2393,10 @@ static struct bt_bap_pac *bap_pac_find(struct bt_bap_db *bdb, uint8_t type,
return queue_find(bdb->sources, match_codec, codec);
case BT_BAP_SINK:
return queue_find(bdb->sinks, match_codec, codec);
+ case BT_BAP_BCAST_SOURCE:
+ return queue_find(bdb->broadcast_sources, match_codec, codec);
+ case BT_BAP_BCAST_SINK:
+ return queue_find(bdb->broadcast_sinks, match_codec, codec);
}

return NULL;
@@ -2518,7 +2536,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_brodcast_sink;
+ struct bt_bap_pac *pac, *pac_broadcast_sink;
struct bt_bap_codec codec;

if (!db)
@@ -2545,11 +2563,19 @@ struct bt_bap_pac *bt_bap_add_vendor_pac(struct gatt_db *db,
bap_add_source(pac);
break;
case BT_BAP_BCAST_SOURCE:
- // For broadcast add local pac and remote pac
bap_add_broadcast_source(pac);
- pac_brodcast_sink = bap_pac_new(bdb, name, type, &codec, qos,
+ 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_brodcast_sink);
+ bap_add_broadcast_sink(pac_broadcast_sink);
+ }
+ break;
+ case BT_BAP_BCAST_SINK:
+ bap_add_broadcast_sink(pac);
break;
default:
bap_pac_free(pac);
@@ -2579,6 +2605,14 @@ uint8_t bt_bap_pac_get_type(struct bt_bap_pac *pac)
return pac->type;
}

+char *bt_bap_pac_get_name(struct bt_bap_pac *pac)
+{
+ if (!pac)
+ return NULL;
+
+ return pac->name;
+}
+
uint32_t bt_bap_pac_get_locations(struct bt_bap_pac *pac)
{
struct bt_pacs *pacs = pac->bdb->pacs;
@@ -3996,7 +4030,7 @@ clone:
return true;
}

-bool bt_bap_attach_broadcast(struct bt_bap *bap)
+bool bt_bap_attach_broadcast(struct bt_bap *bap, uint8_t type)
{
struct bt_bap_endpoint *ep;

@@ -4008,7 +4042,7 @@ bool bt_bap_attach_broadcast(struct bt_bap *bap)

queue_push_tail(sessions, bap);

- ep = bap_get_endpoint_bcast(bap->remote_eps, bap->ldb);
+ ep = bap_get_endpoint_bcast(bap->remote_eps, bap->ldb, type);
if (ep)
ep->bap = bap;

@@ -4221,9 +4255,19 @@ void bt_bap_foreach_pac(struct bt_bap *bap, uint8_t type,
return bap_foreach_pac(bap->ldb->sinks, bap->rdb->sources,
func, user_data);
case BT_BAP_BCAST_SOURCE:
- return bap_foreach_pac(bap->ldb->broadcast_sources,
+ 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);
}
}

@@ -4382,6 +4426,11 @@ unsigned int bt_bap_stream_config(struct bt_bap_stream *stream,
return req->id;
case BT_BAP_STREAM_TYPE_BCAST:
stream->qos = *qos;
+ if (stream->lpac->type == BT_BAP_BCAST_SINK) {
+ if (data)
+ stream_config(stream, data, NULL);
+ stream_set_state(stream, BT_BAP_STREAM_STATE_CONFIG);
+ }
return 1;
}

@@ -4446,13 +4495,19 @@ struct bt_bap_stream *bt_bap_stream_new(struct bt_bap *bap,
if (rpac)
type = rpac->type;
else if (lpac) {
- switch(lpac->type) {
+ switch (lpac->type) {
case BT_BAP_SINK:
type = BT_BAP_SOURCE;
break;
case BT_BAP_SOURCE:
type = BT_BAP_SINK;
break;
+ case BT_BAP_BCAST_SOURCE:
+ type = BT_BAP_BCAST_SINK;
+ break;
+ case BT_BAP_BCAST_SINK:
+ type = BT_BAP_BCAST_SOURCE;
+ break;
default:
return NULL;
}
@@ -4913,6 +4968,18 @@ struct io *bt_bap_stream_get_io(struct bt_bap_stream *stream)
return io->io;
}

+char *bt_bap_stream_get_remote_name(struct bt_bap_stream *stream)
+{
+ return bt_bap_pac_get_name(stream->rpac);
+}
+
+bool bt_bap_match_bcast_sink_stream(const void *data, const void *user_data)
+{
+ const struct bt_bap_stream *stream = data;
+
+ return stream->lpac->type == BT_BAP_BCAST_SINK;
+}
+
static bool stream_io_disconnected(struct io *io, void *user_data)
{
struct bt_bap_stream *stream = user_data;
@@ -4944,6 +5011,14 @@ static bool match_req_id(const void *data, const void *match_data)
return (req->id == id);
}

+static bool match_name(const void *data, const void *match_data)
+{
+ const struct bt_bap_pac *pac = data;
+ const char *name = match_data;
+
+ return (!strcmp(pac->name, name));
+}
+
int bt_bap_stream_cancel(struct bt_bap_stream *stream, unsigned int id)
{
struct bt_bap_req *req;
@@ -5132,3 +5207,43 @@ bool bt_bap_stream_io_is_connecting(struct bt_bap_stream *stream, int *fd)

return io->connecting;
}
+
+bool bt_bap_new_bcast_source(struct bt_bap *bap, const char *name,
+ uint8_t codec)
+{
+ struct bt_bap_endpoint *ep;
+ struct bt_bap_pac *pac_broadcast_source, *local_sink;
+ struct bt_bap_codec bap_codec;
+
+ bap_codec.id = codec;
+ bap_codec.cid = 0;
+ bap_codec.vid = 0;
+
+ /* Add remote source endpoint */
+ if (!bap->rdb->broadcast_sources)
+ bap->rdb->broadcast_sources = queue_new();
+
+ if (queue_find(bap->rdb->broadcast_sources, match_name, name)) {
+ DBG(bap, "broadcast source already registered");
+ return true;
+ }
+
+ local_sink = queue_peek_head(bap->ldb->broadcast_sinks);
+ pac_broadcast_source = bap_pac_new(bap->rdb, name, BT_BAP_BCAST_SOURCE,
+ &bap_codec, NULL, local_sink->data, NULL);
+ queue_push_tail(bap->rdb->broadcast_sources, pac_broadcast_source);
+
+ if (!pac_broadcast_source) {
+ DBG(bap, "No broadcast source could be created");
+ return false;
+ }
+ queue_foreach(bap->pac_cbs, notify_pac_added, pac_broadcast_source);
+
+ /* Push remote endpoint with direction sink */
+ ep = bap_endpoint_new_broadcast(bap->rdb, BT_BAP_BCAST_SINK);
+
+ if (ep)
+ queue_push_tail(bap->remote_eps, ep);
+
+ return true;
+}
diff --git a/src/shared/bap.h b/src/shared/bap.h
index 50b567663..cf98ec8b7 100644
--- a/src/shared/bap.h
+++ b/src/shared/bap.h
@@ -163,6 +163,8 @@ bool bt_bap_remove_pac(struct bt_bap_pac *pac);

uint8_t bt_bap_pac_get_type(struct bt_bap_pac *pac);

+char *bt_bap_pac_get_name(struct bt_bap_pac *pac);
+
uint32_t bt_bap_pac_get_locations(struct bt_bap_pac *pac);

uint8_t bt_bap_stream_get_type(struct bt_bap_stream *stream);
@@ -186,7 +188,7 @@ struct bt_bap *bt_bap_ref(struct bt_bap *bap);
void bt_bap_unref(struct bt_bap *bap);

bool bt_bap_attach(struct bt_bap *bap, struct bt_gatt_client *client);
-bool bt_bap_attach_broadcast(struct bt_bap *bap);
+bool bt_bap_attach_broadcast(struct bt_bap *bap, uint8_t type);
void bt_bap_detach(struct bt_bap *bap);

bool bt_bap_set_debug(struct bt_bap *bap, bt_bap_debug_func_t cb,
@@ -289,7 +291,7 @@ struct bt_bap_qos *bt_bap_stream_get_qos(struct bt_bap_stream *stream);
struct iovec *bt_bap_stream_get_metadata(struct bt_bap_stream *stream);

struct io *bt_bap_stream_get_io(struct bt_bap_stream *stream);
-
+bool bt_bap_match_bcast_sink_stream(const void *data, const void *user_data);
bool bt_bap_stream_set_io(struct bt_bap_stream *stream, int fd);

int bt_bap_stream_cancel(struct bt_bap_stream *stream, unsigned int id);
@@ -305,3 +307,8 @@ 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_source(struct bt_bap *bap, const char *name,
+ uint8_t codec);
+
+char *bt_bap_stream_get_remote_name(struct bt_bap_stream *stream);
--
2.34.1