* [PATCH BlueZ 2/3] doc: Add documentation for readwrite CIS transport Links property
2026-04-20 16:13 [PATCH BlueZ 1/3] audio: Add ability to force CIS transport Links property Frédéric Danis
@ 2026-04-20 16:13 ` Frédéric Danis
2026-04-20 16:13 ` [PATCH BlueZ 3/3] client/player: Add support to unlink transports Frédéric Danis
` (3 subsequent siblings)
4 siblings, 0 replies; 7+ messages in thread
From: Frédéric Danis @ 2026-04-20 16:13 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] 7+ messages in thread* [PATCH BlueZ 3/3] client/player: Add support to unlink transports
2026-04-20 16:13 [PATCH BlueZ 1/3] audio: Add ability to force CIS transport Links property Frédéric Danis
2026-04-20 16:13 ` [PATCH BlueZ 2/3] doc: Add documentation for readwrite " Frédéric Danis
@ 2026-04-20 16:13 ` Frédéric Danis
2026-04-20 16:32 ` [PATCH BlueZ 1/3] audio: Add ability to force CIS transport Links property Pauli Virtanen
` (2 subsequent siblings)
4 siblings, 0 replies; 7+ messages in thread
From: Frédéric Danis @ 2026-04-20 16:13 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] 7+ messages in thread* Re: [PATCH BlueZ 1/3] audio: Add ability to force CIS transport Links property
2026-04-20 16:13 [PATCH BlueZ 1/3] audio: Add ability to force CIS transport Links property Frédéric Danis
2026-04-20 16:13 ` [PATCH BlueZ 2/3] doc: Add documentation for readwrite " Frédéric Danis
2026-04-20 16:13 ` [PATCH BlueZ 3/3] client/player: Add support to unlink transports Frédéric Danis
@ 2026-04-20 16:32 ` Pauli Virtanen
2026-04-20 17:44 ` [BlueZ,1/3] " bluez.test.bot
2026-04-20 19:52 ` [PATCH BlueZ 1/3] " Luiz Augusto von Dentz
4 siblings, 0 replies; 7+ messages in thread
From: Pauli Virtanen @ 2026-04-20 16:32 UTC (permalink / raw)
To: Frédéric Danis, linux-bluetooth
Hi,
ma, 2026-04-20 kello 18:13 +0200, Frédéric Danis kirjoitti:
> 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.
> ---
> profiles/audio/bap.c | 44 +++++++++++++++++++++++++++++
> profiles/audio/transport.c | 57 +++++++++++++++++++++++++++++++-------
> 2 files changed, 91 insertions(+), 10 deletions(-)
>
> diff --git a/profiles/audio/bap.c b/profiles/audio/bap.c
> index 375026440..8f1f75240 100644
> --- a/profiles/audio/bap.c
> +++ b/profiles/audio/bap.c
> @@ -2651,13 +2651,57 @@ 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;
> +};
> +
> +static bool find_enabling_stream(const void *data, const void *match_data)
> +{
> + const struct bap_setup *setup = data;
> + const struct connect_io_data *d = match_data;
> + uint8_t state;
> +
> + state = bt_bap_stream_get_state(setup->stream);
> + if (state == BT_BAP_STREAM_STATE_ENABLING &&
> + setup->qos.ucast.cig_id == d->cig_id)
> + iso_connect_cb(d->chan, d->err, setup->stream);
> +
> + return false;
> +}
> +
> +static bool find_enabling_ep(const void *data, const void *match_data)
> +{
> + const struct bap_ep *ep = data;
> +
> + if (ep->setups)
> + queue_find(ep->setups, find_enabling_stream, match_data);
> +
> + return false;
> +}
> +
> 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 (bt_bap_stream_get_state(setup->stream) !=
> + BT_BAP_STREAM_STATE_ENABLING) {
The order of transition to STATE_STREAMING and iso_connect_cb() has
been reported to be nondeterministic
https://github.com/bluez/bluez/issues/1506
and it appears in normal usage sometimes the state is STATE_STREAMING
here.
Probably the order in which the Glib mainloop handles the fd wakeups is
unspecified if the events arrive too close to each other, regardless of
the order in which kernel sees the events.
It's probably not right to assume here != ENABLING means the testing
feature is used, but it probably should check some extra unlink flag.
> + /* The stream may have manually been unliked for tests and the
> + * connect event refers to another stream of the CIG.
> + */
> + data.chan = chan;
> + data.err = err;
> + data.cig_id = setup->qos.ucast.cig_id;
> + queue_find(setup->ep->data->snks, find_enabling_ep, &data);
> + queue_find(setup->ep->data->srcs, find_enabling_ep, &data);
> + return;
> + }
> +
> iso_connect_cb(chan, err, setup->stream);
> }
>
> diff --git a/profiles/audio/transport.c b/profiles/audio/transport.c
> index d9feef768..794c492af 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,
> @@ -1417,6 +1419,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)
> @@ -1434,6 +1439,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;
> @@ -1484,6 +1499,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)
> {
> @@ -1879,8 +1909,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;
> @@ -2535,10 +2563,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, \
> @@ -2560,26 +2589,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, \
> @@ -2587,7 +2618,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
> @@ -2642,6 +2673,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)
> @@ -2696,9 +2728,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;
--
Pauli Virtanen
^ permalink raw reply [flat|nested] 7+ messages in thread* RE: [BlueZ,1/3] audio: Add ability to force CIS transport Links property
2026-04-20 16:13 [PATCH BlueZ 1/3] audio: Add ability to force CIS transport Links property Frédéric Danis
` (2 preceding siblings ...)
2026-04-20 16:32 ` [PATCH BlueZ 1/3] audio: Add ability to force CIS transport Links property Pauli Virtanen
@ 2026-04-20 17:44 ` bluez.test.bot
2026-04-20 19:52 ` [PATCH BlueZ 1/3] " Luiz Augusto von Dentz
4 siblings, 0 replies; 7+ messages in thread
From: bluez.test.bot @ 2026-04-20 17:44 UTC (permalink / raw)
To: linux-bluetooth, frederic.danis
[-- Attachment #1: Type: text/plain, Size: 990 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=1083418
---Test result---
Test Summary:
CheckPatch PASS 1.69 seconds
GitLint PASS 1.04 seconds
BuildEll PASS 20.38 seconds
BluezMake PASS 641.43 seconds
MakeCheck PASS 18.94 seconds
MakeDistcheck PASS 248.34 seconds
CheckValgrind PASS 297.20 seconds
CheckSmatch PASS 348.73 seconds
bluezmakeextell PASS 184.69 seconds
IncrementalBuild PASS 642.44 seconds
ScanBuild PASS 1015.02 seconds
https://github.com/bluez/bluez/pull/2058
---
Regards,
Linux Bluetooth
^ permalink raw reply [flat|nested] 7+ messages in thread* Re: [PATCH BlueZ 1/3] audio: Add ability to force CIS transport Links property
2026-04-20 16:13 [PATCH BlueZ 1/3] audio: Add ability to force CIS transport Links property Frédéric Danis
` (3 preceding siblings ...)
2026-04-20 17:44 ` [BlueZ,1/3] " bluez.test.bot
@ 2026-04-20 19:52 ` Luiz Augusto von Dentz
2026-04-21 8:22 ` Frédéric Danis
4 siblings, 1 reply; 7+ messages in thread
From: Luiz Augusto von Dentz @ 2026-04-20 19:52 UTC (permalink / raw)
To: Frédéric Danis; +Cc: linux-bluetooth
Hi Frédéric,
On Mon, Apr 20, 2026 at 1:27 PM Frédéric Danis
<frederic.danis@collabora.com> wrote:
>
> 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.
> ---
> profiles/audio/bap.c | 44 +++++++++++++++++++++++++++++
> profiles/audio/transport.c | 57 +++++++++++++++++++++++++++++++-------
> 2 files changed, 91 insertions(+), 10 deletions(-)
>
> diff --git a/profiles/audio/bap.c b/profiles/audio/bap.c
> index 375026440..8f1f75240 100644
> --- a/profiles/audio/bap.c
> +++ b/profiles/audio/bap.c
> @@ -2651,13 +2651,57 @@ 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;
> +};
> +
> +static bool find_enabling_stream(const void *data, const void *match_data)
> +{
> + const struct bap_setup *setup = data;
> + const struct connect_io_data *d = match_data;
> + uint8_t state;
> +
> + state = bt_bap_stream_get_state(setup->stream);
> + if (state == BT_BAP_STREAM_STATE_ENABLING &&
> + setup->qos.ucast.cig_id == d->cig_id)
> + iso_connect_cb(d->chan, d->err, setup->stream);
> +
> + return false;
> +}
> +
> +static bool find_enabling_ep(const void *data, const void *match_data)
> +{
> + const struct bap_ep *ep = data;
> +
> + if (ep->setups)
> + queue_find(ep->setups, find_enabling_stream, match_data);
> +
> + return false;
> +}
> +
> 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 (bt_bap_stream_get_state(setup->stream) !=
> + BT_BAP_STREAM_STATE_ENABLING) {
> + /* The stream may have manually been unliked for tests and the
> + * connect event refers to another stream of the CIG.
If this is happening it already means the setup shouldn't be waiting
for a connect IO to begin with. Alternatively, the problem might be
that it will connect regardless because the CIS is bidirectional;
connecting one stream connects both. However, one stream might have
the defer setup marked. Did you check that? And in that case we should
consider it connected, or maybe we should not advance and send the
start operation if it is still deferred.
> + */
> + data.chan = chan;
> + data.err = err;
> + data.cig_id = setup->qos.ucast.cig_id;
> + queue_find(setup->ep->data->snks, find_enabling_ep, &data);
> + queue_find(setup->ep->data->srcs, find_enabling_ep, &data);
> + return;
> + }
> +
> iso_connect_cb(chan, err, setup->stream);
> }
>
> diff --git a/profiles/audio/transport.c b/profiles/audio/transport.c
> index d9feef768..794c492af 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,
> @@ -1417,6 +1419,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)
> @@ -1434,6 +1439,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;
> @@ -1484,6 +1499,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)
> {
> @@ -1879,8 +1909,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;
> @@ -2535,10 +2563,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, \
> @@ -2560,26 +2589,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, \
> @@ -2587,7 +2618,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
> @@ -2642,6 +2673,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)
> @@ -2696,9 +2728,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
>
>
--
Luiz Augusto von Dentz
^ permalink raw reply [flat|nested] 7+ messages in thread* Re: [PATCH BlueZ 1/3] audio: Add ability to force CIS transport Links property
2026-04-20 19:52 ` [PATCH BlueZ 1/3] " Luiz Augusto von Dentz
@ 2026-04-21 8:22 ` Frédéric Danis
0 siblings, 0 replies; 7+ messages in thread
From: Frédéric Danis @ 2026-04-21 8:22 UTC (permalink / raw)
To: Luiz Augusto von Dentz; +Cc: linux-bluetooth
Hi Luiz,
On 20/04/2026 21:52, Luiz Augusto von Dentz wrote:
> Hi Frédéric,
>
> On Mon, Apr 20, 2026 at 1:27 PM Frédéric Danis
> <frederic.danis@collabora.com> wrote:
>> 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.
>> ---
>> profiles/audio/bap.c | 44 +++++++++++++++++++++++++++++
>> profiles/audio/transport.c | 57 +++++++++++++++++++++++++++++++-------
>> 2 files changed, 91 insertions(+), 10 deletions(-)
>>
>> diff --git a/profiles/audio/bap.c b/profiles/audio/bap.c
>> index 375026440..8f1f75240 100644
>> --- a/profiles/audio/bap.c
>> +++ b/profiles/audio/bap.c
>> @@ -2651,13 +2651,57 @@ 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;
>> +};
>> +
>> +static bool find_enabling_stream(const void *data, const void *match_data)
>> +{
>> + const struct bap_setup *setup = data;
>> + const struct connect_io_data *d = match_data;
>> + uint8_t state;
>> +
>> + state = bt_bap_stream_get_state(setup->stream);
>> + if (state == BT_BAP_STREAM_STATE_ENABLING &&
>> + setup->qos.ucast.cig_id == d->cig_id)
>> + iso_connect_cb(d->chan, d->err, setup->stream);
>> +
>> + return false;
>> +}
>> +
>> +static bool find_enabling_ep(const void *data, const void *match_data)
>> +{
>> + const struct bap_ep *ep = data;
>> +
>> + if (ep->setups)
>> + queue_find(ep->setups, find_enabling_stream, match_data);
>> +
>> + return false;
>> +}
>> +
>> 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 (bt_bap_stream_get_state(setup->stream) !=
>> + BT_BAP_STREAM_STATE_ENABLING) {
>> + /* The stream may have manually been unliked for tests and the
>> + * connect event refers to another stream of the CIG.
> If this is happening it already means the setup shouldn't be waiting
> for a connect IO to begin with. Alternatively, the problem might be
> that it will connect regardless because the CIS is bidirectional;
> connecting one stream connects both. However, one stream might have
> the defer setup marked. Did you check that? And in that case we should
> consider it connected, or maybe we should not advance and send the
> start operation if it is still deferred.
Afaiu, setup_connect_io() is only called for one of the stream when
it is part of a CIG, with setup->stream set to this stream.
But, as for BAP/UCL/STR/BV-546-C the transports are unlinked after
creation of the transports, when the other stream is acquired first
bap_ucast_set_io() is called with the stream from setup which is not
in enabling state and with empty links, preventing to call
bt_bap_stream_start().
Here is the log without the bap_connect_io_cb() changes:
bluetoothd[197722]: [:1.493:method_call] <
org.bluez.MediaEndpoint1.SelectProperties
bluetoothd[197722]: profiles/audio/media.c:media_endpoint_async_call()
Calling SelectProperties: name = :1.493 path = /local/endpoint/ep0
bluetoothd[197722]: [:1.493:method_call] <
org.bluez.MediaEndpoint1.SelectProperties
bluetoothd[197722]: profiles/audio/media.c:media_endpoint_async_call()
Calling SelectProperties: name = :1.493 path = /local/endpoint/ep1
bluetoothd[197722]: profiles/audio/bap.c:bap_select_complete() selecting 2
bluetoothd[197722]: profiles/audio/bap.c:setup_new() ep 0x50600004bb00
setup 0x50f0000017b0
bluetoothd[197722]: profiles/audio/bap.c:bap_select_complete() selecting 1
bluetoothd[197722]: profiles/audio/bap.c:setup_new() ep 0x50600004bda0
setup 0x50f0000018a0
bluetoothd[197722]: profiles/audio/bap.c:bap_select_complete() selecting 0
bluetoothd[197722]: profiles/audio/bap.c:bap_select_complete() configure
(err 0)
bluetoothd[197722]: profiles/audio/bap.c:setup_config() setup
0x50f0000017b0 caps 0x50200006ecd0 metadata 0x50200006ed10
bluetoothd[197722]: #0: len 0x02 type 0x01
bluetoothd[197722]: Sampling Frequency: 16 Khz (0x03)
bluetoothd[197722]: #1: len 0x02 type 0x02
bluetoothd[197722]: Frame Duration: 10 ms (0x01)
bluetoothd[197722]: #2: len 0x03 type 0x04
bluetoothd[197722]: Frame Length: 40 (0x0028)
bluetoothd[197722]: #3: len 0x05 type 0x03
bluetoothd[197722]: Location: 0x00000001
bluetoothd[197722]: Location: Front Left (0x00000001)
bluetoothd[197722]: src/shared/bap.c:bap_queue_req() req 0x50600004c2e0
(op 0x01) queue 0x503000033a00
bluetoothd[197722]: profiles/audio/bap.c:setup_config() setup
0x50f0000018a0 caps 0x50200006ed50 metadata 0x50200006ed90
bluetoothd[197722]: #0: len 0x02 type 0x01
bluetoothd[197722]: Sampling Frequency: 16 Khz (0x03)
bluetoothd[197722]: #1: len 0x02 type 0x02
bluetoothd[197722]: Frame Duration: 10 ms (0x01)
bluetoothd[197722]: #2: len 0x03 type 0x04
bluetoothd[197722]: Frame Length: 40 (0x0028)
bluetoothd[197722]: #3: len 0x05 type 0x03
bluetoothd[197722]: Location: 0x00000001
bluetoothd[197722]: Location: Front Left (0x00000001)
bluetoothd[197722]: src/shared/bap.c:bap_queue_req() req 0x50600004c340
(op 0x01) queue 0x503000071410
bluetoothd[197722]: profiles/audio/bap.c:bap_config_setup_cb() setup
(nil) code 0 remain 2
bluetoothd[197722]: src/shared/bap.c:bap_process_queue()
bluetoothd[197722]: src/shared/bap.c:bap_send() req 0x50600004c2e0 len 52
bluetoothd[197722]: src/shared/att.c:can_read_data() (chan
0x508000010d20) ATT PDU received: 0x1b
bluetoothd[197722]: profiles/audio/bap.c:config_cb() stream
0x50e000008460 code 0x00 reason 0x00
bluetoothd[197722]: profiles/audio/bap.c:config_cb() stream
0x50e000008540 code 0x00 reason 0x00
bluetoothd[197722]: src/shared/bap.c:bap_process_queue()
bluetoothd[197722]: profiles/audio/bap.c:bap_update_cig() adapter
0x513000000200 CIG 0x0
bluetoothd[197722]: profiles/audio/bap.c:bap_update_cig() setup
0x50f0000018a0 stream 0x50e000008540 busy
bluetoothd[197722]: profiles/audio/bap.c:bap_update_cig() setup
0x50f0000018a0 stream 0x50e000008540 readying
bluetoothd[197722]: src/shared/att.c:can_read_data() (chan
0x508000010d20) ATT PDU received: 0x1b
bluetoothd[197722]: src/shared/bap.c:bap_ep_set_status() ASE status: ep
0x504000021950 id 0x01 handle 0x0121 state config len 39
bluetoothd[197722]: src/shared/bap.c:ep_status_config() codec 0x06
framing 0x00.phys 0x03 rtn 2 latency 10 pd 40000 - 40000 ppd 0 - 0
bluetoothd[197722]: src/shared/bap.c:ep_status_config() Codec Config #0:
type 0x01 len 2
bluetoothd[197722]: src/shared/bap.c:ep_status_config() Codec Config #1:
type 0x02 len 2
bluetoothd[197722]: src/shared/bap.c:ep_status_config() Codec Config #2:
type 0x04 len 3
bluetoothd[197722]: src/shared/bap.c:ep_status_config() Codec Config #3:
type 0x03 len 5
bluetoothd[197722]: src/shared/bap.c:bap_stream_update_io_links() stream
0x50e000008540
bluetoothd[197722]: src/shared/bap.c:bap_ucast_io_link() stream
0x50e000008460 link 0x50e000008540
bluetoothd[197722]: profiles/audio/bap.c:bap_state() stream
0x50e000008540: idle(0) -> config(1)
bluetoothd[197722]: profiles/audio/bap.c:bap_update_cig() adapter
0x513000000200 CIG 0x0
bluetoothd[197722]: profiles/audio/bap.c:bap_update_cig() setup
0x50f0000017b0 stream 0x50e000008460 busy
bluetoothd[197722]: profiles/audio/bap.c:bap_update_cig() setup
0x50f0000018a0 stream 0x50e000008540 readying
bluetoothd[197722]: src/shared/att.c:can_read_data() (chan
0x508000010d20) ATT PDU received: 0x1b
bluetoothd[197722]: src/shared/bap.c:bap_ep_set_status() ASE status: ep
0x504000021a50 id 0x0a handle 0x0127 state config len 39
bluetoothd[197722]: src/shared/bap.c:ep_status_config() codec 0x06
framing 0x00.phys 0x03 rtn 2 latency 10 pd 40000 - 40000 ppd 0 - 0
bluetoothd[197722]: src/shared/bap.c:ep_status_config() Codec Config #0:
type 0x01 len 2
bluetoothd[197722]: src/shared/bap.c:ep_status_config() Codec Config #1:
type 0x02 len 2
bluetoothd[197722]: src/shared/bap.c:ep_status_config() Codec Config #2:
type 0x04 len 3
bluetoothd[197722]: src/shared/bap.c:ep_status_config() Codec Config #3:
type 0x03 len 5
bluetoothd[197722]: src/shared/bap.c:bap_stream_update_io_links() stream
0x50e000008460
bluetoothd[197722]: profiles/audio/bap.c:bap_state() stream
0x50e000008460: idle(0) -> config(1)
bluetoothd[197722]: profiles/audio/bap.c:bap_update_cig() adapter
0x513000000200 CIG 0x0
bluetoothd[197722]: profiles/audio/bap.c:update_cig_setup_qos()
0x50f0000018a0
bluetoothd[197722]: profiles/audio/bap.c:setup_create_io() setup
0x50f0000018a0 stream 0x50e000008540 defer true
bluetoothd[197722]: src/shared/bap.c:bt_bap_stream_io_get_qos() in
0x50e000008498 out 0x50e000008578
bluetoothd[197722]: profiles/audio/bap.c:setup_connect_io() setup
0x50f0000018a0 stream 0x50e000008540 defer true setup->stream 0x50e000008540
bluetoothd[197722]: src/shared/bap.c:bap_stream_io_attach() stream
0x50e000008540 connecting true
bluetoothd[197722]: src/shared/bap.c:stream_io_new() fd 26
bluetoothd[197722]: profiles/audio/bap.c:bap_connecting() stream
0x50e000008540 fd 26: CIG 0x00 CIS 0x00
bluetoothd[197722]: profiles/audio/bap.c:bap_connecting() stream
0x50e000008460 fd 26: CIG 0x00 CIS 0x00
bluetoothd[197722]: src/shared/bap.c:bap_queue_req() req 0x50600004c520
(op 0x02) queue 0x503000033a00
bluetoothd[197722]: profiles/audio/bap.c:update_cig_setup_qos()
0x50f0000017b0
bluetoothd[197722]: profiles/audio/bap.c:setup_create_io() setup
0x50f0000017b0 stream 0x50e000008460 defer true
bluetoothd[197722]: src/shared/bap.c:bt_bap_stream_io_get_qos() in
0x50e000008498 out 0x50e000008578
bluetoothd[197722]: src/shared/bap.c:bap_queue_req() req 0x50600004c5e0
(op 0x02) queue 0x503000071b30
bluetoothd[197722]: profiles/audio/bap.c:bap_update_cig() setup
0x50f0000018a0 stream 0x50e000008540 readying
bluetoothd[197722]: src/shared/bap.c:bap_process_queue()
bluetoothd[197722]: src/shared/bap.c:bap_send() req 0x50600004c520 len 34
bluetoothd[197722]: src/shared/att.c:can_read_data() (chan
0x508000010d20) ATT PDU received: 0x1b
bluetoothd[197722]: profiles/audio/bap.c:qos_cb() stream 0x50e000008540
code 0x00 reason 0x00
bluetoothd[197722]: profiles/audio/bap.c:qos_cb() stream 0x50e000008460
code 0x00 reason 0x00
bluetoothd[197722]: src/shared/bap.c:bap_process_queue()
bluetoothd[197722]: profiles/audio/bap.c:bap_update_cig() adapter
0x513000000200 CIG 0x0
bluetoothd[197722]: profiles/audio/bap.c:bap_update_cig() setup
0x50f0000018a0 stream 0x50e000008540 readying
bluetoothd[197722]: src/shared/att.c:can_read_data() (chan
0x508000010d20) ATT PDU received: 0x1b
bluetoothd[197722]: src/shared/bap.c:bap_ep_set_status() ASE status: ep
0x504000021950 id 0x01 handle 0x0121 state qos len 15
bluetoothd[197722]: src/shared/bap.c:ep_status_qos() CIG 0x00 CIS 0x00
interval 10000 framing 0x00 phys 0x02 rtn 2 latency 10 pd 40000
bluetoothd[197722]: profiles/audio/media.c:pac_config() endpoint
0x50f000000f40 stream 0x50e000008540
bluetoothd[197722]: [signal]
org.freedesktop.DBus.ObjectManager.InterfacesAdded
bluetoothd[197722]: [:1.493:method_call] <
org.bluez.MediaEndpoint1.SetConfiguration
bluetoothd[197722]: profiles/audio/media.c:media_endpoint_async_call()
Calling SetConfiguration: name = :1.493 path = /local/endpoint/ep1
bluetoothd[197722]: src/shared/bap.c:bap_stream_update_io_links() stream
0x50e000008540
bluetoothd[197722]: profiles/audio/bap.c:bap_state() stream
0x50e000008540: config(1) -> qos(2)
bluetoothd[197722]: profiles/audio/bap.c:bap_config_setup_cb() setup
0x50f0000018a0 code 0 remain 1
bluetoothd[197722]: profiles/audio/transport.c:bap_state_changed()
stream 0x50e000008540: config(1) -> qos(2)
bluetoothd[197722]:
profiles/audio/transport.c:transport_update_playing()
/org/bluez/hci0/dev_9C_47_2A_39_56_FE/pac_sink0/fd0
State=TRANSPORT_STATE_IDLE Playing=0
bluetoothd[197722]: profiles/audio/bap.c:bap_update_cig() adapter
0x513000000200 CIG 0x0
bluetoothd[197722]: profiles/audio/bap.c:bap_update_cig() setup
0x50f0000017b0 stream 0x50e000008460 readying
bluetoothd[197722]: src/shared/att.c:can_read_data() (chan
0x508000010d20) ATT PDU received: 0x1b
bluetoothd[197722]: src/shared/bap.c:bap_ep_set_status() ASE status: ep
0x504000021a50 id 0x0a handle 0x0127 state qos len 15
bluetoothd[197722]: src/shared/bap.c:ep_status_qos() CIG 0x00 CIS 0x00
interval 10000 framing 0x00 phys 0x02 rtn 2 latency 10 pd 40000
bluetoothd[197722]: profiles/audio/media.c:pac_config() endpoint
0x50f000000d60 stream 0x50e000008460
bluetoothd[197722]: [signal]
org.freedesktop.DBus.ObjectManager.InterfacesAdded
bluetoothd[197722]: [:1.493:method_call] <
org.bluez.MediaEndpoint1.SetConfiguration
bluetoothd[197722]: profiles/audio/media.c:media_endpoint_async_call()
Calling SetConfiguration: name = :1.493 path = /local/endpoint/ep0
bluetoothd[197722]: src/shared/bap.c:bap_stream_update_io_links() stream
0x50e000008460
bluetoothd[197722]: profiles/audio/bap.c:bap_state() stream
0x50e000008460: config(1) -> qos(2)
bluetoothd[197722]: profiles/audio/bap.c:bap_config_setup_cb() setup
0x50f0000017b0 code 0 remain 0
bluetoothd[197722]: profiles/audio/transport.c:bap_state_changed()
stream 0x50e000008460: config(1) -> qos(2)
bluetoothd[197722]:
profiles/audio/transport.c:transport_bap_update_links_uc() stream
0x50e000008540 linked true
bluetoothd[197722]:
profiles/audio/transport.c:transport_bap_update_links_uc() stream
0x50e000008460 linked true
bluetoothd[197722]:
profiles/audio/transport.c:transport_update_playing()
/org/bluez/hci0/dev_9C_47_2A_39_56_FE/pac_source0/fd1
State=TRANSPORT_STATE_IDLE Playing=0
bluetoothd[197722]: profiles/audio/bap.c:bap_update_cig() adapter
0x513000000200 CIG 0x0
bluetoothd[197722]: [signal]
org.freedesktop.DBus.Properties.PropertiesChanged
bluetoothd[197722]: [signal]
org.freedesktop.DBus.Properties.PropertiesChanged
bluetoothd[197722]: [0x511000013a00]
src/shared/gatt-client.c:discover_primary_cb() Primary services found: 6
bluetoothd[197722]: [0x511000013a00]
src/shared/gatt-client.c:discovery_parse_services() start: 0x0001, end:
0x000f, uuid: 00001800-0000-1000-8000-00805f9b34fb
bluetoothd[197722]: [0x511000013a00]
src/shared/gatt-client.c:discovery_parse_services() start: 0x0040, end:
0x0047, uuid: 00001801-0000-1000-8000-00805f9b34fb
bluetoothd[197722]: [0x511000013a00]
src/shared/gatt-client.c:discovery_parse_services() start: 0x0080, end:
0x0099, uuid: 0000180a-0000-1000-8000-00805f9b34fb
bluetoothd[197722]: [0x511000013a00]
src/shared/gatt-client.c:discovery_parse_services() start: 0x00d0, end:
0x00e2, uuid: 00001850-0000-1000-8000-00805f9b34fb
bluetoothd[197722]: [0x511000013a00]
src/shared/gatt-client.c:discovery_parse_services() start: 0x0120, end:
0x012f, uuid: 0000184e-0000-1000-8000-00805f9b34fb
bluetoothd[197722]: [0x511000013a00]
src/shared/gatt-client.c:discovery_parse_services() start: 0x0160, end:
0x0165, uuid: 0000184f-0000-1000-8000-00805f9b34fb
bluetoothd[197722]: [0x511000013a00]
src/shared/gatt-client.c:write_client_features() Writing Client Features
0x05
bluetoothd[197722]: src/device.c:gatt_client_ready_cb() status: success,
error: 10
bluetoothd[197722]: profiles/ranging/rap.c:rap_accept() 9C:47:2A:39:56:FE
bluetoothd[197722]: profiles/ranging/rap.c:rap_accept() RAP unable to attach
bluetoothd[197722]: src/service.c:service_accept() rap profile accept
failed for 9C:47:2A:39:56:FE
bluetoothd[197722]: src/gatt-client.c:btd_gatt_client_ready() GATT
client ready
bluetoothd[197722]: src/gatt-client.c:create_services() Exporting
objects for GATT services: 9C:47:2A:39:56:FE
bluetoothd[197722]: src/gatt-client.c:btd_gatt_client_ready() Features 0x05
bluetoothd[197722]: src/device.c:device_svc_resolved()
/org/bluez/hci0/dev_9C_47_2A_39_56_FE err 0
bluetoothd[197722]: [signal]
org.freedesktop.DBus.Properties.PropertiesChanged
bluetoothd[197722]: [0x511000013a00]
src/shared/gatt-client.c:service_changed_register_cb() Registered
handler for "Service Changed": 1
bluetoothd[197722]: [:1.493:method_call] >
org.freedesktop.DBus.Properties.Set [#21661]
bluetoothd[197722]: src/shared/bap.c:stream_unlink_ucast() stream
0x50e000008460 unlink
bluetoothd[197722]: src/shared/bap.c:bap_ucast_io_unlink() stream
0x50e000008540 unlink
bluetoothd[197722]:
profiles/audio/transport.c:transport_bap_update_links_uc() stream
0x50e000008540 linked false
bluetoothd[197722]: src/shared/bap.c:bap_ucast_io_unlink() stream
0x50e000008460 unlink
bluetoothd[197722]:
profiles/audio/transport.c:transport_bap_update_links_uc() stream
0x50e000008460 linked false
bluetoothd[197722]: [signal]
org.freedesktop.DBus.Properties.PropertiesChanged
bluetoothd[197722]: [signal]
org.freedesktop.DBus.Properties.PropertiesChanged
bluetoothd[197722]: [:1.493:method_return] < [#21661]
bluetoothd[197722]: [:1.493:method_call] >
org.bluez.MediaTransport1.Acquire [#21662]
bluetoothd[197722]: profiles/audio/transport.c:media_owner_create()
Owner created: sender=:1.493
bluetoothd[197722]: profiles/audio/transport.c:media_transport_resume()
Transport /org/bluez/hci0/dev_9C_47_2A_39_56_FE/pac_source0/fd1 Owner :1.493
bluetoothd[197722]: src/shared/bap.c:bap_queue_req() req 0x506000052580
(op 0x03) queue 0x503000033a00
bluetoothd[197722]: profiles/audio/transport.c:transport_set_state()
State changed /org/bluez/hci0/dev_9C_47_2A_39_56_FE/pac_source0/fd1:
TRANSPORT_STATE_IDLE -> TRANSPORT_STATE_REQUESTING
bluetoothd[197722]: profiles/audio/transport.c:media_request_create()
Request created: method=Acquire id=19
bluetoothd[197722]: profiles/audio/transport.c:media_owner_add() Owner
:1.493 Request Acquire
bluetoothd[197722]:
profiles/audio/transport.c:media_transport_set_owner() Transport
/org/bluez/hci0/dev_9C_47_2A_39_56_FE/pac_source0/fd1 Owner :1.493
bluetoothd[197722]: src/shared/bap.c:bap_process_queue()
bluetoothd[197722]: src/shared/bap.c:bap_send() req 0x506000052580 len 8
bluetoothd[197722]: src/shared/att.c:can_read_data() (chan
0x508000010d20) ATT PDU received: 0x1b
bluetoothd[197722]: src/shared/bap.c:bap_process_queue()
bluetoothd[197722]: src/shared/att.c:can_read_data() (chan
0x508000010d20) ATT PDU received: 0x1b
bluetoothd[197722]: src/shared/bap.c:bap_ep_set_status() ASE status: ep
0x504000021a50 id 0x0a handle 0x0127 state enabling len 7
bluetoothd[197722]: src/shared/bap.c:ep_status_metadata() CIS 0x00 CIG
0x00 metadata len 4
bluetoothd[197722]: profiles/audio/bap.c:bap_state() stream
0x50e000008460: qos(2) -> enabling(3)
bluetoothd[197722]: profiles/audio/transport.c:bap_state_changed()
stream 0x50e000008460: qos(2) -> enabling(3)
bluetoothd[197722]: profiles/audio/bap.c:bap_update_cig() adapter
0x513000000200 CIG 0x0
bluetoothd[197722]: profiles/audio/bap.c:update_cig_setup_enable()
0x50f0000017b0
bluetoothd[197722]: profiles/audio/bap.c:setup_create_io() setup
0x50f0000017b0 stream 0x50e000008460 defer false
bluetoothd[197722]: src/shared/bap.c:bt_bap_stream_io_get_qos() in
0x50e000008498 out (nil)
bluetoothd[197722]: profiles/audio/bap.c:setup_accept_io() stream
0x50e000008460 fd 26 defer true
bluetoothd[197722]: profiles/audio/bap.c:iso_connect_cb() ISO connected
>> + */
>> + data.chan = chan;
>> + data.err = err;
>> + data.cig_id = setup->qos.ucast.cig_id;
>> + queue_find(setup->ep->data->snks, find_enabling_ep, &data);
>> + queue_find(setup->ep->data->srcs, find_enabling_ep, &data);
>> + return;
>> + }
>> +
>> iso_connect_cb(chan, err, setup->stream);
>> }
>>
>> diff --git a/profiles/audio/transport.c b/profiles/audio/transport.c
>> index d9feef768..794c492af 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,
>> @@ -1417,6 +1419,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)
>> @@ -1434,6 +1439,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;
>> @@ -1484,6 +1499,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)
>> {
>> @@ -1879,8 +1909,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;
>> @@ -2535,10 +2563,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, \
>> @@ -2560,26 +2589,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, \
>> @@ -2587,7 +2618,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
>> @@ -2642,6 +2673,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)
>> @@ -2696,9 +2728,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
>>
>>
>
--
Frédéric Danis
Senior Software Engineer
Collabora Ltd.
Platinum Building, St John's Innovation Park, Cambridge CB4 0DS, United Kingdom
Registered in England & Wales, no. 5513718
^ permalink raw reply [flat|nested] 7+ messages in thread