* [PATCH BlueZ v2 3/5] iso-tester: Add tests for checking proper handling of Sync Lost
2025-11-07 18:54 [PATCH BlueZ v2 1/5] emulator: Generate PA Sync Lost Luiz Augusto von Dentz
2025-11-07 18:54 ` [PATCH BlueZ v2 2/5] bthost: Add support for terminating a BIG Luiz Augusto von Dentz
@ 2025-11-07 18:54 ` Luiz Augusto von Dentz
2025-11-07 18:54 ` [PATCH BlueZ v2 4/5] bass: Fix not cleaning up delegator properly Luiz Augusto von Dentz
` (2 subsequent siblings)
4 siblings, 0 replies; 6+ messages in thread
From: Luiz Augusto von Dentz @ 2025-11-07 18:54 UTC (permalink / raw)
To: linux-bluetooth
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
This introduces the following tests to check if BIG/PA Sync Lost are
handled properly:
ISO Broadcaster Receiver Sync Lost - Success
ISO Broadcaster PAST Receiver Sync Lost - Success
---
tools/iso-tester.c | 49 ++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 49 insertions(+)
diff --git a/tools/iso-tester.c b/tools/iso-tester.c
index 4bf1a287bc68..ff5c85ae410c 100644
--- a/tools/iso-tester.c
+++ b/tools/iso-tester.c
@@ -502,6 +502,7 @@ struct iso_client_data {
bool listen_bind;
bool pa_bind;
bool big;
+ bool terminate;
/* Enable BT_PKT_SEQNUM for RX packet sequence numbers */
bool pkt_seqnum;
@@ -1578,6 +1579,27 @@ static const struct iso_client_data bcast_16_2_1_recv_defer_get_base = {
.base_len = sizeof(base_lc3_ac_12),
};
+static const struct iso_client_data bcast_16_2_1_recv_terminate = {
+ .qos = QOS_IN_16_2_1,
+ .expect_err = 0,
+ .recv = &send_16_2_1,
+ .bcast = true,
+ .server = true,
+ .big = true,
+ .terminate = true,
+};
+
+static const struct iso_client_data past_16_2_1_recv_terminate = {
+ .qos = QOS_IN_16_2_1,
+ .expect_err = 0,
+ .recv = &send_16_2_1,
+ .bcast = true,
+ .past = true,
+ .server = true,
+ .big = true,
+ .terminate = true,
+};
+
static const struct iso_client_data bcast_ac_12 = {
.qos = BCAST_AC_12,
.expect_err = 0,
@@ -2389,6 +2411,21 @@ static void iso_shutdown(struct test_data *data, GIOChannel *io)
tester_print("Disconnecting...");
}
+static void iso_terminate(struct test_data *data, GIOChannel *io)
+{
+ struct bthost *host;
+
+ /* Setup watcher to check if fd is closed properly after termination */
+ data->io_id[0] = g_io_add_watch(io, G_IO_HUP, iso_disconnected, data);
+
+ tester_print("Terminating...");
+
+ host = hciemu_client_get_host(data->hciemu);
+
+ bthost_set_pa_enable(host, 0x00);
+ bthost_terminate_big(host, BT_HCI_ERR_LOCAL_HOST_TERM);
+}
+
static gboolean iso_recv_data(GIOChannel *io, GIOCondition cond,
gpointer user_data)
{
@@ -2498,6 +2535,8 @@ static gboolean iso_recv_data(GIOChannel *io, GIOCondition cond,
return TRUE;
else if (isodata->disconnect)
iso_shutdown(data, io);
+ else if (isodata->terminate)
+ iso_terminate(data, io);
else
tester_test_passed();
@@ -4311,6 +4350,16 @@ int main(int argc, char *argv[])
setup_powered,
test_bcast_recv);
+ test_iso("ISO Broadcaster Receiver Sync Lost - Success",
+ &bcast_16_2_1_recv_terminate,
+ setup_powered,
+ test_bcast_recv);
+
+ test_iso("ISO Broadcaster PAST Receiver Sync Lost - Success",
+ &past_16_2_1_recv_terminate,
+ setup_powered,
+ test_bcast_recv);
+
test_iso("ISO Broadcaster AC 12 - Success", &bcast_ac_12, setup_powered,
test_bcast);
--
2.51.1
^ permalink raw reply related [flat|nested] 6+ messages in thread* [PATCH BlueZ v2 4/5] bass: Fix not cleaning up delegator properly
2025-11-07 18:54 [PATCH BlueZ v2 1/5] emulator: Generate PA Sync Lost Luiz Augusto von Dentz
2025-11-07 18:54 ` [PATCH BlueZ v2 2/5] bthost: Add support for terminating a BIG Luiz Augusto von Dentz
2025-11-07 18:54 ` [PATCH BlueZ v2 3/5] iso-tester: Add tests for checking proper handling of Sync Lost Luiz Augusto von Dentz
@ 2025-11-07 18:54 ` Luiz Augusto von Dentz
2025-11-07 18:54 ` [PATCH BlueZ v2 5/5] btio: Fix endless loop if accept return -EBADFD Luiz Augusto von Dentz
2025-11-07 20:16 ` [BlueZ,v2,1/5] emulator: Generate PA Sync Lost bluez.test.bot
4 siblings, 0 replies; 6+ messages in thread
From: Luiz Augusto von Dentz @ 2025-11-07 18:54 UTC (permalink / raw)
To: linux-bluetooth
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
When BIG sync is lost, or the assistant modify removing all streams,
delegator should be freed to so the assistant can start over and share
another stream.
---
profiles/audio/bap.c | 27 +++---
profiles/audio/bass.c | 208 ++++++++++++++++++++++--------------------
2 files changed, 126 insertions(+), 109 deletions(-)
diff --git a/profiles/audio/bap.c b/profiles/audio/bap.c
index c10f019edfea..0883f6c47902 100644
--- a/profiles/audio/bap.c
+++ b/profiles/audio/bap.c
@@ -3270,14 +3270,6 @@ static void pac_removed_broadcast(struct bt_bap_pac *pac, void *user_data)
ep_unregister(ep);
}
-static bool match_device(const void *data, const void *match_data)
-{
- const struct bap_data *bdata = data;
- const struct btd_device *device = match_data;
-
- return bdata->device == device;
-}
-
static struct bap_data *bap_data_new(struct btd_device *device)
{
struct bap_data *data;
@@ -3673,6 +3665,14 @@ static int bap_bcast_probe(struct btd_service *service)
return 0;
}
+static bool match_service(const void *data, const void *match_data)
+{
+ const struct bap_data *bdata = data;
+ const struct btd_service *service = match_data;
+
+ return bdata->service == service;
+}
+
static void bap_bcast_remove(struct btd_service *service)
{
struct btd_device *device = btd_service_get_device(service);
@@ -3682,7 +3682,10 @@ static void bap_bcast_remove(struct btd_service *service)
ba2str(device_get_address(device), addr);
DBG("%s", addr);
- data = queue_find(sessions, match_device, device);
+ /* Lookup the bap session for this service since in case of
+ * bass_delegator its user data is set by bass plugin.
+ */
+ data = queue_find(sessions, match_service, service);
if (!data) {
error("BAP service not handled by profile");
return;
@@ -3791,10 +3794,12 @@ static int bap_disconnect(struct btd_service *service)
static int bap_bcast_disconnect(struct btd_service *service)
{
- struct btd_device *device = btd_service_get_device(service);
struct bap_data *data;
- data = queue_find(sessions, match_device, device);
+ /* Lookup the bap session for this service since in case of
+ * bass_delegator its user data is set by bass plugin.
+ */
+ data = queue_find(sessions, match_service, service);
if (!data) {
error("BAP service not handled by profile");
return -EINVAL;
diff --git a/profiles/audio/bass.c b/profiles/audio/bass.c
index 0ba29f939f61..9ace372376f9 100644
--- a/profiles/audio/bass.c
+++ b/profiles/audio/bass.c
@@ -269,14 +269,6 @@ static void bass_req_bcode(struct bt_bap_stream *stream,
dg->timeout = g_timeout_add_seconds(10, req_timeout, dg);
}
-static bool delegator_match_device(const void *data, const void *match_data)
-{
- const struct bass_delegator *dg = data;
- const struct btd_device *device = match_data;
-
- return dg->device == device;
-}
-
static int stream_get_bis(struct bt_bap_stream *stream)
{
char *path = bt_bap_stream_get_user_data(stream);
@@ -366,6 +358,33 @@ static void setup_free(void *data)
free(setup);
}
+static void delegator_disconnect(struct bass_delegator *dg)
+{
+ struct btd_device *device = dg->device;
+ struct btd_service *service = dg->service;
+
+ DBG("%p", dg);
+
+ /* Disconnect service so BAP driver is cleanup properly and bt_bap is
+ * detached from the device.
+ */
+ btd_service_disconnect(service);
+
+ /* Remove service since delegator shold have been freed at this point */
+ device_remove_profile(device, btd_service_get_profile(service));
+
+ /* If the device is no longer consider connected it means no other
+ * service was connected so it has no longer any use and can be safely
+ * removed.
+ */
+ if (!btd_device_is_connected(device)) {
+ struct btd_adapter *adapter;
+
+ adapter = device_get_adapter(device);
+ btd_adapter_remove_device(adapter, device);
+ }
+}
+
static void bap_state_changed(struct bt_bap_stream *stream, uint8_t old_state,
uint8_t new_state, void *user_data)
{
@@ -459,6 +478,8 @@ static void bap_state_changed(struct bt_bap_stream *stream, uint8_t old_state,
setup->stream = NULL;
queue_remove(setup->dg->setups, setup);
setup_free(setup);
+ if (queue_isempty(dg->setups))
+ delegator_disconnect(dg);
break;
}
}
@@ -1296,15 +1317,65 @@ static void bap_bc_attached(struct bt_bap *bap, void *user_data)
bass_data_add(data);
}
+static bool delegator_match_device(const void *data, const void *match_data)
+{
+ const struct bass_delegator *dg = data;
+ const struct btd_device *device = match_data;
+
+ return dg->device == device;
+}
+
+static void delegator_attach(struct bt_bap *bap, struct btd_device *device,
+ struct btd_service *service)
+{
+ struct bass_delegator *dg;
+ GError *err = NULL;
+
+ dg = queue_find(delegators, delegator_match_device, device);
+ if (!dg)
+ /* Only probe devices added via Broadcast Assistants */
+ return;
+
+ DBG("delegator %p", dg);
+
+ if (dg->service)
+ /* Service has already been probed */
+ return;
+
+ dg->service = service;
+ dg->bap = bap;
+
+ dg->io = bt_io_listen(NULL, confirm_cb, dg,
+ NULL, &err,
+ BT_IO_OPT_SOURCE_BDADDR,
+ btd_adapter_get_address(device_get_adapter(device)),
+ BT_IO_OPT_SOURCE_TYPE,
+ btd_adapter_get_address_type(device_get_adapter(device)),
+ BT_IO_OPT_DEST_BDADDR,
+ device_get_address(device),
+ BT_IO_OPT_DEST_TYPE,
+ btd_device_get_bdaddr_type(device),
+ BT_IO_OPT_MODE, BT_IO_MODE_ISO,
+ BT_IO_OPT_QOS, &bap_sink_pa_qos,
+ BT_IO_OPT_ISO_BC_SID, dg->sid,
+ BT_IO_OPT_INVALID);
+ if (!dg->io) {
+ error("%s", err->message);
+ g_error_free(err);
+ return;
+ }
+
+ /* Take ownership for the service by setting the user data. */
+ btd_service_set_user_data(service, dg);
+}
+
static void bap_attached(struct bt_bap *bap, void *user_data)
{
struct btd_service *service;
struct btd_profile *p;
struct btd_device *device;
struct btd_adapter *adapter;
- struct bass_delegator *dg;
struct bass_data *data;
- GError *err = NULL;
service = bt_bap_get_user_data(bap);
if (!service)
@@ -1330,40 +1401,7 @@ static void bap_attached(struct bt_bap *bap, void *user_data)
bass_data_add(data);
- dg = queue_find(delegators, delegator_match_device, device);
- if (!dg)
- /* Only probe devices added via Broadcast Assistants */
- return;
-
- if (dg->service)
- /* Service has already been probed */
- return;
-
- dg->service = service;
- dg->bap = bap;
-
- dg->io = bt_io_listen(NULL, confirm_cb, dg,
- NULL, &err,
- BT_IO_OPT_SOURCE_BDADDR,
- btd_adapter_get_address(adapter),
- BT_IO_OPT_SOURCE_TYPE,
- btd_adapter_get_address_type(adapter),
- BT_IO_OPT_DEST_BDADDR,
- device_get_address(device),
- BT_IO_OPT_DEST_TYPE,
- btd_device_get_bdaddr_type(device),
- BT_IO_OPT_MODE, BT_IO_MODE_ISO,
- BT_IO_OPT_QOS, &bap_sink_pa_qos,
- BT_IO_OPT_ISO_BC_SID, dg->sid,
- BT_IO_OPT_INVALID);
- if (!dg->io) {
- error("%s", err->message);
- g_error_free(err);
- return;
- }
-
- /* Take ownership for the service by setting the user data. */
- btd_service_set_user_data(service, dg);
+ delegator_attach(bap, device, service);
}
static bool match_bap(const void *data, const void *match_data)
@@ -1417,12 +1455,35 @@ static void delegator_free(struct bass_delegator *dg)
free(dg);
}
+static bool match_service(const void *data, const void *match_data)
+{
+ const struct bass_data *bdata = data;
+ const struct btd_service *service = match_data;
+
+ return bdata->service == service;
+}
+
+static void delegator_detach(struct btd_service *service)
+{
+ struct bass_delegator *dg;
+
+ dg = btd_service_get_user_data(service);
+ if (!dg)
+ return;
+
+ if (!queue_remove(delegators, dg))
+ return;
+
+ DBG("%p", dg);
+
+ delegator_free(dg);
+
+ btd_service_set_user_data(service, NULL);
+}
+
static void bap_detached(struct bt_bap *bap, void *user_data)
{
struct btd_service *service;
- struct btd_profile *p;
- struct btd_device *device;
- struct bass_delegator *dg;
struct bass_data *data;
data = queue_find(sessions, match_bap, bap);
@@ -1435,31 +1496,15 @@ static void bap_detached(struct bt_bap *bap, void *user_data)
if (!service)
return;
- p = btd_service_get_profile(service);
- if (!p)
- return;
-
- /* Only handle sessions with Broadcast Sources */
- if (!g_str_equal(p->remote_uuid, BCAAS_UUID_STR))
- return;
-
- device = btd_service_get_device(service);
-
/* Remove BASS session with the Broadcast Source device */
- data = queue_find(sessions, match_device, device);
+ data = queue_find(sessions, match_service, service);
if (data) {
bt_bap_bis_cb_unregister(bap, data->bis_id);
bt_bap_state_unregister(bap, data->state_id);
bass_data_remove(data);
}
- dg = queue_remove_if(delegators, delegator_match_device, device);
- if (!dg)
- return;
-
- delegator_free(dg);
-
- btd_service_set_user_data(service, NULL);
+ delegator_detach(service);
}
static void bis_probe(uint8_t sid, uint8_t bis, uint8_t sgrp,
@@ -1807,39 +1852,6 @@ static int handle_mod_src_req(struct bt_bcast_src *bcast_src,
switch (sync_state) {
case BT_BASS_SYNCHRONIZED_TO_PA:
bass_update_bis_sync(dg, bcast_src);
-
- /* Check if there are any setups left since it means the PA
- * should be no longer synchronized.
- */
- if (queue_isempty(dg->setups)) {
- /* IO is no longer needed since there are no setups */
- g_io_channel_shutdown(dg->io, TRUE, NULL);
- g_io_channel_unref(dg->io);
- dg->io = NULL;
-
- bt_bass_set_pa_sync(dg->src,
- BT_BASS_NOT_SYNCHRONIZED_TO_PA);
-
- if (!dg->service)
- return 0;
-
- /* Disconnect service so BAP driver is cleanup
- * properly.
- */
- btd_service_disconnect(dg->service);
-
- /* If the device is no longer consider connected
- * it means no other service was connected so it
- * has no longer any use and can be safely removed.
- */
- if (!btd_device_is_connected(dg->device)) {
- struct btd_adapter *adapter;
-
- adapter = device_get_adapter(dg->device);
- btd_adapter_remove_device(adapter, dg->device);
- }
- }
-
break;
case BT_BASS_NOT_SYNCHRONIZED_TO_PA:
if (params->pa_sync == PA_SYNC_NO_PAST) {
--
2.51.1
^ permalink raw reply related [flat|nested] 6+ messages in thread