diff mbox series

[BlueZ,4/6] bass: Rework Scan Delegator to handle BAP streams

Message ID 20241115103825.19761-5-iulia.tanasescu@nxp.com (mailing list archive)
State New
Headers show
Series bass: Rework Scan Delegator | expand

Commit Message

Iulia Tanasescu Nov. 15, 2024, 10:38 a.m. UTC
This updates the Scan Delegator implementation to be handled internally in
BASS: The BASS Server is responsible to handle Write Commands for the Add
Source operation by creating long-lived PA sync, parsing the BASE,
creating and configuring BAP streams, as well as enabling them.
---
 profiles/audio/bap.c  |  18 ++-
 profiles/audio/bass.c | 264 ++++++++++++++++++++++++++++++++++++------
 profiles/audio/bass.h |   4 +-
 3 files changed, 237 insertions(+), 49 deletions(-)
diff mbox series

Patch

diff --git a/profiles/audio/bap.c b/profiles/audio/bap.c
index 8635c383d..6e6d68b57 100644
--- a/profiles/audio/bap.c
+++ b/profiles/audio/bap.c
@@ -1125,14 +1125,6 @@  static void bis_handler(uint8_t bis, uint8_t sgrp, struct iovec *caps,
 
 	bass_add_stream(data->device, meta, caps, qos, sgrp, bis);
 
-	if (!bass_check_bis(data->device, bis))
-		/* If this Broadcast Sink is acting as a Scan
-		 * Delegator, only attempt to create streams
-		 * for the BISes required by the peer Broadcast
-		 * Assistant.
-		 */
-		return;
-
 	/* Check if this BIS matches any local PAC */
 	bt_bap_verify_bis(data->bap, bis,
 			caps, &lpac);
@@ -1325,9 +1317,6 @@  static gboolean big_info_report_cb(GIOChannel *io, GIOCondition cond,
 		g_io_channel_shutdown(io, TRUE, NULL);
 	}
 
-	/* Notify the BASS plugin about the session. */
-	bass_bcast_probe(data->device, data->bap);
-
 	/* Analyze received BASE data and create remote media endpoints for each
 	 * BIS matching our capabilities
 	 */
@@ -2729,6 +2718,8 @@  static void bap_state_bcast_sink(struct bt_bap_stream *stream,
 		return;
 
 	setup = bap_find_setup_by_stream(data, stream);
+	if (!setup)
+		return;
 
 	switch (new_state) {
 	case BT_BAP_STREAM_STATE_IDLE:
@@ -3276,6 +3267,7 @@  static int bap_bcast_probe(struct btd_service *service)
 	struct bap_bcast_pa_req *req;
 	uint8_t type = BAP_PA_LONG_REQ;
 	struct bap_data *data;
+	int ret = 0;
 
 	if (!btd_adapter_has_exp_feature(adapter, EXP_FEAT_ISO_SOCKET)) {
 		error("BAP requires ISO Socket which is not enabled");
@@ -3329,6 +3321,10 @@  static int bap_bcast_probe(struct btd_service *service)
 
 	bt_bap_set_user_data(data->bap, service);
 
+	if (bass_bcast_probe(service, &ret))
+		/* Return if probed device was handled inside BASS. */
+		return ret;
+
 	/* Start the PA timer if it hasn't been started yet */
 	if (data->adapter->pa_timer_id == 0)
 		data->adapter->pa_timer_id = g_timeout_add_seconds(
diff --git a/profiles/audio/bass.c b/profiles/audio/bass.c
index 6237f5acc..e53241a1a 100644
--- a/profiles/audio/bass.c
+++ b/profiles/audio/bass.c
@@ -29,6 +29,7 @@ 
 
 #include "lib/bluetooth.h"
 #include "lib/uuid.h"
+#include "lib/iso.h"
 
 #include "src/dbus-common.h"
 #include "src/shared/util.h"
@@ -42,6 +43,7 @@ 
 #include "src/shared/bap.h"
 #include "src/shared/ad.h"
 
+#include "btio/btio.h"
 #include "src/plugin.h"
 #include "src/gatt-database.h"
 #include "src/device.h"
@@ -101,12 +103,16 @@  struct bass_assistant {
 
 struct bass_delegator {
 	struct btd_device *device;	/* Broadcast source device */
+	struct btd_service *service;
 	struct bt_bcast_src *src;
 	struct bt_bap *bap;
 	unsigned int state_id;
 	uint8_t *bcode;
 	unsigned int timeout;
 	struct queue *bcode_reqs;
+	struct queue *streams;
+	unsigned int io_id;
+	GIOChannel *io;
 };
 
 struct bass_bcode_req {
@@ -218,18 +224,58 @@  static bool delegator_match_device(const void *data, const void *match_data)
 	return dg->device == device;
 }
 
-bool bass_check_bis(struct btd_device *device, uint8_t bis)
+static int stream_get_bis(struct bt_bap_stream *stream)
 {
-	struct bass_delegator *dg;
+	char *path = bt_bap_stream_get_user_data(stream);
+	const char *strbis;
+	int bis;
 
-	dg = queue_find(delegators, delegator_match_device, device);
-	if (!dg)
-		return true;
+	strbis = strstr(path, "/bis");
+	if (!strbis)
+		return 0;
 
-	if (!bt_bass_check_bis(dg->src, bis))
-		return false;
+	if (sscanf(strbis, "/bis%d", &bis) < 0)
+		return 0;
 
-	return true;
+	return bis;
+}
+
+static void append_stream(void *data, void *user_data)
+{
+	struct bt_bap_stream *stream = data;
+	struct sockaddr_iso_bc *addr = user_data;
+	uint8_t bis = stream_get_bis(stream);
+
+	DBG("%d", bis);
+
+	addr->bc_bis[addr->bc_num_bis] = bis;
+	addr->bc_num_bis++;
+}
+
+static bool link_io_unset(const void *data, const void *match_data)
+{
+	struct bt_bap_stream *link = (struct bt_bap_stream *)data;
+
+	return !bt_bap_stream_get_io(link);
+}
+
+static void connect_cb(GIOChannel *io, GError *err, void *user_data)
+{
+	struct bt_bap_stream *stream = user_data;
+	struct queue *links = bt_bap_stream_io_get_links(stream);
+	int fd;
+
+	DBG("");
+
+	/* Set fds for the stream and all its links. */
+	if (bt_bap_stream_get_io(stream))
+		stream = queue_find(links, link_io_unset, NULL);
+
+	fd = g_io_channel_unix_get_fd(io);
+
+	if (bt_bap_stream_set_io(stream, fd)) {
+		g_io_channel_set_close_on_unref(io, FALSE);
+	}
 }
 
 static void bap_state_changed(struct bt_bap_stream *stream, uint8_t old_state,
@@ -237,31 +283,54 @@  static void bap_state_changed(struct bt_bap_stream *stream, uint8_t old_state,
 {
 	struct bass_delegator *dg = user_data;
 	int bis;
-	char *path = bt_bap_stream_get_user_data(stream);
 	struct bt_bap *bap = bt_bap_stream_get_session(stream);
-	const char *strbis;
-	int err;
+	struct sockaddr_iso_bc iso_bc_addr = {0};
+	struct queue *links;
+	GError *gerr = NULL;
+	struct bt_bap_qos *bap_qos = bt_bap_stream_get_qos(stream);
+	struct bt_iso_qos qos;
 
 	if (dg->bap != bap)
 		return;
 
-	strbis = strstr(path, "/bis");
-	if (strbis == NULL) {
-		DBG("bis index cannot be found");
-		return;
-	}
-
-	err = sscanf(strbis, "/bis%d", &bis);
-	if (err < 0) {
-		DBG("sscanf error");
-		return;
-	}
+	bis = stream_get_bis(stream);
 
 	DBG("stream %p: %s(%u) -> %s(%u)", stream,
 			bt_bap_stream_statestr(old_state), old_state,
 			bt_bap_stream_statestr(new_state), new_state);
 
 	switch (new_state) {
+	case BT_BAP_STREAM_STATE_ENABLING:
+		iso_bc_addr.bc_bdaddr_type =
+				btd_device_get_bdaddr_type(dg->device);
+		memcpy(&iso_bc_addr.bc_bdaddr, device_get_address(dg->device),
+				sizeof(bdaddr_t));
+
+		append_stream(stream, &iso_bc_addr);
+
+		links = bt_bap_stream_io_get_links(stream);
+
+		queue_foreach(links, append_stream, &iso_bc_addr);
+
+		bap_qos_to_iso_qos(bap_qos, &qos);
+
+		if (!bt_io_set(dg->io, &gerr,
+				BT_IO_OPT_QOS, &qos,
+				BT_IO_OPT_INVALID)) {
+			error("bt_io_set: %s", gerr->message);
+			g_error_free(gerr);
+			break;
+		}
+
+		if (!bt_io_bcast_accept(dg->io,
+				connect_cb, stream, NULL, &gerr,
+				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", gerr->message);
+			g_error_free(gerr);
+		}
+		break;
 	case BT_BAP_STREAM_STATE_STREAMING:
 		/* BAP stream was started. Mark BIS index as synced inside the
 		 * Broadcast Receive State characteristic and notify peers about
@@ -280,17 +349,79 @@  static void bap_state_changed(struct bt_bap_stream *stream, uint8_t old_state,
 	}
 }
 
-bool bass_bcast_probe(struct btd_device *device, struct bt_bap *bap)
+static void bis_handler(uint8_t bis, uint8_t sgrp, struct iovec *caps,
+	struct iovec *meta, struct bt_iso_qos *qos, void *user_data)
 {
-	struct bass_delegator *dg;
+	struct bass_delegator *dg = user_data;
+	struct bt_bap_pac *lpac;
+	char *path;
+	struct bt_bap_stream *stream;
+	struct bt_bap_qos bap_qos = {0};
 
-	dg = queue_find(delegators, delegator_match_device, device);
-	if (!dg)
-		return false;
+	/* Only handle streams required by the Brodcast Assistant. */
+	if (!bt_bass_check_bis(dg->src, bis))
+		return;
 
-	DBG("%p", dg);
+	/* Check if this stream caps match any local PAC */
+	bt_bap_verify_bis(dg->bap, bis, caps, &lpac);
+	if (!lpac)
+		return;
+
+	if (asprintf(&path, "%s/bis%d", device_get_path(dg->device), bis) < 0)
+		return;
+
+	bap_iso_qos_to_bap_qos(qos, &bap_qos);
+
+	stream = bt_bap_stream_new(dg->bap, lpac, NULL, &bap_qos, caps);
+	if (!stream)
+		return;
+
+	queue_push_tail(dg->streams, stream);
+
+	bt_bap_stream_set_user_data(stream, path);
+	bt_bap_stream_config(stream, &bap_qos,
+			caps, NULL, NULL);
+	bt_bap_stream_metadata(stream, meta,
+			NULL, NULL);
+}
+
+static gboolean big_info_cb(GIOChannel *io, GIOCondition cond,
+							gpointer user_data)
+{
+	struct bass_delegator *dg = user_data;
+	GError *err = NULL;
+	struct bt_iso_base base;
+	struct bt_iso_qos qos;
+
+	dg->io_id = 0;
+
+	bt_io_get(io, &err,
+			BT_IO_OPT_BASE, &base,
+			BT_IO_OPT_QOS, &qos,
+			BT_IO_OPT_INVALID);
+	if (err) {
+		error("%s", err->message);
+		g_error_free(err);
+		return FALSE;
+	}
+
+	parse_base(&base, &qos, bass_debug, bis_handler, dg);
+
+	return FALSE;
+}
+
+static void confirm_cb(GIOChannel *io, void *user_data)
+{
+	struct bass_delegator *dg = user_data;
+
+	DBG("");
 
-	dg->bap = bap;
+	/* Close the listen io */
+	g_io_channel_shutdown(dg->io, TRUE, NULL);
+	g_io_channel_unref(dg->io);
+
+	g_io_channel_ref(io);
+	dg->io = io;
 
 	/* Update Broadcast Receive State characteristic value and notify
 	 * peers.
@@ -298,15 +429,64 @@  bool bass_bcast_probe(struct btd_device *device, struct bt_bap *bap)
 	if (bt_bass_set_pa_sync(dg->src, BT_BASS_SYNCHRONIZED_TO_PA))
 		DBG("Failed to update Broadcast Receive State characteristic");
 
-	/* Register BAP stream state changed callback, to keep up to
-	 * date with BIG/PA sync state.
-	 */
-	dg->state_id = bt_bap_state_register(bap, bap_state_changed,
+	/* Register BAP stream state changed callback. */
+	dg->state_id = bt_bap_state_register(dg->bap, bap_state_changed,
 			NULL, dg, NULL);
 
+	dg->io_id = g_io_add_watch(io, G_IO_OUT, big_info_cb, dg);
+}
+
+bool bass_bcast_probe(struct btd_service *service, int *ret)
+{
+	struct btd_device *device = btd_service_get_device(service);
+	struct btd_adapter *adapter = device_get_adapter(device);
+	struct bass_delegator *dg;
+	GError *err = NULL;
+
+	dg = queue_find(delegators, delegator_match_device, device);
+	if (!dg)
+		/* Only probe devices added via Broadcast Assistants */
+		return false;
+
+	if (dg->service) {
+		/* Service has already been probed */
+		*ret = -EINVAL;
+		return true;
+	}
+
+	dg->service = service;
+	dg->bap = bap_get_session(device);
+
+	dg->io = bt_io_listen(NULL, confirm_cb, dg,
+		NULL, &err,
+		BT_IO_OPT_SOURCE_BDADDR,
+		btd_adapter_get_address(adapter),
+		BT_IO_OPT_SOURCE_TYPE,
+		btd_adapter_get_address_type(adapter),
+		BT_IO_OPT_DEST_BDADDR,
+		device_get_address(device),
+		BT_IO_OPT_DEST_TYPE,
+		btd_device_get_bdaddr_type(device),
+		BT_IO_OPT_MODE, BT_IO_MODE_ISO,
+		BT_IO_OPT_QOS, &bap_sink_pa_qos,
+		BT_IO_OPT_INVALID);
+	if (!dg->io) {
+		error("%s", err->message);
+		*ret = -err->code;
+		g_error_free(err);
+	}
+
 	return true;
 }
 
+static void stream_clear(void *data, void *user_data)
+{
+	struct bt_bap_stream *stream = data;
+	struct bass_delegator *dg = user_data;
+
+	bt_bass_clear_bis_sync(dg->src, stream_get_bis(stream));
+}
+
 bool bass_bcast_remove(struct btd_device *device)
 {
 	struct bass_delegator *dg;
@@ -317,6 +497,17 @@  bool bass_bcast_remove(struct btd_device *device)
 
 	DBG("%p", dg);
 
+	if (dg->io_id)
+		g_source_remove(dg->io_id);
+
+	if (dg->io) {
+		g_io_channel_shutdown(dg->io, TRUE, NULL);
+		g_io_channel_unref(dg->io);
+	}
+
+	queue_foreach(dg->streams, stream_clear, dg);
+	queue_destroy(dg->streams, NULL);
+
 	/* Update Broadcast Receive State characteristic value and notify
 	 * peers.
 	 */
@@ -895,6 +1086,7 @@  probe:
 	dg->device = device;
 	dg->src = bcast_src;
 	dg->bcode_reqs = queue_new();
+	dg->streams = queue_new();
 
 	if (!delegators)
 		delegators = queue_new();
@@ -903,8 +1095,10 @@  probe:
 
 	DBG("delegator %p", dg);
 
-	/* Probe device with BAP. */
-	bap_scan_delegator_probe(device);
+	/* Add Broadcast Audio Announcement Service UUID
+	 * to device and probe service.
+	 */
+	btd_device_add_uuid(device, BCAAS_UUID_STR);
 
 	return 0;
 }
diff --git a/profiles/audio/bass.h b/profiles/audio/bass.h
index 257346374..f299ac029 100644
--- a/profiles/audio/bass.h
+++ b/profiles/audio/bass.h
@@ -12,11 +12,9 @@  void bass_add_stream(struct btd_device *device, struct iovec *meta,
 			uint8_t sgrp, uint8_t bis);
 void bass_remove_stream(struct btd_device *device);
 
-bool bass_bcast_probe(struct btd_device *device, struct bt_bap *bap);
+bool bass_bcast_probe(struct btd_service *service, int *ret);
 bool bass_bcast_remove(struct btd_device *device);
 
-bool bass_check_bis(struct btd_device *device, uint8_t bis);
-
 typedef void (*bt_bass_bcode_func_t)(void *user_data, int err);
 
 void bass_req_bcode(struct bt_bap_stream *stream,