* [PATCH BlueZ 0/5] bass: wait for PAST listener readiness
@ 2026-10-11 20:40 Pauli Virtanen
2026-10-11 20:40 ` [PATCH BlueZ 1/5] iso-tester: remove IO sources during teardown Pauli Virtanen
` (4 more replies)
0 siblings, 5 replies; 12+ messages in thread
From: Pauli Virtanen @ 2026-10-11 20:40 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Pauli Virtanen
The other kernel patch series adds POLLOUT as notification when after
listen() PAST is ready to receive.
Document this ISO socket uAPI and add tests.
Use it in profiles/audio/bass.c to indicate PAST reception readiness
only after kernel indicates so.
Fixes test_bass_past_transport_* sporadic failures.
Pauli Virtanen (5):
iso-tester: remove IO sources during teardown
lib: add BT_ISO_PAST_POLLOUT socket option
tools: iso-tester: test PAST listener readiness
doc: iso: document ISO PAST listener readiness
bass: wait for PAST listener readiness
doc/iso-protocol.rst | 81 ++++++++++++++-
lib/bluetooth/bluetooth.h | 2 +
profiles/audio/bass.c | 66 ++++++++++--
tools/iso-tester.c | 212 +++++++++++++++++++++++++++++++++++++-
4 files changed, 348 insertions(+), 13 deletions(-)
--
2.56.0
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH BlueZ 1/5] iso-tester: remove IO sources during teardown
2026-10-11 20:40 [PATCH BlueZ 0/5] bass: wait for PAST listener readiness Pauli Virtanen
@ 2026-10-11 20:40 ` Pauli Virtanen
2026-10-11 23:54 ` bass: wait for PAST listener readiness bluez.test.bot
2026-10-11 20:40 ` [PATCH BlueZ 2/5] lib: add BT_ISO_PAST_POLLOUT socket option Pauli Virtanen
` (3 subsequent siblings)
4 siblings, 1 reply; 12+ messages in thread
From: Pauli Virtanen @ 2026-10-11 20:40 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Pauli Virtanen
Drop watches / timers in test teardown, not after all tests have run.
Avoids leaking them to following tests.
Assisted-by: opencode:gpt-6.1-sol
---
tools/iso-tester.c | 14 +++++++++-----
1 file changed, 9 insertions(+), 5 deletions(-)
diff --git a/tools/iso-tester.c b/tools/iso-tester.c
index fe27eaaf3..6fa1bd007 100644
--- a/tools/iso-tester.c
+++ b/tools/iso-tester.c
@@ -707,6 +707,15 @@ static void io_free(void *data)
static void test_post_teardown(const void *test_data)
{
struct test_data *data = tester_get_data();
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(data->io_id); i++) {
+ if (!data->io_id[i])
+ continue;
+
+ g_source_remove(data->io_id[i]);
+ data->io_id[i] = 0;
+ }
if (data->io_queue)
queue_remove_all(data->io_queue, NULL, NULL, io_free);
@@ -722,15 +731,10 @@ static void test_post_teardown(const void *test_data)
static void test_data_free(void *test_data)
{
struct test_data *data = test_data;
- unsigned int i;
if (data->io_queue)
queue_destroy(data->io_queue, io_free);
- for (i = 0; i < ARRAY_SIZE(data->io_id); ++i)
- if (data->io_id[i] > 0)
- g_source_remove(data->io_id[i]);
-
free(data);
}
--
2.56.0
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH BlueZ 2/5] lib: add BT_ISO_PAST_POLLOUT socket option
2026-10-11 20:40 [PATCH BlueZ 0/5] bass: wait for PAST listener readiness Pauli Virtanen
2026-10-11 20:40 ` [PATCH BlueZ 1/5] iso-tester: remove IO sources during teardown Pauli Virtanen
@ 2026-10-11 20:40 ` Pauli Virtanen
2026-10-11 20:40 ` [PATCH BlueZ 3/5] tools: iso-tester: test PAST listener readiness Pauli Virtanen
` (2 subsequent siblings)
4 siblings, 0 replies; 12+ messages in thread
From: Pauli Virtanen @ 2026-10-11 20:40 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Pauli Virtanen
Add kernel uAPI definition for PAST listener readiness option.
Assisted-by: opencode:qwen3.8-flash-next
---
lib/bluetooth/bluetooth.h | 2 ++
1 file changed, 2 insertions(+)
diff --git a/lib/bluetooth/bluetooth.h b/lib/bluetooth/bluetooth.h
index f9f22c3f7..a0f7405d5 100644
--- a/lib/bluetooth/bluetooth.h
+++ b/lib/bluetooth/bluetooth.h
@@ -257,6 +257,8 @@ enum {
#define BT_SCM_PKT_SEQNUM 0x05
+#define BT_ISO_PAST_POLLOUT 23
+
/* Byte order conversions */
#if __BYTE_ORDER == __LITTLE_ENDIAN
#define htobs(d) (d)
--
2.56.0
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH BlueZ 3/5] tools: iso-tester: test PAST listener readiness
2026-10-11 20:40 [PATCH BlueZ 0/5] bass: wait for PAST listener readiness Pauli Virtanen
2026-10-11 20:40 ` [PATCH BlueZ 1/5] iso-tester: remove IO sources during teardown Pauli Virtanen
2026-10-11 20:40 ` [PATCH BlueZ 2/5] lib: add BT_ISO_PAST_POLLOUT socket option Pauli Virtanen
@ 2026-10-11 20:40 ` Pauli Virtanen
2026-10-11 20:40 ` [PATCH BlueZ 4/5] doc: iso: document ISO " Pauli Virtanen
2026-10-11 20:40 ` [PATCH BlueZ 5/5] bass: wait for " Pauli Virtanen
4 siblings, 0 replies; 12+ messages in thread
From: Pauli Virtanen @ 2026-10-11 20:40 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Pauli Virtanen
Test POLLOUT notification after LE Set PAST Parameters completes, with
and without deferred setup. Query BT_ISO_PAST_POLLOUT and skip the tests
when listener readiness notifications are unavailable.
Add tests:
ISO Broadcaster PAST Listen Ready Receive - Success
ISO Broadcaster PAST Listen Ready Defer - Success
Assisted-by: opencode:gpt-6.1-sol
---
tools/iso-tester.c | 198 +++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 198 insertions(+)
diff --git a/tools/iso-tester.c b/tools/iso-tester.c
index 6fa1bd007..ec6422c91 100644
--- a/tools/iso-tester.c
+++ b/tools/iso-tester.c
@@ -478,6 +478,7 @@ struct test_data {
bool suspending;
struct tx_tstamp_data tx_ts;
int seqnum;
+ bool past_params_complete;
};
struct iso_client_data {
@@ -1494,6 +1495,17 @@ static const struct iso_client_data past_16_2_1_recv = {
.big = true,
};
+static const struct iso_client_data past_16_2_1_recv_defer = {
+ .qos = QOS_IN_16_2_1,
+ .recv = &send_16_2_1,
+ .bcast = true,
+ .past = true,
+ .server = true,
+ .defer = true,
+ .pa_bind = true,
+ .big = true,
+};
+
static const struct iso_client_data bcast_16_2_1_recv2 = {
.qos = QOS_IN_16_2_1,
.expect_err = 0,
@@ -3909,6 +3921,184 @@ static void test_past_recv(const void *test_data)
setup_listen(data, 0, iso_accept_cb);
}
+static bool past_listener_check(int sk, bool ready)
+{
+ struct pollfd pfd = {
+ .fd = sk,
+ .events = POLLIN | POLLOUT,
+ };
+ int err, new_sk;
+ socklen_t len;
+
+ if (poll(&pfd, 1, 0) < 0) {
+ tester_warn("poll: %s (%d)", strerror(errno), errno);
+ return false;
+ }
+
+ if (pfd.revents != (ready ? POLLOUT : 0)) {
+ tester_warn("Unexpected listener events: 0x%x", pfd.revents);
+ return false;
+ }
+
+ len = sizeof(err);
+
+ if (getsockopt(sk, SOL_SOCKET, SO_ERROR, &err, &len) < 0 || err) {
+ tester_warn("Unexpected listener socket error");
+ return false;
+ }
+
+ new_sk = accept(sk, NULL, NULL);
+ if (new_sk >= 0) {
+ tester_warn("Accepted connection before PAST");
+ close(new_sk);
+ return false;
+ }
+
+ if (errno != EAGAIN && errno != EWOULDBLOCK) {
+ tester_warn("accept: %s (%d)", strerror(errno), errno);
+ return false;
+ }
+
+ return true;
+}
+
+static gboolean past_params_resume(gpointer user_data)
+{
+ struct test_data *data = tester_get_data();
+ GIOChannel *io = user_data;
+ struct vhci *vhci = hciemu_get_vhci(data->hciemu);
+
+ data->io_id[3] = 0;
+
+ if (!past_listener_check(g_io_channel_unix_get_fd(io), false)) {
+ tester_test_failed();
+ return FALSE;
+ }
+
+ if (!vhci_pause_input(vhci, false)) {
+ tester_warn("Can't resume controller input");
+ tester_test_failed();
+ }
+
+ return FALSE;
+}
+
+static bool past_params_complete(const void *param, uint16_t len,
+ void *user_data)
+{
+ struct test_data *data = tester_get_data();
+ const struct bt_hci_rsp_le_past_params *rp = param;
+
+ if (len != sizeof(*rp) || rp->status) {
+ tester_warn("PAST Parameters failed");
+ tester_test_failed();
+ return false;
+ }
+
+ data->past_params_complete = true;
+ return true;
+}
+
+static gboolean iso_past_ready(GIOChannel *io, GIOCondition cond,
+ gpointer user_data)
+{
+ struct test_data *data = tester_get_data();
+ struct bthost *host;
+ int sk = g_io_channel_unix_get_fd(io);
+
+ data->io_id[0] = 0;
+
+ if (cond != G_IO_OUT || !data->past_params_complete ||
+ !past_listener_check(sk, true)) {
+ tester_warn("Unexpected PAST listener readiness");
+ tester_test_failed();
+ return FALSE;
+ }
+
+ tester_print("PAST listener ready");
+
+ data->io_id[0] = g_io_add_watch(io, G_IO_IN, iso_accept_cb, NULL);
+ host = hciemu_client_get_host(data->hciemu);
+ bthost_past_set_info(host, data->acl_handle);
+
+ return FALSE;
+}
+
+static void test_past_ready(const void *test_data)
+{
+ struct test_data *data = tester_get_data();
+ const struct iso_client_data *isodata = data->test_data;
+ struct vhci *vhci = hciemu_get_vhci(data->hciemu);
+ GIOChannel *io;
+ socklen_t len;
+ int sk, past_pollout, err;
+
+ sk = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_ISO);
+ if (sk < 0) {
+ tester_warn("Can't create socket: %s (%d)", strerror(errno),
+ errno);
+ tester_test_abort();
+ return;
+ }
+
+ len = sizeof(past_pollout);
+ err = getsockopt(sk, SOL_BLUETOOTH, BT_ISO_PAST_POLLOUT,
+ &past_pollout, &len);
+ if (err < 0) {
+ err = -errno;
+ tester_warn("Can't get PAST POLLOUT support: %s (%d)",
+ strerror(-err), -err);
+
+ if (err == -ENOPROTOOPT)
+ tester_test_abort();
+ else
+ tester_test_failed();
+
+ close(sk);
+ return;
+ }
+
+ close(sk);
+
+ if (!past_pollout) {
+ tester_print("PAST POLLOUT is not supported");
+ tester_test_abort();
+ return;
+ }
+
+ if (!vhci_pause_input(vhci, true)) {
+ tester_warn("Can't pause controller input");
+ tester_test_failed();
+ return;
+ }
+
+ sk = listen_iso_sock(data, 0);
+ if (sk < 0) {
+ if (sk == -EPROTONOSUPPORT)
+ tester_test_abort();
+ else
+ tester_test_failed();
+ return;
+ }
+
+ io = g_io_channel_unix_new(sk);
+ g_io_channel_set_close_on_unref(io, TRUE);
+
+ if (hciemu_add_hook(data->hciemu, HCIEMU_HOOK_POST_CMD,
+ BT_HCI_CMD_LE_PAST_PARAMS, past_params_complete, NULL) < 0) {
+ g_io_channel_unref(io);
+ tester_test_failed();
+ return;
+ }
+
+ data->step = isodata->defer ? 2 : 1;
+ data->io_id[0] = g_io_add_watch(io, G_IO_OUT | G_IO_ERR | G_IO_HUP,
+ iso_past_ready, NULL);
+ data->io_id[3] = g_timeout_add_full(G_PRIORITY_DEFAULT, 50,
+ past_params_resume, g_io_channel_ref(io), io_free);
+ g_io_channel_unref(io);
+}
+
static void test_connect2_suspend(const void *test_data)
{
test_connect2(test_data);
@@ -4332,6 +4522,14 @@ int main(int argc, char *argv[])
&past_16_2_1_recv,
setup_powered,
test_past_recv);
+ test_iso("ISO Broadcaster PAST Listen Ready Receive - Success",
+ &past_16_2_1_recv,
+ setup_powered,
+ test_past_ready);
+ test_iso("ISO Broadcaster PAST Listen Ready Defer - Success",
+ &past_16_2_1_recv_defer,
+ setup_powered,
+ test_past_ready);
test_iso("ISO Broadcaster Receiver SID auto - Success",
&bcast_16_2_1_recv_sid,
setup_powered,
--
2.56.0
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH BlueZ 4/5] doc: iso: document ISO PAST listener readiness
2026-10-11 20:40 [PATCH BlueZ 0/5] bass: wait for PAST listener readiness Pauli Virtanen
` (2 preceding siblings ...)
2026-10-11 20:40 ` [PATCH BlueZ 3/5] tools: iso-tester: test PAST listener readiness Pauli Virtanen
@ 2026-10-11 20:40 ` Pauli Virtanen
2026-10-11 20:40 ` [PATCH BlueZ 5/5] bass: wait for " Pauli Virtanen
4 siblings, 0 replies; 12+ messages in thread
From: Pauli Virtanen @ 2026-10-11 20:40 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Pauli Virtanen
Document PAST receiver setup and waiting for POLLOUT before requesting
PAST. Describe the read-only BT_ISO_PAST_POLLOUT capability query and
handling kernels without readiness notifications.
Assisted-by: LLM
---
doc/iso-protocol.rst | 81 +++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 80 insertions(+), 1 deletion(-)
diff --git a/doc/iso-protocol.rst b/doc/iso-protocol.rst
index 4d315132f..7e98374c4 100644
--- a/doc/iso-protocol.rst
+++ b/doc/iso-protocol.rst
@@ -145,11 +145,90 @@ Advertising Sync Transfer (PAST):
/* Rebind already connected socket to PAST address */
bind(iso_socket, (struct sockaddr *)addr, addr_len);
+Receiving PAST: set *PAST enabled* with MGMT *Set Device Flags*.
+
+Bind the ISO socket to that peer and its advertising SID, set receiver
+QoS, then call **listen(2)**. Setup is asynchronous: wait for
+``POLLOUT`` before requesting PAST from the peer. ``POLLIN`` indicates
+a connection ready for **accept(2)** as usual. On error, ``POLLIN`` is
+indicated and **accept(2)** produces a socket in error state.
+``POLLOUT`` is not necessarily indicated on error.
+
+Signaling ``POLLOUT`` on PAST ready is not available in Linux <= 7.3.
+Query **BT_ISO_PAST_POLLOUT** with **getsockopt(2)** to check whether
+this notification is supported before waiting for it.
+
+Example (``qos`` contains the receiver QoS):
+
+.. code-block::
+
+ struct {
+ struct sockaddr_iso iso;
+ struct sockaddr_iso_bc bc;
+ } addr = {
+ .iso.iso_family = AF_BLUETOOTH,
+ .iso.iso_bdaddr_type = BDADDR_LE_PUBLIC,
+ .bc.bc_bdaddr_type = peer_addr_type,
+ .bc.bc_sid = sid,
+ .bc.bc_num_bis = 1,
+ .bc.bc_bis = {1},
+ };
+ struct pollfd pfd = {
+ .fd = iso_socket,
+ .events = POLLIN | POLLOUT,
+ };
+ int past_pollout;
+ socklen_t optlen = sizeof(past_pollout);
+
+ if (getsockopt(iso_socket, SOL_BLUETOOTH, BT_ISO_PAST_POLLOUT,
+ &past_pollout, &optlen) < 0) {
+ if (errno != ENOPROTOOPT)
+ goto error;
+
+ past_pollout = 0;
+ }
+
+ bacpy(&addr.iso.iso_bdaddr, &local_addr);
+ bacpy(&addr.bc.bc_bdaddr, &peer_addr);
+
+ if (bind(iso_socket, (struct sockaddr *)&addr, sizeof(addr)) < 0)
+ goto error;
+
+ if (setsockopt(iso_socket, SOL_BLUETOOTH, BT_ISO_QOS,
+ &qos, sizeof(qos)) < 0)
+ goto error;
+
+ if (listen(iso_socket, 5) < 0)
+ goto error;
+
+ if (past_pollout) {
+ if (poll(&pfd, 1, 5000) <= 0)
+ goto error;
+ }
+ if (!past_pollout || (pfd.revents & POLLOUT))
+ notify_bcast_rcv_state(peer);
+
+ sk = accept(iso_socket, ...);
+
SOCKET OPTIONS (SOL_BLUETOOTH)
==============================
The socket options listed below can be set by using **setsockopt(2)** and read
-with **getsockopt(2)** with the socket level set to SOL_BLUETOOTH.
+with **getsockopt(2)** with the socket level set to SOL_BLUETOOTH, unless
+otherwise noted.
+
+BT_ISO_PAST_POLLOUT (since Linux 7.4)
+--------------------------------------
+
+Read-only capability query (option number 23). **getsockopt(2)** returns
+an ``int`` with value **1** when the kernel supports signaling ``POLLOUT``
+on a PAST listen socket after receiver setup completes. It reports
+support for the notification, not the current readiness of the socket;
+use **poll(2)** to wait for readiness.
+
+Kernels without this option return **ENOPROTOOPT**. Applications running
+on those kernels must not wait for the readiness notification.
+**setsockopt(2)** returns **EINVAL** for this option.
BT_SECURITY
-----------
--
2.56.0
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH BlueZ 5/5] bass: wait for PAST listener readiness
2026-10-11 20:40 [PATCH BlueZ 0/5] bass: wait for PAST listener readiness Pauli Virtanen
` (3 preceding siblings ...)
2026-10-11 20:40 ` [PATCH BlueZ 4/5] doc: iso: document ISO " Pauli Virtanen
@ 2026-10-11 20:40 ` Pauli Virtanen
2026-10-12 19:58 ` Luiz Augusto von Dentz
4 siblings, 1 reply; 12+ messages in thread
From: Pauli Virtanen @ 2026-10-11 20:40 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Pauli Virtanen
Defer SYNC_INFO_RE until the PA sync listener signals POLLOUT, so the
Assistant cannot start PAST before receiver setup completes.
Check kernel support via BT_ISO_PAST_POLLOUT, otherwise skip waiting.
Fixes test_bass_past_transport_* test failures.
Assisted-by: LLM
---
profiles/audio/bass.c | 66 ++++++++++++++++++++++++++++++++++++++-----
1 file changed, 59 insertions(+), 7 deletions(-)
diff --git a/profiles/audio/bass.c b/profiles/audio/bass.c
index fd05195f3..90e758c08 100644
--- a/profiles/audio/bass.c
+++ b/profiles/audio/bass.c
@@ -146,7 +146,9 @@ struct bass_delegator {
struct queue *bcode_reqs;
struct queue *setups;
unsigned int io_id;
+ unsigned int io_ready_id;
GIOChannel *io;
+ bool past;
};
struct bass_setup {
@@ -751,6 +753,11 @@ static void confirm_cb(GIOChannel *io, void *user_data)
DBG("");
+ if (dg->io_ready_id) {
+ g_source_remove(dg->io_ready_id);
+ dg->io_ready_id = 0;
+ }
+
/* Close the listen io */
g_io_channel_shutdown(dg->io, TRUE, NULL);
g_io_channel_unref(dg->io);
@@ -1528,11 +1535,28 @@ static bool delegator_match_device(const void *data, const void *match_data)
return dg->device == device;
}
+static gboolean pa_sync_ready_cb(GIOChannel *io, GIOCondition cond,
+ gpointer user_data)
+{
+ struct bass_delegator *dg = user_data;
+
+ dg->io_ready_id = 0;
+
+ if (cond & (G_IO_ERR | G_IO_HUP | G_IO_NVAL))
+ return FALSE;
+
+ bt_bass_set_pa_sync(dg->src, BT_BASS_SYNC_INFO_RE);
+
+ return FALSE;
+}
+
static void delegator_attach(struct bt_bap *bap, struct btd_device *device,
struct btd_service *service)
{
struct bass_delegator *dg;
GError *err = NULL;
+ socklen_t len;
+ int sk, past_pollout;
dg = queue_find(delegators, delegator_match_device, device);
if (!dg)
@@ -1570,6 +1594,30 @@ static void delegator_attach(struct bt_bap *bap, struct btd_device *device,
/* Take ownership for the service by setting the user data. */
btd_service_set_user_data(service, dg);
+
+ if (!dg->past)
+ return;
+
+ sk = g_io_channel_unix_get_fd(dg->io);
+ len = sizeof(past_pollout);
+
+ if (getsockopt(sk, SOL_BLUETOOTH, BT_ISO_PAST_POLLOUT,
+ &past_pollout, &len) < 0) {
+ if (errno != ENOPROTOOPT) {
+ error("Unable to query PAST readiness support: %s",
+ strerror(errno));
+ return;
+ }
+
+ past_pollout = 0;
+ }
+
+ if (past_pollout)
+ dg->io_ready_id = g_io_add_watch(dg->io,
+ G_IO_OUT | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
+ pa_sync_ready_cb, dg);
+ else
+ bt_bass_set_pa_sync(dg->src, BT_BASS_SYNC_INFO_RE);
}
static void bap_attached(struct bt_bap *bap, void *user_data)
@@ -1645,6 +1693,9 @@ static void delegator_free(struct bass_delegator *dg)
if (dg->io_id)
g_source_remove(dg->io_id);
+ if (dg->io_ready_id)
+ g_source_remove(dg->io_ready_id);
+
if (dg->io) {
g_io_channel_shutdown(dg->io, TRUE, NULL);
g_io_channel_unref(dg->io);
@@ -1906,7 +1957,7 @@ static void bass_detached(struct bt_bass *bass, void *user_data)
static struct bass_delegator *
bass_delegator_new(struct btd_device *device, struct bt_bcast_src *src,
- uint8_t sid)
+ uint8_t sid, bool past)
{
struct bass_delegator *dg;
@@ -1917,6 +1968,7 @@ bass_delegator_new(struct btd_device *device, struct bt_bcast_src *src,
dg->device = device;
dg->src = src;
dg->sid = sid;
+ dg->past = past;
dg->bcode_reqs = queue_new();
dg->setups = queue_new();
@@ -1945,6 +1997,7 @@ static int handle_add_src_req(struct bt_bcast_src *bcast_src,
struct btd_adapter *adapter = device_get_adapter(data->device);
struct btd_device *device;
struct bass_delegator *dg;
+ bool past = false;
/* Detect if PAST can be used then it can be used as destination since
* PAST Receiver uses the ACL connection itself.
@@ -1957,6 +2010,7 @@ static int handle_add_src_req(struct bt_bcast_src *bcast_src,
if (btd_adapter_has_settings(data->adapter,
MGMT_SETTING_PAST_RECEIVER)) {
device_set_past_support(data->device, true);
+ past = true;
device = data->device;
goto done;
}
@@ -1980,16 +2034,14 @@ done:
* autonomously probed inside BAP.
*/
if (!btd_device_get_service(device, BCAAS_UUID_STR)) {
- dg = bass_delegator_new(device, bcast_src, params->sid);
+ dg = bass_delegator_new(device, bcast_src, params->sid, past);
if (!dg)
return -ENOMEM;
+
+ if (past)
+ return 0;
}
- /* Set PA sync state, this has to be done after probing otherwise there
- * is a race where the remote may receive BT_BASS_SYNC_INFO_RE and start
- * PAST before the kernel has sent PAST Parameter command that enables
- * receiving it.
- */
if (params->pa_sync == PA_SYNC_PAST) {
if (btd_adapter_has_settings(data->adapter,
MGMT_SETTING_PAST_RECEIVER))
--
2.56.0
^ permalink raw reply related [flat|nested] 12+ messages in thread
* RE: bass: wait for PAST listener readiness
2026-10-11 20:40 ` [PATCH BlueZ 1/5] iso-tester: remove IO sources during teardown Pauli Virtanen
@ 2026-10-11 23:54 ` bluez.test.bot
0 siblings, 0 replies; 12+ messages in thread
From: bluez.test.bot @ 2026-10-11 23:54 UTC (permalink / raw)
To: linux-bluetooth, pav
[-- Attachment #1: Type: text/plain, Size: 3311 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/1183466/
---Test result---
Test Summary:
CheckPatch PASS 2.00 seconds
GitLint PASS 1.20 seconds
BuildEll PASS 19.26 seconds
BluezMake PASS 382.69 seconds
MakeCheck PASS 14.01 seconds
MakeDistcheck PASS 138.19 seconds
CheckValgrind PASS 228.68 seconds
CheckSmatch PASS 289.47 seconds
bluezmakeextell PASS 93.09 seconds
TestFunctional FAIL 1235.95 seconds
IncrementalBuild PASS 749.51 seconds
ScanBuild PASS 1116.57 seconds
Details
##############################
Test: TestFunctional - FAIL
Desc: Run test-functional
Output:
FAIL functional.test_bluetoothctl::test_bluetoothctl_pair_bredr[hosts15-vm2]: pytest_bluezenv.rpc.RemoteError: <pexpect.popen_spawn.PopenSpawn object at 0x7fbeb5906510>
searcher: searcher_re:
0: re.compile(b'Changing pairable on succeeded')
<pexpect.popen_spawn.PopenSpawn object at 0x7fbeb5906510>
searcher: searcher_re:
0: re.compile(b'Changing pairable on succeeded')
Remote traceback:
Traceback (most recent call last):
File "/usr/local/lib/python3.14/dist-packages/pytest_bluezenv/../pytest_bluezenv/rpc.py", line 102, in server_stream
result = method(*msg["a"], **msg["kw"])
File "/usr/local/lib/python3.14/dist-packages/pytest_bluezenv/../pytest_bluezenv/env.py", line 256, in call_plugin
return getattr(self.plugins[name], method)(*a, **kw)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^
File "/usr/local/lib/python3.14/dist-packages/pytest_bluezenv/../pytest_bluezenv/host_plugins.py", line 502, in expect
ret = self.ctl.expect(*a, **kw)
File "/usr/lib/python3/dist-packages/pexpect/spawnbase.py", line 354, in expect
return self.expect_list(compiled_pattern_list,
~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^
timeout, searchwindowsize, async_)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3/dist-packages/pexpect/spawnbase.py", line 383, in expect_list
return exp.expect_loop(timeout)
~~~~~~~~~~~~~~~^^^^^^^^^
File "/usr/lib/python3/dist-packages/pexpect/expect.py", line 181, in expect_loop
return self.timeout(e)
~~~~~~~~~~~~^^^
File "/usr/lib/python3/dist-packages/pexpect/expect.py", line 144, in timeout
raise exc
pexpect.exceptions.TIMEOUT: <pexpect.popen_spawn.PopenSpawn object at 0x7fbeb5906510>
searcher: searcher_re:
0: re.compile(b'Changing pairable on succeeded')
<pexpect.popen_spawn.PopenSpawn object at 0x7fbeb5906510>
searcher: searcher_re:
0: re.compile(b'Changing pairable on succeeded')
https://github.com/bluez/bluez/pull/2662
---
Regards,
Linux Bluetooth
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH BlueZ 5/5] bass: wait for PAST listener readiness
2026-10-11 20:40 ` [PATCH BlueZ 5/5] bass: wait for " Pauli Virtanen
@ 2026-10-12 19:58 ` Luiz Augusto von Dentz
2026-10-12 20:24 ` Pauli Virtanen
0 siblings, 1 reply; 12+ messages in thread
From: Luiz Augusto von Dentz @ 2026-10-12 19:58 UTC (permalink / raw)
To: Pauli Virtanen; +Cc: linux-bluetooth
Hi Pauli,
On Sun, Oct 11, 2026 at 4:42 PM Pauli Virtanen <pav@iki.fi> wrote:
>
> Defer SYNC_INFO_RE until the PA sync listener signals POLLOUT, so the
> Assistant cannot start PAST before receiver setup completes.
>
> Check kernel support via BT_ISO_PAST_POLLOUT, otherwise skip waiting.
>
> Fixes test_bass_past_transport_* test failures.
>
> Assisted-by: LLM
> ---
> profiles/audio/bass.c | 66 ++++++++++++++++++++++++++++++++++++++-----
> 1 file changed, 59 insertions(+), 7 deletions(-)
>
> diff --git a/profiles/audio/bass.c b/profiles/audio/bass.c
> index fd05195f3..90e758c08 100644
> --- a/profiles/audio/bass.c
> +++ b/profiles/audio/bass.c
> @@ -146,7 +146,9 @@ struct bass_delegator {
> struct queue *bcode_reqs;
> struct queue *setups;
> unsigned int io_id;
> + unsigned int io_ready_id;
> GIOChannel *io;
> + bool past;
> };
>
> struct bass_setup {
> @@ -751,6 +753,11 @@ static void confirm_cb(GIOChannel *io, void *user_data)
>
> DBG("");
>
> + if (dg->io_ready_id) {
> + g_source_remove(dg->io_ready_id);
> + dg->io_ready_id = 0;
> + }
> +
> /* Close the listen io */
> g_io_channel_shutdown(dg->io, TRUE, NULL);
> g_io_channel_unref(dg->io);
> @@ -1528,11 +1535,28 @@ static bool delegator_match_device(const void *data, const void *match_data)
> return dg->device == device;
> }
>
> +static gboolean pa_sync_ready_cb(GIOChannel *io, GIOCondition cond,
> + gpointer user_data)
> +{
> + struct bass_delegator *dg = user_data;
> +
> + dg->io_ready_id = 0;
> +
> + if (cond & (G_IO_ERR | G_IO_HUP | G_IO_NVAL))
> + return FALSE;
> +
> + bt_bass_set_pa_sync(dg->src, BT_BASS_SYNC_INFO_RE);
> +
> + return FALSE;
> +}
> +
> static void delegator_attach(struct bt_bap *bap, struct btd_device *device,
> struct btd_service *service)
> {
> struct bass_delegator *dg;
> GError *err = NULL;
> + socklen_t len;
> + int sk, past_pollout;
>
> dg = queue_find(delegators, delegator_match_device, device);
> if (!dg)
> @@ -1570,6 +1594,30 @@ static void delegator_attach(struct bt_bap *bap, struct btd_device *device,
>
> /* Take ownership for the service by setting the user data. */
> btd_service_set_user_data(service, dg);
> +
> + if (!dg->past)
> + return;
> +
> + sk = g_io_channel_unix_get_fd(dg->io);
> + len = sizeof(past_pollout);
> +
> + if (getsockopt(sk, SOL_BLUETOOTH, BT_ISO_PAST_POLLOUT,
> + &past_pollout, &len) < 0) {
> + if (errno != ENOPROTOOPT) {
> + error("Unable to query PAST readiness support: %s",
> + strerror(errno));
> + return;
> + }
> +
> + past_pollout = 0;
> + }
While I agree it needs fixing since not only it can create problem for
testing but it may be possible to hit this over the air as well, the
solution though is a little bitter to my taste, perhaps we could use
to indicate when HCI_OP_LE_PAST_PARAMS has been completed (with
status=0) then we notify with flags changed. If that is still a little
too much perhaps we can issue right away when setting upon receiving
MGMT_OP_SET_DEVICE_FLAGS(HCI_CONN_FLAG_PAST) and somehow makeup for
the qos arguments if conn->iso_qos is not set yet, either way that
should gate the response of BT_BASS_SYNC_INFO_RE by indicating
HCI_CONN_FLAG_PAST is actually programmed in the controller via
HCI_OP_LE_PAST_PARAMS.
> + if (past_pollout)
> + dg->io_ready_id = g_io_add_watch(dg->io,
> + G_IO_OUT | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
> + pa_sync_ready_cb, dg);
> + else
> + bt_bass_set_pa_sync(dg->src, BT_BASS_SYNC_INFO_RE);
> }
>
> static void bap_attached(struct bt_bap *bap, void *user_data)
> @@ -1645,6 +1693,9 @@ static void delegator_free(struct bass_delegator *dg)
> if (dg->io_id)
> g_source_remove(dg->io_id);
>
> + if (dg->io_ready_id)
> + g_source_remove(dg->io_ready_id);
> +
> if (dg->io) {
> g_io_channel_shutdown(dg->io, TRUE, NULL);
> g_io_channel_unref(dg->io);
> @@ -1906,7 +1957,7 @@ static void bass_detached(struct bt_bass *bass, void *user_data)
>
> static struct bass_delegator *
> bass_delegator_new(struct btd_device *device, struct bt_bcast_src *src,
> - uint8_t sid)
> + uint8_t sid, bool past)
> {
> struct bass_delegator *dg;
>
> @@ -1917,6 +1968,7 @@ bass_delegator_new(struct btd_device *device, struct bt_bcast_src *src,
> dg->device = device;
> dg->src = src;
> dg->sid = sid;
> + dg->past = past;
> dg->bcode_reqs = queue_new();
> dg->setups = queue_new();
>
> @@ -1945,6 +1997,7 @@ static int handle_add_src_req(struct bt_bcast_src *bcast_src,
> struct btd_adapter *adapter = device_get_adapter(data->device);
> struct btd_device *device;
> struct bass_delegator *dg;
> + bool past = false;
>
> /* Detect if PAST can be used then it can be used as destination since
> * PAST Receiver uses the ACL connection itself.
> @@ -1957,6 +2010,7 @@ static int handle_add_src_req(struct bt_bcast_src *bcast_src,
> if (btd_adapter_has_settings(data->adapter,
> MGMT_SETTING_PAST_RECEIVER)) {
> device_set_past_support(data->device, true);
> + past = true;
> device = data->device;
> goto done;
> }
> @@ -1980,16 +2034,14 @@ done:
> * autonomously probed inside BAP.
> */
> if (!btd_device_get_service(device, BCAAS_UUID_STR)) {
> - dg = bass_delegator_new(device, bcast_src, params->sid);
> + dg = bass_delegator_new(device, bcast_src, params->sid, past);
> if (!dg)
> return -ENOMEM;
> +
> + if (past)
> + return 0;
> }
>
> - /* Set PA sync state, this has to be done after probing otherwise there
> - * is a race where the remote may receive BT_BASS_SYNC_INFO_RE and start
> - * PAST before the kernel has sent PAST Parameter command that enables
> - * receiving it.
> - */
> if (params->pa_sync == PA_SYNC_PAST) {
> if (btd_adapter_has_settings(data->adapter,
> MGMT_SETTING_PAST_RECEIVER))
> --
> 2.56.0
>
>
--
Luiz Augusto von Dentz
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH BlueZ 5/5] bass: wait for PAST listener readiness
2026-10-12 19:58 ` Luiz Augusto von Dentz
@ 2026-10-12 20:24 ` Pauli Virtanen
2026-10-12 20:40 ` Luiz Augusto von Dentz
0 siblings, 1 reply; 12+ messages in thread
From: Pauli Virtanen @ 2026-10-12 20:24 UTC (permalink / raw)
To: Luiz Augusto von Dentz; +Cc: linux-bluetooth
Hi Luiz,
ma, 2026-10-12 kello 15:58 -0400, Luiz Augusto von Dentz kirjoitti:
> On Sun, Oct 11, 2026 at 4:42 PM Pauli Virtanen <pav@iki.fi> wrote:
> >
> > Defer SYNC_INFO_RE until the PA sync listener signals POLLOUT, so the
> > Assistant cannot start PAST before receiver setup completes.
> >
> > Check kernel support via BT_ISO_PAST_POLLOUT, otherwise skip waiting.
> >
> > Fixes test_bass_past_transport_* test failures.
> >
> > Assisted-by: LLM
> > ---
> > profiles/audio/bass.c | 66 ++++++++++++++++++++++++++++++++++++++-----
> > 1 file changed, 59 insertions(+), 7 deletions(-)
> >
> > diff --git a/profiles/audio/bass.c b/profiles/audio/bass.c
> > index fd05195f3..90e758c08 100644
> > --- a/profiles/audio/bass.c
> > +++ b/profiles/audio/bass.c
> > @@ -146,7 +146,9 @@ struct bass_delegator {
> > struct queue *bcode_reqs;
> > struct queue *setups;
> > unsigned int io_id;
> > + unsigned int io_ready_id;
> > GIOChannel *io;
> > + bool past;
> > };
> >
> > struct bass_setup {
> > @@ -751,6 +753,11 @@ static void confirm_cb(GIOChannel *io, void *user_data)
> >
> > DBG("");
> >
> > + if (dg->io_ready_id) {
> > + g_source_remove(dg->io_ready_id);
> > + dg->io_ready_id = 0;
> > + }
> > +
> > /* Close the listen io */
> > g_io_channel_shutdown(dg->io, TRUE, NULL);
> > g_io_channel_unref(dg->io);
> > @@ -1528,11 +1535,28 @@ static bool delegator_match_device(const void *data, const void *match_data)
> > return dg->device == device;
> > }
> >
> > +static gboolean pa_sync_ready_cb(GIOChannel *io, GIOCondition cond,
> > + gpointer user_data)
> > +{
> > + struct bass_delegator *dg = user_data;
> > +
> > + dg->io_ready_id = 0;
> > +
> > + if (cond & (G_IO_ERR | G_IO_HUP | G_IO_NVAL))
> > + return FALSE;
> > +
> > + bt_bass_set_pa_sync(dg->src, BT_BASS_SYNC_INFO_RE);
> > +
> > + return FALSE;
> > +}
> > +
> > static void delegator_attach(struct bt_bap *bap, struct btd_device *device,
> > struct btd_service *service)
> > {
> > struct bass_delegator *dg;
> > GError *err = NULL;
> > + socklen_t len;
> > + int sk, past_pollout;
> >
> > dg = queue_find(delegators, delegator_match_device, device);
> > if (!dg)
> > @@ -1570,6 +1594,30 @@ static void delegator_attach(struct bt_bap *bap, struct btd_device *device,
> >
> > /* Take ownership for the service by setting the user data. */
> > btd_service_set_user_data(service, dg);
> > +
> > + if (!dg->past)
> > + return;
> > +
> > + sk = g_io_channel_unix_get_fd(dg->io);
> > + len = sizeof(past_pollout);
> > +
> > + if (getsockopt(sk, SOL_BLUETOOTH, BT_ISO_PAST_POLLOUT,
> > + &past_pollout, &len) < 0) {
> > + if (errno != ENOPROTOOPT) {
> > + error("Unable to query PAST readiness support: %s",
> > + strerror(errno));
> > + return;
> > + }
> > +
> > + past_pollout = 0;
> > + }
>
> While I agree it needs fixing since not only it can create problem for
> testing but it may be possible to hit this over the air as well, the
> solution though is a little bitter to my taste, perhaps we could use
> to indicate when HCI_OP_LE_PAST_PARAMS has been completed (with
> status=0) then we notify with flags changed. If that is still a little
> too much perhaps we can issue right away when setting upon receiving
> MGMT_OP_SET_DEVICE_FLAGS(HCI_CONN_FLAG_PAST) and somehow makeup for
> the qos arguments if conn->iso_qos is not set yet, either way that
> should gate the response of BT_BASS_SYNC_INFO_RE by indicating
> HCI_CONN_FLAG_PAST is actually programmed in the controller via
> HCI_OP_LE_PAST_PARAMS.
Note the issue is not only waiting for HCI_OP_LE_PAST_PARAMS, but also
closing the race window before hdev->req_skb gets assigned, otherwise
it appears the hci_sync wait could miss early event and end up in
create_pa_complete() with error.
We could ignore this as too small window (-> drop patch 1), but I'm not
certain it's impossible to hit, and LLMs will be complaining.
IIUC you would rather signal readiness via MGMT than mess with poll(),
via eg. new MGMT device flags bit + inform bluetoothd via change
event? Controller settings would also allow backward-compatibility
detection.
So: Keep patch 1 + signal PAST readiness via device flags bit?
> > + if (past_pollout)
> > + dg->io_ready_id = g_io_add_watch(dg->io,
> > + G_IO_OUT | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
> > + pa_sync_ready_cb, dg);
> > + else
> > + bt_bass_set_pa_sync(dg->src, BT_BASS_SYNC_INFO_RE);
> > }
> >
> > static void bap_attached(struct bt_bap *bap, void *user_data)
> > @@ -1645,6 +1693,9 @@ static void delegator_free(struct bass_delegator *dg)
> > if (dg->io_id)
> > g_source_remove(dg->io_id);
> >
> > + if (dg->io_ready_id)
> > + g_source_remove(dg->io_ready_id);
> > +
> > if (dg->io) {
> > g_io_channel_shutdown(dg->io, TRUE, NULL);
> > g_io_channel_unref(dg->io);
> > @@ -1906,7 +1957,7 @@ static void bass_detached(struct bt_bass *bass, void *user_data)
> >
> > static struct bass_delegator *
> > bass_delegator_new(struct btd_device *device, struct bt_bcast_src *src,
> > - uint8_t sid)
> > + uint8_t sid, bool past)
> > {
> > struct bass_delegator *dg;
> >
> > @@ -1917,6 +1968,7 @@ bass_delegator_new(struct btd_device *device, struct bt_bcast_src *src,
> > dg->device = device;
> > dg->src = src;
> > dg->sid = sid;
> > + dg->past = past;
> > dg->bcode_reqs = queue_new();
> > dg->setups = queue_new();
> >
> > @@ -1945,6 +1997,7 @@ static int handle_add_src_req(struct bt_bcast_src *bcast_src,
> > struct btd_adapter *adapter = device_get_adapter(data->device);
> > struct btd_device *device;
> > struct bass_delegator *dg;
> > + bool past = false;
> >
> > /* Detect if PAST can be used then it can be used as destination since
> > * PAST Receiver uses the ACL connection itself.
> > @@ -1957,6 +2010,7 @@ static int handle_add_src_req(struct bt_bcast_src *bcast_src,
> > if (btd_adapter_has_settings(data->adapter,
> > MGMT_SETTING_PAST_RECEIVER)) {
> > device_set_past_support(data->device, true);
> > + past = true;
> > device = data->device;
> > goto done;
> > }
> > @@ -1980,16 +2034,14 @@ done:
> > * autonomously probed inside BAP.
> > */
> > if (!btd_device_get_service(device, BCAAS_UUID_STR)) {
> > - dg = bass_delegator_new(device, bcast_src, params->sid);
> > + dg = bass_delegator_new(device, bcast_src, params->sid, past);
> > if (!dg)
> > return -ENOMEM;
> > +
> > + if (past)
> > + return 0;
> > }
> >
> > - /* Set PA sync state, this has to be done after probing otherwise there
> > - * is a race where the remote may receive BT_BASS_SYNC_INFO_RE and start
> > - * PAST before the kernel has sent PAST Parameter command that enables
> > - * receiving it.
> > - */
> > if (params->pa_sync == PA_SYNC_PAST) {
> > if (btd_adapter_has_settings(data->adapter,
> > MGMT_SETTING_PAST_RECEIVER))
> > --
> > 2.56.0
> >
> >
>
--
Pauli Virtanen
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH BlueZ 5/5] bass: wait for PAST listener readiness
2026-10-12 20:24 ` Pauli Virtanen
@ 2026-10-12 20:40 ` Luiz Augusto von Dentz
2026-10-12 21:03 ` Pauli Virtanen
0 siblings, 1 reply; 12+ messages in thread
From: Luiz Augusto von Dentz @ 2026-10-12 20:40 UTC (permalink / raw)
To: Pauli Virtanen; +Cc: linux-bluetooth
Hi Pauli,
On Mon, Oct 12, 2026 at 4:24 PM Pauli Virtanen <pav@iki.fi> wrote:
>
> Hi Luiz,
>
> ma, 2026-10-12 kello 15:58 -0400, Luiz Augusto von Dentz kirjoitti:
> > On Sun, Oct 11, 2026 at 4:42 PM Pauli Virtanen <pav@iki.fi> wrote:
> > >
> > > Defer SYNC_INFO_RE until the PA sync listener signals POLLOUT, so the
> > > Assistant cannot start PAST before receiver setup completes.
> > >
> > > Check kernel support via BT_ISO_PAST_POLLOUT, otherwise skip waiting.
> > >
> > > Fixes test_bass_past_transport_* test failures.
> > >
> > > Assisted-by: LLM
> > > ---
> > > profiles/audio/bass.c | 66 ++++++++++++++++++++++++++++++++++++++-----
> > > 1 file changed, 59 insertions(+), 7 deletions(-)
> > >
> > > diff --git a/profiles/audio/bass.c b/profiles/audio/bass.c
> > > index fd05195f3..90e758c08 100644
> > > --- a/profiles/audio/bass.c
> > > +++ b/profiles/audio/bass.c
> > > @@ -146,7 +146,9 @@ struct bass_delegator {
> > > struct queue *bcode_reqs;
> > > struct queue *setups;
> > > unsigned int io_id;
> > > + unsigned int io_ready_id;
> > > GIOChannel *io;
> > > + bool past;
> > > };
> > >
> > > struct bass_setup {
> > > @@ -751,6 +753,11 @@ static void confirm_cb(GIOChannel *io, void *user_data)
> > >
> > > DBG("");
> > >
> > > + if (dg->io_ready_id) {
> > > + g_source_remove(dg->io_ready_id);
> > > + dg->io_ready_id = 0;
> > > + }
> > > +
> > > /* Close the listen io */
> > > g_io_channel_shutdown(dg->io, TRUE, NULL);
> > > g_io_channel_unref(dg->io);
> > > @@ -1528,11 +1535,28 @@ static bool delegator_match_device(const void *data, const void *match_data)
> > > return dg->device == device;
> > > }
> > >
> > > +static gboolean pa_sync_ready_cb(GIOChannel *io, GIOCondition cond,
> > > + gpointer user_data)
> > > +{
> > > + struct bass_delegator *dg = user_data;
> > > +
> > > + dg->io_ready_id = 0;
> > > +
> > > + if (cond & (G_IO_ERR | G_IO_HUP | G_IO_NVAL))
> > > + return FALSE;
> > > +
> > > + bt_bass_set_pa_sync(dg->src, BT_BASS_SYNC_INFO_RE);
> > > +
> > > + return FALSE;
> > > +}
> > > +
> > > static void delegator_attach(struct bt_bap *bap, struct btd_device *device,
> > > struct btd_service *service)
> > > {
> > > struct bass_delegator *dg;
> > > GError *err = NULL;
> > > + socklen_t len;
> > > + int sk, past_pollout;
> > >
> > > dg = queue_find(delegators, delegator_match_device, device);
> > > if (!dg)
> > > @@ -1570,6 +1594,30 @@ static void delegator_attach(struct bt_bap *bap, struct btd_device *device,
> > >
> > > /* Take ownership for the service by setting the user data. */
> > > btd_service_set_user_data(service, dg);
> > > +
> > > + if (!dg->past)
> > > + return;
> > > +
> > > + sk = g_io_channel_unix_get_fd(dg->io);
> > > + len = sizeof(past_pollout);
> > > +
> > > + if (getsockopt(sk, SOL_BLUETOOTH, BT_ISO_PAST_POLLOUT,
> > > + &past_pollout, &len) < 0) {
> > > + if (errno != ENOPROTOOPT) {
> > > + error("Unable to query PAST readiness support: %s",
> > > + strerror(errno));
> > > + return;
> > > + }
> > > +
> > > + past_pollout = 0;
> > > + }
> >
> > While I agree it needs fixing since not only it can create problem for
> > testing but it may be possible to hit this over the air as well, the
> > solution though is a little bitter to my taste, perhaps we could use
> > to indicate when HCI_OP_LE_PAST_PARAMS has been completed (with
> > status=0) then we notify with flags changed. If that is still a little
> > too much perhaps we can issue right away when setting upon receiving
> > MGMT_OP_SET_DEVICE_FLAGS(HCI_CONN_FLAG_PAST) and somehow makeup for
> > the qos arguments if conn->iso_qos is not set yet, either way that
> > should gate the response of BT_BASS_SYNC_INFO_RE by indicating
> > HCI_CONN_FLAG_PAST is actually programmed in the controller via
> > HCI_OP_LE_PAST_PARAMS.
>
> Note the issue is not only waiting for HCI_OP_LE_PAST_PARAMS, but also
> closing the race window before hdev->req_skb gets assigned, otherwise
> it appears the hci_sync wait could miss early event and end up in
> create_pa_complete() with error.
>
> We could ignore this as too small window (-> drop patch 1), but I'm not
> certain it's impossible to hit, and LLMs will be complaining.
>
> IIUC you would rather signal readiness via MGMT than mess with poll(),
> via eg. new MGMT device flags bit + inform bluetoothd via change
> event? Controller settings would also allow backward-compatibility
> detection.
>
> So: Keep patch 1 + signal PAST readiness via device flags bit?
But for 1 to happen after we change to wait for the MGMT event that
would mean the peer had sent ahead of receiving BT_BASS_SYNC_INFO_RE?
We shall be waiting already once the MGMT signal trigger and we
respond with BT_BASS_SYNC_INFO_RE, perhaps Im not understanding the
race here, is that still about HCI_EV_LE_PAST_RECEIVED so we only do
the MGMT event while waiting it?
> > > + if (past_pollout)
> > > + dg->io_ready_id = g_io_add_watch(dg->io,
> > > + G_IO_OUT | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
> > > + pa_sync_ready_cb, dg);
> > > + else
> > > + bt_bass_set_pa_sync(dg->src, BT_BASS_SYNC_INFO_RE);
> > > }
> > >
> > > static void bap_attached(struct bt_bap *bap, void *user_data)
> > > @@ -1645,6 +1693,9 @@ static void delegator_free(struct bass_delegator *dg)
> > > if (dg->io_id)
> > > g_source_remove(dg->io_id);
> > >
> > > + if (dg->io_ready_id)
> > > + g_source_remove(dg->io_ready_id);
> > > +
> > > if (dg->io) {
> > > g_io_channel_shutdown(dg->io, TRUE, NULL);
> > > g_io_channel_unref(dg->io);
> > > @@ -1906,7 +1957,7 @@ static void bass_detached(struct bt_bass *bass, void *user_data)
> > >
> > > static struct bass_delegator *
> > > bass_delegator_new(struct btd_device *device, struct bt_bcast_src *src,
> > > - uint8_t sid)
> > > + uint8_t sid, bool past)
> > > {
> > > struct bass_delegator *dg;
> > >
> > > @@ -1917,6 +1968,7 @@ bass_delegator_new(struct btd_device *device, struct bt_bcast_src *src,
> > > dg->device = device;
> > > dg->src = src;
> > > dg->sid = sid;
> > > + dg->past = past;
> > > dg->bcode_reqs = queue_new();
> > > dg->setups = queue_new();
> > >
> > > @@ -1945,6 +1997,7 @@ static int handle_add_src_req(struct bt_bcast_src *bcast_src,
> > > struct btd_adapter *adapter = device_get_adapter(data->device);
> > > struct btd_device *device;
> > > struct bass_delegator *dg;
> > > + bool past = false;
> > >
> > > /* Detect if PAST can be used then it can be used as destination since
> > > * PAST Receiver uses the ACL connection itself.
> > > @@ -1957,6 +2010,7 @@ static int handle_add_src_req(struct bt_bcast_src *bcast_src,
> > > if (btd_adapter_has_settings(data->adapter,
> > > MGMT_SETTING_PAST_RECEIVER)) {
> > > device_set_past_support(data->device, true);
> > > + past = true;
> > > device = data->device;
> > > goto done;
> > > }
> > > @@ -1980,16 +2034,14 @@ done:
> > > * autonomously probed inside BAP.
> > > */
> > > if (!btd_device_get_service(device, BCAAS_UUID_STR)) {
> > > - dg = bass_delegator_new(device, bcast_src, params->sid);
> > > + dg = bass_delegator_new(device, bcast_src, params->sid, past);
> > > if (!dg)
> > > return -ENOMEM;
> > > +
> > > + if (past)
> > > + return 0;
> > > }
> > >
> > > - /* Set PA sync state, this has to be done after probing otherwise there
> > > - * is a race where the remote may receive BT_BASS_SYNC_INFO_RE and start
> > > - * PAST before the kernel has sent PAST Parameter command that enables
> > > - * receiving it.
> > > - */
> > > if (params->pa_sync == PA_SYNC_PAST) {
> > > if (btd_adapter_has_settings(data->adapter,
> > > MGMT_SETTING_PAST_RECEIVER))
> > > --
> > > 2.56.0
> > >
> > >
> >
>
> --
> Pauli Virtanen
--
Luiz Augusto von Dentz
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH BlueZ 5/5] bass: wait for PAST listener readiness
2026-10-12 20:40 ` Luiz Augusto von Dentz
@ 2026-10-12 21:03 ` Pauli Virtanen
2026-10-12 21:31 ` Luiz Augusto von Dentz
0 siblings, 1 reply; 12+ messages in thread
From: Pauli Virtanen @ 2026-10-12 21:03 UTC (permalink / raw)
To: Luiz Augusto von Dentz; +Cc: linux-bluetooth
Hi Luiz,
ma, 2026-10-12 kello 16:40 -0400, Luiz Augusto von Dentz kirjoitti:
> Hi Pauli,
>
> On Mon, Oct 12, 2026 at 4:24 PM Pauli Virtanen <pav@iki.fi> wrote:
> >
> > Hi Luiz,
> >
> > ma, 2026-10-12 kello 15:58 -0400, Luiz Augusto von Dentz kirjoitti:
> > > On Sun, Oct 11, 2026 at 4:42 PM Pauli Virtanen <pav@iki.fi> wrote:
> > > >
> > > > Defer SYNC_INFO_RE until the PA sync listener signals POLLOUT, so the
> > > > Assistant cannot start PAST before receiver setup completes.
> > > >
> > > > Check kernel support via BT_ISO_PAST_POLLOUT, otherwise skip waiting.
> > > >
> > > > Fixes test_bass_past_transport_* test failures.
> > > >
> > > > Assisted-by: LLM
> > > > ---
> > > > profiles/audio/bass.c | 66 ++++++++++++++++++++++++++++++++++++++-----
> > > > 1 file changed, 59 insertions(+), 7 deletions(-)
> > > >
> > > > diff --git a/profiles/audio/bass.c b/profiles/audio/bass.c
> > > > index fd05195f3..90e758c08 100644
> > > > --- a/profiles/audio/bass.c
> > > > +++ b/profiles/audio/bass.c
> > > > @@ -146,7 +146,9 @@ struct bass_delegator {
> > > > struct queue *bcode_reqs;
> > > > struct queue *setups;
> > > > unsigned int io_id;
> > > > + unsigned int io_ready_id;
> > > > GIOChannel *io;
> > > > + bool past;
> > > > };
> > > >
> > > > struct bass_setup {
> > > > @@ -751,6 +753,11 @@ static void confirm_cb(GIOChannel *io, void *user_data)
> > > >
> > > > DBG("");
> > > >
> > > > + if (dg->io_ready_id) {
> > > > + g_source_remove(dg->io_ready_id);
> > > > + dg->io_ready_id = 0;
> > > > + }
> > > > +
> > > > /* Close the listen io */
> > > > g_io_channel_shutdown(dg->io, TRUE, NULL);
> > > > g_io_channel_unref(dg->io);
> > > > @@ -1528,11 +1535,28 @@ static bool delegator_match_device(const void *data, const void *match_data)
> > > > return dg->device == device;
> > > > }
> > > >
> > > > +static gboolean pa_sync_ready_cb(GIOChannel *io, GIOCondition cond,
> > > > + gpointer user_data)
> > > > +{
> > > > + struct bass_delegator *dg = user_data;
> > > > +
> > > > + dg->io_ready_id = 0;
> > > > +
> > > > + if (cond & (G_IO_ERR | G_IO_HUP | G_IO_NVAL))
> > > > + return FALSE;
> > > > +
> > > > + bt_bass_set_pa_sync(dg->src, BT_BASS_SYNC_INFO_RE);
> > > > +
> > > > + return FALSE;
> > > > +}
> > > > +
> > > > static void delegator_attach(struct bt_bap *bap, struct btd_device *device,
> > > > struct btd_service *service)
> > > > {
> > > > struct bass_delegator *dg;
> > > > GError *err = NULL;
> > > > + socklen_t len;
> > > > + int sk, past_pollout;
> > > >
> > > > dg = queue_find(delegators, delegator_match_device, device);
> > > > if (!dg)
> > > > @@ -1570,6 +1594,30 @@ static void delegator_attach(struct bt_bap *bap, struct btd_device *device,
> > > >
> > > > /* Take ownership for the service by setting the user data. */
> > > > btd_service_set_user_data(service, dg);
> > > > +
> > > > + if (!dg->past)
> > > > + return;
> > > > +
> > > > + sk = g_io_channel_unix_get_fd(dg->io);
> > > > + len = sizeof(past_pollout);
> > > > +
> > > > + if (getsockopt(sk, SOL_BLUETOOTH, BT_ISO_PAST_POLLOUT,
> > > > + &past_pollout, &len) < 0) {
> > > > + if (errno != ENOPROTOOPT) {
> > > > + error("Unable to query PAST readiness support: %s",
> > > > + strerror(errno));
> > > > + return;
> > > > + }
> > > > +
> > > > + past_pollout = 0;
> > > > + }
> > >
> > > While I agree it needs fixing since not only it can create problem for
> > > testing but it may be possible to hit this over the air as well, the
> > > solution though is a little bitter to my taste, perhaps we could use
> > > to indicate when HCI_OP_LE_PAST_PARAMS has been completed (with
> > > status=0) then we notify with flags changed. If that is still a little
> > > too much perhaps we can issue right away when setting upon receiving
> > > MGMT_OP_SET_DEVICE_FLAGS(HCI_CONN_FLAG_PAST) and somehow makeup for
> > > the qos arguments if conn->iso_qos is not set yet, either way that
> > > should gate the response of BT_BASS_SYNC_INFO_RE by indicating
> > > HCI_CONN_FLAG_PAST is actually programmed in the controller via
> > > HCI_OP_LE_PAST_PARAMS.
> >
> > Note the issue is not only waiting for HCI_OP_LE_PAST_PARAMS, but also
> > closing the race window before hdev->req_skb gets assigned, otherwise
> > it appears the hci_sync wait could miss early event and end up in
> > create_pa_complete() with error.
> >
> > We could ignore this as too small window (-> drop patch 1), but I'm not
> > certain it's impossible to hit, and LLMs will be complaining.
> >
> > IIUC you would rather signal readiness via MGMT than mess with poll(),
> > via eg. new MGMT device flags bit + inform bluetoothd via change
> > event? Controller settings would also allow backward-compatibility
> > detection.
> >
> > So: Keep patch 1 + signal PAST readiness via device flags bit?
>
> But for 1 to happen after we change to wait for the MGMT event that
> would mean the peer had sent ahead of receiving BT_BASS_SYNC_INFO_RE?
> We shall be waiting already once the MGMT signal trigger and we
> respond with BT_BASS_SYNC_INFO_RE, perhaps Im not understanding the
> race here, is that still about HCI_EV_LE_PAST_RECEIVED so we only do
> the MGMT event while waiting it?
1. HCI_OP_LE_PAST_PARAMS Command Completed -> hci_sync continues
2. hci_sync signals userspace via MGMT
3.1. Userspace sees MGMT signal, sends SYNC_INFO_RE notify to remote
3.2. Remote RX notify, execute PAST
3.3. Kernel handles HCI_EV_LE_PAST_RECEIVED
3.4. Event handler checks hdev->req_skb -> hci_cmd_sync_complete()
If hdev->req_skb not set: no hci_sync wakeup
4.1. hci_sync enters __hci_cmd_sync_status_sk(hdev, HCI_OP_NOP, ...)
4.2. hci_req_sync_run sets bt_cb(skb)->hci.req_complete_skb =
hci_cmd_sync_complete, then queues hdev->cmd_wrk
4.3. cmd_work wakes up, hci_send_cmd_sync() sees HCI_OP_NOP and
only sets hdev->req_skb (= PAST Received wait starts)
AFAICS the branches 3 and 4 are technically not ordered, so you could
end up in 3.4 before 4.3 and miss hci_cmd_sync_complete() wakeup.
This race is of course originally spotted by LLM, but I don't currently
see why it would be impossible, except the required timing is probably
very unlikely in reality.
> > > > + if (past_pollout)
> > > > + dg->io_ready_id = g_io_add_watch(dg->io,
> > > > + G_IO_OUT | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
> > > > + pa_sync_ready_cb, dg);
> > > > + else
> > > > + bt_bass_set_pa_sync(dg->src, BT_BASS_SYNC_INFO_RE);
> > > > }
> > > >
> > > > static void bap_attached(struct bt_bap *bap, void *user_data)
> > > > @@ -1645,6 +1693,9 @@ static void delegator_free(struct bass_delegator *dg)
> > > > if (dg->io_id)
> > > > g_source_remove(dg->io_id);
> > > >
> > > > + if (dg->io_ready_id)
> > > > + g_source_remove(dg->io_ready_id);
> > > > +
> > > > if (dg->io) {
> > > > g_io_channel_shutdown(dg->io, TRUE, NULL);
> > > > g_io_channel_unref(dg->io);
> > > > @@ -1906,7 +1957,7 @@ static void bass_detached(struct bt_bass *bass, void *user_data)
> > > >
> > > > static struct bass_delegator *
> > > > bass_delegator_new(struct btd_device *device, struct bt_bcast_src *src,
> > > > - uint8_t sid)
> > > > + uint8_t sid, bool past)
> > > > {
> > > > struct bass_delegator *dg;
> > > >
> > > > @@ -1917,6 +1968,7 @@ bass_delegator_new(struct btd_device *device, struct bt_bcast_src *src,
> > > > dg->device = device;
> > > > dg->src = src;
> > > > dg->sid = sid;
> > > > + dg->past = past;
> > > > dg->bcode_reqs = queue_new();
> > > > dg->setups = queue_new();
> > > >
> > > > @@ -1945,6 +1997,7 @@ static int handle_add_src_req(struct bt_bcast_src *bcast_src,
> > > > struct btd_adapter *adapter = device_get_adapter(data->device);
> > > > struct btd_device *device;
> > > > struct bass_delegator *dg;
> > > > + bool past = false;
> > > >
> > > > /* Detect if PAST can be used then it can be used as destination since
> > > > * PAST Receiver uses the ACL connection itself.
> > > > @@ -1957,6 +2010,7 @@ static int handle_add_src_req(struct bt_bcast_src *bcast_src,
> > > > if (btd_adapter_has_settings(data->adapter,
> > > > MGMT_SETTING_PAST_RECEIVER)) {
> > > > device_set_past_support(data->device, true);
> > > > + past = true;
> > > > device = data->device;
> > > > goto done;
> > > > }
> > > > @@ -1980,16 +2034,14 @@ done:
> > > > * autonomously probed inside BAP.
> > > > */
> > > > if (!btd_device_get_service(device, BCAAS_UUID_STR)) {
> > > > - dg = bass_delegator_new(device, bcast_src, params->sid);
> > > > + dg = bass_delegator_new(device, bcast_src, params->sid, past);
> > > > if (!dg)
> > > > return -ENOMEM;
> > > > +
> > > > + if (past)
> > > > + return 0;
> > > > }
> > > >
> > > > - /* Set PA sync state, this has to be done after probing otherwise there
> > > > - * is a race where the remote may receive BT_BASS_SYNC_INFO_RE and start
> > > > - * PAST before the kernel has sent PAST Parameter command that enables
> > > > - * receiving it.
> > > > - */
> > > > if (params->pa_sync == PA_SYNC_PAST) {
> > > > if (btd_adapter_has_settings(data->adapter,
> > > > MGMT_SETTING_PAST_RECEIVER))
> > > > --
> > > > 2.56.0
> > > >
> > > >
> > >
> >
> > --
> > Pauli Virtanen
>
>
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH BlueZ 5/5] bass: wait for PAST listener readiness
2026-10-12 21:03 ` Pauli Virtanen
@ 2026-10-12 21:31 ` Luiz Augusto von Dentz
0 siblings, 0 replies; 12+ messages in thread
From: Luiz Augusto von Dentz @ 2026-10-12 21:31 UTC (permalink / raw)
To: Pauli Virtanen; +Cc: linux-bluetooth
Hi Pauli,
On Mon, Oct 12, 2026 at 5:03 PM Pauli Virtanen <pav@iki.fi> wrote:
>
> Hi Luiz,
>
> ma, 2026-10-12 kello 16:40 -0400, Luiz Augusto von Dentz kirjoitti:
> > Hi Pauli,
> >
> > On Mon, Oct 12, 2026 at 4:24 PM Pauli Virtanen <pav@iki.fi> wrote:
> > >
> > > Hi Luiz,
> > >
> > > ma, 2026-10-12 kello 15:58 -0400, Luiz Augusto von Dentz kirjoitti:
> > > > On Sun, Oct 11, 2026 at 4:42 PM Pauli Virtanen <pav@iki.fi> wrote:
> > > > >
> > > > > Defer SYNC_INFO_RE until the PA sync listener signals POLLOUT, so the
> > > > > Assistant cannot start PAST before receiver setup completes.
> > > > >
> > > > > Check kernel support via BT_ISO_PAST_POLLOUT, otherwise skip waiting.
> > > > >
> > > > > Fixes test_bass_past_transport_* test failures.
> > > > >
> > > > > Assisted-by: LLM
> > > > > ---
> > > > > profiles/audio/bass.c | 66 ++++++++++++++++++++++++++++++++++++++-----
> > > > > 1 file changed, 59 insertions(+), 7 deletions(-)
> > > > >
> > > > > diff --git a/profiles/audio/bass.c b/profiles/audio/bass.c
> > > > > index fd05195f3..90e758c08 100644
> > > > > --- a/profiles/audio/bass.c
> > > > > +++ b/profiles/audio/bass.c
> > > > > @@ -146,7 +146,9 @@ struct bass_delegator {
> > > > > struct queue *bcode_reqs;
> > > > > struct queue *setups;
> > > > > unsigned int io_id;
> > > > > + unsigned int io_ready_id;
> > > > > GIOChannel *io;
> > > > > + bool past;
> > > > > };
> > > > >
> > > > > struct bass_setup {
> > > > > @@ -751,6 +753,11 @@ static void confirm_cb(GIOChannel *io, void *user_data)
> > > > >
> > > > > DBG("");
> > > > >
> > > > > + if (dg->io_ready_id) {
> > > > > + g_source_remove(dg->io_ready_id);
> > > > > + dg->io_ready_id = 0;
> > > > > + }
> > > > > +
> > > > > /* Close the listen io */
> > > > > g_io_channel_shutdown(dg->io, TRUE, NULL);
> > > > > g_io_channel_unref(dg->io);
> > > > > @@ -1528,11 +1535,28 @@ static bool delegator_match_device(const void *data, const void *match_data)
> > > > > return dg->device == device;
> > > > > }
> > > > >
> > > > > +static gboolean pa_sync_ready_cb(GIOChannel *io, GIOCondition cond,
> > > > > + gpointer user_data)
> > > > > +{
> > > > > + struct bass_delegator *dg = user_data;
> > > > > +
> > > > > + dg->io_ready_id = 0;
> > > > > +
> > > > > + if (cond & (G_IO_ERR | G_IO_HUP | G_IO_NVAL))
> > > > > + return FALSE;
> > > > > +
> > > > > + bt_bass_set_pa_sync(dg->src, BT_BASS_SYNC_INFO_RE);
> > > > > +
> > > > > + return FALSE;
> > > > > +}
> > > > > +
> > > > > static void delegator_attach(struct bt_bap *bap, struct btd_device *device,
> > > > > struct btd_service *service)
> > > > > {
> > > > > struct bass_delegator *dg;
> > > > > GError *err = NULL;
> > > > > + socklen_t len;
> > > > > + int sk, past_pollout;
> > > > >
> > > > > dg = queue_find(delegators, delegator_match_device, device);
> > > > > if (!dg)
> > > > > @@ -1570,6 +1594,30 @@ static void delegator_attach(struct bt_bap *bap, struct btd_device *device,
> > > > >
> > > > > /* Take ownership for the service by setting the user data. */
> > > > > btd_service_set_user_data(service, dg);
> > > > > +
> > > > > + if (!dg->past)
> > > > > + return;
> > > > > +
> > > > > + sk = g_io_channel_unix_get_fd(dg->io);
> > > > > + len = sizeof(past_pollout);
> > > > > +
> > > > > + if (getsockopt(sk, SOL_BLUETOOTH, BT_ISO_PAST_POLLOUT,
> > > > > + &past_pollout, &len) < 0) {
> > > > > + if (errno != ENOPROTOOPT) {
> > > > > + error("Unable to query PAST readiness support: %s",
> > > > > + strerror(errno));
> > > > > + return;
> > > > > + }
> > > > > +
> > > > > + past_pollout = 0;
> > > > > + }
> > > >
> > > > While I agree it needs fixing since not only it can create problem for
> > > > testing but it may be possible to hit this over the air as well, the
> > > > solution though is a little bitter to my taste, perhaps we could use
> > > > to indicate when HCI_OP_LE_PAST_PARAMS has been completed (with
> > > > status=0) then we notify with flags changed. If that is still a little
> > > > too much perhaps we can issue right away when setting upon receiving
> > > > MGMT_OP_SET_DEVICE_FLAGS(HCI_CONN_FLAG_PAST) and somehow makeup for
> > > > the qos arguments if conn->iso_qos is not set yet, either way that
> > > > should gate the response of BT_BASS_SYNC_INFO_RE by indicating
> > > > HCI_CONN_FLAG_PAST is actually programmed in the controller via
> > > > HCI_OP_LE_PAST_PARAMS.
> > >
> > > Note the issue is not only waiting for HCI_OP_LE_PAST_PARAMS, but also
> > > closing the race window before hdev->req_skb gets assigned, otherwise
> > > it appears the hci_sync wait could miss early event and end up in
> > > create_pa_complete() with error.
> > >
> > > We could ignore this as too small window (-> drop patch 1), but I'm not
> > > certain it's impossible to hit, and LLMs will be complaining.
> > >
> > > IIUC you would rather signal readiness via MGMT than mess with poll(),
> > > via eg. new MGMT device flags bit + inform bluetoothd via change
> > > event? Controller settings would also allow backward-compatibility
> > > detection.
> > >
> > > So: Keep patch 1 + signal PAST readiness via device flags bit?
> >
> > But for 1 to happen after we change to wait for the MGMT event that
> > would mean the peer had sent ahead of receiving BT_BASS_SYNC_INFO_RE?
> > We shall be waiting already once the MGMT signal trigger and we
> > respond with BT_BASS_SYNC_INFO_RE, perhaps Im not understanding the
> > race here, is that still about HCI_EV_LE_PAST_RECEIVED so we only do
> > the MGMT event while waiting it?
>
> 1. HCI_OP_LE_PAST_PARAMS Command Completed -> hci_sync continues
> 2. hci_sync signals userspace via MGMT
>
> 3.1. Userspace sees MGMT signal, sends SYNC_INFO_RE notify to remote
> 3.2. Remote RX notify, execute PAST
> 3.3. Kernel handles HCI_EV_LE_PAST_RECEIVED
> 3.4. Event handler checks hdev->req_skb -> hci_cmd_sync_complete()
> If hdev->req_skb not set: no hci_sync wakeup
>
> 4.1. hci_sync enters __hci_cmd_sync_status_sk(hdev, HCI_OP_NOP, ...)
> 4.2. hci_req_sync_run sets bt_cb(skb)->hci.req_complete_skb =
> hci_cmd_sync_complete, then queues hdev->cmd_wrk
> 4.3. cmd_work wakes up, hci_send_cmd_sync() sees HCI_OP_NOP and
> only sets hdev->req_skb (= PAST Received wait starts)
>
> AFAICS the branches 3 and 4 are technically not ordered, so you could
> end up in 3.4 before 4.3 and miss hci_cmd_sync_complete() wakeup.
>
> This race is of course originally spotted by LLM, but I don't currently
> see why it would be impossible, except the required timing is probably
> very unlikely in reality.
I see; branch 3 requires some IO/response that branch 4 lacks, but I
see the point that the lack of ordering could theoretically cause
issues later, making it better to fix now. Patch 1 is not that small,
so it might be worth considering whether we should require the listen
to occur before notifying the MGMT API requiring a ISO socket
operation isn't the cleanest approach, but if we must enforce
ordering, I suppose that's the only way.
>
> > > > > + if (past_pollout)
> > > > > + dg->io_ready_id = g_io_add_watch(dg->io,
> > > > > + G_IO_OUT | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
> > > > > + pa_sync_ready_cb, dg);
> > > > > + else
> > > > > + bt_bass_set_pa_sync(dg->src, BT_BASS_SYNC_INFO_RE);
> > > > > }
> > > > >
> > > > > static void bap_attached(struct bt_bap *bap, void *user_data)
> > > > > @@ -1645,6 +1693,9 @@ static void delegator_free(struct bass_delegator *dg)
> > > > > if (dg->io_id)
> > > > > g_source_remove(dg->io_id);
> > > > >
> > > > > + if (dg->io_ready_id)
> > > > > + g_source_remove(dg->io_ready_id);
> > > > > +
> > > > > if (dg->io) {
> > > > > g_io_channel_shutdown(dg->io, TRUE, NULL);
> > > > > g_io_channel_unref(dg->io);
> > > > > @@ -1906,7 +1957,7 @@ static void bass_detached(struct bt_bass *bass, void *user_data)
> > > > >
> > > > > static struct bass_delegator *
> > > > > bass_delegator_new(struct btd_device *device, struct bt_bcast_src *src,
> > > > > - uint8_t sid)
> > > > > + uint8_t sid, bool past)
> > > > > {
> > > > > struct bass_delegator *dg;
> > > > >
> > > > > @@ -1917,6 +1968,7 @@ bass_delegator_new(struct btd_device *device, struct bt_bcast_src *src,
> > > > > dg->device = device;
> > > > > dg->src = src;
> > > > > dg->sid = sid;
> > > > > + dg->past = past;
> > > > > dg->bcode_reqs = queue_new();
> > > > > dg->setups = queue_new();
> > > > >
> > > > > @@ -1945,6 +1997,7 @@ static int handle_add_src_req(struct bt_bcast_src *bcast_src,
> > > > > struct btd_adapter *adapter = device_get_adapter(data->device);
> > > > > struct btd_device *device;
> > > > > struct bass_delegator *dg;
> > > > > + bool past = false;
> > > > >
> > > > > /* Detect if PAST can be used then it can be used as destination since
> > > > > * PAST Receiver uses the ACL connection itself.
> > > > > @@ -1957,6 +2010,7 @@ static int handle_add_src_req(struct bt_bcast_src *bcast_src,
> > > > > if (btd_adapter_has_settings(data->adapter,
> > > > > MGMT_SETTING_PAST_RECEIVER)) {
> > > > > device_set_past_support(data->device, true);
> > > > > + past = true;
> > > > > device = data->device;
> > > > > goto done;
> > > > > }
> > > > > @@ -1980,16 +2034,14 @@ done:
> > > > > * autonomously probed inside BAP.
> > > > > */
> > > > > if (!btd_device_get_service(device, BCAAS_UUID_STR)) {
> > > > > - dg = bass_delegator_new(device, bcast_src, params->sid);
> > > > > + dg = bass_delegator_new(device, bcast_src, params->sid, past);
> > > > > if (!dg)
> > > > > return -ENOMEM;
> > > > > +
> > > > > + if (past)
> > > > > + return 0;
> > > > > }
> > > > >
> > > > > - /* Set PA sync state, this has to be done after probing otherwise there
> > > > > - * is a race where the remote may receive BT_BASS_SYNC_INFO_RE and start
> > > > > - * PAST before the kernel has sent PAST Parameter command that enables
> > > > > - * receiving it.
> > > > > - */
> > > > > if (params->pa_sync == PA_SYNC_PAST) {
> > > > > if (btd_adapter_has_settings(data->adapter,
> > > > > MGMT_SETTING_PAST_RECEIVER))
> > > > > --
> > > > > 2.56.0
> > > > >
> > > > >
> > > >
> > >
> > > --
> > > Pauli Virtanen
> >
> >
--
Luiz Augusto von Dentz
^ permalink raw reply [flat|nested] 12+ messages in thread
end of thread, other threads:[~2026-10-12 21:31 UTC | newest]
Thread overview: 12+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-11 20:40 [PATCH BlueZ 0/5] bass: wait for PAST listener readiness Pauli Virtanen
2026-10-11 20:40 ` [PATCH BlueZ 1/5] iso-tester: remove IO sources during teardown Pauli Virtanen
2026-10-11 23:54 ` bass: wait for PAST listener readiness bluez.test.bot
2026-10-11 20:40 ` [PATCH BlueZ 2/5] lib: add BT_ISO_PAST_POLLOUT socket option Pauli Virtanen
2026-10-11 20:40 ` [PATCH BlueZ 3/5] tools: iso-tester: test PAST listener readiness Pauli Virtanen
2026-10-11 20:40 ` [PATCH BlueZ 4/5] doc: iso: document ISO " Pauli Virtanen
2026-10-11 20:40 ` [PATCH BlueZ 5/5] bass: wait for " Pauli Virtanen
2026-10-12 19:58 ` Luiz Augusto von Dentz
2026-10-12 20:24 ` Pauli Virtanen
2026-10-12 20:40 ` Luiz Augusto von Dentz
2026-10-12 21:03 ` Pauli Virtanen
2026-10-12 21:31 ` Luiz Augusto von Dentz
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.