public inbox for linux-bluetooth@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH BlueZ v3 0/7] Add TMAP & GMAP information services
@ 2025-12-02 20:47 Pauli Virtanen
  2025-12-02 20:47 ` [PATCH BlueZ v3 1/7] profile: add after_services for ordering profile startup Pauli Virtanen
                   ` (7 more replies)
  0 siblings, 8 replies; 10+ messages in thread
From: Pauli Virtanen @ 2025-12-02 20:47 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Pauli Virtanen

Add support for TMAP and GMAP services. They contain only device
audio capability bitmasks.

v3:
- rename after_uuids -> after_services + make it a struct
- add tests for btd_profile_sort_list()
- leave it as the GSList version for now, tests use queue
- add commits to mark VCP connected only after its attach has finished

v2:
- Rework the service wait to be general mechanism that also determines
  the service autoconnect order.

  This is now slightly more involved, but this sort of "soft" ordering
  dependency must know which services the autoconnect mechanism is going
  to start.

  Sorting autoconnect services is one of the ways to do it, probably
  makes sense also otherwise, and we could insert service conflict
  handling at the same place.

- Make org.bluez.MediaEndpoint::SupportedFeatures per-uuid dict

---

Expose the values from remote devices in
org.bluez.MediaEndpoint->SupportedFeatures

This maybe could also be org.bluez.Device->SupportedFeatures instead,
but MediaEndpoint looks OK too.

Sound server can use theses to determine which mandatory capabilities
devices have.

TODO (maybe later): add way to configure advertised local service
values, e.g.  via config file.

Pauli Virtanen (7):
  profile: add after_services for ordering profile startup
  test-profile: add tests for profile sorting
  device: use after_services in service autoconnect and sort also GATT
  service: implement btd_profile::after_services callback
  bap: have unicast client wait for VCS & TMAS & GMAP
  shared/vcp: add ready callback to bt_vcp_attach()
  vcp: wait until client ready before marking profile connected

 .gitignore           |   1 +
 Makefile.am          |  22 ++-
 profiles/audio/bap.c |  36 ++++-
 profiles/audio/vcp.c |  11 +-
 src/device.c         |  74 ++++++++--
 src/profile.c        |  90 +++++++++++
 src/profile.h        |  26 ++++
 src/service.c        | 106 +++++++++++++
 src/shared/vcp.c     |  29 +++-
 src/shared/vcp.h     |   3 +-
 unit/btd.c           |  41 ++++++
 unit/test-profile.c  | 344 +++++++++++++++++++++++++++++++++++++++++++
 12 files changed, 758 insertions(+), 25 deletions(-)
 create mode 100644 unit/btd.c
 create mode 100644 unit/test-profile.c

-- 
2.51.1


^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH BlueZ v3 1/7] profile: add after_services for ordering profile startup
  2025-12-02 20:47 [PATCH BlueZ v3 0/7] Add TMAP & GMAP information services Pauli Virtanen
@ 2025-12-02 20:47 ` Pauli Virtanen
  2025-12-02 20:47 ` [PATCH BlueZ v3 2/7] test-profile: add tests for profile sorting Pauli Virtanen
                   ` (6 subsequent siblings)
  7 siblings, 0 replies; 10+ messages in thread
From: Pauli Virtanen @ 2025-12-02 20:47 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Pauli Virtanen

Add btd_profile::after_servicess to specify the profile connect/accept
order for autoconnect.  This is a "soft" dependency so it doesn't fail
if the other services fail to start nor try to start them if they
otherwise wouldn't.

Add btd_profile_sort_list() for sorting a list according to profile
ordering, taking account priority and after_services.

Add btd_profile_find_remote_uuid() lookup utility, needed when using
btd_profile_sort_list() with uuid lists.
---

Notes:
    v3:
    - rename to after_services
    - put callback and uuids inside a struct

 src/profile.c | 90 +++++++++++++++++++++++++++++++++++++++++++++++++++
 src/profile.h | 26 +++++++++++++++
 2 files changed, 116 insertions(+)

diff --git a/src/profile.c b/src/profile.c
index 66405d7e4..46a3d3b45 100644
--- a/src/profile.c
+++ b/src/profile.c
@@ -773,6 +773,30 @@ static struct btd_profile *btd_profile_find_uuid(const char *uuid)
 	return NULL;
 }
 
+struct btd_profile *btd_profile_find_remote_uuid(const char *uuid)
+{
+	GSList *l, *next;
+
+	for (l = profiles; l != NULL; l = next) {
+		struct btd_profile *p = l->data;
+
+		if (!g_strcmp0(p->remote_uuid, uuid))
+			return p;
+		next = g_slist_next(l);
+	}
+
+	for (l = ext_profiles; l != NULL; l = next) {
+		struct ext_profile *ext = l->data;
+		struct btd_profile *p = &ext->p;
+
+		if (!g_strcmp0(p->remote_uuid, uuid))
+			return p;
+		next = g_slist_next(l);
+	}
+
+	return NULL;
+}
+
 int btd_profile_register(struct btd_profile *profile)
 {
 	if (profile->experimental && !(g_dbus_get_flags() &
@@ -2650,3 +2674,69 @@ void btd_profile_cleanup(void)
 	g_dbus_unregister_interface(btd_get_dbus_connection(),
 				"/org/bluez", "org.bluez.ProfileManager1");
 }
+
+/* Stable sort of a list according to profile priority & after_services */
+GSList *btd_profile_sort_list(GSList *list, btd_profile_list_get get,
+							void *user_data)
+{
+	GSList *result = NULL;
+	GSList *remain = list;
+	GHashTable *uuids;
+	GSList *entry;
+	const struct btd_profile *p;
+
+	uuids = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
+
+	/* Unsatisfied remote uuids */
+	for (entry = remain; entry; entry = g_slist_next(entry)) {
+		p = get(entry->data, user_data);
+		if (p->remote_uuid)
+			g_hash_table_add(uuids, g_strdup(p->remote_uuid));
+	}
+
+	/* Sort */
+	while (remain) {
+		GSList *first_entry = remain;
+		int max_priority = INT_MIN;
+
+		/* Find max priority */
+		for (entry = remain; entry; entry = g_slist_next(entry)) {
+			p = get(entry->data, user_data);
+			if (p->priority > max_priority) {
+				first_entry = entry;
+				max_priority = p->priority;
+			}
+		}
+
+		/* Find max priority entry with no unsatisfied dependencies */
+		for (entry = remain; entry; entry = g_slist_next(entry)) {
+			unsigned int i;
+
+			p = get(entry->data, user_data);
+			if (p->priority < max_priority)
+				continue;
+
+			for (i = 0; i < p->after_services.count; ++i)
+				if (g_hash_table_contains(uuids,
+						p->after_services.uuids[i]))
+					break;
+			if (i == p->after_services.count)
+				break;
+		}
+
+		/* If cyclic dependencies: break preserving priority & order */
+		if (!entry)
+			entry = first_entry;
+
+		p = get(entry->data, user_data);
+		if (p->remote_uuid)
+			g_hash_table_remove(uuids, p->remote_uuid);
+
+		remain = g_slist_remove_link(remain, entry);
+		result = g_slist_concat(result, entry);
+	}
+
+	g_hash_table_destroy(uuids);
+
+	return result;
+}
diff --git a/src/profile.h b/src/profile.h
index 424ce55ad..6f236a38a 100644
--- a/src/profile.h
+++ b/src/profile.h
@@ -14,6 +14,19 @@
 
 struct btd_service;
 
+#define BTD_PROFILE_UUID_CB(func_, ...) \
+	{ \
+		.func = (func_), \
+		.count = ARRAY_SIZE(((const char *[]) { __VA_ARGS__ })), \
+		.uuids = ((const char *[]) { __VA_ARGS__ }), \
+	}
+
+struct btd_profile_uuid_cb {
+	void (*func)(struct btd_service *service);
+	unsigned int count;
+	const char **uuids;
+};
+
 struct btd_profile {
 	const char *name;
 	int priority;
@@ -38,6 +51,12 @@ struct btd_profile {
 	 */
 	bool testing;
 
+	/* Indicates the profile should be ordered after profiles providing
+	 * these remote uuids when connecting. The callback function is called
+	 * when all uuids have finished connecting (successfully or not).
+	 */
+	struct btd_profile_uuid_cb after_services;
+
 	int (*device_probe) (struct btd_service *service);
 	void (*device_remove) (struct btd_service *service);
 
@@ -76,3 +95,10 @@ bool btd_profile_remove_custom_prop(const char *uuid, const char *name);
 
 void btd_profile_init(void);
 void btd_profile_cleanup(void);
+
+struct btd_profile *btd_profile_find_remote_uuid(const char *uuid);
+
+typedef const struct btd_profile *(*btd_profile_list_get)(void *item,
+							void *user_data);
+GSList *btd_profile_sort_list(GSList *list, btd_profile_list_get get,
+							void *user_data);
-- 
2.51.1


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH BlueZ v3 2/7] test-profile: add tests for profile sorting
  2025-12-02 20:47 [PATCH BlueZ v3 0/7] Add TMAP & GMAP information services Pauli Virtanen
  2025-12-02 20:47 ` [PATCH BlueZ v3 1/7] profile: add after_services for ordering profile startup Pauli Virtanen
@ 2025-12-02 20:47 ` Pauli Virtanen
  2025-12-02 20:47 ` [PATCH BlueZ v3 3/7] device: use after_services in service autoconnect and sort also GATT Pauli Virtanen
                   ` (5 subsequent siblings)
  7 siblings, 0 replies; 10+ messages in thread
From: Pauli Virtanen @ 2025-12-02 20:47 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Pauli Virtanen

Add tests to check btd_profile_sort_list() works correctly.

The test uses queue instead of GSList in case device.c is converted to
use queue later.

Makefile.am: separate out bluetoothd_internal_sources (excludes plugins
& main.c) and add stub for symbols in main.c.  The profile.c pulls in
large parts of these so it's simpler to depend on them all for the test.
This doesn't cause any recompilation.
---

Notes:
    v3:
    - new commit

 .gitignore          |   1 +
 Makefile.am         |  22 ++-
 unit/btd.c          |  41 ++++++
 unit/test-profile.c | 344 ++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 403 insertions(+), 5 deletions(-)
 create mode 100644 unit/btd.c
 create mode 100644 unit/test-profile.c

diff --git a/.gitignore b/.gitignore
index ba29d9d5e..80427e1dd 100644
--- a/.gitignore
+++ b/.gitignore
@@ -120,6 +120,7 @@ unit/test-bass
 unit/test-battery
 unit/test-tmap
 unit/test-gmap
+unit/test-profile
 tools/mgmt-tester
 tools/smp-tester
 tools/gap-tester
diff --git a/Makefile.am b/Makefile.am
index 7221e4cba..e152ae648 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -304,9 +304,9 @@ include Makefile.plugins
 
 pkglibexec_PROGRAMS += src/bluetoothd
 
-src_bluetoothd_SOURCES = $(builtin_sources) \
+bluetoothd_internal_sources = \
 			$(attrib_sources) $(btio_sources) \
-			src/main.c src/log.h src/log.c \
+			src/log.h src/log.c \
 			src/backtrace.h src/backtrace.c \
 			src/rfkill.c src/btd.h src/sdpd.h \
 			src/sdpd-server.c src/sdpd-request.c \
@@ -316,7 +316,6 @@ src_bluetoothd_SOURCES = $(builtin_sources) \
 			src/sdp-client.h src/sdp-client.c \
 			src/textfile.h src/textfile.c \
 			src/uuid-helper.h src/uuid-helper.c \
-			src/plugin.h src/plugin.c \
 			src/storage.h src/storage.c \
 			src/advertising.h src/advertising.c \
 			src/agent.h src/agent.c \
@@ -333,10 +332,17 @@ src_bluetoothd_SOURCES = $(builtin_sources) \
 			src/settings.h src/settings.c \
 			src/set.h src/set.c \
 			src/bearer.h src/bearer.c
-src_bluetoothd_LDADD = lib/libbluetooth-internal.la \
+
+bluetoothd_internal_ldadd = lib/libbluetooth-internal.la \
 			gdbus/libgdbus-internal.la \
 			src/libshared-glib.la \
-			$(BACKTRACE_LIBS) $(GLIB_LIBS) $(DBUS_LIBS) -ldl -lrt \
+			$(BACKTRACE_LIBS) $(GLIB_LIBS) $(DBUS_LIBS) -ldl -lrt
+
+src_bluetoothd_SOURCES = $(builtin_sources) \
+			$(bluetoothd_internal_sources) \
+			src/plugin.h src/plugin.c \
+			src/main.c
+src_bluetoothd_LDADD = $(bluetoothd_internal_ldadd) \
 			$(builtin_ldadd)
 
 if EXTERNAL_PLUGINS
@@ -576,6 +582,12 @@ unit_tests += unit/test-uhid
 unit_test_uhid_SOURCES = unit/test-uhid.c
 unit_test_uhid_LDADD = src/libshared-glib.la $(GLIB_LIBS)
 
+unit_tests += unit/test-profile
+
+unit_test_profile_SOURCES = unit/test-profile.c \
+			unit/btd.c $(bluetoothd_internal_sources)
+unit_test_profile_LDADD = $(bluetoothd_internal_ldadd)
+
 unit_tests += unit/test-sdp
 
 unit_test_sdp_SOURCES = unit/test-sdp.c \
diff --git a/unit/btd.c b/unit/btd.c
new file mode 100644
index 000000000..06b2a1220
--- /dev/null
+++ b/unit/btd.c
@@ -0,0 +1,41 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2000-2001  Qualcomm Incorporated
+ *  Copyright (C) 2002-2003  Maxim Krasnyansky <maxk@qualcomm.com>
+ *  Copyright (C) 2002-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ */
+
+/* Stub replacement for daemon main.c for tests */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <stdbool.h>
+#include <stdint.h>
+
+#include <glib.h>
+
+#include "../src/btd.h"
+
+struct btd_opts btd_opts;
+
+GKeyFile *btd_get_main_conf(void)
+{
+	return NULL;
+}
+
+bool btd_kernel_experimental_enabled(const char *uuid)
+{
+	return false;
+}
+
+void btd_exit(void)
+{
+}
diff --git a/unit/test-profile.c b/unit/test-profile.c
new file mode 100644
index 000000000..31c96596e
--- /dev/null
+++ b/unit/test-profile.c
@@ -0,0 +1,344 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2015  Intel Corporation. All rights reserved.
+ *
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <unistd.h>
+#include <string.h>
+#include <limits.h>
+#include <stdint.h>
+#include <sys/socket.h>
+#include <fcntl.h>
+
+#include <glib.h>
+
+#include "bluetooth/bluetooth.h"
+#include "bluetooth/uuid.h"
+
+#include "src/shared/util.h"
+#include "src/shared/queue.h"
+#include "src/shared/tester.h"
+
+#include "src/adapter.h"
+#include "src/profile.h"
+
+#define FAIL_TEST() \
+	do { tester_warn("%s:%d: failed in %s", __FILE__, __LINE__, __func__); \
+		tester_test_failed(); } while (0)
+
+struct test_config {
+	const struct btd_profile *profiles;
+	unsigned int profiles_count;
+	unsigned int shuffle_count;
+	const char *cycle_break;
+};
+
+struct test_data {
+	const struct test_config *cfg;
+};
+
+#define define_test(name, _cfg, setup, function)		\
+	do {							\
+		static struct test_data data;			\
+		data.cfg = _cfg;				\
+		tester_add(name, &data, setup, function,	\
+						test_teardown);	\
+	} while (0)
+
+static void test_teardown(const void *user_data)
+{
+	tester_teardown_complete();
+}
+
+#define SORT_PROFILE(expect_pos_, ...) \
+	{ .name = UINT_TO_PTR(expect_pos_), __VA_ARGS__ }
+#define AFTER(...) \
+	.after_services = BTD_PROFILE_UUID_CB(NULL, __VA_ARGS__)
+
+const struct test_config sort_priority = {
+	.profiles = (const struct btd_profile []) {
+		SORT_PROFILE(3, .priority = 1),
+		SORT_PROFILE(4, .priority = 1),
+		SORT_PROFILE(1, .priority = 2),
+		SORT_PROFILE(5, .priority = 0),
+		SORT_PROFILE(2, .priority = 2),
+		SORT_PROFILE(6, .priority = 0),
+	},
+	.profiles_count = 6,
+};
+
+const struct test_config sort_after_service = {
+	.profiles = (const struct btd_profile []) {
+		SORT_PROFILE(4, .priority = 1, AFTER("B", "C")),
+		SORT_PROFILE(3, .priority = 1, .remote_uuid = "C"),
+		SORT_PROFILE(2, .priority = 2, AFTER("B")),
+		SORT_PROFILE(1, .priority = 2, .remote_uuid = "B"),
+		SORT_PROFILE(6, .priority = 0),
+		SORT_PROFILE(5, .priority = 1, AFTER("invalid")),
+	},
+	.profiles_count = 6,
+};
+
+const struct test_config sort_cycle = {
+	.profiles = (const struct btd_profile []) {
+		SORT_PROFILE(2, .remote_uuid = "B", AFTER("F")),
+		SORT_PROFILE(4, .remote_uuid = "D", AFTER("A", "C")),
+		SORT_PROFILE(5, .remote_uuid = "E", AFTER("D")),
+		SORT_PROFILE(3, .remote_uuid = "C", AFTER("B")),
+		SORT_PROFILE(6, .remote_uuid = "F", AFTER("E")),
+		SORT_PROFILE(1, .remote_uuid = "A"),
+	},
+	.profiles_count = 6,
+	.cycle_break = "F",
+};
+
+const struct test_config sort_fuzz = {
+	.profiles_count = 50,
+	.shuffle_count = 100,
+};
+
+static const struct btd_profile *sort_get(void *item, void *user_data)
+{
+	return item;
+}
+
+static bool check_sort(struct queue *list, unsigned int count,
+							const char *cycle_break)
+{
+	int priority = INT_MAX;
+	GHashTable *uuids, *items;
+	const struct queue_entry *entry;
+	unsigned int n;
+
+	uuids = g_hash_table_new(g_str_hash, g_str_equal);
+	items = g_hash_table_new(NULL, NULL);
+
+	if (queue_length(list) != count) {
+		FAIL_TEST();
+		return false;
+	}
+
+	for (entry = queue_get_entries(list), n = 0; entry;
+						entry = entry->next, ++n) {
+		const struct btd_profile *profile = entry->data;
+
+		g_hash_table_add(uuids, (void *)profile->remote_uuid);
+	}
+
+	if (cycle_break)
+		g_hash_table_remove(uuids, (void *)cycle_break);
+
+	for (entry = queue_get_entries(list), n = 0; entry;
+						entry = entry->next, ++n) {
+		const struct btd_profile *profile = entry->data;
+		unsigned int i;
+
+		/* No duplicates */
+		if (g_hash_table_contains(items, profile)) {
+			FAIL_TEST();
+			return false;
+		}
+		g_hash_table_add(items, (void *)profile);
+
+		/* Decreasing priority */
+		if (profile->priority > priority) {
+			FAIL_TEST();
+			return false;
+		} else if (profile->priority < priority) {
+			priority = profile->priority;
+		}
+
+		/* Ordered by after_services */
+		g_hash_table_remove(uuids, (void *)profile->remote_uuid);
+
+		for (i = 0; i < profile->after_services.count; ++i) {
+			if (g_hash_table_contains(uuids,
+					profile->after_services.uuids[i])) {
+				FAIL_TEST();
+				return false;
+			}
+		}
+
+		/* Manual sort check */
+		if (profile->name && profile->name != UINT_TO_PTR(n + 1)) {
+			FAIL_TEST();
+			return false;
+		}
+	}
+
+	g_hash_table_destroy(uuids);
+	g_hash_table_destroy(items);
+
+	return true;
+}
+
+static struct queue *make_profile_list(const struct btd_profile *profiles,
+							unsigned int count)
+{
+	struct queue *list = queue_new();
+	unsigned int i;
+
+	for (i = 0; i < count; ++i) {
+		struct btd_profile *profile;
+
+		profile = util_memdup(&profiles[i], sizeof(*profile));
+		if (profile->remote_uuid)
+			profile->remote_uuid = g_strdup(profile->remote_uuid);
+		else
+			profile->remote_uuid = g_strdup_printf("%d", i);
+
+		queue_push_tail(list, profile);
+	}
+
+	return list;
+}
+
+static void free_profile_list(struct queue *list)
+{
+	const struct queue_entry *entry;
+
+	for (entry = queue_get_entries(list); entry; entry = entry->next) {
+		const struct btd_profile *profile = entry->data;
+
+		g_free((void *)profile->remote_uuid);
+		free((void *)profile);
+	}
+
+	queue_destroy(list, NULL);
+}
+
+static void *queue_peek_nth(struct queue *list, unsigned int i)
+{
+	const struct queue_entry *entry;
+	unsigned int n = 0;
+
+	for (entry = queue_get_entries(list); entry; entry = entry->next, n++) {
+		if (n == i)
+			return entry->data;
+	}
+
+	return NULL;
+}
+
+static void shuffle_list(struct queue *list)
+{
+	struct queue *shuffled = queue_new();
+
+	while (!queue_isempty(list)) {
+		int i = g_random_int_range(0, queue_length(list));
+		void *data = queue_peek_nth(list, i);
+
+		queue_remove(list, data);
+		queue_push_tail(shuffled, data);
+	}
+
+	/* Put back to original list */
+	while (!queue_isempty(shuffled))
+		queue_push_tail(list, queue_pop_head(shuffled));
+	queue_destroy(shuffled, NULL);
+}
+
+static void btd_profile_sort(struct queue *queue, btd_profile_list_get get,
+							void *user_data)
+{
+	const struct queue_entry *entry;
+	GSList *list = NULL, *item;
+
+	for (entry = queue_get_entries(queue); entry; entry = entry->next)
+		list = g_slist_append(list, entry->data);
+
+	list = btd_profile_sort_list(list, get, user_data);
+
+	queue_remove_all(queue, NULL, NULL, NULL);
+
+	for (item = list; item; item = item->next)
+		queue_push_tail(queue, item->data);
+
+	g_slist_free(list);
+}
+
+static void test_sort(const void *user_data)
+{
+	struct test_data *data = (void *)user_data;
+	const struct test_config *cfg = data->cfg;
+	struct queue *list;
+
+	list = make_profile_list(cfg->profiles, cfg->profiles_count);
+
+	btd_profile_sort(list, sort_get, NULL);
+	check_sort(list, cfg->profiles_count, cfg->cycle_break);
+
+	free_profile_list(list);
+	tester_test_passed();
+}
+
+static void test_sort_fuzz(const void *user_data)
+{
+	struct test_data *data = (void *)user_data;
+	const struct test_config *cfg = data->cfg;
+	unsigned int i, j;
+
+	for (i = 0; i < cfg->shuffle_count; ++i) {
+		struct queue *list;
+		struct btd_profile profiles[64] = { 0 };
+		char *uuids[64];
+
+		g_random_set_seed(i);
+
+		for (j = 0; j < ARRAY_SIZE(uuids); ++j)
+			uuids[j] = g_strdup_printf("%d", j);
+
+		for (j = 0; j < cfg->profiles_count; ++j) {
+			int count;
+
+			profiles[j].priority = 3 - 3 * j / cfg->profiles_count;
+
+			if (g_random_int_range(0, 3) == 0 || j == 0)
+				continue;
+
+			count = g_random_int_range(1, j + 1);
+			if (count > 5)
+				count = 5;
+			profiles[j].after_services.count = count;
+			profiles[j].after_services.uuids = (const char **)uuids
+				+ g_random_int_range(0, j + 1 - count);
+		}
+
+		list = make_profile_list(profiles, cfg->profiles_count);
+		shuffle_list(list);
+
+		btd_profile_sort(list, sort_get, NULL);
+		if (!check_sort(list, cfg->profiles_count, NULL))
+			return;
+
+		free_profile_list(list);
+
+		for (j = 0; j < ARRAY_SIZE(uuids); ++j)
+			g_free(uuids[j]);
+	}
+
+	tester_test_passed();
+}
+
+int main(int argc, char *argv[])
+{
+	tester_init(&argc, &argv);
+
+	define_test("Sort Priority - Success", &sort_priority, NULL, test_sort);
+	define_test("Sort After Service - Success", &sort_after_service, NULL,
+								test_sort);
+	define_test("Sort Cycle - Success", &sort_cycle, NULL, test_sort);
+	define_test("Sort Fuzz - Success", &sort_fuzz, NULL, test_sort_fuzz);
+
+	return tester_run();
+}
-- 
2.51.1


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH BlueZ v3 3/7] device: use after_services in service autoconnect and sort also GATT
  2025-12-02 20:47 [PATCH BlueZ v3 0/7] Add TMAP & GMAP information services Pauli Virtanen
  2025-12-02 20:47 ` [PATCH BlueZ v3 1/7] profile: add after_services for ordering profile startup Pauli Virtanen
  2025-12-02 20:47 ` [PATCH BlueZ v3 2/7] test-profile: add tests for profile sorting Pauli Virtanen
@ 2025-12-02 20:47 ` Pauli Virtanen
  2025-12-02 20:47 ` [PATCH BlueZ v3 4/7] service: implement btd_profile::after_services callback Pauli Virtanen
                   ` (4 subsequent siblings)
  7 siblings, 0 replies; 10+ messages in thread
From: Pauli Virtanen @ 2025-12-02 20:47 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Pauli Virtanen

Use btd_profile_sort_list() for selecting the order in which services
are connected: first by priority, then by after_services.

Probe and accept also GATT services in profile order. Previously this
was done in the order they were in GATT db.
---

Notes:
    v3:
    - no change
    - GSList *pending -> struct queue *pending refactoring left for later...

 src/device.c | 74 +++++++++++++++++++++++++++++++++++++++++++---------
 1 file changed, 62 insertions(+), 12 deletions(-)

diff --git a/src/device.c b/src/device.c
index eb184633a..3ecbfd67c 100644
--- a/src/device.c
+++ b/src/device.c
@@ -2523,14 +2523,6 @@ static struct btd_service *find_connectable_service(struct btd_device *dev,
 	return NULL;
 }
 
-static int service_prio_cmp(gconstpointer a, gconstpointer b)
-{
-	struct btd_profile *p1 = btd_service_get_profile(a);
-	struct btd_profile *p2 = btd_service_get_profile(b);
-
-	return p2->priority - p1->priority;
-}
-
 bool btd_device_all_services_allowed(struct btd_device *dev)
 {
 	GSList *l;
@@ -2581,6 +2573,12 @@ void btd_device_update_allowed_services(struct btd_device *dev)
 	}
 }
 
+static const struct btd_profile *get_service_profile(void *data,
+								void *user_data)
+{
+	return btd_service_get_profile(data);
+}
+
 static GSList *create_pending_list(struct btd_device *dev, const char *uuid)
 {
 	struct btd_service *service;
@@ -2619,10 +2617,13 @@ static GSList *create_pending_list(struct btd_device *dev, const char *uuid)
 						BTD_SERVICE_STATE_DISCONNECTED)
 			continue;
 
-		dev->pending = g_slist_insert_sorted(dev->pending, service,
-							service_prio_cmp);
+		dev->pending = g_slist_append(dev->pending, service);
 	}
 
+	/* Connect in priority order */
+	dev->pending = btd_profile_sort_list(dev->pending, get_service_profile,
+									NULL);
+
 	return dev->pending;
 }
 
@@ -4630,9 +4631,42 @@ done:
 	service_accept(service, btd_device_is_initiator(device));
 }
 
+
+static const struct btd_profile *get_gatt_profile(void *data, void *user_data)
+{
+	struct gatt_db_attribute *attr = data;
+	struct btd_profile *profile;
+	bt_uuid_t uuid;
+	struct btd_profile *dummy_profile = user_data;
+	char *uuid_str = (char *)dummy_profile->remote_uuid;
+
+	gatt_db_attribute_get_service_uuid(attr, &uuid);
+	bt_uuid_to_string(&uuid, uuid_str, MAX_LEN_UUID_STR);
+
+	profile = btd_profile_find_remote_uuid(uuid_str);
+	if (!profile)
+		profile = dummy_profile;
+
+	return profile;
+}
+
+static void get_gatt_attrs(struct gatt_db_attribute *attr,
+							void *user_data)
+{
+	GSList **list = user_data;
+
+	*list = g_slist_append(*list, attr);
+}
+
 static void device_add_gatt_services(struct btd_device *device)
 {
 	char addr[18];
+	GSList *attrs = NULL;
+	GSList *entry;
+	char uuid_str[MAX_LEN_UUID_STR];
+	struct btd_profile dummy_profile = {
+		.remote_uuid = uuid_str,
+	};
 
 	ba2str(&device->bdaddr, addr);
 
@@ -4641,18 +4675,34 @@ static void device_add_gatt_services(struct btd_device *device)
 		return;
 	}
 
-	gatt_db_foreach_service(device->db, NULL, add_gatt_service, device);
+	/* Probe and accept in profile priority order */
+	gatt_db_foreach_service(device->db, NULL, get_gatt_attrs, &attrs);
+
+	attrs = btd_profile_sort_list(attrs, get_gatt_profile,
+							&dummy_profile);
+
+	for (entry = attrs; entry; entry = g_slist_next(entry))
+		add_gatt_service(entry->data, device);
+
+	g_slist_free(attrs);
 }
 
 static void device_accept_gatt_profiles(struct btd_device *device)
 {
 	GSList *l;
 	bool initiator = btd_device_is_initiator(device);
+	GSList *services;
 
 	DBG("initiator %s", initiator ? "true" : "false");
 
-	for (l = device->services; l != NULL; l = g_slist_next(l))
+	/* Accept in profile priority order */
+	services = g_slist_copy(device->services);
+	services = btd_profile_sort_list(services, get_service_profile, NULL);
+
+	for (l = services; l != NULL; l = g_slist_next(l))
 		service_accept(l->data, initiator);
+
+	g_slist_free(services);
 }
 
 static void device_remove_gatt_service(struct btd_device *device,
-- 
2.51.1


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH BlueZ v3 4/7] service: implement btd_profile::after_services callback
  2025-12-02 20:47 [PATCH BlueZ v3 0/7] Add TMAP & GMAP information services Pauli Virtanen
                   ` (2 preceding siblings ...)
  2025-12-02 20:47 ` [PATCH BlueZ v3 3/7] device: use after_services in service autoconnect and sort also GATT Pauli Virtanen
@ 2025-12-02 20:47 ` Pauli Virtanen
  2025-12-02 20:47 ` [PATCH BlueZ v3 5/7] bap: have unicast client wait for VCS & TMAS & GMAP Pauli Virtanen
                   ` (3 subsequent siblings)
  7 siblings, 0 replies; 10+ messages in thread
From: Pauli Virtanen @ 2025-12-02 20:47 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Pauli Virtanen

Handle btd_profile::after_services callback when after_services
dependencies have finished connecting.
---

Notes:
    v3:
    - after_uuids -> after_services
    - don't log "dependencies ready" if there is no deps or callback

 src/service.c | 106 ++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 106 insertions(+)

diff --git a/src/service.c b/src/service.c
index 7690a1261..8301b1ead 100644
--- a/src/service.c
+++ b/src/service.c
@@ -26,6 +26,8 @@
 #include "bluetooth/bluetooth.h"
 #include "bluetooth/sdp.h"
 
+#include "src/shared/queue.h"
+
 #include "log.h"
 #include "backtrace.h"
 
@@ -43,6 +45,8 @@ struct btd_service {
 	int			err;
 	bool			is_allowed;
 	bool			initiator;
+	struct queue		*depends;
+	struct queue		*dependents;
 };
 
 struct service_state_callback {
@@ -71,6 +75,50 @@ static const char *state2str(btd_service_state_t state)
 	return NULL;
 }
 
+static void depends_ready(void *item, void *user_data)
+{
+	struct btd_service *service = item;
+	struct btd_service *dep = user_data;
+	struct btd_profile_uuid_cb *after = &service->profile->after_services;
+	char addr[18];
+
+	if (dep && !queue_remove(service->depends, dep))
+		return;
+	if (!service->depends || !queue_isempty(service->depends))
+		return;
+
+	queue_destroy(service->depends, NULL);
+	service->depends = NULL;
+
+	if (!after->count && !after->func)
+		return;
+
+	ba2str(device_get_address(service->device), addr);
+	DBG("%p: device %s profile %s dependencies ready", service,
+						addr, service->profile->name);
+
+	switch (service->state) {
+	case BTD_SERVICE_STATE_CONNECTING:
+	case BTD_SERVICE_STATE_CONNECTED:
+		if (after->func)
+			after->func(service);
+		break;
+	case BTD_SERVICE_STATE_UNAVAILABLE:
+	case BTD_SERVICE_STATE_DISCONNECTING:
+	case BTD_SERVICE_STATE_DISCONNECTED:
+		break;
+	}
+}
+
+static void service_ready(struct btd_service *service)
+{
+	queue_foreach(service->dependents, depends_ready, service);
+	queue_destroy(service->dependents, NULL);
+	service->dependents = NULL;
+
+	depends_ready(service, NULL);
+}
+
 static void change_state(struct btd_service *service, btd_service_state_t state,
 									int err)
 {
@@ -98,6 +146,9 @@ static void change_state(struct btd_service *service, btd_service_state_t state,
 		cb->cb(service, old, state, cb->user_data);
 	}
 
+	if (state != BTD_SERVICE_STATE_CONNECTING)
+		service_ready(service);
+
 	if (state == BTD_SERVICE_STATE_DISCONNECTED)
 		service->initiator = false;
 }
@@ -111,6 +162,20 @@ struct btd_service *btd_service_ref(struct btd_service *service)
 	return service;
 }
 
+static void depends_remove(void *item, void *user_data)
+{
+	struct btd_service *service = item;
+
+	queue_remove(service->dependents, user_data);
+}
+
+static void dependents_remove(void *item, void *user_data)
+{
+	struct btd_service *service = item;
+
+	queue_remove(service->depends, user_data);
+}
+
 void btd_service_unref(struct btd_service *service)
 {
 	service->ref--;
@@ -120,6 +185,11 @@ void btd_service_unref(struct btd_service *service)
 	if (service->ref > 0)
 		return;
 
+	queue_foreach(service->depends, depends_remove, service);
+	queue_foreach(service->dependents, dependents_remove, service);
+	queue_destroy(service->depends, NULL);
+	queue_destroy(service->dependents, NULL);
+
 	g_free(service);
 }
 
@@ -172,6 +242,39 @@ void service_remove(struct btd_service *service)
 	btd_service_unref(service);
 }
 
+static void add_depends(struct btd_service *service)
+{
+	struct btd_profile_uuid_cb *after = &service->profile->after_services;
+	unsigned int i;
+
+	queue_foreach(service->depends, depends_remove, service);
+	queue_destroy(service->depends, NULL);
+	service->depends = queue_new();
+
+	for (i = 0; i < after->count; ++i) {
+		const char *uuid = after->uuids[i];
+		struct btd_service *dep;
+
+		dep = btd_device_get_service(service->device, uuid);
+		if (!dep)
+			continue;
+
+		/* Profiles are sorted vs after_uuids, so the dependency will
+		 * have started connecting before us if it is going to connect.
+		 */
+		if (dep->state != BTD_SERVICE_STATE_CONNECTING)
+			continue;
+		if (queue_find(service->depends, NULL, dep))
+			continue;
+
+		queue_push_tail(service->depends, dep);
+
+		if (!dep->dependents)
+			dep->dependents = queue_new();
+		queue_push_tail(dep->dependents, service);
+	}
+}
+
 int service_accept(struct btd_service *service, bool initiator)
 {
 	char addr[18];
@@ -199,6 +302,7 @@ int service_accept(struct btd_service *service, bool initiator)
 	}
 
 	service->initiator = initiator;
+	add_depends(service);
 
 	err = service->profile->accept(service);
 	if (!err)
@@ -265,6 +369,8 @@ int btd_service_connect(struct btd_service *service)
 		return -ECONNABORTED;
 	}
 
+	add_depends(service);
+
 	err = profile->connect(service);
 	if (err == 0) {
 		service->initiator = true;
-- 
2.51.1


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH BlueZ v3 5/7] bap: have unicast client wait for VCS & TMAS & GMAP
  2025-12-02 20:47 [PATCH BlueZ v3 0/7] Add TMAP & GMAP information services Pauli Virtanen
                   ` (3 preceding siblings ...)
  2025-12-02 20:47 ` [PATCH BlueZ v3 4/7] service: implement btd_profile::after_services callback Pauli Virtanen
@ 2025-12-02 20:47 ` Pauli Virtanen
  2025-12-02 20:47 ` [PATCH BlueZ v3 6/7] shared/vcp: add ready callback to bt_vcp_attach() Pauli Virtanen
                   ` (2 subsequent siblings)
  7 siblings, 0 replies; 10+ messages in thread
From: Pauli Virtanen @ 2025-12-02 20:47 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Pauli Virtanen

Have unicast client to wait for VCS, TMAS, and GMAP before creating
endpoints and transports, so that their information is available at that
point.
---

Notes:
    v3:
    - ready + after_uuids -> after_services
    v2:
    - use the generic mechanism to do this

 profiles/audio/bap.c | 36 +++++++++++++++++++++++++++++++++---
 1 file changed, 33 insertions(+), 3 deletions(-)

diff --git a/profiles/audio/bap.c b/profiles/audio/bap.c
index 0dcc57eb5..295e4552d 100644
--- a/profiles/audio/bap.c
+++ b/profiles/audio/bap.c
@@ -145,6 +145,8 @@ struct bap_data {
 	GIOChannel *listen_io;
 	unsigned int io_id;
 	unsigned int cig_update_id;
+	bool services_ready;
+	bool bap_ready;
 };
 
 static struct queue *sessions;
@@ -2137,10 +2139,10 @@ static bool pac_found_bcast(struct bt_bap_pac *lpac, struct bt_bap_pac *rpac,
 	return true;
 }
 
-static void bap_ready(struct bt_bap *bap, void *user_data)
+static void bap_ucast_start(struct bap_data *data)
 {
-	struct btd_service *service = user_data;
-	struct bap_data *data = btd_service_get_user_data(service);
+	struct btd_service *service = data->service;
+	struct bt_bap *bap = data->bap;
 
 	DBG("bap %p", bap);
 
@@ -3723,6 +3725,29 @@ static void pa_and_big_sync(struct bap_setup *setup)
 	}
 }
 
+static void bap_ready(struct bt_bap *bap, void *user_data)
+{
+	struct btd_service *service = user_data;
+	struct bap_data *data = btd_service_get_user_data(service);
+
+	DBG("bap %p", bap);
+
+	data->bap_ready = true;
+	if (data->services_ready)
+		bap_ucast_start(data);
+}
+
+static void bap_services_ready(struct btd_service *service)
+{
+	struct bap_data *data = btd_service_get_user_data(service);
+
+	DBG("%p", data);
+
+	data->services_ready = true;
+	if (data->bap_ready)
+		bap_ucast_start(data);
+}
+
 static int bap_bcast_probe(struct btd_service *service)
 {
 	struct btd_device *device = btd_service_get_device(service);
@@ -3877,6 +3902,9 @@ static int bap_accept(struct btd_service *service)
 		return -EINVAL;
 	}
 
+	data->bap_ready = false;
+	data->services_ready = false;
+
 	if (!bt_bap_attach(data->bap, client)) {
 		error("BAP unable to attach");
 		return -EINVAL;
@@ -4000,6 +4028,8 @@ static struct btd_profile bap_profile = {
 	.adapter_remove	= bap_adapter_remove,
 	.auto_connect	= true,
 	.experimental	= true,
+	.after_services	= BTD_PROFILE_UUID_CB(bap_services_ready,
+				VCS_UUID_STR, TMAS_UUID_STR, GMAS_UUID_STR),
 };
 
 static struct btd_profile bap_bcast_profile = {
-- 
2.51.1


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH BlueZ v3 6/7] shared/vcp: add ready callback to bt_vcp_attach()
  2025-12-02 20:47 [PATCH BlueZ v3 0/7] Add TMAP & GMAP information services Pauli Virtanen
                   ` (4 preceding siblings ...)
  2025-12-02 20:47 ` [PATCH BlueZ v3 5/7] bap: have unicast client wait for VCS & TMAS & GMAP Pauli Virtanen
@ 2025-12-02 20:47 ` Pauli Virtanen
  2025-12-02 20:47 ` [PATCH BlueZ v3 7/7] vcp: wait until client ready before marking profile connected Pauli Virtanen
  2025-12-03 21:30 ` [PATCH BlueZ v3 0/7] Add TMAP & GMAP information services patchwork-bot+bluetooth
  7 siblings, 0 replies; 10+ messages in thread
From: Pauli Virtanen @ 2025-12-02 20:47 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Pauli Virtanen

Add callback that is called when VCP client has finished attaching, so
that caller can know when it can be used.
---

Notes:
    v3:
    - new commit

 src/shared/vcp.c | 29 ++++++++++++++++++++++++++++-
 src/shared/vcp.h |  3 ++-
 2 files changed, 30 insertions(+), 2 deletions(-)

diff --git a/src/shared/vcp.c b/src/shared/vcp.c
index b766faef6..c7f74956e 100644
--- a/src/shared/vcp.c
+++ b/src/shared/vcp.c
@@ -203,6 +203,10 @@ struct bt_vcp {
 	unsigned int aics_ip_status_id;
 	unsigned int aics_ip_descr_id;
 
+	unsigned int idle_id;
+	bt_vcp_func_t ready_func;
+	void *ready_data;
+
 	struct queue *notify;
 	struct queue *pending;
 
@@ -427,6 +431,11 @@ void bt_vcp_detach(struct bt_vcp *vcp)
 	if (!queue_remove(sessions, vcp))
 		return;
 
+	if (vcp->idle_id) {
+		bt_gatt_client_idle_unregister(vcp->client, vcp->idle_id);
+		vcp->idle_id = 0;
+	}
+
 	if (vcp->client) {
 		bt_gatt_client_unref(vcp->client);
 		vcp->client = NULL;
@@ -2976,7 +2985,18 @@ static void foreach_aics_service(struct gatt_db_attribute *attr,
 	gatt_db_service_foreach_char(attr, foreach_aics_char, vcp);
 }
 
-bool bt_vcp_attach(struct bt_vcp *vcp, struct bt_gatt_client *client)
+static void vcp_idle(void *data)
+{
+	struct bt_vcp *vcp = data;
+
+	vcp->idle_id = 0;
+
+	if (vcp->ready_func)
+		vcp->ready_func(vcp, vcp->ready_data);
+}
+
+bool bt_vcp_attach(struct bt_vcp *vcp, struct bt_gatt_client *client,
+				bt_vcp_func_t ready, void *ready_user_data)
 {
 	bt_uuid_t uuid;
 
@@ -3004,5 +3024,12 @@ bool bt_vcp_attach(struct bt_vcp *vcp, struct bt_gatt_client *client)
 	bt_uuid16_create(&uuid, AUDIO_INPUT_CS_UUID);
 	gatt_db_foreach_service(vcp->rdb->db, &uuid, foreach_aics_service, vcp);
 
+	vcp->ready_func = ready;
+	vcp->ready_data = ready_user_data;
+
+	if (!vcp->idle_id)
+		vcp->idle_id = bt_gatt_client_idle_register(vcp->client,
+							vcp_idle, vcp, NULL);
+
 	return true;
 }
diff --git a/src/shared/vcp.h b/src/shared/vcp.h
index 705b6f301..89efaa09c 100644
--- a/src/shared/vcp.h
+++ b/src/shared/vcp.h
@@ -43,7 +43,8 @@ void bt_vcp_unref(struct bt_vcp *vcp);
 
 void bt_vcp_add_db(struct gatt_db *db);
 
-bool bt_vcp_attach(struct bt_vcp *vcp, struct bt_gatt_client *client);
+bool bt_vcp_attach(struct bt_vcp *vcp, struct bt_gatt_client *client,
+				bt_vcp_func_t ready, void *ready_user_data);
 void bt_vcp_detach(struct bt_vcp *vcp);
 
 uint8_t bt_vcp_get_volume(struct bt_vcp *vcp);
-- 
2.51.1


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH BlueZ v3 7/7] vcp: wait until client ready before marking profile connected
  2025-12-02 20:47 [PATCH BlueZ v3 0/7] Add TMAP & GMAP information services Pauli Virtanen
                   ` (5 preceding siblings ...)
  2025-12-02 20:47 ` [PATCH BlueZ v3 6/7] shared/vcp: add ready callback to bt_vcp_attach() Pauli Virtanen
@ 2025-12-02 20:47 ` Pauli Virtanen
  2025-12-03 21:30 ` [PATCH BlueZ v3 0/7] Add TMAP & GMAP information services patchwork-bot+bluetooth
  7 siblings, 0 replies; 10+ messages in thread
From: Pauli Virtanen @ 2025-12-02 20:47 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Pauli Virtanen

Mark vcp profile connected only when VCP client is ready, so that
dependent profiles do not try to use it when it is not yet initialized.
---

Notes:
    v3:
    - new commit

 profiles/audio/vcp.c | 11 ++++++++---
 1 file changed, 8 insertions(+), 3 deletions(-)

diff --git a/profiles/audio/vcp.c b/profiles/audio/vcp.c
index 8949c7185..b2c9d1b32 100644
--- a/profiles/audio/vcp.c
+++ b/profiles/audio/vcp.c
@@ -282,6 +282,13 @@ static void vcp_remove(struct btd_service *service)
 	vcp_data_remove(data);
 }
 
+static void vcp_ready(struct bt_vcp *vcp, void *user_data)
+{
+	struct btd_service *service = user_data;
+
+	btd_service_connecting_complete(service, 0);
+}
+
 static int vcp_accept(struct btd_service *service)
 {
 	struct btd_device *device = btd_service_get_device(service);
@@ -297,13 +304,11 @@ static int vcp_accept(struct btd_service *service)
 		return -EINVAL;
 	}
 
-	if (!bt_vcp_attach(data->vcp, client)) {
+	if (!bt_vcp_attach(data->vcp, client, vcp_ready, service)) {
 		error("VCP unable to attach");
 		return -EINVAL;
 	}
 
-	btd_service_connecting_complete(service, 0);
-
 	return 0;
 }
 
-- 
2.51.1


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* Re: [PATCH BlueZ v3 0/7] Add TMAP & GMAP information services
  2025-12-02 20:47 [PATCH BlueZ v3 0/7] Add TMAP & GMAP information services Pauli Virtanen
                   ` (6 preceding siblings ...)
  2025-12-02 20:47 ` [PATCH BlueZ v3 7/7] vcp: wait until client ready before marking profile connected Pauli Virtanen
@ 2025-12-03 21:30 ` patchwork-bot+bluetooth
  2025-12-03 21:56   ` Luiz Augusto von Dentz
  7 siblings, 1 reply; 10+ messages in thread
From: patchwork-bot+bluetooth @ 2025-12-03 21:30 UTC (permalink / raw)
  To: Pauli Virtanen; +Cc: linux-bluetooth

Hello:

This series was applied to bluetooth/bluez.git (master)
by Luiz Augusto von Dentz <luiz.von.dentz@intel.com>:

On Tue,  2 Dec 2025 22:47:45 +0200 you wrote:
> Add support for TMAP and GMAP services. They contain only device
> audio capability bitmasks.
> 
> v3:
> - rename after_uuids -> after_services + make it a struct
> - add tests for btd_profile_sort_list()
> - leave it as the GSList version for now, tests use queue
> - add commits to mark VCP connected only after its attach has finished
> 
> [...]

Here is the summary with links:
  - [BlueZ,v3,1/7] profile: add after_services for ordering profile startup
    https://git.kernel.org/pub/scm/bluetooth/bluez.git/?id=5806271a69d6
  - [BlueZ,v3,2/7] test-profile: add tests for profile sorting
    https://git.kernel.org/pub/scm/bluetooth/bluez.git/?id=37f1d5de78d4
  - [BlueZ,v3,3/7] device: use after_services in service autoconnect and sort also GATT
    https://git.kernel.org/pub/scm/bluetooth/bluez.git/?id=cdcd845f87ee
  - [BlueZ,v3,4/7] service: implement btd_profile::after_services callback
    https://git.kernel.org/pub/scm/bluetooth/bluez.git/?id=7f8ac0c1fbcd
  - [BlueZ,v3,5/7] bap: have unicast client wait for VCS & TMAS & GMAP
    https://git.kernel.org/pub/scm/bluetooth/bluez.git/?id=b78808cfb3b8
  - [BlueZ,v3,6/7] shared/vcp: add ready callback to bt_vcp_attach()
    (no matching commit)
  - [BlueZ,v3,7/7] vcp: wait until client ready before marking profile connected
    (no matching commit)

You are awesome, thank you!
-- 
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html



^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH BlueZ v3 0/7] Add TMAP & GMAP information services
  2025-12-03 21:30 ` [PATCH BlueZ v3 0/7] Add TMAP & GMAP information services patchwork-bot+bluetooth
@ 2025-12-03 21:56   ` Luiz Augusto von Dentz
  0 siblings, 0 replies; 10+ messages in thread
From: Luiz Augusto von Dentz @ 2025-12-03 21:56 UTC (permalink / raw)
  To: patchwork-bot+bluetooth; +Cc: Pauli Virtanen, linux-bluetooth

Hi Pauli,

On Wed, Dec 3, 2025 at 4:33 PM <patchwork-bot+bluetooth@kernel.org> wrote:
>
> Hello:
>
> This series was applied to bluetooth/bluez.git (master)
> by Luiz Augusto von Dentz <luiz.von.dentz@intel.com>:
>
> On Tue,  2 Dec 2025 22:47:45 +0200 you wrote:
> > Add support for TMAP and GMAP services. They contain only device
> > audio capability bitmasks.
> >
> > v3:
> > - rename after_uuids -> after_services + make it a struct
> > - add tests for btd_profile_sort_list()
> > - leave it as the GSList version for now, tests use queue
> > - add commits to mark VCP connected only after its attach has finished
> >
> > [...]
>
> Here is the summary with links:
>   - [BlueZ,v3,1/7] profile: add after_services for ordering profile startup
>     https://git.kernel.org/pub/scm/bluetooth/bluez.git/?id=5806271a69d6
>   - [BlueZ,v3,2/7] test-profile: add tests for profile sorting
>     https://git.kernel.org/pub/scm/bluetooth/bluez.git/?id=37f1d5de78d4
>   - [BlueZ,v3,3/7] device: use after_services in service autoconnect and sort also GATT
>     https://git.kernel.org/pub/scm/bluetooth/bluez.git/?id=cdcd845f87ee
>   - [BlueZ,v3,4/7] service: implement btd_profile::after_services callback
>     https://git.kernel.org/pub/scm/bluetooth/bluez.git/?id=7f8ac0c1fbcd
>   - [BlueZ,v3,5/7] bap: have unicast client wait for VCS & TMAS & GMAP
>     https://git.kernel.org/pub/scm/bluetooth/bluez.git/?id=b78808cfb3b8
>   - [BlueZ,v3,6/7] shared/vcp: add ready callback to bt_vcp_attach()
>     (no matching commit)
>   - [BlueZ,v3,7/7] vcp: wait until client ready before marking profile connected
>     (no matching commit)
>
> You are awesome, thank you!
> --
> Deet-doot-dot, I am a bot.
> https://korg.docs.kernel.org/patchwork/pwbot.html

In case you are wondering, I just had to fix the building of 6/7 since
you changed bt_vcp_attach but only changed it on 7/7, anyway brilliant
work, you really took it to another level with fuzzy testing on
test-profile.

-- 
Luiz Augusto von Dentz

^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2025-12-03 21:56 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-12-02 20:47 [PATCH BlueZ v3 0/7] Add TMAP & GMAP information services Pauli Virtanen
2025-12-02 20:47 ` [PATCH BlueZ v3 1/7] profile: add after_services for ordering profile startup Pauli Virtanen
2025-12-02 20:47 ` [PATCH BlueZ v3 2/7] test-profile: add tests for profile sorting Pauli Virtanen
2025-12-02 20:47 ` [PATCH BlueZ v3 3/7] device: use after_services in service autoconnect and sort also GATT Pauli Virtanen
2025-12-02 20:47 ` [PATCH BlueZ v3 4/7] service: implement btd_profile::after_services callback Pauli Virtanen
2025-12-02 20:47 ` [PATCH BlueZ v3 5/7] bap: have unicast client wait for VCS & TMAS & GMAP Pauli Virtanen
2025-12-02 20:47 ` [PATCH BlueZ v3 6/7] shared/vcp: add ready callback to bt_vcp_attach() Pauli Virtanen
2025-12-02 20:47 ` [PATCH BlueZ v3 7/7] vcp: wait until client ready before marking profile connected Pauli Virtanen
2025-12-03 21:30 ` [PATCH BlueZ v3 0/7] Add TMAP & GMAP information services patchwork-bot+bluetooth
2025-12-03 21:56   ` Luiz Augusto von Dentz

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox