Thread (10 messages) flat view 10 messages, 2 authors, 5d ago
COOLING5d

Revision v3 of 3 in this series.

Revisions (3)
  1. v1 [diff vs current]
  2. v2 [diff vs current]
  3. v3 current

[PATCH BlueZ v3 7/8] client/btpclient: CIG/CIS assignment based on Client/Server role

From: Frédéric Danis <hidden>
Date: 2026-09-10 08:49:28
Subsystem: the rest · Maintainer: Linus Torvalds

Previously, CIG/CIS ids were eagerly assigned to each ASE as soon as
it was discovered (CIG 0, next available CIS), regardless of whether
the btpclient was acting as the Unicast Client or Server for that ASE.
This did not match how CIG/CIS ids are actually negotiated, nor what
auto-pts expects: unicast client tests expect CIG/CIS to start at 0/0,
while unicast server tests expect them to start at 1/1.

- As Unicast Client, the CIG/CIS ids are chosen locally when building
  the QoS properties returned from SelectProperties(), starting at 0.
- As Unicast Server, the CIG/CIS ids are only known once the transport
  reports them; if not yet available, all of the device's ASEs should
  share a common CIG (1) with CIS ids starting at 1 as well.

Move get_next_cis() from bap.c to ascs.c and extend it to take a base
CIS value, so it can be reused both for the Client and Server code
paths (base 0 and base 1 respectively) and skips ASEs whose CIS id is
still unset. Leave ase->cig_id and ase->cis_id unset
(BT_ISO_QOS_CIG_UNSET/BT_ISO_QOS_CIS_UNSET) at ASE discovery time, and
resolve them lazily:

- In get_properties_reply(), assign CIG 0 and the next CIS starting at
  0 when building the QoS reply for SelectProperties() (Client role).
- In ascs_proxy_added(), when the transport does not yet report a
  CIG/CIS, assign CIG 1 to all of the device's ASEs and derive CIS ids
  starting at 1 via the new set_cig_cis()/set_cig_cis_data helpers
  (Server role).
---
 client/btpclient/ascs.c | 144 ++++++++++++++++++++++++++++++++++------
 client/btpclient/bap.c  |  74 ++-------------------
 2 files changed, 127 insertions(+), 91 deletions(-)
diff --git a/client/btpclient/ascs.c b/client/btpclient/ascs.c
index 85f6f171a..6b6154614 100644
--- a/client/btpclient/ascs.c
+++ b/client/btpclient/ascs.c
@@ -918,6 +918,76 @@ static void ltv_find(size_t i, uint8_t l, uint8_t t, uint8_t *v,
 	*found = true;
 }
 
+static uint8_t get_next_cis(struct btp_device *device, uint8_t dir,
+							uint8_t base)
+{
+	const struct l_queue_entry *adapter_entry;
+	const struct l_queue_entry *ase_entry;
+	uint8_t cis = base;
+	bool found = false;
+
+	/* For the same device, reuse the opposite cis_id if there is no ASE
+	 * in the requested direction already using that same cis_id
+	 */
+	for (ase_entry = l_queue_get_entries(device->ases); ase_entry;
+					ase_entry = ase_entry->next) {
+		struct btp_ase *ase = ase_entry->data;
+		const struct l_queue_entry *entry;
+		bool has_same_dir = false;
+
+		if (ase->dir == dir)
+			continue;
+
+		for (entry = l_queue_get_entries(device->ases); entry;
+					entry = entry->next) {
+			struct btp_ase *peer = entry->data;
+
+			if (peer->dir == dir && peer->cis_id == ase->cis_id) {
+				has_same_dir = true;
+				break;
+			}
+		}
+
+		if (!has_same_dir)
+			return ase->cis_id;
+	}
+
+	/* Else returns the global highest cis_id + 1 across all ASEs of all
+	 * devices, or 'base' if none has already been assigned
+	 */
+	for (adapter_entry = l_queue_get_entries(get_adapters_list());
+					adapter_entry;
+					adapter_entry = adapter_entry->next) {
+		struct btp_adapter *adapter = adapter_entry->data;
+		const struct l_queue_entry *device_entry;
+
+		for (device_entry = l_queue_get_entries(adapter->devices);
+					device_entry;
+					device_entry = device_entry->next) {
+			struct btp_device *dev = device_entry->data;
+
+			for (ase_entry = l_queue_get_entries(dev->ases);
+						ase_entry;
+						ase_entry = ase_entry->next) {
+				struct btp_ase *ase = ase_entry->data;
+
+				if (ase->cis_id == BT_ISO_QOS_CIS_UNSET)
+					continue;
+
+				if (!found || ase->cis_id > cis)
+					cis = ase->cis_id;
+
+				found = true;
+			}
+		}
+	}
+
+	if (!found)
+		return cis;
+
+	return cis + 1;
+}
+
 static struct l_dbus_message *get_properties_reply(
 						struct l_dbus_message *message,
 						struct btp_adapter *adapter,
@@ -1019,27 +1089,31 @@ static struct l_dbus_message *get_properties_reply(
 	l_dbus_message_builder_enter_variant(builder, "a{sv}");
 	l_dbus_message_builder_enter_array(builder, "{sv}");
 
-	if (ase->cig_id != BT_ISO_QOS_CIG_UNSET) {
-		l_dbus_message_builder_enter_dict(builder, "sv");
-		l_dbus_message_builder_append_basic(builder, 's',
-						"CIG");
-		l_dbus_message_builder_enter_variant(builder, "y");
-		l_dbus_message_builder_append_basic(builder, 'y',
-						&ase->cig_id);
-		l_dbus_message_builder_leave_variant(builder);
-		l_dbus_message_builder_leave_dict(builder);
-	}
+	/* SelectProperties() is only invoked when acting as the Unicast Client,
+	 * so assign CIG 0 here and pick the next available CIS for this ASE.
+	 */
+	if (ase->cig_id == BT_ISO_QOS_CIG_UNSET)
+		ase->cig_id = 0;
+	if (ase->cis_id == BT_ISO_QOS_CIS_UNSET)
+		ase->cis_id = get_next_cis(ase->device, ase->dir, 0);
 
-	if (ase->cis_id != BT_ISO_QOS_CIS_UNSET) {
-		l_dbus_message_builder_enter_dict(builder, "sv");
-		l_dbus_message_builder_append_basic(builder, 's',
-						"CIS");
-		l_dbus_message_builder_enter_variant(builder, "y");
-		l_dbus_message_builder_append_basic(builder, 'y',
-						&ase->cis_id);
-		l_dbus_message_builder_leave_variant(builder);
-		l_dbus_message_builder_leave_dict(builder);
-	}
+	l_dbus_message_builder_enter_dict(builder, "sv");
+	l_dbus_message_builder_append_basic(builder, 's',
+					"CIG");
+	l_dbus_message_builder_enter_variant(builder, "y");
+	l_dbus_message_builder_append_basic(builder, 'y',
+					&ase->cig_id);
+	l_dbus_message_builder_leave_variant(builder);
+	l_dbus_message_builder_leave_dict(builder);
+
+	l_dbus_message_builder_enter_dict(builder, "sv");
+	l_dbus_message_builder_append_basic(builder, 's',
+					"CIS");
+	l_dbus_message_builder_enter_variant(builder, "y");
+	l_dbus_message_builder_append_basic(builder, 'y',
+					&ase->cis_id);
+	l_dbus_message_builder_leave_variant(builder);
+	l_dbus_message_builder_leave_dict(builder);
 
 	l_dbus_message_builder_enter_dict(builder, "sv");
 	l_dbus_message_builder_append_basic(builder, 's',
@@ -1283,6 +1357,24 @@ static bool transport_get_cig_cis(struct l_dbus_proxy *proxy, uint8_t *cig,
 	return true;
 }
 
+struct set_cig_cis_data {
+	struct btp_device *device;
+	uint8_t cig;
+};
+
+static void set_cig_cis(void *data, void *user_data)
+{
+	struct btp_ase *ase = data;
+	struct set_cig_cis_data *param = user_data;
+	uint8_t cis = get_next_cis(param->device, ase->dir, 1);
+
+	if (ase->cig_id != BT_ISO_QOS_CIG_UNSET)
+		return;
+
+	ase->cig_id = param->cig;
+	ase->cis_id = cis;
+}
+
 void ascs_proxy_added(struct l_dbus_proxy *proxy, void *user_data)
 {
 	char *str, *state;
@@ -1307,8 +1399,18 @@ void ascs_proxy_added(struct l_dbus_proxy *proxy, void *user_data)
 	else
 		dir = BTP_BAP_DIR_SINK;
 
-	if (!transport_get_cig_cis(proxy, &cig, &cis))
+	if (!transport_get_cig_cis(proxy, &cig, &cis)) {
+		struct set_cig_cis_data data;
+
+		/* No CIG/CIS reported on the transport yet, meaning this ASE
+		 * is acting as the Unicast Server: assign CIG 1 to all of the
+		 * device's ASEs and let set_cig_cis() derive their CIS ids.
+		 */
+		data.device = device;
+		data.cig = 1;
+		l_queue_foreach(device->ases, set_cig_cis, &data);
 		return;
+	}
 
 	ase = find_ase(device, cig, cis, dir);
 	if (!ase)
diff --git a/client/btpclient/bap.c b/client/btpclient/bap.c
index b1d70a50d..edb220f54 100644
--- a/client/btpclient/bap.c
+++ b/client/btpclient/bap.c
@@ -317,72 +317,6 @@ failed:
 	btp_send_error(btp, BTP_BAP_SERVICE, adapter->index, BTP_ERROR_FAIL);
 }
 
-static uint8_t get_next_cis(struct btp_device *device, uint8_t dir)
-{
-	const struct l_queue_entry *adapter_entry;
-	const struct l_queue_entry *ase_entry;
-	uint8_t cis = 0;
-	bool found = false;
-
-	/* For the same device, reuse the opposite cis_id if there is no ASE
-	 * in the requested direction already using that same cis_id
-	 */
-	for (ase_entry = l_queue_get_entries(device->ases); ase_entry;
-					ase_entry = ase_entry->next) {
-		struct btp_ase *ase = ase_entry->data;
-		const struct l_queue_entry *entry;
-		bool has_same_dir = false;
-
-		if (ase->dir == dir)
-			continue;
-
-		for (entry = l_queue_get_entries(device->ases); entry;
-					entry = entry->next) {
-			struct btp_ase *peer = entry->data;
-
-			if (peer->dir == dir && peer->cis_id == ase->cis_id) {
-				has_same_dir = true;
-				break;
-			}
-		}
-
-		if (!has_same_dir)
-			return ase->cis_id;
-	}
-
-	/* Else returns the global highest cis_id + 1 across all ASEs of all
-	 * devices, or 0 if no ASE exists
-	 */
-	for (adapter_entry = l_queue_get_entries(get_adapters_list());
-					adapter_entry;
-					adapter_entry = adapter_entry->next) {
-		struct btp_adapter *adapter = adapter_entry->data;
-		const struct l_queue_entry *device_entry;
-
-		for (device_entry = l_queue_get_entries(adapter->devices);
-					device_entry;
-					device_entry = device_entry->next) {
-			struct btp_device *dev = device_entry->data;
-
-			for (ase_entry = l_queue_get_entries(dev->ases);
-						ase_entry;
-						ase_entry = ase_entry->next) {
-				struct btp_ase *ase = ase_entry->data;
-
-				if (!found || ase->cis_id > cis)
-					cis = ase->cis_id;
-
-				found = true;
-			}
-		}
-	}
-
-	if (!found)
-		return 0;
-
-	return cis + 1;
-}
-
 void bap_proxy_added(struct l_dbus_proxy *proxy, void *user_data)
 {
 	struct btp_device *device = user_data;
@@ -403,8 +337,8 @@ void bap_proxy_added(struct l_dbus_proxy *proxy, void *user_data)
 			ase->device = device;
 			ase->dir = BTP_BAP_DIR_SINK;
 			ase->uuid = uuid;
-			ase->cig_id = 0;
-			ase->cis_id = get_next_cis(device, ase->dir);
+			ase->cig_id = BT_ISO_QOS_CIG_UNSET;
+			ase->cis_id = BT_ISO_QOS_CIS_UNSET;
 			l_queue_push_tail(device->ases, ase);
 
 			l_dbus_proxy_method_call(proxy, "ReadValue",
@@ -420,8 +354,8 @@ void bap_proxy_added(struct l_dbus_proxy *proxy, void *user_data)
 			ase->device = device;
 			ase->dir = BTP_BAP_DIR_SOURCE;
 			ase->uuid = uuid;
-			ase->cig_id = 0;
-			ase->cis_id = get_next_cis(device, ase->dir);
+			ase->cig_id = BT_ISO_QOS_CIG_UNSET;
+			ase->cis_id = BT_ISO_QOS_CIS_UNSET;
 			l_queue_push_tail(device->ases, ase);
 
 			l_dbus_proxy_method_call(proxy, "ReadValue",
-- 
2.43.0
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help