From 786dc1cf04c9ee52afdeb644405e13b3fc1cecfd Mon Sep 17 00:00:00 2001 From: Emil Gydesen Date: Fri, 13 Jun 2025 15:22:33 +0200 Subject: [PATCH 1/4] Bluetooth: BAP: BA: Refactor discover to not do read Refactor the bt_bap_broadcast_assistant_discover function to not read receives at the end of discovery. This makes the function more true to what it is supposed to do, and significantly reduces the complexity of the procedure and the read callback. Users will be required to, if wanted, to read the receive state themselves with the existing bt_bap_broadcast_assistant_read_recv_state. The start_offset had to be modified to avoid issues with PA sync. Signed-off-by: Emil Gydesen --- doc/releases/migration-guide-4.3.rst | 5 +- .../bluetooth/audio/bap_broadcast_assistant.c | 66 ++++--------------- .../test_scripts/bap_bass_client_sync.sh | 2 +- 3 files changed, 16 insertions(+), 57 deletions(-) diff --git a/doc/releases/migration-guide-4.3.rst b/doc/releases/migration-guide-4.3.rst index daa9bab05538c..6a834a124d9be 100644 --- a/doc/releases/migration-guide-4.3.rst +++ b/doc/releases/migration-guide-4.3.rst @@ -173,7 +173,10 @@ Bluetooth Audio BAP Scan Delegator is used together with the BAP Broadcast Sink, then the PA state of the receive state of a :c:struct:`bt_bap_broadcast_sink` will still be automatically updated when the PA state changes. (:github:`95453`) - +* :c:func:`bt_bap_broadcast_assistant_discover` will now no longer perform reads of the remote BASS + receive states at the end of the procedure. Users will have to manually call + :c:func:`bt_bap_broadcast_assistant_read_recv_state` to read the existing receive states, if any, + prior to performing any operations. (:github:`91587``) .. zephyr-keep-sorted-stop diff --git a/subsys/bluetooth/audio/bap_broadcast_assistant.c b/subsys/bluetooth/audio/bap_broadcast_assistant.c index 77ead34fef688..9494ce3ec3608 100644 --- a/subsys/bluetooth/audio/bap_broadcast_assistant.c +++ b/subsys/bluetooth/audio/bap_broadcast_assistant.c @@ -2,7 +2,7 @@ /* * Copyright (c) 2019 Bose Corporation - * Copyright (c) 2022-2023 Nordic Semiconductor ASA + * Copyright (c) 2022-2025 Nordic Semiconductor ASA * Copyright (c) 2024 Demant A/S * * SPDX-License-Identifier: Apache-2.0 @@ -565,20 +565,15 @@ static uint8_t read_recv_state_cb(struct bt_conn *conn, uint8_t err, const void *data, uint16_t length) { struct bap_broadcast_assistant_instance *inst = inst_by_conn(conn); + bool active_recv_state = data != NULL && length != 0; + struct bt_bap_scan_delegator_recv_state recv_state; + uint16_t handle = params->single.handle; + int cb_err = err; if (inst == NULL) { return BT_GATT_ITER_STOP; } - uint16_t handle = params->single.handle; - uint8_t last_handle_index = inst->recv_state_cnt - 1; - uint16_t last_handle = inst->recv_state_handles[last_handle_index]; - struct bt_bap_scan_delegator_recv_state recv_state; - int cb_err = err; - bool active_recv_state = data != NULL && length != 0; - - /* TODO: Split discovery and receive state characteristic read */ - (void)memset(params, 0, sizeof(*params)); LOG_DBG("%s receive state", active_recv_state ? "Active " : "Inactive"); @@ -611,48 +606,12 @@ static uint8_t read_recv_state_cb(struct bt_conn *conn, uint8_t err, if (cb_err != 0) { LOG_DBG("err %d", cb_err); - - if (atomic_test_bit(inst->flags, BAP_BA_FLAG_DISCOVER_IN_PROGRESS)) { - bap_broadcast_assistant_discover_complete(conn, cb_err, 0); - } else { - atomic_clear_bit(inst->flags, BAP_BA_FLAG_BUSY); - bap_broadcast_assistant_recv_state_changed(conn, cb_err, NULL); - } - } else if (handle == last_handle) { - if (atomic_test_bit(inst->flags, BAP_BA_FLAG_DISCOVER_IN_PROGRESS)) { - const uint8_t recv_state_cnt = inst->recv_state_cnt; - - bap_broadcast_assistant_discover_complete(conn, cb_err, recv_state_cnt); - } else { - atomic_clear_bit(inst->flags, BAP_BA_FLAG_BUSY); - bap_broadcast_assistant_recv_state_changed(conn, cb_err, - active_recv_state ? - &recv_state : NULL); - } + atomic_clear_bit(inst->flags, BAP_BA_FLAG_BUSY); + bap_broadcast_assistant_recv_state_changed(conn, cb_err, NULL); } else { - for (uint8_t i = 0U; i < inst->recv_state_cnt; i++) { - if (handle != inst->recv_state_handles[i]) { - continue; - } - - if (i + 1 < ARRAY_SIZE(inst->recv_state_handles)) { - cb_err = read_recv_state(inst, i + 1); - if (cb_err != 0) { - LOG_DBG("Failed to read receive state: %d", cb_err); - - if (atomic_test_bit(inst->flags, - BAP_BA_FLAG_DISCOVER_IN_PROGRESS)) { - bap_broadcast_assistant_discover_complete( - conn, cb_err, 0); - } else { - atomic_clear_bit(inst->flags, BAP_BA_FLAG_BUSY); - bap_broadcast_assistant_recv_state_changed( - conn, cb_err, NULL); - } - } - } - break; - } + atomic_clear_bit(inst->flags, BAP_BA_FLAG_BUSY); + bap_broadcast_assistant_recv_state_changed(conn, cb_err, + active_recv_state ? &recv_state : NULL); } return BT_GATT_ITER_STOP; @@ -686,10 +645,7 @@ static uint8_t char_discover_func(struct bt_conn *conn, LOG_DBG("Found %u BASS receive states", inst->recv_state_cnt); (void)memset(params, 0, sizeof(*params)); - err = read_recv_state(inst, 0); - if (err != 0) { - bap_broadcast_assistant_discover_complete(conn, err, 0); - } + bap_broadcast_assistant_discover_complete(conn, 0, inst->recv_state_cnt); return BT_GATT_ITER_STOP; } diff --git a/tests/bsim/bluetooth/audio/test_scripts/bap_bass_client_sync.sh b/tests/bsim/bluetooth/audio/test_scripts/bap_bass_client_sync.sh index 6c3d71511d887..c9d71b0d99500 100755 --- a/tests/bsim/bluetooth/audio/test_scripts/bap_bass_client_sync.sh +++ b/tests/bsim/bluetooth/audio/test_scripts/bap_bass_client_sync.sh @@ -25,7 +25,7 @@ Execute ./bs_${BOARD_TS}_tests_bsim_bluetooth_audio_prj_conf \ Execute ./bs_${BOARD_TS}_tests_bsim_bluetooth_audio_prj_conf \ -v=${VERBOSITY_LEVEL} -s=${SIMULATION_ID} -d=2 -testid=bass_broadcaster \ - -RealEncryption=1 -rs=69 -D=3 -start_offset=2e3 + -RealEncryption=1 -rs=69 -D=3 -start_offset=4e3 # Simulation time should be larger than the WAIT_TIME in common.h Execute ./bs_2G4_phy_v1 -v=${VERBOSITY_LEVEL} -s=${SIMULATION_ID} -D=3 \ From 80b4978e9aac707051c4cedb850c1b300c0eddc9 Mon Sep 17 00:00:00 2001 From: Emil Gydesen Date: Sat, 16 Aug 2025 12:34:29 +0200 Subject: [PATCH 2/4] samples: Bluetooth: BA: Read receive states on connection Once the BASS has been discovered, the sample will now attempt to read all the receive states on the remote device. The receive states will only be logged, and the sample will not (yet) perform any logic on them (to e.g. determiner whether it needs to perform a remove source operation before the add source, or perform a modify source). That behavior is postponed to a future commit, as the purpose of this is to showcase reading the receive states. Signed-off-by: Emil Gydesen --- .../bap_broadcast_assistant/src/main.c | 64 +++++++++++++++++++ .../test_scripts/bap_bass_client_sync.sh | 2 +- 2 files changed, 65 insertions(+), 1 deletion(-) diff --git a/samples/bluetooth/bap_broadcast_assistant/src/main.c b/samples/bluetooth/bap_broadcast_assistant/src/main.c index 6458bfe60fe3b..3f9420f426cf3 100644 --- a/samples/bluetooth/bap_broadcast_assistant/src/main.c +++ b/samples/bluetooth/bap_broadcast_assistant/src/main.c @@ -52,6 +52,7 @@ struct scan_recv_info { }; static struct bt_conn *broadcast_sink_conn; +static uint8_t remote_recv_state_count; static uint32_t selected_broadcast_id; static uint8_t selected_sid; static uint16_t selected_pa_interval; @@ -71,6 +72,7 @@ static K_SEM_DEFINE(sem_sink_connected, 0, 1); static K_SEM_DEFINE(sem_sink_disconnected, 0, 1); static K_SEM_DEFINE(sem_security_updated, 0, 1); static K_SEM_DEFINE(sem_bass_discovered, 0, 1); +static K_SEM_DEFINE(sem_recv_state_read, 0, 1); static K_SEM_DEFINE(sem_pa_synced, 0, 1); static K_SEM_DEFINE(sem_pa_sync_terminted, 0, 1); static K_SEM_DEFINE(sem_received_base_subgroups, 0, 1); @@ -511,6 +513,7 @@ static void bap_broadcast_assistant_discover_cb(struct bt_conn *conn, int err, if (err == 0) { printk("BASS discover done with %u recv states\n", recv_state_count); + remote_recv_state_count = recv_state_count; k_sem_give(&sem_bass_discovered); } else { printk("BASS discover failed (%d)\n", err); @@ -526,6 +529,35 @@ static void bap_broadcast_assistant_add_src_cb(struct bt_conn *conn, int err) } } +static void +bap_broadcast_assistant_recv_state_read_cb(struct bt_conn *conn, int err, + const struct bt_bap_scan_delegator_recv_state *state) +{ + if (err != 0) { + printk("BASS recv state read failed (%d)\n", err); + return; + } + + if (state != NULL) { + char le_addr[BT_ADDR_LE_STR_LEN]; + + bt_addr_le_to_str(&state->addr, le_addr, sizeof(le_addr)); + printk("BASS recv state: src_id %u, addr %s, sid %u, sync_state %u, encrypt_state " + "%u, num_subgroups %u\n", + state->src_id, le_addr, state->adv_sid, state->pa_sync_state, + state->encrypt_state, state->num_subgroups); + + for (uint8_t i = 0; i < state->num_subgroups; i++) { + const struct bt_bap_bass_subgroup *subgroup = &state->subgroups[i]; + + printk("\t[%d]: BIS sync %u, metadata_len %u\n", i, subgroup->bis_sync, + subgroup->metadata_len); + } + } /* else empty receive state */ + + k_sem_give(&sem_recv_state_read); +} + static void pa_sync_synced_cb(struct bt_le_per_adv_sync *sync, struct bt_le_per_adv_sync_synced_info *info) { @@ -550,6 +582,7 @@ static void pa_sync_term_cb(struct bt_le_per_adv_sync *sync, static struct bt_bap_broadcast_assistant_cb ba_cbs = { .discover = bap_broadcast_assistant_discover_cb, .add_src = bap_broadcast_assistant_add_src_cb, + .recv_state = bap_broadcast_assistant_recv_state_read_cb, }; static struct bt_le_per_adv_sync_cb pa_synced_cb = { @@ -618,6 +651,31 @@ BT_CONN_CB_DEFINE(conn_callbacks) = { .security_changed = security_changed_cb }; +static int read_recv_states(void) +{ + /* Attempts to read all found receive states - Some or all may be empty */ + for (uint8_t i = 0U; i < remote_recv_state_count; i++) { + int err; + + err = bt_bap_broadcast_assistant_read_recv_state(broadcast_sink_conn, i); + + if (err != 0) { + printk("Failed to read receive state[%u]: %d\n", i, err); + + return err; + } + + err = k_sem_take(&sem_recv_state_read, SEM_TIMEOUT); + if (err != 0) { + printk("Failed to take sem_recv_state_read: %d\n", err); + + return err; + } + } + + return 0; +} + int main(void) { int err; @@ -667,6 +725,12 @@ int main(void) continue; } + err = read_recv_states(); + if (err != 0) { + printk("Failed to read receive states\n"); + continue; + } + /* TODO: Discover and parse the PACS on the sink and use the information * when discovering and adding a source to the sink. * Also, before populating the parameters to sync to the broadcast source diff --git a/tests/bsim/bluetooth/audio/test_scripts/bap_bass_client_sync.sh b/tests/bsim/bluetooth/audio/test_scripts/bap_bass_client_sync.sh index c9d71b0d99500..76744e5a54ec6 100755 --- a/tests/bsim/bluetooth/audio/test_scripts/bap_bass_client_sync.sh +++ b/tests/bsim/bluetooth/audio/test_scripts/bap_bass_client_sync.sh @@ -25,7 +25,7 @@ Execute ./bs_${BOARD_TS}_tests_bsim_bluetooth_audio_prj_conf \ Execute ./bs_${BOARD_TS}_tests_bsim_bluetooth_audio_prj_conf \ -v=${VERBOSITY_LEVEL} -s=${SIMULATION_ID} -d=2 -testid=bass_broadcaster \ - -RealEncryption=1 -rs=69 -D=3 -start_offset=4e3 + -RealEncryption=1 -rs=79 -D=3 -start_offset=4e3 # Simulation time should be larger than the WAIT_TIME in common.h Execute ./bs_2G4_phy_v1 -v=${VERBOSITY_LEVEL} -s=${SIMULATION_ID} -D=3 \ From 6d5419f170b89d5bd9309591c8a8389bca395b95 Mon Sep 17 00:00:00 2001 From: Emil Gydesen Date: Mon, 8 Sep 2025 10:57:46 +0200 Subject: [PATCH 3/4] tests: Bluetooth: BAP: BSIM: SD/BA: Fix/add some print statements Modify and add some printk statements for the tests, as some were missing the newline. Signed-off-by: Emil Gydesen --- .../audio/src/bap_broadcast_assistant_test.c | 1 + .../audio/src/bap_scan_delegator_test.c | 18 +++++++++--------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/tests/bsim/bluetooth/audio/src/bap_broadcast_assistant_test.c b/tests/bsim/bluetooth/audio/src/bap_broadcast_assistant_test.c index 5d7d3ab7db8e9..d9effeb9f6e52 100644 --- a/tests/bsim/bluetooth/audio/src/bap_broadcast_assistant_test.c +++ b/tests/bsim/bluetooth/audio/src/bap_broadcast_assistant_test.c @@ -179,6 +179,7 @@ static void bap_broadcast_assistant_recv_state_cb( #if defined(CONFIG_BT_PER_ADV_SYNC_TRANSFER_SENDER) if (state->pa_sync_state == BT_BAP_PA_STATE_INFO_REQ) { + printk("Sending PAST %p to %p\n", g_pa_sync, conn); err = bt_le_per_adv_sync_transfer(g_pa_sync, conn, BT_UUID_BASS_VAL); if (err != 0) { diff --git a/tests/bsim/bluetooth/audio/src/bap_scan_delegator_test.c b/tests/bsim/bluetooth/audio/src/bap_scan_delegator_test.c index ca0e38ec600fc..855154b2b0aae 100644 --- a/tests/bsim/bluetooth/audio/src/bap_scan_delegator_test.c +++ b/tests/bsim/bluetooth/audio/src/bap_scan_delegator_test.c @@ -152,11 +152,11 @@ static int pa_sync_past(struct bt_conn *conn, param.skip = PA_SYNC_SKIP; param.timeout = interval_to_sync_timeout(pa_interval); + printk("Subscribing to PAST from %p\n", conn); err = bt_le_per_adv_sync_transfer_subscribe(conn, ¶m); if (err != 0) { printk("Could not do PAST subscribe: %d\n", err); } else { - printk("Syncing with PAST: %d\n", err); state->pa_syncing = true; k_work_init_delayable(&state->pa_timer, pa_timer_handler); (void)k_work_reschedule(&state->pa_timer, @@ -233,14 +233,14 @@ static void recv_state_updated_cb(struct bt_conn *conn, state = sync_state_get_by_src_id(recv_state->src_id); if (state == NULL) { - FAIL("Could not get state"); + FAIL("Could not get state\n"); return; } if (state->recv_state != NULL) { if (state->recv_state != recv_state) { - FAIL("Sync state receive state mismatch: %p - %p", - state->recv_state, recv_state); + FAIL("Sync state receive state mismatch: %p - %p\n", state->recv_state, + recv_state); return; } } else { @@ -265,12 +265,12 @@ static int pa_sync_req_cb(struct bt_conn *conn, int err; reset_cp_flags(); - printk("PA Sync request: past_avail %u, pa_interval 0x%04x\n: %p", - past_avail, pa_interval, recv_state); + printk("PA Sync request: past_avail %u, pa_interval 0x%04x: %p\n", past_avail, pa_interval, + recv_state); state = sync_state_get_or_new(recv_state); if (state == NULL) { - FAIL("Could not get state"); + FAIL("Could not get state\n"); return -1; } @@ -513,7 +513,7 @@ static bool broadcast_source_found(struct bt_data *data, void *user_data) state = sync_state_get_or_new(NULL); if (state == NULL) { - FAIL("Failed to get sync state"); + FAIL("Failed to get sync state\n"); return true; } @@ -893,7 +893,7 @@ static void test_main_server_sync_server_rem(void) err = bt_le_scan_start(BT_LE_SCAN_PASSIVE, NULL); if (err != 0) { - FAIL("Could not start scan (%d)", err); + FAIL("Could not start scan (%d)\n", err); return; } From cbd41a59cc1afbe7f704d76f7f599367bf6d9371 Mon Sep 17 00:00:00 2001 From: Emil Gydesen Date: Tue, 9 Sep 2025 13:11:47 +0200 Subject: [PATCH 4/4] tests: Bluetooth: BAP: BA: Update conn params for PAST When using PAST, it is best not to use an ACL that is a multiple of the PA or ISO interval to avoid overlaps. Signed-off-by: Emil Gydesen --- .../audio/src/bap_broadcast_assistant_test.c | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/tests/bsim/bluetooth/audio/src/bap_broadcast_assistant_test.c b/tests/bsim/bluetooth/audio/src/bap_broadcast_assistant_test.c index d9effeb9f6e52..47cf617f189b8 100644 --- a/tests/bsim/bluetooth/audio/src/bap_broadcast_assistant_test.c +++ b/tests/bsim/bluetooth/audio/src/bap_broadcast_assistant_test.c @@ -337,6 +337,29 @@ static void test_exchange_mtu(void) printk("MTU exchanged\n"); } +static void update_conn_params(void) +{ + /* When we are the broadcast assistant we do not know the PA interval or ISO interval, so + * set the connection parameters to something that is unlike to be a multiple of either to + * avoid issues with e.g. PAST. + * 45 fits nicely as it is not a multiple of neither BT_BAP_ADV_PARAM_BROADCAST_FAST or + * BT_BAP_ADV_PARAM_BROADCAST_SLOW, nor 7.5 ms or 10 ms for ISO + */ + int err; + + UNSET_FLAG(flag_conn_updated); + err = bt_conn_le_param_update(default_conn, + BT_LE_CONN_PARAM(BT_GAP_MS_TO_CONN_INTERVAL(35), + BT_GAP_MS_TO_CONN_INTERVAL(35), 0, + BT_GAP_MS_TO_CONN_TIMEOUT(4000))); + if (err != 0) { + FAIL("Failed to update connection parameters %d\n", err); + return; + } + + WAIT_FOR_FLAG(flag_conn_updated); +} + static void test_bass_discover(void) { int err; @@ -750,6 +773,8 @@ static int common_init(void) WAIT_FOR_FLAG(flag_connected); + update_conn_params(); + test_exchange_mtu(); test_bass_discover(); test_bass_read_receive_states();