Diff between d15d9669ce4483023e75f272faab21ba8c9991d1 and 3030883005c02c77766e1a27a8d5c4d579daa9b5

Changed Files

File Additions Deletions Status
profiles/audio/bap.c +498 -44 modified
profiles/audio/media.c +80 -21 modified
profiles/audio/media.h +2 -0 modified
profiles/audio/transport.c +43 -16 modified

Full Patch

diff --git a/profiles/audio/bap.c b/profiles/audio/bap.c
index cbaf705..f3564c9 100644
--- a/profiles/audio/bap.c
+++ b/profiles/audio/bap.c
@@ -35,6 +35,7 @@
 #include "lib/sdp.h"
 #include "lib/uuid.h"
 
+#include "src/btd.h"
 #include "src/dbus-common.h"
 #include "src/shared/util.h"
 #include "src/shared/att.h"
@@ -54,6 +55,7 @@
 #include "src/log.h"
 #include "src/error.h"
 
+#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"
 
@@ -75,6 +77,7 @@ struct bap_ep {
 
 struct bap_data {
 	struct btd_device *device;
+	struct btd_adapter *adapter;
 	struct btd_service *service;
 	struct bt_bap *bap;
 	unsigned int ready_id;
@@ -82,13 +85,25 @@ struct bap_data {
 	unsigned int pac_id;
 	struct queue *srcs;
 	struct queue *snks;
+	struct queue *bcast;
 	struct queue *streams;
 	GIOChannel *listen_io;
 	int selecting;
+	void *user_data;
 };
 
 static struct queue *sessions;
 
+static bool bap_data_set_user_data(struct bap_data *data, void *user_data)
+{
+	if (!data)
+		return false;
+
+	data->user_data = user_data;
+
+	return true;
+}
+
 static void bap_debug(const char *str, void *user_data)
 {
 	DBG_IDX(0xffff, "%s", str);
@@ -167,8 +182,10 @@ static gboolean get_uuid(const GDBusPropertyTable *property,
 
 	if (queue_find(ep->data->snks, NULL, ep))
 		uuid = PAC_SINK_UUID;
-	else
+	else if (queue_find(ep->data->srcs, NULL, ep))
 		uuid = PAC_SOURCE_UUID;
+	else
+		uuid = BAA_SERVICE_UUID;
 
 	dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &uuid);
 
@@ -214,7 +231,10 @@ static gboolean get_device(const GDBusPropertyTable *property,
 	struct bap_ep *ep = data;
 	const char *path;
 
-	path = device_get_path(ep->data->device);
+	if (bt_bap_pac_get_type(ep->lpac) == BT_BAP_BCAST_SOURCE)
+		path = adapter_get_path(ep->data->adapter);
+	else
+		path = device_get_path(ep->data->device);
 
 	dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &path);
 
@@ -254,7 +274,10 @@ static int parse_properties(DBusMessageIter *props, struct iovec **caps,
 {
 	const char *key;
 	struct bt_bap_io_qos io_qos;
+	uint8_t framing = 0;
+	bool broadcast = false;
 
+	memset(&io_qos, 0, sizeof(io_qos));
 	while (dbus_message_iter_get_arg_type(props) == DBUS_TYPE_DICT_ENTRY) {
 		DBusMessageIter value, entry;
 		int var;
@@ -284,11 +307,21 @@ static int parse_properties(DBusMessageIter *props, struct iovec **caps,
 				goto fail;
 
 			dbus_message_iter_get_basic(&value, &qos->ucast.cig_id);
+		} else if (!strcasecmp(key, "BIG")) {
+			if (var != DBUS_TYPE_BYTE)
+				goto fail;
+
+			dbus_message_iter_get_basic(&value, &qos->bcast.big);
 		} else if (!strcasecmp(key, "CIS")) {
 			if (var != DBUS_TYPE_BYTE)
 				goto fail;
 
 			dbus_message_iter_get_basic(&value, &qos->ucast.cis_id);
+		} else if (!strcasecmp(key, "BIS")) {
+			if (var != DBUS_TYPE_BYTE)
+				goto fail;
+
+			dbus_message_iter_get_basic(&value, &qos->bcast.bis);
 		} else if (!strcasecmp(key, "Interval")) {
 			if (var != DBUS_TYPE_UINT32)
 				goto fail;
@@ -302,7 +335,7 @@ static int parse_properties(DBusMessageIter *props, struct iovec **caps,
 
 			dbus_message_iter_get_basic(&value, &val);
 
-			qos->ucast.framing = val;
+			framing = val;
 		} else if (!strcasecmp(key, "PHY")) {
 			const char *str;
 
@@ -342,13 +375,78 @@ static int parse_properties(DBusMessageIter *props, struct iovec **caps,
 				goto fail;
 
 			dbus_message_iter_get_basic(&value,
-					&qos->ucast.target_latency);
+						&qos->ucast.target_latency);
+		} else if (!strcasecmp(key, "Encryption")) {
+			if (var != DBUS_TYPE_BYTE)
+				goto fail;
+
+			dbus_message_iter_get_basic(&value,
+							&qos->bcast.encryption);
+			broadcast = true;
+		} else if (!strcasecmp(key, "Options")) {
+			if (var != DBUS_TYPE_BYTE)
+				goto fail;
+
+			dbus_message_iter_get_basic(&value,
+							&qos->bcast.options);
+		} else if (!strcasecmp(key, "Skip")) {
+			if (var != DBUS_TYPE_UINT16)
+				goto fail;
+
+			dbus_message_iter_get_basic(&value,
+							&qos->bcast.skip);
+		} else if (!strcasecmp(key, "SyncTimeout")) {
+			if (var != DBUS_TYPE_UINT16)
+				goto fail;
+
+			dbus_message_iter_get_basic(&value,
+						&qos->bcast.sync_timeout);
+		} else if (!strcasecmp(key, "SyncCteType")) {
+			if (var != DBUS_TYPE_BYTE)
+				goto fail;
+
+			dbus_message_iter_get_basic(&value,
+						&qos->bcast.sync_cte_type);
+
+		} else if (!strcasecmp(key, "SyncInterval")) {
+			if (var != DBUS_TYPE_BYTE)
+				goto fail;
+
+			dbus_message_iter_get_basic(&value,
+						&qos->bcast.sync_interval);
+		} else if (!strcasecmp(key, "MSE")) {
+			if (var != DBUS_TYPE_BYTE)
+				goto fail;
+
+			dbus_message_iter_get_basic(&value,
+							&qos->bcast.mse);
+		} else if (!strcasecmp(key, "Timeout")) {
+			if (var != DBUS_TYPE_UINT16)
+				goto fail;
+
+			dbus_message_iter_get_basic(&value,
+							&qos->bcast.timeout);
+		} else if (!strcasecmp(key, "BroadcastCode")) {
+			struct iovec *iov;
+
+			if (var != DBUS_TYPE_ARRAY)
+				goto fail;
+			iov = &qos->bcast.bcode;
+			parse_array(&value, &iov);
 		}
 
 		dbus_message_iter_next(props);
 	}
 
-	memcpy(&qos->ucast.io_qos, &io_qos, sizeof(io_qos));
+	if (broadcast) {
+		memcpy(&qos->bcast.io_qos, &io_qos, sizeof(io_qos));
+		qos->bcast.framing = framing;
+
+	} else {
+		memcpy(&qos->ucast.io_qos, &io_qos, sizeof(io_qos));
+		qos->ucast.framing = framing;
+	}
+
 	return 0;
 
 fail:
@@ -457,9 +555,15 @@ static DBusMessage *set_configuration(DBusConnection *conn, DBusMessage *msg,
 		bt_bap_stream_io_connecting(ep->stream, -1);
 	}
 
-	/* Mark CIG and CIS to be auto assigned */
-	ep->qos.ucast.cig_id = BT_ISO_QOS_CIG_UNSET;
-	ep->qos.ucast.cis_id = BT_ISO_QOS_CIS_UNSET;
+	if (bt_bap_pac_get_type(ep->lpac) == BT_BAP_BCAST_SOURCE) {
+		/* Mark CIG and CIS to be auto assigned */
+		ep->qos.bcast.big = BT_ISO_QOS_BIG_UNSET;
+		ep->qos.bcast.bis = BT_ISO_QOS_BIS_UNSET;
+	} else {
+		/* Mark CIG and CIS to be auto assigned */
+		ep->qos.ucast.cig_id = BT_ISO_QOS_CIG_UNSET;
+		ep->qos.ucast.cis_id = BT_ISO_QOS_CIS_UNSET;
+	}
 
 	if (parse_properties(&props, &ep->caps, &ep->metadata, &ep->qos) < 0) {
 		DBG("Unable to parse properties");
@@ -483,11 +587,20 @@ static DBusMessage *set_configuration(DBusConnection *conn, DBusMessage *msg,
 	}
 
 	bt_bap_stream_set_user_data(ep->stream, ep->path);
-	ep->msg = dbus_message_ref(msg);
 
 	if (ep->metadata && ep->metadata->iov_len)
 		bt_bap_stream_metadata(ep->stream, ep->metadata, NULL, NULL);
 
+	switch (bt_bap_stream_get_type(ep->stream)) {
+	case BT_BAP_STREAM_TYPE_UCAST:
+		ep->msg = dbus_message_ref(msg);
+		break;
+	case BT_BAP_STREAM_TYPE_BCAST:
+		/* No message sent over the air for broadcast */
+		ep->id = 0;
+		return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+	}
+
 	return NULL;
 }
 
@@ -510,6 +623,8 @@ static void ep_free(void *data)
 
 	util_iov_free(ep->caps, 1);
 	util_iov_free(ep->metadata, 1);
+	if (bt_bap_stream_get_type(ep->stream) == BT_BAP_STREAM_TYPE_BCAST)
+		util_iov_free(&ep->qos.bcast.bcode, 1);
 	free(ep->path);
 	free(ep);
 }
@@ -530,6 +645,63 @@ static bool match_ep(const void *data, const void *user_data)
 	return ep->rpac == match->rpac;
 }
 
+static struct bap_ep *ep_register_bcast(struct bap_data *data,
+					struct bt_bap_pac *lpac,
+					struct bt_bap_pac *rpac)
+{
+	struct btd_adapter *adapter = data->user_data;
+	struct bap_ep *ep;
+	struct queue *queue;
+	int i, err;
+	const char *suffix;
+	struct match_ep match = { lpac, rpac };
+
+	switch (bt_bap_pac_get_type(rpac)) {
+	case BT_BAP_BCAST_SOURCE:
+		queue = data->bcast;
+		i = queue_length(data->bcast);
+		suffix = "bcast";
+		break;
+	default:
+		return NULL;
+	}
+
+	ep = queue_find(queue, match_ep, &match);
+	if (ep)
+		return ep;
+
+	ep = new0(struct bap_ep, 1);
+	ep->data = data;
+	ep->lpac = lpac;
+	ep->rpac = rpac;
+
+	err = asprintf(&ep->path, "%s/pac_%s%d", adapter_get_path(adapter),
+		       suffix, i);
+	if (err < 0) {
+		error("Could not allocate path for remote pac %s/pac%d",
+				adapter_get_path(adapter), i);
+		free(ep);
+		return NULL;
+	}
+
+	if (g_dbus_register_interface(btd_get_dbus_connection(),
+				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);
+
+	queue_push_tail(queue, ep);
+
+	return ep;
+}
+
 static struct bap_ep *ep_register(struct btd_service *service,
 					struct bt_bap_pac *lpac,
 					struct bt_bap_pac *rpac)
@@ -677,6 +849,22 @@ static bool pac_found(struct bt_bap_pac *lpac, struct bt_bap_pac *rpac,
 	return true;
 }
 
+static bool pac_found_bcast(struct bt_bap_pac *lpac, struct bt_bap_pac *rpac,
+							void *user_data)
+{
+	struct bap_ep *ep;
+
+	DBG("lpac %p rpac %p", lpac, rpac);
+
+	ep = ep_register_bcast(user_data, lpac, rpac);
+	if (!ep) {
+		error("Unable to register endpoint for pac %p", rpac);
+		return true;
+	}
+
+	return true;
+}
+
 static void bap_ready(struct bt_bap *bap, void *user_data)
 {
 	struct btd_service *service = user_data;
@@ -700,11 +888,18 @@ static struct bap_ep *bap_find_ep_by_stream(struct bap_data *data,
 {
 	struct bap_ep *ep;
 
-	ep = queue_find(data->snks, match_ep_by_stream, stream);
-	if (ep)
-		return ep;
+	switch (bt_bap_stream_get_type(stream)) {
+	case BT_BAP_STREAM_TYPE_UCAST:
+		ep = queue_find(data->snks, match_ep_by_stream, stream);
+		if (ep)
+			return ep;
+
+		return queue_find(data->srcs, match_ep_by_stream, stream);
+	case BT_BAP_STREAM_TYPE_BCAST:
+		return queue_find(data->bcast, match_ep_by_stream, stream);
+	}
 
-	return queue_find(data->srcs, match_ep_by_stream, stream);
+	return NULL;
 }
 
 static void iso_connect_cb(GIOChannel *chan, GError *err, gpointer user_data)
@@ -943,6 +1138,61 @@ static void bap_connect_io(struct bap_data *data, struct bap_ep *ep,
 	bt_bap_stream_io_connecting(stream, g_io_channel_unix_get_fd(io));
 }
 
+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 btd_adapter *adapter = data->user_data;
+	GIOChannel *io = NULL;
+	GError *err = NULL;
+	bdaddr_t dst_addr = {0};
+	char addr[18];
+	struct bt_iso_base base;
+
+	/* If IO already set and we are in the creation step,
+	 * skip creating it again
+	 */
+	if (bt_bap_stream_get_io(stream))
+		return;
+
+	if (ep->io_id) {
+		g_source_remove(ep->io_id);
+		ep->io_id = 0;
+	}
+	base.base_len = ep->caps->iov_len;
+
+	memset(base.base, 0, 248);
+	memcpy(base.base, ep->caps->iov_base, base.base_len);
+	DBG("ep %p stream %p ", ep, stream);
+	ba2str(btd_adapter_get_address(adapter), addr);
+
+	io = bt_io_connect(bap_connect_io_cb, ep, NULL, &err,
+			BT_IO_OPT_SOURCE_BDADDR,
+			btd_adapter_get_address(adapter),
+			BT_IO_OPT_DEST_BDADDR,
+			&dst_addr,
+			BT_IO_OPT_DEST_TYPE,
+			BDADDR_LE_PUBLIC,
+			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_INVALID);
+
+	if (!io) {
+		error("%s", err->message);
+		g_error_free(err);
+		return;
+	}
+
+	ep->io_id = g_io_add_watch(io, G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+			bap_io_disconnected, ep);
+
+	ep->io = io;
+
+	bt_bap_stream_io_connecting(stream, g_io_channel_unix_get_fd(io));
+}
+
 static void bap_listen_io(struct bap_data *data, struct bt_bap_stream *stream,
 						struct bt_iso_qos *qos)
 {
@@ -975,26 +1225,19 @@ static void bap_listen_io(struct bap_data *data, struct bt_bap_stream *stream,
 	data->listen_io = io;
 }
 
-static void bap_create_io(struct bap_data *data, struct bap_ep *ep,
+static void bap_create_ucast_io(struct bap_data *data, struct bap_ep *ep,
 				struct bt_bap_stream *stream, int defer)
 {
 	struct bt_bap_qos *qos[2] = {};
 	struct bt_iso_qos iso_qos;
 
-	DBG("ep %p stream %p defer %s", ep, stream, defer ? "true" : "false");
-
-	if (!data->streams)
-		data->streams = queue_new();
-
-	if (!queue_find(data->streams, NULL, stream))
-		queue_push_tail(data->streams, stream);
-
 	if (!bt_bap_stream_io_get_qos(stream, &qos[0], &qos[1])) {
 		error("bt_bap_stream_get_qos_links: failed");
 		return;
 	}
 
 	memset(&iso_qos, 0, sizeof(iso_qos));
+
 	iso_qos.ucast.cig = qos[0] ? qos[0]->ucast.cig_id :
 						qos[1]->ucast.cig_id;
 	iso_qos.ucast.cis = qos[0] ? qos[0]->ucast.cis_id :
@@ -1009,6 +1252,59 @@ static void bap_create_io(struct bap_data *data, struct bap_ep *ep,
 		bap_listen_io(data, stream, &iso_qos);
 }
 
+static void bap_create_bcast_io(struct bap_data *data, struct bap_ep *ep,
+				struct bt_bap_stream *stream, int defer)
+{
+	struct bt_iso_qos iso_qos;
+
+	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_interval = ep->qos.bcast.sync_interval;
+	iso_qos.bcast.packing = ep->qos.bcast.packing;
+	iso_qos.bcast.framing = ep->qos.bcast.framing;
+	iso_qos.bcast.encryption = ep->qos.bcast.encryption;
+	memcpy(iso_qos.bcast.bcode, ep->qos.bcast.bcode.iov_base, 16);
+	iso_qos.bcast.options = ep->qos.bcast.options;
+	iso_qos.bcast.skip = ep->qos.bcast.skip;
+	iso_qos.bcast.sync_timeout = ep->qos.bcast.sync_timeout;
+	iso_qos.bcast.sync_cte_type = ep->qos.bcast.sync_cte_type;
+	iso_qos.bcast.mse = ep->qos.bcast.mse;
+	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 (ep)
+		bap_connect_io_broadcast(data, ep, stream, &iso_qos);
+	else
+		bap_listen_io(data, stream, &iso_qos);
+}
+
+static void bap_create_io(struct bap_data *data, struct bap_ep *ep,
+				struct bt_bap_stream *stream, int defer)
+{
+	DBG("ep %p stream %p defer %s", ep, stream, defer ? "true" : "false");
+
+	if (!data->streams)
+		data->streams = queue_new();
+
+	if (!queue_find(data->streams, NULL, stream))
+		queue_push_tail(data->streams, stream);
+
+	switch (bt_bap_stream_get_type(stream)) {
+	case BT_BAP_STREAM_TYPE_UCAST:
+		bap_create_ucast_io(data, ep, stream, defer);
+		break;
+	case BT_BAP_STREAM_TYPE_BCAST:
+		bap_create_bcast_io(data, ep, stream, defer);
+		break;
+	}
+}
+
 static void bap_state(struct bt_bap_stream *stream, uint8_t old_state,
 				uint8_t new_state, void *user_data)
 {
@@ -1043,12 +1339,16 @@ static void bap_state(struct bt_bap_stream *stream, uint8_t old_state,
 			}
 
 
-			/* Wait QoS response to respond */
-			ep->id = bt_bap_stream_qos(stream, &ep->qos, qos_cb,
-									ep);
-			if (!ep->id) {
-				error("Failed to Configure QoS");
-				bt_bap_stream_release(stream, NULL, NULL);
+			if (bt_bap_stream_get_type(stream) ==
+					BT_BAP_STREAM_TYPE_UCAST) {
+				/* Wait QoS response to respond */
+				ep->id = bt_bap_stream_qos(stream, &ep->qos,
+								qos_cb,	ep);
+				if (!ep->id) {
+					error("Failed to Configure QoS");
+					bt_bap_stream_release(stream,
+								NULL, NULL);
+				}
 			}
 		}
 		break;
@@ -1059,6 +1359,13 @@ static void bap_state(struct bt_bap_stream *stream, uint8_t old_state,
 		if (ep)
 			bap_create_io(data, ep, stream, false);
 		break;
+	case BT_BAP_STREAM_STATE_STREAMING:
+		if (bt_bap_stream_get_type(stream) ==
+				BT_BAP_STREAM_TYPE_BCAST) {
+			if (ep)
+				bap_create_io(data, ep, stream, false);
+		}
+		break;
 	}
 }
 
@@ -1078,6 +1385,18 @@ static void pac_added(struct bt_bap_pac *pac, void *user_data)
 	bt_bap_foreach_pac(data->bap, BT_BAP_SINK, pac_found, service);
 }
 
+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) {
+		DBG("pac %p", pac);
+
+		bt_bap_foreach_pac(data->bap, BT_BAP_BCAST_SOURCE,
+						pac_found_bcast, data);
+	}
+}
+
 static bool ep_match_pac(const void *data, const void *match_data)
 {
 	const struct bap_ep *ep = data;
@@ -1118,6 +1437,38 @@ static void pac_removed(struct bt_bap_pac *pac, void *user_data)
 	ep_unregister(ep);
 }
 
+static void pac_removed_broadcast(struct bt_bap_pac *pac, void *user_data)
+{
+	struct btd_service *service = user_data;
+	struct bap_data *data;
+	struct queue *queue;
+	struct bap_ep *ep;
+
+	DBG("pac %p", pac);
+
+	data = btd_service_get_user_data(service);
+
+	switch (bt_bap_pac_get_type(pac)) {
+	case BT_BAP_SINK:
+		queue = data->srcs;
+		break;
+	case BT_BAP_SOURCE:
+		queue = data->snks;
+		break;
+	case BT_BAP_BCAST_SOURCE:
+		queue = data->bcast;
+		break;
+	default:
+		return;
+	}
+
+	ep = queue_remove_if(queue, ep_match_pac, pac);
+	if (!ep)
+		return;
+
+	ep_unregister(ep);
+}
+
 static struct bap_data *bap_data_new(struct btd_device *device)
 {
 	struct bap_data *data;
@@ -1126,6 +1477,7 @@ static struct bap_data *bap_data_new(struct btd_device *device)
 	data->device = device;
 	data->srcs = queue_new();
 	data->snks = queue_new();
+	data->bcast = queue_new();
 
 	return data;
 }
@@ -1158,6 +1510,28 @@ 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;
+	bool ret;
+
+	ret = bt_io_get(io, &err, BT_IO_OPT_QOS, qos, BT_IO_OPT_INVALID);
+	if (!ret) {
+		error("%s", err->message);
+		g_error_free(err);
+	}
+
+	return ret;
+}
+
 static void bap_connecting(struct bt_bap_stream *stream, bool state, int fd,
 							void *user_data)
 {
@@ -1182,26 +1556,43 @@ static void bap_connecting(struct bt_bap_stream *stream, bool state, int fd,
 
 	g_io_channel_set_close_on_unref(io, FALSE);
 
-	/* Attempt to get CIG/CIS if they have not been set */
-	if (ep->qos.ucast.cig_id == BT_ISO_QOS_CIG_UNSET ||
+	switch (bt_bap_stream_get_type(ep->stream)) {
+	case BT_BAP_STREAM_TYPE_UCAST:
+		/* Attempt to get CIG/CIS if they have not been set */
+		if (ep->qos.ucast.cig_id == BT_ISO_QOS_CIG_UNSET ||
 				ep->qos.ucast.cis_id == BT_ISO_QOS_CIS_UNSET) {
-		struct bt_iso_qos qos;
-		GError *err = NULL;
-
-		if (!bt_io_get(io, &err, BT_IO_OPT_QOS, &qos,
-					BT_IO_OPT_INVALID)) {
-			error("%s", err->message);
-			g_error_free(err);
-			g_io_channel_unref(io);
-			return;
-		}
+			struct bt_iso_qos qos;
 
-		ep->qos.ucast.cig_id = qos.ucast.cig;
-		ep->qos.ucast.cis_id = qos.ucast.cis;
-	}
+			if (!io_get_qos(io, &qos)) {
+				g_io_channel_unref(io);
+				return;
+			}
+
+			ep->qos.ucast.cig_id = qos.ucast.cig;
+			ep->qos.ucast.cis_id = qos.ucast.cis;
+		}
 
-	DBG("stream %p fd %d: CIG 0x%02x CIS 0x%02x", stream, fd,
+		DBG("stream %p fd %d: CIG 0x%02x CIS 0x%02x", stream, fd,
 				ep->qos.ucast.cig_id, ep->qos.ucast.cis_id);
+		break;
+	case BT_BAP_STREAM_TYPE_BCAST:
+		/* Attempt to get BIG/BIS if they have not been set */
+		if (ep->qos.bcast.big == BT_ISO_QOS_BIG_UNSET ||
+				ep->qos.bcast.bis == BT_ISO_QOS_BIS_UNSET) {
+			struct bt_iso_qos qos;
+
+			if (!io_get_qos(io, &qos)) {
+				g_io_channel_unref(io);
+				return;
+			}
+
+			ep->qos.bcast.big = qos.bcast.big;
+			ep->qos.bcast.bis = qos.bcast.bis;
+		}
+
+		DBG("stream %p fd %d: BIG 0x%02x BIS 0x%02x", stream, fd,
+					ep->qos.bcast.big, ep->qos.bcast.bis);
+	}
 }
 
 static void bap_attached(struct bt_bap *bap, void *user_data)
@@ -1349,6 +1740,67 @@ static int bap_disconnect(struct btd_service *service)
 	return 0;
 }
 
+static int bap_adapter_probe(struct btd_profile *p,
+				struct btd_adapter *adapter)
+{
+	struct btd_gatt_database *database = btd_adapter_get_database(adapter);
+	struct bap_data *data;
+	char addr[18];
+
+	ba2str(btd_adapter_get_address(adapter), addr);
+	DBG("%s", addr);
+
+	if (!btd_kernel_experimental_enabled(ISO_SOCKET_UUID)) {
+		error("BAP requires ISO Socket which is not enabled");
+		return -ENOTSUP;
+	}
+
+	data = bap_data_new(NULL);
+	data->adapter = adapter;
+
+	data->bap = bt_bap_new(btd_gatt_database_get_db(database),
+					btd_gatt_database_get_db(database));
+	if (!data->bap) {
+		error("Unable to create BAP instance");
+		free(data);
+		return -EINVAL;
+	}
+
+	bap_data_add(data);
+
+	if (!bt_bap_attach_broadcast(data->bap)) {
+		error("BAP unable to attach");
+		return -EINVAL;
+	}
+
+	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,
+					pac_removed_broadcast, data, NULL);
+
+	bt_bap_set_user_data(data->bap, adapter);
+	bap_data_set_user_data(data, adapter);
+	return 0;
+}
+
+static void bap_adapter_remove(struct btd_profile *p,
+					struct btd_adapter *adapter)
+{
+	struct bap_data *data = queue_find(sessions, match_data_bap_data,
+						adapter);
+	char addr[18];
+
+	ba2str(btd_adapter_get_address(adapter), addr);
+	DBG("%s", addr);
+
+	if (!data) {
+		error("BAP service not handled by profile");
+		return;
+	}
+
+	bap_data_remove(data);
+}
+
 static struct btd_profile bap_profile = {
 	.name		= "bap",
 	.priority	= BTD_PROFILE_PRIORITY_MEDIUM,
@@ -1357,6 +1809,8 @@ static struct btd_profile bap_profile = {
 	.device_remove	= bap_remove,
 	.accept		= bap_accept,
 	.disconnect	= bap_disconnect,
+	.adapter_probe	= bap_adapter_probe,
+	.adapter_remove	= bap_adapter_remove,
 	.auto_connect	= true,
 	.experimental	= true,
 };
diff --git a/profiles/audio/media.c b/profiles/audio/media.c
index b2dfc9b..bcf4eae 100644
--- a/profiles/audio/media.c
+++ b/profiles/audio/media.c
@@ -1024,6 +1024,54 @@ static void pac_config_cb(struct media_endpoint *endpoint, void *ret, int size,
 	data->cb(data->stream, ret_value ? 0 : -EINVAL);
 }
 
+static struct media_transport *pac_ucast_config(struct bt_bap_stream *stream,
+						struct iovec *cfg,
+						struct media_endpoint *endpoint)
+{
+	struct bt_bap *bap = bt_bap_stream_get_session(stream);
+	struct btd_service *service = bt_bap_get_user_data(bap);
+	struct btd_device *device;
+	const char *path;
+
+	if (service)
+		device = btd_service_get_device(service);
+	else {
+		struct bt_att *att = bt_bap_get_att(bap);
+		int fd = bt_att_get_fd(att);
+
+		device = btd_adapter_find_device_by_fd(fd);
+	}
+
+	if (!device) {
+		error("Unable to find device");
+		return NULL;
+	}
+
+	path = bt_bap_stream_get_user_data(stream);
+
+	return media_transport_create(device, path, cfg->iov_base, cfg->iov_len,
+					endpoint, stream);
+}
+
+static struct media_transport *pac_bcast_config(struct bt_bap_stream *stream,
+						struct iovec *cfg,
+						struct media_endpoint *endpoint)
+{
+	struct bt_bap *bap = bt_bap_stream_get_session(stream);
+	struct btd_adapter *adapter = bt_bap_get_user_data(bap);
+	const char *path;
+
+	if (!adapter) {
+		error("Unable to find adapter");
+		return NULL;
+	}
+
+	path = bt_bap_stream_get_user_data(stream);
+
+	return media_transport_create(NULL, path, cfg->iov_base, cfg->iov_len,
+					endpoint, stream);
+}
+
 static int pac_config(struct bt_bap_stream *stream, struct iovec *cfg,
 			struct bt_bap_qos *qos, bt_bap_pac_config_t cb,
 			void *user_data)
@@ -1040,29 +1088,15 @@ static int pac_config(struct bt_bap_stream *stream, struct iovec *cfg,
 
 	transport = find_transport(endpoint, stream);
 	if (!transport) {
-		struct bt_bap *bap = bt_bap_stream_get_session(stream);
-		struct btd_service *service = bt_bap_get_user_data(bap);
-		struct btd_device *device;
-
-		if (service)
-			device = btd_service_get_device(service);
-		else {
-			struct bt_att *att = bt_bap_get_att(bap);
-			int fd = bt_att_get_fd(att);
-
-			device = btd_adapter_find_device_by_fd(fd);
-		}
-
-		if (!device) {
-			error("Unable to find device");
-			return -EINVAL;
+		switch (bt_bap_stream_get_type(stream)) {
+		case BT_BAP_STREAM_TYPE_UCAST:
+			transport = pac_ucast_config(stream, cfg, endpoint);
+			break;
+		case BT_BAP_STREAM_TYPE_BCAST:
+			transport = pac_bcast_config(stream, cfg, endpoint);
+			break;
 		}
 
-		path = bt_bap_stream_get_user_data(stream);
-
-		transport = media_transport_create(device, path, cfg->iov_base,
-							cfg->iov_len, endpoint,
-							stream);
 		if (!transport)
 			return -EINVAL;
 
@@ -1198,6 +1232,12 @@ static bool endpoint_init_pac_source(struct media_endpoint *endpoint, int *err)
 	return endpoint_init_pac(endpoint, BT_BAP_SOURCE, err);
 }
 
+static bool endpoint_init_broadcast_source(struct media_endpoint *endpoint,
+						int *err)
+{
+	return endpoint_init_pac(endpoint, BT_BAP_BCAST_SOURCE, err);
+}
+
 static bool endpoint_properties_exists(const char *uuid,
 						struct btd_device *dev,
 						void *user_data)
@@ -1300,6 +1340,17 @@ static bool experimental_endpoint_supported(struct btd_adapter *adapter)
 	return g_dbus_get_flags() & G_DBUS_FLAG_ENABLE_EXPERIMENTAL;
 }
 
+static bool experimental_broadcaster_ep_supported(struct btd_adapter *adapter)
+{
+	if (!btd_adapter_has_exp_feature(adapter, EXP_FEAT_ISO_SOCKET))
+		return false;
+
+	if (!btd_adapter_has_settings(adapter, MGMT_SETTING_ISO_BROADCASTER))
+		return false;
+
+	return g_dbus_get_flags() & G_DBUS_FLAG_ENABLE_EXPERIMENTAL;
+}
+
 static struct media_endpoint_init {
 	const char *uuid;
 	bool (*func)(struct media_endpoint *endpoint, int *err);
@@ -1313,6 +1364,8 @@ static struct media_endpoint_init {
 				experimental_endpoint_supported },
 	{ PAC_SOURCE_UUID, endpoint_init_pac_source,
 				experimental_endpoint_supported },
+	{ BAA_SERVICE_UUID, endpoint_init_broadcast_source,
+			experimental_broadcaster_ep_supported },
 };
 
 static struct media_endpoint *
@@ -3186,3 +3239,9 @@ uint8_t media_endpoint_get_codec(struct media_endpoint *endpoint)
 {
 	return endpoint->codec;
 }
+
+struct btd_adapter *media_endpoint_get_btd_adapter(
+					struct media_endpoint *endpoint)
+{
+	return endpoint->adapter->btd_adapter;
+}
diff --git a/profiles/audio/media.h b/profiles/audio/media.h
index 96bea9d..1de84a8 100644
--- a/profiles/audio/media.h
+++ b/profiles/audio/media.h
@@ -20,5 +20,7 @@ void media_unregister(struct btd_adapter *btd_adapter);
 struct a2dp_sep *media_endpoint_get_sep(struct media_endpoint *endpoint);
 const char *media_endpoint_get_uuid(struct media_endpoint *endpoint);
 uint8_t media_endpoint_get_codec(struct media_endpoint *endpoint);
+struct btd_adapter *media_endpoint_get_btd_adapter(
+					struct media_endpoint *endpoint);
 
 int8_t media_player_get_device_volume(struct btd_device *device);
diff --git a/profiles/audio/transport.c b/profiles/audio/transport.c
index 82f5fa6..48a4085 100644
--- a/profiles/audio/transport.c
+++ b/profiles/audio/transport.c
@@ -91,6 +91,7 @@ struct bap_transport {
 struct media_transport {
 	char			*path;		/* Transport object path */
 	struct btd_device	*device;	/* Transport device */
+	struct btd_adapter	*adapter;	/* Transport adapter bcast*/
 	const char		*remote_endpoint; /* Transport remote SEP */
 	struct media_endpoint	*endpoint;	/* Transport endpoint */
 	struct media_owner	*owner;		/* Transport owner */
@@ -526,6 +527,13 @@ static void media_owner_add(struct media_owner *owner,
 	owner->pending = req;
 }
 
+static void *get_stream_bap(struct media_transport *transport)
+{
+	struct bap_transport *bap = transport->data;
+
+	return bap->stream;
+}
+
 static DBusMessage *acquire(DBusConnection *conn, DBusMessage *msg,
 					void *data)
 {
@@ -541,15 +549,24 @@ static DBusMessage *acquire(DBusConnection *conn, DBusMessage *msg,
 		return btd_error_not_authorized(msg);
 
 	owner = media_owner_create(msg);
+	if (bt_bap_stream_get_type(get_stream_bap(transport)) ==
+				BT_BAP_STREAM_TYPE_BCAST) {
+		req = media_request_create(msg, 0x00);
+		media_owner_add(owner, req);
+		media_transport_set_owner(transport, owner);
+	}
 	id = transport->resume(transport, owner);
 	if (id == 0) {
 		media_owner_free(owner);
 		return btd_error_not_authorized(msg);
 	}
 
-	req = media_request_create(msg, id);
-	media_owner_add(owner, req);
-	media_transport_set_owner(transport, owner);
+	if (bt_bap_stream_get_type(get_stream_bap(transport)) ==
+				BT_BAP_STREAM_TYPE_UCAST) {
+		req = media_request_create(msg, id);
+		media_owner_add(owner, req);
+		media_transport_set_owner(transport, owner);
+	}
 
 	return NULL;
 }
@@ -628,7 +645,12 @@ static gboolean get_device(const GDBusPropertyTable *property,
 					DBusMessageIter *iter, void *data)
 {
 	struct media_transport *transport = data;
-	const char *path = device_get_path(transport->device);
+	const char *path;
+
+	if (transport->device)
+		path = device_get_path(transport->device);
+	else
+		path = adapter_get_path(transport->adapter);
 
 	dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &path);
 
@@ -1483,13 +1505,6 @@ static void bap_connecting(struct bt_bap_stream *stream, bool state, int fd,
 	bap_update_links(transport);
 }
 
-static void *get_stream_bap(struct media_transport *transport)
-{
-	struct bap_transport *bap = transport->data;
-
-	return bap->stream;
-}
-
 static void free_bap(void *data)
 {
 	struct bap_transport *bap = data;
@@ -1539,15 +1554,26 @@ struct media_transport *media_transport_create(struct btd_device *device,
 	const GDBusPropertyTable *properties;
 
 	transport = g_new0(struct media_transport, 1);
-	transport->device = device;
+	if (device)
+		transport->device = device;
+	else
+		transport->adapter = media_endpoint_get_btd_adapter(endpoint);
+
 	transport->endpoint = endpoint;
 	transport->configuration = g_new(uint8_t, size);
 	memcpy(transport->configuration, configuration, size);
 	transport->size = size;
 	transport->remote_endpoint = remote_endpoint;
-	transport->path = g_strdup_printf("%s/fd%d",
-				remote_endpoint ? remote_endpoint :
-				device_get_path(device), fd++);
+
+	if (device)
+		transport->path = g_strdup_printf("%s/fd%d",
+					remote_endpoint ? remote_endpoint :
+					device_get_path(device), fd++);
+	else
+		transport->path = g_strdup_printf("%s/fd%d",
+					remote_endpoint ? remote_endpoint :
+					adapter_get_path(transport->adapter),
+					fd++);
 	transport->fd = -1;
 
 	uuid = media_endpoint_get_uuid(endpoint);
@@ -1560,7 +1586,8 @@ struct media_transport *media_transport_create(struct btd_device *device,
 			goto fail;
 		properties = a2dp_properties;
 	} else if (!strcasecmp(uuid, PAC_SINK_UUID) ||
-				!strcasecmp(uuid, PAC_SOURCE_UUID)) {
+				!strcasecmp(uuid, PAC_SOURCE_UUID) ||
+				!strcasecmp(uuid, BAA_SERVICE_UUID)) {
 		if (media_transport_init_bap(transport, stream) < 0)
 			goto fail;
 		properties = bap_properties;