* [PATCH BlueZ v1 0/6] Add true wireless BAP broadcast tests
@ 2026-09-21 21:01 Luiz Augusto von Dentz
2026-09-21 21:01 ` [PATCH BlueZ v1 1/6] doc: describe the true wireless broadcast test cases Luiz Augusto von Dentz
` (6 more replies)
0 siblings, 7 replies; 9+ messages in thread
From: Luiz Augusto von Dentz @ 2026-09-21 21:01 UTC (permalink / raw)
To: linux-bluetooth
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
This adds functional tests for broadcast setups where the left and the
right side are separate devices, as in a pair of earbuds, which so far
were only covered with a single sink taking both channels.
The source broadcasts one BIS per channel, and each side registers a
single location, so each of them only gets a transport for the BIS
carrying its own channel: the sides are told apart by the BIS index of
the transport path.
Two topologies are covered. In the first one there is no connection at
all, and each side scans, syncs to the periodic advertising and reads
the BASE on its own. In the second one the two sides are Scan Delegators
forming a coordinated set, so the assistant discovers and connects them
as one device, and pushes one BIS to each of them, each receiving the
periodic advertising sync over its own ACL (PAST).
Writing the second one turned up two bugs, fixed here as well:
- Every MediaAssistant object of a local broadcast shared the bass_data
of the adapter, and a push stored the session it was pushed to in it,
so pushing to the second side replaced the target of the first one.
The Broadcast Code the left side asked for was then written to the
right one, and the left side never synced. This only shows with more
than one delegator, which is why it went unnoticed so far.
- Pairing a device that is already paired was rejected with
AlreadyExists, although the documentation described it as re-pairing.
It connects the device instead now, which is of actual use to the
user, and lets a test run against hosts that a previous one left
bonded, as the hosts are reused.
Luiz Augusto von Dentz (6):
doc: describe the true wireless broadcast test cases
doc: describe the true wireless broadcast assistant test case
test: functional: add the true wireless broadcast tests
bass: keep the push target per MediaAssistant object
client: connect a device that is already paired
test: functional: add the true wireless broadcast assistant test
client/main.c | 40 +++
client/scripts/broadcast-delegator-left.bt | 32 +++
client/scripts/broadcast-delegator-right.bt | 32 +++
client/scripts/broadcast-sink-left.bt | 23 ++
client/scripts/broadcast-sink-right.bt | 23 ++
doc/bluetoothctl.rst | 6 +-
doc/functional-bap.rst | 246 +++++++++++++++++
profiles/audio/bass.c | 39 ++-
test/functional/test_bap.py | 285 +++++++++++++++++++-
9 files changed, 701 insertions(+), 25 deletions(-)
create mode 100644 client/scripts/broadcast-delegator-left.bt
create mode 100644 client/scripts/broadcast-delegator-right.bt
create mode 100644 client/scripts/broadcast-sink-left.bt
create mode 100644 client/scripts/broadcast-sink-right.bt
--
2.55.0
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH BlueZ v1 1/6] doc: describe the true wireless broadcast test cases
2026-09-21 21:01 [PATCH BlueZ v1 0/6] Add true wireless BAP broadcast tests Luiz Augusto von Dentz
@ 2026-09-21 21:01 ` Luiz Augusto von Dentz
2026-09-21 23:47 ` Add true wireless BAP broadcast tests bluez.test.bot
2026-09-21 21:01 ` [PATCH BlueZ v1 2/6] doc: describe the true wireless broadcast assistant test case Luiz Augusto von Dentz
` (5 subsequent siblings)
6 siblings, 1 reply; 9+ messages in thread
From: Luiz Augusto von Dentz @ 2026-09-21 21:01 UTC (permalink / raw)
To: linux-bluetooth
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
Assisted-by: opencode:claude-opus-5
---
doc/functional-bap.rst | 112 +++++++++++++++++++++++++++++++++++++++++
1 file changed, 112 insertions(+)
diff --git a/doc/functional-bap.rst b/doc/functional-bap.rst
index 60c38c5ed0e0..926d9a54a632 100644
--- a/doc/functional-bap.rst
+++ b/doc/functional-bap.rst
@@ -221,6 +221,118 @@ test_bap_broadcast_transport_acquire[lc3|pbp]
match the one the source encrypted the BIG with, otherwise the
sink cannot decrypt the stream.
+TRUE WIRELESS BROADCAST
+=======================
+
+Three hosts, with the two sinks acting as the sides of a true wireless
+pair, e.g. a pair of earbuds, each one a device of its own:
+
+.. code-block::
+
+ +------------------------+ +------------------------+
+ | host0 | extended + | host1 |
+ | Broadcast Source | periodic | Broadcast Sink, left |
+ | bluetoothctl -a auto | advertising | bluetoothctl -a auto |
+ | broadcast-source-2bis | --------------> | broadcast-sink-left.bt |
+ | BCAA endpoint (0x1852) | | BAA endpoint (0x1851) |
+ | | BIS 1, F.Left | Front Left |
+ | | ==============> | |
+ | | +------------------------+
+ | |
+ | | extended + +------------------------+
+ | | periodic | host2 |
+ | | advertising | Broadcast Sink, right |
+ | | --------------> | bluetoothctl -a auto |
+ | | | broadcast-sink-right |
+ | | BIS 2, F.Right | BAA endpoint (0x1851) |
+ | | ==============> | Front Right |
+ +------------------------+ +------------------------+
+
+ one BIG holding one BIS per channel, and each side syncing to the
+ BIS carrying its own channel
+
+ --> advertising is scanned by ==> audio flows towards
+
+There is no connection at all here, not between the source and the
+sides and not between the sides either: each side scans, finds the
+Broadcast Source, syncs to the periodic advertising, reads the BASE and
+syncs to the BIG on its own.
+
+``client/scripts/broadcast-source-2bis.bt`` on host0
+ Registers a Broadcast Source endpoint
+ (``00001852-0000-1000-8000-00805f9b34fb``) with LC3 and configures
+ it twice with the 16_2_1 preset, in the same BIG, once with Front
+ Left and once with Front Right as the channel allocation, so the
+ BIG carries one BIS per channel. Both transports are acquired,
+ which starts the broadcast, encrypted with the broadcast code
+ `bluetoothctl` uses by default.
+
+``client/scripts/broadcast-sink-left.bt`` on host1
+ Registers a Broadcast Sink endpoint
+ (``00001851-0000-1000-8000-00805f9b34fb``) with LC3 and Front Left
+ as its only location, and scans.
+
+``client/scripts/broadcast-sink-right.bt`` on host2
+ As above, with Front Right as its only location.
+
+Registering a single location is what makes a side take a single
+channel: `bluetoothctl` adds the channel count to the capabilities, and
+`bluetoothd` only creates a transport for a BIS whose Channel
+Allocation is covered by the locations of the local PAC. A sink
+registering both locations, as in the BROADCAST section above, takes
+every BIS of the BIG instead.
+
+test_bap_broadcast_earbuds_transport_created
+--------------------------------------------
+
+:Setup: As above.
+
+:Steps:
+ 1. Start `bluetoothctl` with the source script on host0.
+ 2. Start `bluetoothctl` with the sink scripts on host1 and host2.
+ 3. Each side: ``transport.list``, to check that nothing besides
+ the transport of its own BIS was created.
+
+:Expected:
+ 1. ``Endpoint /local/endpoint/ep0 registered``, then two
+ ``Acquire successful: fd <fd> MTU <read>:<write>`` on the
+ source, one per BIS, i.e. it is broadcasting both channels.
+ 2. Each side syncs to the periodic advertising on its own and
+ creates exactly one transport, for the BIS carrying its own
+ channel: ``.../sid0/bis1/fdN`` on the left side and
+ ``.../sid0/bis2/fdN`` on the right one.
+ 3. Each transport reports ``Codec: 0x06`` for LC3 and
+ ``State: idle``.
+
+:Notes: The BIS index of the path is what tells the two apart, as each
+ side only ever sees its own.
+
+ Both transports of the source have to be acquired, as for the CIS
+ of a CIG: the BIG is only created once every BIS of it is ready.
+
+test_bap_broadcast_earbuds_transport_acquire
+--------------------------------------------
+
+:Setup: As above, with the transport of each side already created.
+
+:Steps:
+ 1. Each side: ``transport.select <transport>``.
+ 2. Answer ``Enter bcode[value/no]:`` with the broadcast code the
+ source used.
+
+:Expected: On each side the transport moves to ``State: broadcasting``,
+ i.e. it synced to the BIG, then ``Acquire successful: fd <fd> MTU
+ <read>:<write>`` and ``State: active``.
+
+:Notes: The two sides sync independently, so each has to be given the
+ broadcast code of its own, and one side reaching ``active`` says
+ nothing about the other: both are checked.
+
+ Nothing keeps the sides in step, unlike a coordinated set, where
+ they are at least discovered and connected together. Rendering
+ them in sync is left to the BIG itself, through the presentation
+ delay.
+
BROADCAST ASSISTANT
===================
--
2.55.0
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH BlueZ v1 2/6] doc: describe the true wireless broadcast assistant test case
2026-09-21 21:01 [PATCH BlueZ v1 0/6] Add true wireless BAP broadcast tests Luiz Augusto von Dentz
2026-09-21 21:01 ` [PATCH BlueZ v1 1/6] doc: describe the true wireless broadcast test cases Luiz Augusto von Dentz
@ 2026-09-21 21:01 ` Luiz Augusto von Dentz
2026-09-21 21:01 ` [PATCH BlueZ v1 3/6] test: functional: add the true wireless broadcast tests Luiz Augusto von Dentz
` (4 subsequent siblings)
6 siblings, 0 replies; 9+ messages in thread
From: Luiz Augusto von Dentz @ 2026-09-21 21:01 UTC (permalink / raw)
To: linux-bluetooth
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
Assisted-by: opencode:claude-opus-5
---
doc/functional-bap.rst | 122 +++++++++++++++++++++++++++++++++++++++++
1 file changed, 122 insertions(+)
diff --git a/doc/functional-bap.rst b/doc/functional-bap.rst
index 926d9a54a632..87917065e1eb 100644
--- a/doc/functional-bap.rst
+++ b/doc/functional-bap.rst
@@ -537,3 +537,125 @@ test_bap_unicast_set_transport_acquire
are only created once every one of them is active, so the
transports of both members have to be acquired: acquiring only
one leaves the group incomplete and no CIS is created at all.
+
+TRUE WIRELESS BROADCAST ASSISTANT
+=================================
+
+Three hosts, with the Broadcast Source and the Broadcast Assistant
+colocated on host0, and the two sides of a true wireless pair as Scan
+Delegators forming a coordinated set:
+
+.. code-block::
+
+ +------------------------+ +------------------------+
+ | host0 | extended + | host1 |
+ | Broadcast Source | periodic adv | Scan Delegator, left |
+ | + Broadcast Assistant | --------------> | bluetoothctl -a auto |
+ | bluetoothctl -a auto | | broadcast-delegator- |
+ | broadcast-source-2bis | BIS 1, F.Left | left.bt |
+ | BCAA endpoint (0x1852) | ==============> | BAA endpoint (0x1851) |
+ | | | Front Left, rank 1 |
+ | | ACL, BASS, PAST | |
+ | | --------------> | |
+ | | +------------------------+
+ | |
+ | | extended + +------------------------+
+ | | periodic adv | host2 |
+ | | --------------> | Scan Delegator, right |
+ | | | bluetoothctl -a auto |
+ | | BIS 2, F.Right | broadcast-delegator- |
+ | | ==============> | right.bt |
+ | | | BAA endpoint (0x1851) |
+ | | ACL, BASS, PAST | Front Right, rank 2 |
+ | | --------------> | |
+ +------------------------+ +------------------------+
+
+ the two delegators share the same SIRK, so they are resolved into
+ a single set and connecting one connects the other
+
+ the assistant pushes one BIS to each side, and each of them
+ receives the periodic advertising sync over its own connection
+
+ --> advertising is scanned by ==> audio flows towards
+
+This combines the two topologies above: the BIG carries one BIS per
+channel, as in TRUE WIRELESS BROADCAST, while the sides are members of
+a coordinated set, as in COORDINATED SET, so the assistant discovers
+and connects them as one device.
+
+The delegators run `bluetoothd` with the same ``[CSIS]`` configuration
+as the acceptors of the COORDINATED SET section, with ``Rank`` 1 for
+the left one and 2 for the right one, and both advertise the RSI.
+
+``client/scripts/broadcast-source-2bis.bt`` on host0
+ As in TRUE WIRELESS BROADCAST: a BIG with one BIS per channel,
+ encrypted with the broadcast code `bluetoothctl` uses by default.
+ The two local streams are exposed as MediaAssistant objects in
+ the ``local`` state, under the adapter path, i.e.
+ ``/org/bluez/hci0/sid0/bis1`` and ``/org/bluez/hci0/sid0/bis2``.
+
+``client/scripts/broadcast-delegator-left.bt`` on host1
+ Registers a Broadcast Sink endpoint
+ (``00001851-0000-1000-8000-00805f9b34fb``) with LC3 and Front Left
+ as its only location, enables automatic transport selection and
+ acquisition, and advertises with the RSI, so the Broadcast
+ Assistant can discover it as a member of the set and connect.
+
+``client/scripts/broadcast-delegator-right.bt`` on host2
+ As above, with Front Right as its only location and rank 2.
+
+test_bass_past_earbuds_transport_acquire
+----------------------------------------
+
+:Setup: As above.
+
+:Steps:
+ 1. Start `bluetoothctl` with the source script on host0 and the
+ delegator scripts on host1 and host2.
+ 2. Assistant: ``scan on``, wait for both members to be found,
+ ``scan off``, then ``pair`` one of them.
+ 3. Assistant: ``assistant.push /org/bluez/hci0/sid0/bis1``,
+ answering the device prompt with the device path of the left
+ delegator, and ``assistant.push /org/bluez/hci0/sid0/bis2``
+ with the right one.
+
+:Expected:
+ 1. Two ``Acquire successful: fd <fd> MTU <read>:<write>`` on the
+ source, i.e. it is broadcasting both channels, with the local
+ streams exposed as ``[NEW] Assistant <adapter>/sid0/bis1`` and
+ ``<adapter>/sid0/bis2``. On both delegators,
+ ``Advertising object registered``.
+ 2. ``Pairing successful``, the members are resolved into a single
+ set, reported as
+ ``[NEW] DeviceSet /org/bluez/hci0/set_<sirk>``, and the other
+ member is connected and bonded on its own, with the services
+ of both resolved.
+ 3. ``Assistant <path> pushed`` for both pushes.
+ 4. On each delegator a transport is created for the BIS that was
+ pushed to it, and selected and acquired automatically, reaching
+ ``State: broadcasting``, i.e. it synced to the BIG, and then
+ ``State: active``.
+
+:Notes: Only one member is paired: finding a member of a set triggers
+ connecting the remaining ones. Both links still have to be
+ encrypted before the pushes, as the Broadcast Receive State
+ characteristic of each member is read over its own connection,
+ which is why the services of both are waited for.
+
+ Each member gets a push of its own: BASS has no notion of a set,
+ so the assistant adds the source to the Broadcast Receive State
+ of each delegator separately, and each of them receives the
+ periodic advertising sync over its own ACL (PAST).
+
+ A push carries the BIS to sync to, but the location of the member
+ is what decides whether a transport is created for it: pushing
+ the right BIS to the left side leaves it without one, as the
+ Channel Allocation does not match its PAC.
+
+ The broadcast code of the local streams is handed to the
+ delegators by the pushes, so their automatic transport selection
+ does not have to prompt for it on either side.
+
+ As in the BROADCAST ASSISTANT case, the streams are verified on
+ the delegators rather than through the state of the MediaAssistant
+ objects, which stay in the ``local`` state.
--
2.55.0
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH BlueZ v1 3/6] test: functional: add the true wireless broadcast tests
2026-09-21 21:01 [PATCH BlueZ v1 0/6] Add true wireless BAP broadcast tests Luiz Augusto von Dentz
2026-09-21 21:01 ` [PATCH BlueZ v1 1/6] doc: describe the true wireless broadcast test cases Luiz Augusto von Dentz
2026-09-21 21:01 ` [PATCH BlueZ v1 2/6] doc: describe the true wireless broadcast assistant test case Luiz Augusto von Dentz
@ 2026-09-21 21:01 ` Luiz Augusto von Dentz
2026-09-21 21:01 ` [PATCH BlueZ v1 4/6] bass: keep the push target per MediaAssistant object Luiz Augusto von Dentz
` (3 subsequent siblings)
6 siblings, 0 replies; 9+ messages in thread
From: Luiz Augusto von Dentz @ 2026-09-21 21:01 UTC (permalink / raw)
To: linux-bluetooth
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
Assisted-by: opencode:claude-opus-5
---
client/scripts/broadcast-sink-left.bt | 23 ++++++
client/scripts/broadcast-sink-right.bt | 23 ++++++
doc/functional-bap.rst | 10 ++-
test/functional/test_bap.py | 104 +++++++++++++++++++++++++
4 files changed, 158 insertions(+), 2 deletions(-)
create mode 100644 client/scripts/broadcast-sink-left.bt
create mode 100644 client/scripts/broadcast-sink-right.bt
diff --git a/client/scripts/broadcast-sink-left.bt b/client/scripts/broadcast-sink-left.bt
new file mode 100644
index 000000000000..d5ad2ed25abb
--- /dev/null
+++ b/client/scripts/broadcast-sink-left.bt
@@ -0,0 +1,23 @@
+power on
+#
+#
+# Register a Broadcast Sink endpoint with LC3 taking a single channel,
+# to act as the left side of a true wireless pair. Each side syncs to
+# the Broadcast Source on its own, with no assistant involved.
+#
+endpoint.register 00001851-0000-1000-8000-00805f9b34fb 0x06
+# Auto Accept: accept the configuration chosen by the peer
+y
+# Max Transports: no limit
+a
+# Locations: Front Left, so only the BIS carrying that channel is synced
+1
+# Supported Context: Media
+4
+#
+#
+# Scan for Broadcast Sources: the sink syncs to the periodic
+# advertising and creates a transport for the BIS matching its own
+# location.
+#
+scan on
diff --git a/client/scripts/broadcast-sink-right.bt b/client/scripts/broadcast-sink-right.bt
new file mode 100644
index 000000000000..58f07dbe67ec
--- /dev/null
+++ b/client/scripts/broadcast-sink-right.bt
@@ -0,0 +1,23 @@
+power on
+#
+#
+# Register a Broadcast Sink endpoint with LC3 taking a single channel,
+# to act as the right side of a true wireless pair. Each side syncs to
+# the Broadcast Source on its own, with no assistant involved.
+#
+endpoint.register 00001851-0000-1000-8000-00805f9b34fb 0x06
+# Auto Accept: accept the configuration chosen by the peer
+y
+# Max Transports: no limit
+a
+# Locations: Front Right, so only the BIS carrying that channel is synced
+2
+# Supported Context: Media
+4
+#
+#
+# Scan for Broadcast Sources: the sink syncs to the periodic
+# advertising and creates a transport for the BIS matching its own
+# location.
+#
+scan on
diff --git a/doc/functional-bap.rst b/doc/functional-bap.rst
index 87917065e1eb..7059e0c735f1 100644
--- a/doc/functional-bap.rst
+++ b/doc/functional-bap.rst
@@ -290,8 +290,9 @@ test_bap_broadcast_earbuds_transport_created
:Steps:
1. Start `bluetoothctl` with the source script on host0.
2. Start `bluetoothctl` with the sink scripts on host1 and host2.
- 3. Each side: ``transport.list``, to check that nothing besides
- the transport of its own BIS was created.
+ 3. Each side: list its **org.bluez.MediaTransport1** objects, to
+ check that nothing besides the transport of its own BIS was
+ created.
:Expected:
1. ``Endpoint /local/endpoint/ep0 registered``, then two
@@ -307,6 +308,11 @@ test_bap_broadcast_earbuds_transport_created
:Notes: The BIS index of the path is what tells the two apart, as each
side only ever sees its own.
+ The objects are listed over D-Bus rather than with
+ ``transport.list``: the endpoint prints the transport it was
+ configured with as well, so a listing cannot be told apart from
+ it in the output.
+
Both transports of the source have to be acquired, as for the CIS
of a CIG: the BIG is only created once every BIS of it is ready.
diff --git a/test/functional/test_bap.py b/test/functional/test_bap.py
index 7bd59777b53b..bfcb43c0ad67 100644
--- a/test/functional/test_bap.py
+++ b/test/functional/test_bap.py
@@ -312,6 +312,20 @@ def remote_transport_properties(remote):
}
+@mainloop_wrap
+def transport_paths():
+ """Paths of every MediaTransport1 object of the host."""
+ objects = dbus.Interface(
+ get_dbus().get_object("org.bluez", "/"),
+ "org.freedesktop.DBus.ObjectManager",
+ ).GetManagedObjects()
+ return sorted(
+ str(path)
+ for path, props in objects.items()
+ if "org.bluez.MediaTransport1" in props
+ )
+
+
def add_preset(ctl, name, props, metadata):
"""Make a custom preset from a transport's codec configuration and QoS."""
@@ -460,6 +474,96 @@ def test_bap_broadcast_transport_acquire(hosts, source_script):
)
+ACQUIRED = r"Acquire successful: fd \d+ MTU \d+:\d+"
+
+# Transport of a given BIS of a broadcast the sink is synced to
+BIS_TRANSPORT = r"Transport (/org/bluez/\S+/bis{}/fd\d+)"
+
+earbuds_host_config = host_config(
+ [Bluetoothd(conf=BAP_CONF), Pexpect()],
+ [Bluetoothd(conf=BAP_CONF), Pexpect()],
+ [Bluetoothd(conf=BAP_CONF), Pexpect()],
+)
+
+
+def start_earbuds_broadcast(hosts):
+ """
+ Source broadcasting one BIS per channel, with the two sides of a
+ true wireless pair scanning for it, each one taking a single
+ channel.
+ """
+ source_host, left_host, right_host = hosts
+
+ source = start_bluetoothctl(source_host, "broadcast-source-2bis.bt")
+
+ # The BIG is only created once every BIS of it is ready, so the
+ # script acquires both transports
+ expect_all(source, [ACQUIRED, ACQUIRED])
+
+ left = start_bluetoothctl(left_host, "broadcast-sink-left.bt")
+ right = start_bluetoothctl(right_host, "broadcast-sink-right.bt")
+
+ return source, left, right
+
+
+def expect_bis_transport(ctl, host, bis):
+ """
+ Transport a side created for the BIS carrying its own channel,
+ checking it is the only one it has.
+ """
+ _, m = ctl.expect(BIS_TRANSPORT.format(bis))
+ transport = m[0].decode("utf-8")
+
+ # Checked over D-Bus rather than with transport.list, as the
+ # endpoint prints the transport it was configured with as well, so
+ # a listing cannot be told apart from it in the output
+ assert host.call(transport_paths) == [transport]
+
+ return transport
+
+
+@earbuds_host_config
+def test_bap_broadcast_earbuds_transport_created(hosts):
+ source_host, left_host, right_host = hosts
+ source, left, right = start_earbuds_broadcast(hosts)
+
+ # Each side syncs to the periodic advertising on its own and only
+ # creates a transport for the BIS whose channel allocation matches
+ # its own location
+ for ctl, host, bis in ((left, left_host, 1), (right, right_host, 2)):
+ transport = expect_bis_transport(ctl, host, bis)
+
+ ctl.send(f"transport.show {transport}\n")
+ ctl.expect(f"Transport {transport}")
+ ctl.expect(r"Codec: 0x06")
+ ctl.expect("State: idle")
+
+
+@earbuds_host_config
+def test_bap_broadcast_earbuds_transport_acquire(hosts):
+ source_host, left_host, right_host = hosts
+ source, left, right = start_earbuds_broadcast(hosts)
+
+ # The two sides sync independently, so each of them is given the
+ # broadcast code of its own, and both are checked
+ for ctl, host, bis in ((left, left_host, 1), (right, right_host, 2)):
+ transport = expect_bis_transport(ctl, host, bis)
+
+ # Selecting the transport syncs to the BIG and starts acquiring
+ ctl.send(f"transport.select {transport}\n")
+ ctl.expect(r"Enter bcode\[value/no\]:")
+ ctl.send(f"{BCAST_CODE}\n")
+
+ expect_all(
+ ctl,
+ [
+ f"Transport {transport} State: broadcasting",
+ ACQUIRED,
+ f"Transport {transport} State: active",
+ ],
+ )
+
+
past_host_config = host_config(
[Bluetoothd(conf=BAP_CONF), Pexpect()],
[Bluetoothd(conf=BAP_CONF), Pexpect()],
--
2.55.0
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH BlueZ v1 4/6] bass: keep the push target per MediaAssistant object
2026-09-21 21:01 [PATCH BlueZ v1 0/6] Add true wireless BAP broadcast tests Luiz Augusto von Dentz
` (2 preceding siblings ...)
2026-09-21 21:01 ` [PATCH BlueZ v1 3/6] test: functional: add the true wireless broadcast tests Luiz Augusto von Dentz
@ 2026-09-21 21:01 ` Luiz Augusto von Dentz
2026-09-21 21:01 ` [PATCH BlueZ v1 5/6] client: connect a device that is already paired Luiz Augusto von Dentz
` (2 subsequent siblings)
6 siblings, 0 replies; 9+ messages in thread
From: Luiz Augusto von Dentz @ 2026-09-21 21:01 UTC (permalink / raw)
To: linux-bluetooth
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
Every MediaAssistant object of a local broadcast shares the bass_data of
the adapter, and pushing one stored the session of the delegator it was
pushed to by overwriting the bass of that shared data.
With a single delegator this goes unnoticed, but with one delegator per
side of a true wireless pair the second push replaces the target of the
first one, so the Broadcast Code of a request coming from the first
delegator is written to the second one, and the first never syncs.
Keep the session of the push in the assistant itself, and only handle
the changes reported by the delegator an assistant was pushed to.
Assisted-by: opencode:claude-opus-5
---
profiles/audio/bass.c | 39 ++++++++++++++++++++++++++++-----------
1 file changed, 28 insertions(+), 11 deletions(-)
diff --git a/profiles/audio/bass.c b/profiles/audio/bass.c
index c9a9111e010f..fd05195f36d2 100644
--- a/profiles/audio/bass.c
+++ b/profiles/audio/bass.c
@@ -108,6 +108,13 @@ struct bass_assistant {
struct btd_adapter *adapter; /* Broadcast source device */
struct btd_device *device; /* Broadcast source device */
struct bass_data *data; /* BASS session with peer device */
+ struct bt_bass *bass; /* BASS session the stream is pushed
+ * to, which for a local stream is
+ * chosen by the push, so it is kept
+ * per assistant: the session of the
+ * data is shared by every assistant
+ * of the adapter.
+ */
uint8_t sgrp;
uint8_t sid;
uint8_t bis;
@@ -803,6 +810,7 @@ assistant_new(struct btd_adapter *adapter,
assistant->adapter = adapter;
assistant->device = device;
assistant->data = data;
+ assistant->bass = bt_bass_ref(data->bass);
assistant->sgrp = sgrp;
assistant->sid = sid;
assistant->bis = bis;
@@ -991,10 +999,10 @@ static int assistant_parse_props(struct bass_assistant *assistant,
goto fail;
}
- if (assistant->data->bass)
- bt_bass_unref(assistant->data->bass);
+ if (assistant->bass)
+ bt_bass_unref(assistant->bass);
- assistant->data->bass = bt_bass_ref(data->bass);
+ assistant->bass = bt_bass_ref(data->bass);
}
dbus_message_iter_next(props);
@@ -1040,7 +1048,7 @@ static void assistant_past(struct bass_assistant *assistant)
return;
if (!device) {
- struct bt_bass *bass = assistant->data->bass;
+ struct bt_bass *bass = assistant->bass;
struct bass_data *data;
data = queue_find(sessions, match_bass, bass);
@@ -1095,7 +1103,7 @@ static DBusMessage *push_mod_src(struct bass_assistant *assistant,
util_iov_append(&iov, &meta_len, sizeof(meta_len));
}
- err = bt_bass_send(assistant->data->bass, &hdr, &iov);
+ err = bt_bass_send(assistant->bass, &hdr, &iov);
if (err) {
DBG("Unable to send BASS Write Command");
return btd_error_failed(msg, strerror(-err));
@@ -1109,7 +1117,7 @@ static DBusMessage *push_mod_src(struct bass_assistant *assistant,
struct bass_src *src;
src = queue_remove_if(assistant->srcs, match_src_data,
- assistant->data->bass);
+ assistant->bass);
free(src);
}
@@ -1210,7 +1218,7 @@ static DBusMessage *push_add_src(struct bass_assistant *assistant,
util_iov_append(&iov, assistant->meta->iov_base,
assistant->meta->iov_len);
- err = bt_bass_send(assistant->data->bass, &hdr, &iov);
+ err = bt_bass_send(assistant->bass, &hdr, &iov);
if (err) {
DBG("Unable to send BASS Write Command");
return btd_error_failed(msg, strerror(-err));
@@ -1219,7 +1227,7 @@ static DBusMessage *push_add_src(struct bass_assistant *assistant,
free(iov.iov_base);
if (assistant->state == ASSISTANT_STATE_LOCAL)
- assistant_add_src(assistant, assistant->data->bass, 0);
+ assistant_add_src(assistant, assistant->bass, 0);
else
assistant_set_state(assistant, ASSISTANT_STATE_PENDING);
@@ -1235,7 +1243,7 @@ static DBusMessage *push_src(struct bass_assistant *assistant,
struct bass_src *src;
src = queue_find(assistant->srcs, match_src_data,
- assistant->data->bass);
+ assistant->bass);
if (src) {
assistant->src_id = src->src_id;
return push_mod_src(assistant, msg);
@@ -1274,7 +1282,7 @@ static DBusMessage *push(DBusConnection *conn, DBusMessage *msg,
* be enabled yet and the state changes it triggers, e.g. a request to
* transfer the sync info, would be missed.
*/
- data = queue_find(sessions, match_bass, assistant->data->bass);
+ data = queue_find(sessions, match_bass, assistant->bass);
if (data && !data->ready) {
DBG("Session not ready, queueing request");
@@ -1391,6 +1399,7 @@ static void assistant_free(void *data)
util_iov_free(assistant->meta, 1);
util_iov_free(assistant->caps, 1);
queue_destroy(assistant->srcs, free);
+ bt_bass_unref(assistant->bass);
free(assistant);
}
@@ -2237,7 +2246,7 @@ static void bass_handle_bcode_req(struct bass_assistant *assistant, int id)
util_iov_push_mem(&iov, sizeof(params), ¶ms);
- err = bt_bass_send(assistant->data->bass, &hdr, &iov);
+ err = bt_bass_send(assistant->bass, &hdr, &iov);
if (err) {
DBG("Unable to send BASS Write Command");
return;
@@ -2280,6 +2289,14 @@ static void bass_src_changed(uint8_t id, uint32_t bid, uint8_t state,
struct bass_assistant *assistant = entry->data;
uint32_t bis = 1 << (assistant->bis - 1);
+ if (assistant->bass != data->bass)
+ /* Only handle assistant objects that have been
+ * pushed to the delegator reporting the change,
+ * otherwise e.g. the Broadcast Code of a request
+ * would be sent to a different delegator.
+ */
+ continue;
+
if (bid && assistant->bid != bid)
/* Only handle assistant objects
* that match the source
--
2.55.0
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH BlueZ v1 5/6] client: connect a device that is already paired
2026-09-21 21:01 [PATCH BlueZ v1 0/6] Add true wireless BAP broadcast tests Luiz Augusto von Dentz
` (3 preceding siblings ...)
2026-09-21 21:01 ` [PATCH BlueZ v1 4/6] bass: keep the push target per MediaAssistant object Luiz Augusto von Dentz
@ 2026-09-21 21:01 ` Luiz Augusto von Dentz
2026-09-21 21:01 ` [PATCH BlueZ v1 6/6] test: functional: add the true wireless broadcast assistant test Luiz Augusto von Dentz
2026-09-22 15:00 ` [PATCH BlueZ v1 0/6] Add true wireless BAP broadcast tests patchwork-bot+bluetooth
6 siblings, 0 replies; 9+ messages in thread
From: Luiz Augusto von Dentz @ 2026-09-21 21:01 UTC (permalink / raw)
To: linux-bluetooth
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
Pairing a device that is already paired is rejected by the daemon with
AlreadyExists, which is of no use to the user: the bond asked for is
already there, and what pairing it anew takes is removing it first.
Connect it instead, telling what happened, so pairing a device is a way
of getting to it whether it is known already or not.
The documentation already described this behaviour, though as removing
the pairing first, which is not what happened either.
Assisted-by: opencode:claude-opus-5
---
client/main.c | 40 ++++++++++++++++++++++++++++++++++++++++
doc/bluetoothctl.rst | 6 ++++--
2 files changed, 44 insertions(+), 2 deletions(-)
diff --git a/client/main.c b/client/main.c
index 3fef1e9a5179..314aead8c9bb 100644
--- a/client/main.c
+++ b/client/main.c
@@ -2068,6 +2068,9 @@ static void cmd_info(int argc, char *argv[])
return bt_shell_noninteractive_quit(EXIT_SUCCESS);
}
+static const char *proxy_address(GDBusProxy *proxy);
+static void connect_device(GDBusProxy *proxy);
+
static void pair_reply(DBusMessage *message, void *user_data)
{
DBusError error;
@@ -2101,11 +2104,31 @@ static const char *proxy_address(GDBusProxy *proxy)
static void cmd_pair(int argc, char *argv[])
{
GDBusProxy *proxy;
+ DBusMessageIter iter;
+ dbus_bool_t paired;
proxy = find_device(argc, argv);
if (!proxy)
return bt_shell_noninteractive_quit(EXIT_FAILURE);
+ /* Pairing a device that is already paired is rejected with
+ * AlreadyExists, so connect it instead: the bond is there, which
+ * is what pairing it again would be for, and removing it first is
+ * what actually pairs it anew.
+ */
+ if (g_dbus_proxy_get_property(proxy, "Paired", &iter)) {
+ dbus_message_iter_get_basic(&iter, &paired);
+
+ if (paired) {
+ bt_shell_printf("Device %s is already paired, "
+ "connecting instead, remove it first "
+ "to pair it again\n",
+ proxy_address(proxy));
+ connect_device(proxy);
+ return;
+ }
+ }
+
if (g_dbus_proxy_method_call(proxy, "Pair", NULL, pair_reply,
NULL, NULL) == FALSE) {
bt_shell_printf("Failed to pair\n");
@@ -2365,6 +2388,23 @@ static void connect_reply(DBusMessage *message, void *user_data)
return bt_shell_noninteractive_quit(EXIT_SUCCESS);
}
+static void connect_device(GDBusProxy *proxy)
+{
+ struct connection_data *data;
+
+ data = new0(struct connection_data, 1);
+ data->proxy = proxy;
+
+ if (g_dbus_proxy_method_call(proxy, "Connect", connection_setup,
+ connect_reply, data, NULL) == FALSE) {
+ bt_shell_printf("Failed to connect\n");
+ g_free(data);
+ return bt_shell_noninteractive_quit(EXIT_FAILURE);
+ }
+
+ bt_shell_printf("Attempting to connect to %s\n", proxy_address(proxy));
+}
+
static void prompt_scan_connect(const char *input, void *user_data)
{
char *address = user_data;
diff --git a/doc/bluetoothctl.rst b/doc/bluetoothctl.rst
index cc58e1f5be19..23f92a512911 100644
--- a/doc/bluetoothctl.rst
+++ b/doc/bluetoothctl.rst
@@ -207,7 +207,8 @@ pair
Pair with device.
This will pair with a device and then trust and connect to it. If the device is
-already paired this will first remove the pairing.
+already paired this will connect to it instead, as the bond is already in
+place: use **remove** first to pair it again.
The command can either be used while the controller is in the connected or not
connected state.
@@ -221,7 +222,8 @@ Before pairing the agent must be selected to choose the authentication
mechanism.
:Usage: **> pair [dev]**
-:Uses: **org.bluez.Device(5)** method **Pair**
+:Uses: **org.bluez.Device(5)** method **Pair**, or **Connect** if the device is
+ already paired
:[dev]: Device MAC address (XX:XX:XX:XX:XX:XX) (optional, uses current device if omitted)
:Example Pair with device using MAC address:
| **> pair 00:11:22:33:44:55**
--
2.55.0
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH BlueZ v1 6/6] test: functional: add the true wireless broadcast assistant test
2026-09-21 21:01 [PATCH BlueZ v1 0/6] Add true wireless BAP broadcast tests Luiz Augusto von Dentz
` (4 preceding siblings ...)
2026-09-21 21:01 ` [PATCH BlueZ v1 5/6] client: connect a device that is already paired Luiz Augusto von Dentz
@ 2026-09-21 21:01 ` Luiz Augusto von Dentz
2026-09-22 15:00 ` [PATCH BlueZ v1 0/6] Add true wireless BAP broadcast tests patchwork-bot+bluetooth
6 siblings, 0 replies; 9+ messages in thread
From: Luiz Augusto von Dentz @ 2026-09-21 21:01 UTC (permalink / raw)
To: linux-bluetooth
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
Assisted-by: opencode:claude-opus-5
---
client/scripts/broadcast-delegator-left.bt | 32 ++++
client/scripts/broadcast-delegator-right.bt | 32 ++++
doc/functional-bap.rst | 6 +
test/functional/test_bap.py | 181 ++++++++++++++++++--
4 files changed, 239 insertions(+), 12 deletions(-)
create mode 100644 client/scripts/broadcast-delegator-left.bt
create mode 100644 client/scripts/broadcast-delegator-right.bt
diff --git a/client/scripts/broadcast-delegator-left.bt b/client/scripts/broadcast-delegator-left.bt
new file mode 100644
index 000000000000..818b30567be6
--- /dev/null
+++ b/client/scripts/broadcast-delegator-left.bt
@@ -0,0 +1,32 @@
+power on
+#
+#
+# Register a Broadcast Sink endpoint with LC3 taking a single channel,
+# to act as the left member of a coordinated set of Scan Delegators.
+# The members of the set are programmed with the same SIRK, see [CSIS]
+# in main.conf.
+#
+endpoint.register 00001851-0000-1000-8000-00805f9b34fb 0x06
+# Auto Accept: accept the configuration chosen by the peer
+y
+# Max Transports: no limit
+a
+# Locations: Front Left, so only the BIS carrying that channel is synced
+1
+# Supported Context: Media
+4
+#
+#
+# Select and acquire automatically the transports of the streams the
+# Broadcast Assistant adds.
+#
+transport.select auto
+transport.acquire auto
+#
+#
+# Advertise the Resolvable Set Identifier, so the assistant is able to
+# resolve this device as a member of the set, and connecting one member
+# connects the other.
+#
+advertise.rsi on
+advertise on
diff --git a/client/scripts/broadcast-delegator-right.bt b/client/scripts/broadcast-delegator-right.bt
new file mode 100644
index 000000000000..bfd1888987bc
--- /dev/null
+++ b/client/scripts/broadcast-delegator-right.bt
@@ -0,0 +1,32 @@
+power on
+#
+#
+# Register a Broadcast Sink endpoint with LC3 taking a single channel,
+# to act as the right member of a coordinated set of Scan Delegators.
+# The members of the set are programmed with the same SIRK, see [CSIS]
+# in main.conf.
+#
+endpoint.register 00001851-0000-1000-8000-00805f9b34fb 0x06
+# Auto Accept: accept the configuration chosen by the peer
+y
+# Max Transports: no limit
+a
+# Locations: Front Right, so only the BIS carrying that channel is synced
+2
+# Supported Context: Media
+4
+#
+#
+# Select and acquire automatically the transports of the streams the
+# Broadcast Assistant adds.
+#
+transport.select auto
+transport.acquire auto
+#
+#
+# Advertise the Resolvable Set Identifier, so the assistant is able to
+# resolve this device as a member of the set, and connecting one member
+# connects the other.
+#
+advertise.rsi on
+advertise on
diff --git a/doc/functional-bap.rst b/doc/functional-bap.rst
index 7059e0c735f1..74a6604ad285 100644
--- a/doc/functional-bap.rst
+++ b/doc/functional-bap.rst
@@ -648,6 +648,12 @@ test_bass_past_earbuds_transport_acquire
characteristic of each member is read over its own connection,
which is why the services of both are waited for.
+ The hosts are reused between tests, so the members may be bonded
+ already, in which case ``pair`` connects them instead, see
+ **bluetoothctl(1)**. What is already in place is then checked over
+ D-Bus rather than waited for on the output, as a device whose
+ services are already resolved does not report it again.
+
Each member gets a push of its own: BASS has no notion of a set,
so the assistant adds the source to the Broadcast Receive State
of each delegator separately, and each of them receives the
diff --git a/test/functional/test_bap.py b/test/functional/test_bap.py
index bfcb43c0ad67..8d0b4489d766 100644
--- a/test/functional/test_bap.py
+++ b/test/functional/test_bap.py
@@ -5,6 +5,7 @@ Tests for BAP (LE Audio) using bluetoothctl in VM instances
"""
import threading
+import time
import warnings
import dbus
@@ -113,7 +114,7 @@ def pair_le(host0, ctl0, host1, ctl1, advertise=True, services=False):
ctl0.expect(f"Device {host1.bdaddr.upper()}")
ctl0.send(f"pair {host1.bdaddr.upper()}\n")
- pending = ["Pairing successful"]
+ pending = [PAIRED]
if services:
pending.append(f"Device {host1.bdaddr.upper()} ServicesResolved: yes")
@@ -170,6 +171,11 @@ def remove_device(ctl0, host):
)
+# A device that is already paired is connected instead, see cmd_pair in
+# client/main.c, so a pairing reports one or the other
+PAIRED = r"Pairing successful|Connection successful"
+
+
def pair_wait(ctl0, ctl1, pending):
"""
Wait for the given events while answering the pairing requests.
@@ -326,6 +332,41 @@ def transport_paths():
)
+@mainloop_wrap
+def device_properties(paths):
+ """Properties of the given devices, empty for those that are unknown."""
+ objects = dbus.Interface(
+ get_dbus().get_object("org.bluez", "/"),
+ "org.freedesktop.DBus.ObjectManager",
+ ).GetManagedObjects()
+ return {
+ path: {
+ str(key): bool(value)
+ for key, value in objects.get(path, {}).get("org.bluez.Device1", {}).items()
+ if key in ("Paired", "Connected", "ServicesResolved")
+ }
+ for path in paths
+ }
+
+
+def wait_properties(host, paths, name):
+ """
+ Wait for the given property of the given devices to be set.
+
+ Polled rather than waited for on the output, as a device that is
+ already in that state does not report it again.
+ """
+ deadline = time.monotonic() + DEFAULT_TIMEOUT
+
+ while True:
+ props = host.call(device_properties, paths)
+ if all(props[path].get(name) for path in paths):
+ return
+
+ assert time.monotonic() < deadline, f"{name} not set on {paths}: {props}"
+ time.sleep(0.2)
+
+
def add_preset(ctl, name, props, metadata):
"""Make a custom preset from a transport's codec configuration and QoS."""
@@ -571,6 +612,28 @@ past_host_config = host_config(
LOCAL_ASSISTANT_RE = r"Assistant (/org/bluez/\S+/sid\d+/bis\d+)"
+# Local MediaAssistant object of a given BIS of the local broadcast
+LOCAL_ASSISTANT = r"Assistant (/org/bluez/hci0/sid\d+/bis{})"
+
+
+def push_assistant(source, assistant_path, host):
+ """
+ Share the local stream of the given MediaAssistant object with the
+ given delegator, which receives the periodic advertising sync over
+ the connection (PAST).
+ """
+ source.send(f"assistant.push {assistant_path}\n")
+ source.expect(r"Enter Device \(path\):")
+ source.send(f"/org/bluez/hci0/dev_{dev_addr(host)}\n")
+
+ # The local stream may already know the broadcast code
+ idx, _ = source.expect(
+ [r"Enter Broadcast Code \(auto/value\):", r"Assistant \S+ pushed"]
+ )
+ if idx == 0:
+ source.send(f"{BCAST_CODE}\n")
+ source.expect(r"Assistant \S+ pushed")
+
@past_host_config
def test_bass_past_transport_acquire(hosts):
@@ -602,17 +665,7 @@ def test_bass_past_transport_acquire(hosts):
# Share the local broadcast: the delegator receives the periodic
# advertising sync over the connection (PAST)
- source.send(f"assistant.push {assistant_path}\n")
- source.expect(r"Enter Device \(path\):")
- source.send(f"/org/bluez/hci0/dev_{dev_addr(delegator_host)}\n")
-
- # The local stream may already know the broadcast code
- idx, _ = source.expect(
- [r"Enter Broadcast Code \(auto/value\):", r"Assistant \S+ pushed"]
- )
- if idx == 0:
- source.send(f"{BCAST_CODE}\n")
- source.expect(r"Assistant \S+ pushed")
+ push_assistant(source, assistant_path, delegator_host)
# A transport is created on the delegator, selected and acquired
# automatically
@@ -795,3 +848,107 @@ def test_bap_unicast_set_transport_acquire(set_hosts):
[acquired, acquired]
+ [f"Transport {transport} State: active" for transport in transports],
)
+
+
+def discover_set(source_host, source, left_host, right_host):
+ """
+ Scan until both members of the set have been found.
+
+ The set is resolved from the RSI of the members that are already
+ known, so connecting before both have been found would leave the
+ one found later out of it.
+ """
+ source.send("scan on\n")
+ source.expect(f"Controller {source_host.bdaddr.upper()} Discovering: yes")
+
+ expect_all(
+ source,
+ [
+ f"Device {left_host.bdaddr.upper()}",
+ f"Device {right_host.bdaddr.upper()}",
+ ],
+ )
+
+ # Stop scanning before connecting, so the discovery does not
+ # interfere with the connections to the members
+ source.send("scan off\n")
+ source.expect(f"Controller {source_host.bdaddr.upper()} Discovering: no")
+
+
+bass_set_host_config = host_config(
+ [Bluetoothd(conf=BAP_CONF), Pexpect()],
+ [Bluetoothd(conf=csip_conf(1)), Pexpect()],
+ [Bluetoothd(conf=csip_conf(2)), Pexpect()],
+)
+
+
+@bass_set_host_config
+def test_bass_past_earbuds_transport_acquire(hosts):
+ source_host, left_host, right_host = hosts
+
+ # Source broadcasting one BIS per channel, with its own streams
+ # exposed as local MediaAssistant objects, one per BIS
+ source = start_bluetoothctl(source_host, "broadcast-source-2bis.bt")
+ groups = expect_all(
+ source,
+ [LOCAL_ASSISTANT.format(1), LOCAL_ASSISTANT.format(2), ACQUIRED, ACQUIRED],
+ )
+ assistants = [m[0].decode("utf-8") for m in groups[:2]]
+
+ # Delegators advertising, selecting and acquiring automatically
+ left = start_bluetoothctl(left_host, "broadcast-delegator-left.bt")
+ right = start_bluetoothctl(right_host, "broadcast-delegator-right.bt")
+
+ # Every member has to be advertising before connecting, so the set
+ # can be resolved and the remaining members found
+ left.expect("Advertising object registered")
+ right.expect("Advertising object registered")
+
+ paths = [
+ f"/org/bluez/hci0/dev_{dev_addr(host)}" for host in (left_host, right_host)
+ ]
+
+ # The hosts are reused, so a previous test may have left the
+ # members bonded, in which case they are known already and the
+ # discovery below would report nothing for them, as only what
+ # changes is reported: they are connected directly instead.
+ props = source_host.call(device_properties, paths)
+
+ if not all(props[path].get("Paired") for path in paths):
+ discover_set(source_host, source, left_host, right_host)
+
+ # Connecting one member connects the rest of the set, so a single
+ # request covers both, and pairing a member that is bonded already
+ # connects it, see cmd_pair in client/main.c
+ source.send(f"pair {left_host.bdaddr.upper()}\n")
+ pair_wait(source, left, [PAIRED])
+
+ # The Broadcast Receive State of each member is read over its own
+ # connection, so the services of both have to be resolved, and an
+ # encrypted link is required for the read to succeed.
+ #
+ # Checked over D-Bus rather than waited for on the output, as a
+ # device whose services are already resolved does not report it
+ # again, e.g. when the members were bonded already.
+ wait_properties(source_host, paths, "ServicesResolved")
+
+ # One push per member, each carrying the BIS of its own channel:
+ # BASS has no notion of a set, so the source is added to the
+ # Broadcast Receive State of each delegator separately, and each of
+ # them receives the periodic advertising sync over its own ACL
+ for assistant, host in zip(assistants, (left_host, right_host)):
+ push_assistant(source, assistant, host)
+
+ # A transport is created on each side for the BIS that was pushed
+ # to it, and selected and acquired automatically
+ for ctl, host, bis in ((left, left_host, 1), (right, right_host, 2)):
+ transport = expect_bis_transport(ctl, host, bis)
+
+ expect_all(
+ ctl,
+ [
+ ACQUIRED,
+ f"Transport {transport} State: broadcasting",
+ f"Transport {transport} State: active",
+ ],
+ )
--
2.55.0
^ permalink raw reply related [flat|nested] 9+ messages in thread
* RE: Add true wireless BAP broadcast tests
2026-09-21 21:01 ` [PATCH BlueZ v1 1/6] doc: describe the true wireless broadcast test cases Luiz Augusto von Dentz
@ 2026-09-21 23:47 ` bluez.test.bot
0 siblings, 0 replies; 9+ messages in thread
From: bluez.test.bot @ 2026-09-21 23:47 UTC (permalink / raw)
To: linux-bluetooth, luiz.dentz
[-- Attachment #1: Type: text/plain, Size: 2012 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/series/1170704/
---Test result---
Test Summary:
CheckPatch FAIL 2.26 seconds
GitLint PASS 1.60 seconds
BuildEll PASS 19.44 seconds
BluezMake PASS 378.74 seconds
MakeCheck PASS 14.45 seconds
MakeDistcheck PASS 139.15 seconds
CheckValgrind PASS 230.87 seconds
CheckSmatch PASS 290.42 seconds
bluezmakeextell PASS 94.60 seconds
TestFunctional PASS 1127.27 seconds
IncrementalBuild PASS 383.55 seconds
ScanBuild PASS 1115.84 seconds
Details
##############################
Test: CheckPatch - FAIL
Desc: Run checkpatch.pl script
Output:
[BlueZ,v1,5/6] client: connect a device that is already paired
ERROR:SPACING: need consistent spacing around '*' (ctx:WxV)
#125: FILE: client/main.c:2071:
+static const char *proxy_address(GDBusProxy *proxy);
^
/home/runner/work/bluez/bluez/src/patch/14836574.patch total: 1 errors, 0 warnings, 81 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.
/home/runner/work/bluez/bluez/src/patch/14836574.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.
https://github.com/bluez/bluez/pull/2560
---
Regards,
Linux Bluetooth
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH BlueZ v1 0/6] Add true wireless BAP broadcast tests
2026-09-21 21:01 [PATCH BlueZ v1 0/6] Add true wireless BAP broadcast tests Luiz Augusto von Dentz
` (5 preceding siblings ...)
2026-09-21 21:01 ` [PATCH BlueZ v1 6/6] test: functional: add the true wireless broadcast assistant test Luiz Augusto von Dentz
@ 2026-09-22 15:00 ` patchwork-bot+bluetooth
6 siblings, 0 replies; 9+ messages in thread
From: patchwork-bot+bluetooth @ 2026-09-22 15:00 UTC (permalink / raw)
To: Luiz Augusto von Dentz; +Cc: linux-bluetooth
Hello:
This series was applied to bluetooth/bluez.git (master)
by Luiz Augusto von Dentz <luiz.von.dentz@intel.com>:
On Mon, 21 Sep 2026 17:01:17 -0400 you wrote:
> From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
>
> This adds functional tests for broadcast setups where the left and the
> right side are separate devices, as in a pair of earbuds, which so far
> were only covered with a single sink taking both channels.
>
> The source broadcasts one BIS per channel, and each side registers a
> single location, so each of them only gets a transport for the BIS
> carrying its own channel: the sides are told apart by the BIS index of
> the transport path.
>
> [...]
Here is the summary with links:
- [BlueZ,v1,1/6] doc: describe the true wireless broadcast test cases
https://git.kernel.org/pub/scm/bluetooth/bluez.git/?id=086a30e3cf47
- [BlueZ,v1,2/6] doc: describe the true wireless broadcast assistant test case
https://git.kernel.org/pub/scm/bluetooth/bluez.git/?id=4c8102eecc20
- [BlueZ,v1,3/6] test: functional: add the true wireless broadcast tests
https://git.kernel.org/pub/scm/bluetooth/bluez.git/?id=547afc7ef35c
- [BlueZ,v1,4/6] bass: keep the push target per MediaAssistant object
https://git.kernel.org/pub/scm/bluetooth/bluez.git/?id=bb59c19e299f
- [BlueZ,v1,5/6] client: connect a device that is already paired
https://git.kernel.org/pub/scm/bluetooth/bluez.git/?id=e691da62c62f
- [BlueZ,v1,6/6] test: functional: add the true wireless broadcast assistant test
https://git.kernel.org/pub/scm/bluetooth/bluez.git/?id=98d6d1350a6a
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] 9+ messages in thread
end of thread, other threads:[~2026-09-22 15:01 UTC | newest]
Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-21 21:01 [PATCH BlueZ v1 0/6] Add true wireless BAP broadcast tests Luiz Augusto von Dentz
2026-09-21 21:01 ` [PATCH BlueZ v1 1/6] doc: describe the true wireless broadcast test cases Luiz Augusto von Dentz
2026-09-21 23:47 ` Add true wireless BAP broadcast tests bluez.test.bot
2026-09-21 21:01 ` [PATCH BlueZ v1 2/6] doc: describe the true wireless broadcast assistant test case Luiz Augusto von Dentz
2026-09-21 21:01 ` [PATCH BlueZ v1 3/6] test: functional: add the true wireless broadcast tests Luiz Augusto von Dentz
2026-09-21 21:01 ` [PATCH BlueZ v1 4/6] bass: keep the push target per MediaAssistant object Luiz Augusto von Dentz
2026-09-21 21:01 ` [PATCH BlueZ v1 5/6] client: connect a device that is already paired Luiz Augusto von Dentz
2026-09-21 21:01 ` [PATCH BlueZ v1 6/6] test: functional: add the true wireless broadcast assistant test Luiz Augusto von Dentz
2026-09-22 15:00 ` [PATCH BlueZ v1 0/6] Add true wireless BAP broadcast tests patchwork-bot+bluetooth
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox