public inbox for linux-bluetooth@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH BlueZ v2 1/3] audio: Add ability to force CIS transport Links property
@ 2026-04-23  7:53 Frédéric Danis
  2026-04-23  7:53 ` [PATCH BlueZ v2 2/3] doc: Add documentation for readwrite " Frédéric Danis
                   ` (2 more replies)
  0 siblings, 3 replies; 4+ messages in thread
From: Frédéric Danis @ 2026-04-23  7:53 UTC (permalink / raw)
  To: linux-bluetooth

If bluetoothd is started in testing mode the Links property for CIS
is readwrite and can be used to force transport objects Links.
This can used to unlink transport objects by sending an empty array.

For unlinked transport objects, each transports needs to be acquired
separately.

This allows to pass PTS tests BAP/UCL/STR/BV-543-C and BV-546-C.
---
v1 -> v2:
  - Add testing mode check in bap_connect_io_cb()
  - Replace stream state check by checking that the stream is not linked
    before passing the connect event to all streams belonging to same 
    CIG/CIS.

 profiles/audio/bap.c       | 46 ++++++++++++++++++++++++++++++
 profiles/audio/transport.c | 57 +++++++++++++++++++++++++++++++-------
 2 files changed, 95 insertions(+), 10 deletions(-)

diff --git a/profiles/audio/bap.c b/profiles/audio/bap.c
index 5333267f7..e20456436 100644
--- a/profiles/audio/bap.c
+++ b/profiles/audio/bap.c
@@ -2655,13 +2655,61 @@ static void bap_connect_bcast_io_cb(GIOChannel *chan, GError *err,
 	iso_connect_bcast_cb(chan, err, setup->stream);
 }
 
+struct connect_io_data {
+	GIOChannel *chan;
+	GError *err;
+	uint8_t cig_id;
+	uint8_t cis_id;
+};
+
+static void connect_stream(void *data, void *user_data)
+{
+	struct bap_setup *setup = data;
+	struct connect_io_data *d = user_data;
+	uint8_t state;
+
+	/* Check stream state to only pass the connect event managed
+	 * by bap_stream_set_io() */
+	state = bt_bap_stream_get_state(setup->stream);
+	if ((state == BT_BAP_STREAM_STATE_ENABLING ||
+			state == BT_BAP_STREAM_STATE_DISABLING) &&
+			setup->qos.ucast.cig_id == d->cig_id &&
+			setup->qos.ucast.cis_id == d->cis_id)
+		iso_connect_cb(d->chan, d->err, setup->stream);
+}
+
+static void connect_ep(void *data, void *user_data)
+{
+	struct bap_ep *ep = data;
+
+	if (ep->setups)
+		queue_foreach(ep->setups, connect_stream, user_data);
+}
+
 static void bap_connect_io_cb(GIOChannel *chan, GError *err, gpointer user_data)
 {
 	struct bap_setup *setup = user_data;
+	struct connect_io_data data;
 
 	if (!setup->stream)
 		return;
 
+	if (queue_isempty(bt_bap_stream_io_get_links(setup->stream)) &&
+		btd_opts.testing) {
+		/* The stream may have manually been unliked for PTS tests,
+		 * e.g. BAP/UCL/STR/BV-543-C or BAP/UCL/STR/BV-546-C,
+		 * in this case send the connect event to all streams
+		 * belonging to the same CIG/CIS.
+		 */
+		data.chan = chan;
+		data.err = err;
+		data.cig_id = setup->qos.ucast.cig_id;
+		data.cis_id = setup->qos.ucast.cis_id;
+		queue_foreach(setup->ep->data->snks, connect_ep, &data);
+		queue_foreach(setup->ep->data->srcs, connect_ep, &data);
+		return;
+	}
+
 	iso_connect_cb(chan, err, setup->stream);
 }
 
diff --git a/profiles/audio/transport.c b/profiles/audio/transport.c
index 5c2a2777e..b6a5dd1fd 100644
--- a/profiles/audio/transport.c
+++ b/profiles/audio/transport.c
@@ -37,6 +37,7 @@
 #include "src/shared/bap.h"
 #include "src/shared/bass.h"
 #include "src/shared/io.h"
+#include "src/btd.h"
 
 #ifdef HAVE_A2DP
 #include "avdtp.h"
@@ -114,6 +115,7 @@ struct bap_transport {
 struct media_transport_ops {
 	const char *uuid;
 	const GDBusPropertyTable *properties;
+	const GDBusPropertyTable *test_properties;
 	void (*set_owner)(struct media_transport *transport,
 				struct media_owner *owner);
 	void (*remove_owner)(struct media_transport *transport,
@@ -1419,6 +1421,9 @@ static struct media_transport *find_transport_by_path(const char *path)
 	return NULL;
 }
 
+static void bap_update_links(const struct media_transport *transport);
+static void transport_unlink(void *data, void *user_data);
+
 static void set_links(const GDBusPropertyTable *property,
 				DBusMessageIter *iter,
 				GDBusPendingPropertySet id, void *user_data)
@@ -1436,6 +1441,16 @@ static void set_links(const GDBusPropertyTable *property,
 
 	dbus_message_iter_recurse(iter, &array);
 
+	if (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_INVALID) {
+		struct queue *links = bt_bap_stream_io_get_links(bap->stream);
+
+		/* Unlink stream from all its links */
+		queue_foreach(links, transport_unlink, bap->stream);
+
+		bt_bap_stream_io_unlink(bap->stream, NULL);
+		bap_update_links(transport);
+	}
+
 	while (dbus_message_iter_get_arg_type(&array) ==
 						DBUS_TYPE_OBJECT_PATH) {
 		struct media_transport *link;
@@ -1486,6 +1501,21 @@ static const GDBusPropertyTable transport_bap_uc_properties[] = {
 	{ }
 };
 
+static const GDBusPropertyTable transport_bap_uc_test_properties[] = {
+	{ "Device", "o", get_device },
+	{ "UUID", "s", get_uuid },
+	{ "Codec", "y", get_codec },
+	{ "Configuration", "ay", get_configuration },
+	{ "State", "s", get_state },
+	{ "QoS", "a{sv}", get_ucast_qos, NULL, qos_ucast_exists },
+	{ "Endpoint", "o", get_endpoint, NULL, endpoint_exists },
+	{ "Location", "u", get_location },
+	{ "Metadata", "ay", get_metadata, set_metadata },
+	{ "Links", "ao", get_links, set_links, links_exists },
+	{ "Volume", "q", get_volume, set_volume, volume_exists },
+	{ }
+};
+
 static gboolean get_bcast_qos(const GDBusPropertyTable *property,
 					DBusMessageIter *iter, void *data)
 {
@@ -1884,8 +1914,6 @@ static void bap_resume_complete(struct media_transport *transport)
 	transport_set_state(transport, TRANSPORT_STATE_ACTIVE);
 }
 
-static void bap_update_links(const struct media_transport *transport);
-
 static bool match_link_transport(const void *data, const void *user_data)
 {
 	const struct bt_bap_stream *stream = data;
@@ -2540,10 +2568,11 @@ static void *transport_asha_init(struct media_transport *transport, void *data)
 #define TRANSPORT_OPS(_uuid, _props, _set_owner, _remove_owner, _init, \
 		      _resume, _suspend, _cancel, _set_state, _get_stream, \
 		      _get_volume, _set_volume, _set_delay, _update_links, \
-		      _destroy) \
+		      _destroy, _test_props) \
 { \
 	.uuid = _uuid, \
 	.properties = _props, \
+	.test_properties = _test_props, \
 	.set_owner = _set_owner, \
 	.remove_owner = _remove_owner, \
 	.init = _init, \
@@ -2565,26 +2594,28 @@ static void *transport_asha_init(struct media_transport *transport, void *data)
 			transport_a2dp_resume, transport_a2dp_suspend, \
 			transport_a2dp_cancel, NULL, \
 			transport_a2dp_get_stream, transport_a2dp_get_volume, \
-			_set_volume, _set_delay, NULL, _destroy)
+			_set_volume, _set_delay, NULL, _destroy, NULL)
 
 #define BAP_OPS(_uuid, _props, _set_owner, _remove_owner, _update_links, \
-		_set_state) \
+		_set_state, _test_props) \
 	TRANSPORT_OPS(_uuid, _props, _set_owner, _remove_owner,\
 			transport_bap_init, \
 			transport_bap_resume, transport_bap_suspend, \
 			transport_bap_cancel, _set_state, \
 			transport_bap_get_stream, transport_bap_get_volume, \
 			transport_bap_set_volume, NULL, \
-			_update_links, transport_bap_destroy)
+			_update_links, transport_bap_destroy, _test_props)
 
 #define BAP_UC_OPS(_uuid) \
 	BAP_OPS(_uuid, transport_bap_uc_properties, \
 			transport_bap_set_owner, transport_bap_remove_owner, \
-			transport_bap_update_links_uc, transport_bap_set_state)
+			transport_bap_update_links_uc, \
+			transport_bap_set_state, \
+			transport_bap_uc_test_properties)
 
 #define BAP_BC_OPS(_uuid) \
 	BAP_OPS(_uuid, transport_bap_bc_properties, NULL, NULL, \
-			transport_bap_update_links_bc, NULL)
+			transport_bap_update_links_bc, NULL, NULL)
 
 #define ASHA_OPS(_uuid) \
 	TRANSPORT_OPS(_uuid, transport_asha_properties, NULL, NULL, \
@@ -2592,7 +2623,7 @@ static void *transport_asha_init(struct media_transport *transport, void *data)
 			transport_asha_resume, transport_asha_suspend, \
 			transport_asha_cancel, NULL, NULL, \
 			transport_asha_get_volume, transport_asha_set_volume, \
-			NULL, NULL, NULL)
+			NULL, NULL, NULL, NULL)
 
 static const struct media_transport_ops transport_ops[] = {
 #ifdef HAVE_A2DP
@@ -2647,6 +2678,7 @@ struct media_transport *media_transport_create(struct btd_device *device,
 	struct media_transport *transport;
 	const struct media_transport_ops *ops;
 	int fd;
+	const GDBusPropertyTable *properties;
 
 	transport = g_new0(struct media_transport, 1);
 	if (device)
@@ -2701,9 +2733,14 @@ struct media_transport *media_transport_create(struct btd_device *device,
 			goto fail;
 	}
 
+	if (btd_opts.testing && ops->test_properties)
+		properties = ops->test_properties;
+	else
+		properties = ops->properties;
+
 	if (g_dbus_register_interface(btd_get_dbus_connection(),
 				transport->path, MEDIA_TRANSPORT_INTERFACE,
-				transport_methods, NULL, ops->properties,
+				transport_methods, NULL, properties,
 				transport, media_transport_free) == FALSE) {
 		error("Could not register transport %s", transport->path);
 		goto fail;
-- 
2.43.0


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

* [PATCH BlueZ v2 2/3] doc: Add documentation for readwrite CIS transport Links property
  2026-04-23  7:53 [PATCH BlueZ v2 1/3] audio: Add ability to force CIS transport Links property Frédéric Danis
@ 2026-04-23  7:53 ` Frédéric Danis
  2026-04-23  7:53 ` [PATCH BlueZ v2 3/3] client/player: Add support to unlink transports Frédéric Danis
  2026-04-23  9:11 ` [BlueZ,v2,1/3] audio: Add ability to force CIS transport Links property bluez.test.bot
  2 siblings, 0 replies; 4+ messages in thread
From: Frédéric Danis @ 2026-04-23  7:53 UTC (permalink / raw)
  To: linux-bluetooth

This is only supported when bluetoothd is started in testing mode.
---
 doc/org.bluez.MediaTransport.rst | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/doc/org.bluez.MediaTransport.rst b/doc/org.bluez.MediaTransport.rst
index 81cf9e4da..fe69f948b 100644
--- a/doc/org.bluez.MediaTransport.rst
+++ b/doc/org.bluez.MediaTransport.rst
@@ -180,6 +180,11 @@ array{object} Links [readonly, optional, CIS only, experimental]
 
 Linked transport objects which the transport is associated with.
 
+If D-Bus testing interfaces as been enabled this property is readwrite.
+
+This can be used to manually unlink transport objects by sending empty array.
+Then, each link needs be acquired separately.
+
 array{object} Links [readwrite, BIS only, experimental]
 ```````````````````````````````````````````````````````
 
-- 
2.43.0


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

* [PATCH BlueZ v2 3/3] client/player: Add support to unlink transports
  2026-04-23  7:53 [PATCH BlueZ v2 1/3] audio: Add ability to force CIS transport Links property Frédéric Danis
  2026-04-23  7:53 ` [PATCH BlueZ v2 2/3] doc: Add documentation for readwrite " Frédéric Danis
@ 2026-04-23  7:53 ` Frédéric Danis
  2026-04-23  9:11 ` [BlueZ,v2,1/3] audio: Add ability to force CIS transport Links property bluez.test.bot
  2 siblings, 0 replies; 4+ messages in thread
From: Frédéric Danis @ 2026-04-23  7:53 UTC (permalink / raw)
  To: linux-bluetooth

This is used to pass PTS tests BAP/UCL/STR/BV-543-C and BV-546-C.
---
 client/player.c | 57 ++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 56 insertions(+), 1 deletion(-)

diff --git a/client/player.c b/client/player.c
index 1444e939d..e3bd28fea 100644
--- a/client/player.c
+++ b/client/player.c
@@ -5003,6 +5003,9 @@ static bool transport_recv(struct io *io, void *user_data)
 static void transport_new(GDBusProxy *proxy, int sk, uint16_t mtu[2])
 {
 	struct transport *transport;
+	DBusMessageIter iter;
+	const char *uuid;
+	bool reader = true;
 
 	transport = new0(struct transport, 1);
 	transport->proxy = proxy;
@@ -5014,7 +5017,23 @@ static void transport_new(GDBusProxy *proxy, int sk, uint16_t mtu[2])
 
 	io_set_disconnect_handler(transport->io, transport_disconnected,
 							transport, NULL);
-	io_set_read_handler(transport->io, transport_recv, transport, NULL);
+
+	if (!g_dbus_proxy_get_property(proxy, "UUID", &iter))
+		return;
+
+	dbus_message_iter_get_basic(&iter, &uuid);
+
+	/* For BAP testing, streams may have been manually unlinked.
+	 * In this case source and sink streams are acquired separately and
+	 * read handler should not be started for source local endpoint.
+	 */
+	if (!g_dbus_proxy_get_property(proxy, "Links", &iter) &&
+			!strcmp(uuid, PAC_SOURCE_UUID))
+		reader = false;
+
+	if (reader)
+		io_set_read_handler(transport->io, transport_recv, transport,
+									NULL);
 
 	if (!ios)
 		ios = queue_new();
@@ -6091,6 +6110,39 @@ static void cmd_metadata_transport(int argc, char *argv[])
 	}
 }
 
+static void unlink_cb(const DBusError *error, void *user_data)
+{
+	if (dbus_error_is_set(error)) {
+		bt_shell_printf("Failed to unlink: %s\n", error->name);
+		return bt_shell_noninteractive_quit(EXIT_FAILURE);
+	}
+
+	bt_shell_printf("Unlink succeeded\n");
+
+	return bt_shell_noninteractive_quit(EXIT_SUCCESS);
+}
+
+static void cmd_unlink_transport(int argc, char *argv[])
+{
+	GDBusProxy *proxy;
+	char *value[0];
+
+	proxy = g_dbus_proxy_lookup(transports, NULL, argv[1],
+					BLUEZ_MEDIA_TRANSPORT_INTERFACE);
+	if (!proxy) {
+		bt_shell_printf("Transport %s not found\n", argv[1]);
+		return bt_shell_noninteractive_quit(EXIT_FAILURE);
+	}
+
+	if (g_dbus_proxy_set_property_array(proxy, "Links",
+				DBUS_TYPE_OBJECT_PATH,
+				value, 0, unlink_cb,
+				NULL, NULL) == FALSE) {
+		bt_shell_printf("Failed to unlink transport\n");
+		return bt_shell_noninteractive_quit(EXIT_FAILURE);
+	}
+}
+
 static const struct bt_shell_menu transport_menu = {
 	.name = "transport",
 	.desc = "Media Transport Submenu",
@@ -6126,6 +6178,9 @@ static const struct bt_shell_menu transport_menu = {
 	{ "metadata",    "<transport> [value...]", cmd_metadata_transport,
 						"Get/Set Transport Metadata",
 						transport_generator },
+	{ "unlink",      "<transport>", cmd_unlink_transport,
+						"Unlink Transport",
+						transport_generator },
 	{} },
 };
 
-- 
2.43.0


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

* RE: [BlueZ,v2,1/3] audio: Add ability to force CIS transport Links property
  2026-04-23  7:53 [PATCH BlueZ v2 1/3] audio: Add ability to force CIS transport Links property Frédéric Danis
  2026-04-23  7:53 ` [PATCH BlueZ v2 2/3] doc: Add documentation for readwrite " Frédéric Danis
  2026-04-23  7:53 ` [PATCH BlueZ v2 3/3] client/player: Add support to unlink transports Frédéric Danis
@ 2026-04-23  9:11 ` bluez.test.bot
  2 siblings, 0 replies; 4+ messages in thread
From: bluez.test.bot @ 2026-04-23  9:11 UTC (permalink / raw)
  To: linux-bluetooth, frederic.danis

[-- Attachment #1: Type: text/plain, Size: 2545 bytes --]

This is automated email and please do not reply to this email!

Dear submitter,

Thank you for submitting the patches to the linux bluetooth mailing list.
This is a CI test results with your patch series:
PW Link:https://patchwork.kernel.org/project/bluetooth/list/?series=1084559

---Test result---

Test Summary:
CheckPatch                    FAIL      1.65 seconds
GitLint                       FAIL      1.02 seconds
BuildEll                      PASS      20.80 seconds
BluezMake                     PASS      660.92 seconds
MakeCheck                     PASS      19.20 seconds
MakeDistcheck                 PASS      248.30 seconds
CheckValgrind                 PASS      297.58 seconds
CheckSmatch                   PASS      352.78 seconds
bluezmakeextell               PASS      183.93 seconds
IncrementalBuild              PASS      654.86 seconds
ScanBuild                     PASS      1020.42 seconds

Details
##############################
Test: CheckPatch - FAIL
Desc: Run checkpatch.pl script
Output:
[BlueZ,v2,1/3] audio: Add ability to force CIS transport Links property
WARNING:BLOCK_COMMENT_STYLE: Block comments use a trailing */ on a separate line
#109: FILE: profiles/audio/bap.c:2672:
+	 * by bap_stream_set_io() */

/github/workspace/src/patch/14534951.patch total: 0 errors, 1 warnings, 204 lines checked

NOTE: For some of the reported defects, checkpatch may be able to
      mechanically convert to the typical style using --fix or --fix-inplace.

/github/workspace/src/patch/14534951.patch has style problems, please review.

NOTE: Ignored message types: COMMIT_MESSAGE COMPLEX_MACRO CONST_STRUCT FILE_PATH_CHANGES MISSING_SIGN_OFF PREFER_PACKED SPDX_LICENSE_TAG SPLIT_STRING SSCANF_TO_KSTRTO

NOTE: If any of the errors are false positives, please report
      them to the maintainer, see CHECKPATCH in MAINTAINERS.


##############################
Test: GitLint - FAIL
Desc: Run gitlint
Output:
[BlueZ,v2,1/3] audio: Add ability to force CIS transport Links property

WARNING: I3 - ignore-body-lines: gitlint will be switching from using Python regex 'match' (match beginning) to 'search' (match anywhere) semantics. Please review your ignore-body-lines.regex option accordingly. To remove this warning, set general.regex-style-search=True. More details: https://jorisroovers.github.io/gitlint/configuration/#regex-style-search
15: B2 Line has trailing whitespace: "    before passing the connect event to all streams belonging to same "


https://github.com/bluez/bluez/pull/2064

---
Regards,
Linux Bluetooth


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

end of thread, other threads:[~2026-04-23  9:12 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-04-23  7:53 [PATCH BlueZ v2 1/3] audio: Add ability to force CIS transport Links property Frédéric Danis
2026-04-23  7:53 ` [PATCH BlueZ v2 2/3] doc: Add documentation for readwrite " Frédéric Danis
2026-04-23  7:53 ` [PATCH BlueZ v2 3/3] client/player: Add support to unlink transports Frédéric Danis
2026-04-23  9:11 ` [BlueZ,v2,1/3] audio: Add ability to force CIS transport Links property bluez.test.bot

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