diff mbox series

[BlueZ,v4,5/5] bap: Set generated BASE on all setups from the same BIG

Message ID 20240223164813.66484-6-silviu.barbulescu@nxp.com
State New
Headers show
Series Add support for multiple BISes on the bcast source | expand

Commit Message

Silviu Florian Barbulescu Feb. 23, 2024, 4:48 p.m. UTC
Set the BASE generated in BlueZ on all setups from the same BIG,
and use defer to inform the kernel when to create the BIG.

---
 profiles/audio/bap.c | 212 +++++++++++++++++++++++++++++++++++++++----
 1 file changed, 196 insertions(+), 16 deletions(-)
diff mbox series

Patch

diff --git a/profiles/audio/bap.c b/profiles/audio/bap.c
index 2c7daa6d4..38577bcf3 100644
--- a/profiles/audio/bap.c
+++ b/profiles/audio/bap.c
@@ -950,8 +950,12 @@  static DBusMessage *set_configuration(DBusConnection *conn, DBusMessage *msg,
 	if (dbus_message_iter_get_arg_type(&props) != DBUS_TYPE_DICT_ENTRY)
 		return btd_error_invalid_args(msg);
 
-	/* Disconnect IOs if connecting since QoS is going to be reconfigured */
-	ep_close(ep);
+	/* Broadcast source supports multiple setups, each setup will be BIS
+	 * and will be configured with the set_configuration command
+	 * TO DO reconfiguration of a BIS.
+	 */
+	if (bt_bap_pac_get_type(ep->lpac) != BT_BAP_BCAST_SOURCE)
+		ep_close(ep);
 
 	setup = setup_new(ep);
 
@@ -987,7 +991,6 @@  static DBusMessage *set_configuration(DBusConnection *conn, DBusMessage *msg,
 		if (bt_bap_pac_get_type(ep->lpac) == BT_BAP_BCAST_SINK)
 			setup->msg = dbus_message_ref(msg);
 		else {
-			setup->base = bt_bap_stream_get_base(setup->stream);
 			setup->id = 0;
 		}
 
@@ -1853,7 +1856,7 @@  static void setup_connect_io(struct bap_data *data, struct bap_setup *setup,
 static void setup_connect_io_broadcast(struct bap_data *data,
 					struct bap_setup *setup,
 					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;
@@ -1890,7 +1893,7 @@  static void setup_connect_io_broadcast(struct bap_data *data,
 			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) {
@@ -2019,9 +2022,6 @@  static void setup_create_bcast_io(struct bap_data *data,
 
 	memset(&iso_qos, 0, sizeof(iso_qos));
 
-	if (!defer)
-		goto done;
-
 	iso_qos.bcast.big = setup->qos.bcast.big;
 	iso_qos.bcast.bis = setup->qos.bcast.bis;
 	iso_qos.bcast.sync_factor = setup->qos.bcast.sync_factor;
@@ -2038,9 +2038,10 @@  static void setup_create_bcast_io(struct bap_data *data,
 	iso_qos.bcast.timeout = setup->qos.bcast.timeout;
 	memcpy(&iso_qos.bcast.out, &setup->qos.bcast.io_qos,
 				sizeof(struct bt_iso_io_qos));
-done:
+
 	if (bt_bap_pac_get_type(setup->ep->lpac) == BT_BAP_BCAST_SOURCE)
-		setup_connect_io_broadcast(data, setup, stream, &iso_qos);
+		setup_connect_io_broadcast(data, setup, stream, &iso_qos,
+			defer);
 	else
 		setup_listen_io_broadcast(data, setup, stream, &iso_qos);
 }
@@ -2128,11 +2129,135 @@  static void bap_state(struct bt_bap_stream *stream, uint8_t old_state,
 	}
 }
 
+/* This function will call setup_create_io on all BISes from a BIG.
+ * The defer parameter will be set on true on all but the last one.
+ * This is done to inform the kernel when to when to start the BIG.
+ */
+static bool create_io_bises(struct bap_setup *setup,
+				uint8_t nb_bises, struct bap_data *data)
+{
+	const struct queue_entry *entry;
+	struct bap_setup *ent_setup;
+	bool defer = true;
+	uint8_t active_bis_cnt = 1;
+
+	for (entry = queue_get_entries(setup->ep->setups);
+				entry; entry = entry->next) {
+		ent_setup = entry->data;
+
+		if (bt_bap_stream_get_qos(ent_setup->stream)->bcast.big !=
+				bt_bap_stream_get_qos(setup->stream)->bcast.big)
+			continue;
+
+		if (active_bis_cnt == nb_bises)
+			defer = false;
+
+		setup_create_io(data, ent_setup, ent_setup->stream, defer);
+		if (!ent_setup->io) {
+			error("Unable to create io");
+			goto fail;
+		}
+
+		active_bis_cnt++;
+	}
+
+	return true;
+
+fail:
+	/* Clear the io of the created sockets if one
+	 * socket creation fails.
+	 */
+	for (entry = queue_get_entries(setup->ep->setups);
+				entry; entry = entry->next) {
+		ent_setup = entry->data;
+
+		if (bt_bap_stream_get_qos(ent_setup->stream)->bcast.big !=
+				bt_bap_stream_get_qos(setup->stream)->bcast.big)
+			continue;
+
+		if (setup->io)
+			g_io_channel_unref(setup->io);
+	}
+	return false;
+}
+
+static void iterate_setup_update_base(void *data, void *user_data)
+{
+	struct bap_setup *setup = data;
+	struct bap_setup *data_setup = user_data;
+
+	if ((setup->stream != data_setup->stream) &&
+		(setup->qos.bcast.big == data_setup->qos.bcast.big)) {
+
+		if (setup->base)
+			util_iov_free(setup->base, 1);
+
+		setup->base = util_iov_dup(data_setup->base, 1);
+	}
+}
+
+/* Function checks the state of all streams in the same BIG
+ * as the parameter stream, so it can decide if any sockets need
+ * to be created. Returns he number of streams that need a socket
+ * from that BIG.
+ */
+static uint8_t get_streams_nb_by_state(struct bap_setup *setup)
+{
+	const struct queue_entry *entry;
+	struct bap_setup *ent_setup;
+	uint8_t stream_cnt = 0;
+
+	if (setup->qos.bcast.big == BT_ISO_QOS_BIG_UNSET)
+		/* If BIG ID is unset this is a single BIS BIG.
+		 * return 1 as create one socket only for this BIS
+		 */
+		return 1;
+
+	for (entry = queue_get_entries(setup->ep->setups);
+				entry; entry = entry->next) {
+		ent_setup = entry->data;
+
+		/* Skip the curent stream form testing */
+		if (ent_setup == setup) {
+			stream_cnt++;
+			continue;
+		}
+
+		/* Test only BISes for the same BIG */
+		if (bt_bap_stream_get_qos(ent_setup->stream)->bcast.big !=
+				bt_bap_stream_get_qos(setup->stream)->bcast.big)
+			continue;
+
+		if (bt_bap_stream_get_state(ent_setup->stream) ==
+				BT_BAP_STREAM_STATE_STREAMING)
+			/* If one stream in a multiple BIS BIG is in
+			 * streaming state this means that just the current
+			 * stream must have is socket created so return 1.
+			 */
+			return 1;
+		else if (bt_bap_stream_get_state(ent_setup->stream) !=
+				BT_BAP_STREAM_STATE_CONFIG)
+			/* Not all streams form a BIG have received transport
+			 * acquire, so wait for the other streams to.
+			 */
+			return 0;
+
+		stream_cnt++;
+	}
+
+	/* Return the number of streams for the BIG
+	 * as all are ready to create sockets
+	 */
+	return stream_cnt;
+}
+
 static void bap_state_bcast(struct bt_bap_stream *stream, uint8_t old_state,
 				uint8_t new_state, void *user_data)
 {
 	struct bap_data *data = user_data;
 	struct bap_setup *setup;
+	bool defer = false;
+	uint8_t nb_bises = 0;
 
 	DBG("stream %p: %s(%u) -> %s(%u)", stream,
 			bt_bap_stream_statestr(old_state), old_state,
@@ -2153,14 +2278,69 @@  static void bap_state_bcast(struct bt_bap_stream *stream, uint8_t old_state,
 			queue_remove(data->streams, stream);
 		break;
 	case BT_BAP_STREAM_STATE_CONFIG:
-		if (setup && !setup->id) {
-			setup_create_io(data, setup, stream, true);
+		if (!setup || setup->id)
+			break;
+		if (bt_bap_stream_io_dir(stream) ==
+				BT_BAP_BCAST_SOURCE) {
+			/* If the stream is attached to a
+			 * broadcast sink endpoint.
+			 */
+			setup_create_io(data, setup, stream, defer);
 			if (!setup->io) {
 				error("Unable to create io");
-				if (old_state != BT_BAP_STREAM_STATE_RELEASING)
-					bt_bap_stream_release(stream, NULL,
-								NULL);
-				return;
+				if (old_state !=
+					BT_BAP_STREAM_STATE_RELEASING)
+					bt_bap_stream_release(stream,
+							NULL, NULL);
+			}
+		} else {
+			/* If the stream attached to a broadcast
+			 * source endpoint generate the base.
+			 */
+			if (setup->base == NULL) {
+				setup->base = bt_bap_stream_get_base(
+						setup->stream);
+				/* Set the generated BASE on all setups
+				 * from the same BIG.
+				 */
+				queue_foreach(setup->ep->setups,
+					iterate_setup_update_base, setup);
+			}
+			/* The kernel has 2 requirements when handling
+			 * multiple BIS connections for the same BIG:
+			 * 1 - setup_create_io for all but the last BIS
+			 * must be with defer true so we can inform the
+			 * kernel when to start the BIG.
+			 * 2 - The order in which the setup_create_io
+			 * are called must be in the order of BIS
+			 * indexes in BASE from first to last.
+			 * To address this requirement we will call
+			 * setup_create_io on all BISes only when all
+			 * transport acquire have been received and will
+			 * send it in the order of the BIS index
+			 * from BASE.
+			 */
+			nb_bises = get_streams_nb_by_state(setup);
+
+			if (nb_bises == 1) {
+				setup_create_io(data, setup,
+				stream, defer);
+				if (!setup->io) {
+					error("Unable to create io");
+					if (old_state !=
+						BT_BAP_STREAM_STATE_RELEASING)
+						bt_bap_stream_release(stream,
+								NULL, NULL);
+				}
+				break;
+			} else if (nb_bises == 0)
+				break;
+
+			if (!create_io_bises(setup, nb_bises, data)) {
+				if (old_state !=
+					BT_BAP_STREAM_STATE_RELEASING)
+					bt_bap_stream_release(stream,
+						NULL, NULL);
 			}
 		}
 		break;