diff --git a/doc/releases/migration-guide-4.3.rst b/doc/releases/migration-guide-4.3.rst index 53e6b64ab72d..e895207d03c3 100644 --- a/doc/releases/migration-guide-4.3.rst +++ b/doc/releases/migration-guide-4.3.rst @@ -114,6 +114,12 @@ Bluetooth Audio * Setting the BGS role for GMAP now requires also supporting and implementing the :kconfig:option:`CONFIG_BT_BAP_BROADCAST_ASSISTANT`. See the :zephyr:code-sample:`bluetooth_bap_broadcast_assistant` sample as a reference. +* The BAP Scan Delegator will no longer automatically update the PA sync state, and + :c:func:`bt_bap_scan_delegator_set_pa_state` must be used to update the state. If the + 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``) + .. zephyr-keep-sorted-stop diff --git a/include/zephyr/bluetooth/audio/bap.h b/include/zephyr/bluetooth/audio/bap.h index 27f42432e227..d480837c9713 100644 --- a/include/zephyr/bluetooth/audio/bap.h +++ b/include/zephyr/bluetooth/audio/bap.h @@ -2661,8 +2661,6 @@ int bt_bap_scan_delegator_unregister(void); * * @param src_id The source id used to identify the receive state. * @param pa_state The Periodic Advertising sync state to set. - * BT_BAP_PA_STATE_NOT_SYNCED and BT_BAP_PA_STATE_SYNCED is - * not necessary to provide, as they are handled internally. * * @return int Error value. 0 on success, errno on fail. */ @@ -2688,6 +2686,14 @@ struct bt_bap_scan_delegator_add_src_param { /** Advertiser SID */ uint8_t sid; + /** + * @brief Periodic Advertising sync state + * + * This will typically be either @ref BT_BAP_PA_STATE_NOT_SYNCED or + * @ref BT_BAP_PA_STATE_SYNCED. + */ + enum bt_bap_pa_state pa_state; + /** The broadcast isochronous group encryption state */ enum bt_bap_big_enc_state encrypt_state; diff --git a/subsys/bluetooth/audio/bap_broadcast_sink.c b/subsys/bluetooth/audio/bap_broadcast_sink.c index 82106a9dfae5..cfe02efd01e6 100644 --- a/subsys/bluetooth/audio/bap_broadcast_sink.c +++ b/subsys/bluetooth/audio/bap_broadcast_sink.c @@ -70,8 +70,8 @@ static struct bt_bap_scan_delegator_mod_src_param mod_src_param; static void broadcast_sink_cleanup(struct bt_bap_broadcast_sink *sink); -static bool find_recv_state_by_sink_cb(const struct bt_bap_scan_delegator_recv_state *recv_state, - void *user_data) +static bool find_recv_state_by_src_id_cb(const struct bt_bap_scan_delegator_recv_state *recv_state, + void *user_data) { const struct bt_bap_broadcast_sink *sink = user_data; @@ -83,26 +83,27 @@ static bool find_recv_state_by_sink_cb(const struct bt_bap_scan_delegator_recv_s return false; } -static bool find_recv_state_by_pa_sync_cb(const struct bt_bap_scan_delegator_recv_state *recv_state, - void *user_data) +static bool +find_recv_state_by_sink_fields_cb(const struct bt_bap_scan_delegator_recv_state *recv_state, + void *user_data) { - struct bt_le_per_adv_sync *sync = user_data; + const struct bt_bap_broadcast_sink *sink = user_data; struct bt_le_per_adv_sync_info sync_info; int err; - err = bt_le_per_adv_sync_get_info(sync, &sync_info); + err = bt_le_per_adv_sync_get_info(sink->pa_sync, &sync_info); if (err != 0) { LOG_DBG("Failed to get sync info: %d", err); return false; } - if (bt_addr_le_eq(&recv_state->addr, &sync_info.addr) && - recv_state->adv_sid == sync_info.sid) { - return true; - } - - return false; + /* BAP 6.5.4 states that the combined Source_Address_Type, Source_Adv_SID, and Broadcast_ID + * fields are what makes a receive state unique. + */ + return recv_state->addr.type == sync_info.addr.type && + recv_state->adv_sid == sync_info.sid && + recv_state->broadcast_id == sink->broadcast_id; }; static void update_recv_state_big_synced(const struct bt_bap_broadcast_sink *sink) @@ -110,7 +111,7 @@ static void update_recv_state_big_synced(const struct bt_bap_broadcast_sink *sin const struct bt_bap_scan_delegator_recv_state *recv_state; int err; - recv_state = bt_bap_scan_delegator_find_state(find_recv_state_by_sink_cb, (void *)sink); + recv_state = bt_bap_scan_delegator_find_state(find_recv_state_by_src_id_cb, (void *)sink); if (recv_state == NULL) { LOG_WRN("Failed to find receive state for sink %p", sink); @@ -156,7 +157,7 @@ static void update_recv_state_big_cleared(const struct bt_bap_broadcast_sink *si bool sink_is_streaming = false; int err; - recv_state = bt_bap_scan_delegator_find_state(find_recv_state_by_sink_cb, (void *)sink); + recv_state = bt_bap_scan_delegator_find_state(find_recv_state_by_src_id_cb, (void *)sink); if (recv_state == NULL) { /* This is likely due to the receive state being removed while we are BIG synced */ LOG_DBG("Could not find receive state for sink %p", sink); @@ -489,6 +490,8 @@ static void broadcast_sink_add_src(struct bt_bap_broadcast_sink *sink) bt_addr_le_copy(&add_src_param.addr, &sync_info.addr); add_src_param.sid = sync_info.sid; + /* When a broadcast sink is created we always assume the PA sync provided is synced */ + add_src_param.pa_state = BT_BAP_PA_STATE_SYNCED; add_src_param.broadcast_id = sink->broadcast_id; /* Will be updated when we receive the BASE */ add_src_param.encrypt_state = BT_BAP_BIG_ENC_STATE_NO_ENC; @@ -542,7 +545,7 @@ static void update_recv_state_base(const struct bt_bap_broadcast_sink *sink, const struct bt_bap_scan_delegator_recv_state *recv_state; int err; - recv_state = bt_bap_scan_delegator_find_state(find_recv_state_by_sink_cb, (void *)sink); + recv_state = bt_bap_scan_delegator_find_state(find_recv_state_by_src_id_cb, (void *)sink); if (recv_state == NULL) { LOG_WRN("Failed to find receive state for sink %p", sink); @@ -745,12 +748,23 @@ static void pa_recv(struct bt_le_per_adv_sync *sync, bt_data_parse(buf, pa_decode_base, (void *)sink); } +static void pa_synced_cb(struct bt_le_per_adv_sync *sync, + struct bt_le_per_adv_sync_synced_info *info) +{ + struct bt_bap_broadcast_sink *sink = broadcast_sink_get_by_pa(sync); + + if (sink != NULL) { + bt_bap_scan_delegator_set_pa_state(sink->bass_src_id, BT_BAP_PA_STATE_SYNCED); + } +} + static void pa_term_cb(struct bt_le_per_adv_sync *sync, const struct bt_le_per_adv_sync_term_info *info) { struct bt_bap_broadcast_sink *sink = broadcast_sink_get_by_pa(sync); if (sink != NULL) { + bt_bap_scan_delegator_set_pa_state(sink->bass_src_id, BT_BAP_PA_STATE_NOT_SYNCED); sink->pa_sync = NULL; sink->base_size = 0U; } @@ -763,7 +777,7 @@ static void update_recv_state_encryption(const struct bt_bap_broadcast_sink *sin __ASSERT(sink->big == NULL, "Encryption state shall not be updated while synced"); - recv_state = bt_bap_scan_delegator_find_state(find_recv_state_by_sink_cb, (void *)sink); + recv_state = bt_bap_scan_delegator_find_state(find_recv_state_by_src_id_cb, (void *)sink); if (recv_state == NULL) { LOG_WRN("Failed to find receive state for sink %p", sink); @@ -1073,8 +1087,8 @@ int bt_bap_broadcast_sink_create(struct bt_le_per_adv_sync *pa_sync, uint32_t br sink->broadcast_id = broadcast_id; sink->pa_sync = pa_sync; - recv_state = bt_bap_scan_delegator_find_state(find_recv_state_by_pa_sync_cb, - (void *)pa_sync); + recv_state = + bt_bap_scan_delegator_find_state(find_recv_state_by_sink_fields_cb, (void *)sink); if (recv_state == NULL) { broadcast_sink_add_src(sink); } else { @@ -1088,8 +1102,26 @@ int bt_bap_broadcast_sink_create(struct bt_le_per_adv_sync *pa_sync, uint32_t br } sink->bass_src_id = recv_state->src_id; + if (recv_state->pa_sync_state != BT_BAP_PA_STATE_SYNCED) { + int err; + + /* When a broadcast sink is created we always assume the PA sync provided is + * synced + */ + err = bt_bap_scan_delegator_set_pa_state(sink->bass_src_id, + BT_BAP_PA_STATE_SYNCED); + if (err != 0) { + LOG_DBG("Failed to set PA state: %d", err); + + broadcast_sink_cleanup(sink); + + return err; + } + } + atomic_set_bit(sink->flags, BT_BAP_BROADCAST_SINK_FLAG_SRC_ID_VALID); } + atomic_set_bit(sink->flags, BT_BAP_BROADCAST_SINK_FLAG_INITIALIZED); *out_sink = sink; @@ -1416,6 +1448,7 @@ int bt_bap_broadcast_sink_delete(struct bt_bap_broadcast_sink *sink) static int broadcast_sink_init(void) { static struct bt_le_per_adv_sync_cb cb = { + .synced = pa_synced_cb, .recv = pa_recv, .biginfo = biginfo_recv, .term = pa_term_cb, diff --git a/subsys/bluetooth/audio/bap_scan_delegator.c b/subsys/bluetooth/audio/bap_scan_delegator.c index 1be1eec56a82..b672640c2b44 100644 --- a/subsys/bluetooth/audio/bap_scan_delegator.c +++ b/subsys/bluetooth/audio/bap_scan_delegator.c @@ -65,7 +65,7 @@ struct bass_recv_state_internal { uint8_t index; struct bt_bap_scan_delegator_recv_state state; uint8_t broadcast_code[BT_ISO_BROADCAST_CODE_SIZE]; - struct bt_le_per_adv_sync *pa_sync; + /** Requested BIS sync bitfield for each subgroup */ uint32_t requested_bis_sync[CONFIG_BT_BAP_BASS_MAX_SUBGROUPS]; @@ -85,7 +85,6 @@ struct bt_bap_scan_delegator_inst { enum scan_delegator_flag { SCAN_DELEGATOR_FLAG_REGISTERED_CONN_CB, SCAN_DELEGATOR_FLAG_REGISTERED_SCAN_DELIGATOR, - SCAN_DELEGATOR_FLAG_REGISTERED_PA_SYNC_CB, SCAN_DELEGATOR_FLAG_NUM, }; @@ -395,27 +394,43 @@ static struct bass_recv_state_internal *bass_lookup_src_id(uint8_t src_id) return NULL; } -static struct bass_recv_state_internal *bass_lookup_pa_sync(struct bt_le_per_adv_sync *sync) +/* BAP 6.5.4 states that the combined Source_Address_Type, Source_Adv_SID, and Broadcast_ID fields + * are what makes a receive state unique. + */ +static struct bass_recv_state_internal *bass_lookup_state(uint8_t addr_type, uint8_t adv_sid, + uint32_t broadcast_id) { - for (size_t i = 0; i < ARRAY_SIZE(scan_delegator.recv_states); i++) { - if (scan_delegator.recv_states[i].pa_sync == sync) { - return &scan_delegator.recv_states[i]; + struct bass_recv_state_internal *res = NULL; + + ARRAY_FOR_EACH_PTR(scan_delegator.recv_states, recv_state_internal) { + int err; + + if (!recv_state_internal->active) { + continue; } - } - return NULL; -} + err = k_mutex_lock(&recv_state_internal->mutex, SCAN_DELEGATOR_BUF_SEM_TIMEOUT); + if (err != 0) { + LOG_DBG("Failed to lock mutex: %d", err); -static struct bass_recv_state_internal *bass_lookup_addr(const bt_addr_le_t *addr) -{ - for (size_t i = 0; i < ARRAY_SIZE(scan_delegator.recv_states); i++) { - if (bt_addr_le_eq(&scan_delegator.recv_states[i].state.addr, - addr)) { - return &scan_delegator.recv_states[i]; + return NULL; + } + + if (recv_state_internal->state.addr.type == addr_type && + recv_state_internal->state.adv_sid == adv_sid && + recv_state_internal->state.broadcast_id == broadcast_id) { + res = recv_state_internal; + } + + err = k_mutex_unlock(&recv_state_internal->mutex); + __ASSERT(err == 0, "Failed to unlock mutex: %d", err); + + if (res != NULL) { + break; } } - return NULL; + return res; } static struct bass_recv_state_internal *get_free_recv_state(void) @@ -432,85 +447,6 @@ static struct bass_recv_state_internal *get_free_recv_state(void) return NULL; } -static void pa_synced(struct bt_le_per_adv_sync *sync, - struct bt_le_per_adv_sync_synced_info *info) -{ - struct bass_recv_state_internal *internal_state; - bool state_changed = false; - int err; - - LOG_DBG("Synced%s", info->conn ? " via PAST" : ""); - - internal_state = bass_lookup_addr(info->addr); - if (internal_state == NULL) { - LOG_DBG("BASS receive state not found"); - return; - } - - err = k_mutex_lock(&internal_state->mutex, SCAN_DELEGATOR_BUF_SEM_TIMEOUT); - if (err != 0) { - LOG_DBG("Failed to lock mutex: %d", err); - return; - } - - internal_state->pa_sync = sync; - - if (internal_state->state.pa_sync_state != BT_BAP_PA_STATE_SYNCED) { - internal_state->state.pa_sync_state = BT_BAP_PA_STATE_SYNCED; - set_receive_state_changed(internal_state); - state_changed = true; - } - - err = k_mutex_unlock(&internal_state->mutex); - __ASSERT(err == 0, "Failed to unlock mutex: %d", err); - - if (state_changed) { - /* app callback */ - receive_state_updated(info->conn, internal_state); - } -} - -static void pa_terminated(struct bt_le_per_adv_sync *sync, - const struct bt_le_per_adv_sync_term_info *info) -{ - struct bass_recv_state_internal *internal_state = bass_lookup_pa_sync(sync); - bool state_changed = false; - int err; - - LOG_DBG("Terminated"); - if (internal_state == NULL) { - LOG_DBG("BASS receive state not found"); - return; - } - - err = k_mutex_lock(&internal_state->mutex, SCAN_DELEGATOR_BUF_SEM_TIMEOUT); - if (err != 0) { - LOG_DBG("Failed to lock mutex: %d", err); - return; - } - - internal_state->pa_sync = NULL; - - if (internal_state->state.pa_sync_state != BT_BAP_PA_STATE_NOT_SYNCED) { - internal_state->state.pa_sync_state = BT_BAP_PA_STATE_NOT_SYNCED; - set_receive_state_changed(internal_state); - state_changed = true; - } - - err = k_mutex_unlock(&internal_state->mutex); - __ASSERT(err == 0, "Failed to unlock mutex: %d", err); - - if (state_changed) { - /* app callback */ - receive_state_updated(NULL, internal_state); - } -} - -static struct bt_le_per_adv_sync_cb pa_sync_cb = { - .synced = pa_synced, - .term = pa_terminated, -}; - static bool supports_past(struct bt_conn *conn, uint8_t pa_sync_val) { if (IS_ENABLED(CONFIG_BT_PER_ADV_SYNC_TRANSFER_RECEIVER)) { @@ -580,35 +516,12 @@ static int pa_sync_term_request(struct bt_conn *conn, return err; } -/* BAP 6.5.4 states that the Broadcast Assistant shall not initiate the Add Source operation - * if the operation would result in duplicate values for the combined Source_Address_Type, - * Source_Adv_SID, and Broadcast_ID fields of any Broadcast Receive State characteristic exposed - * by the Scan Delegator. - */ -static bool bass_source_is_duplicate(uint32_t broadcast_id, uint8_t adv_sid, uint8_t addr_type) -{ - struct bass_recv_state_internal *state; - - for (size_t i = 0; i < ARRAY_SIZE(scan_delegator.recv_states); i++) { - state = &scan_delegator.recv_states[i]; - - if (state != NULL && state->state.broadcast_id == broadcast_id && - state->state.adv_sid == adv_sid && state->state.addr.type == addr_type) { - LOG_DBG("recv_state already exists at src_id=0x%02X", state->state.src_id); - - return true; - } - } - - return false; -} - static int scan_delegator_add_src(struct bt_conn *conn, struct net_buf_simple *buf) { struct bass_recv_state_internal *internal_state = NULL; struct bt_bap_scan_delegator_recv_state *state; - bt_addr_t *addr; + bt_addr_le_t *addr; uint8_t pa_sync; uint16_t pa_interval; uint32_t aggregated_bis_syncs = 0; @@ -617,6 +530,7 @@ static int scan_delegator_add_src(struct bt_conn *conn, uint16_t total_len; struct bt_bap_bass_cp_add_src *add_src; int ret = BT_GATT_ERR(BT_ATT_ERR_SUCCESS); + uint8_t adv_sid; int err; /* subtract 1 as the opcode has already been pulled */ @@ -653,6 +567,29 @@ static int scan_delegator_add_src(struct bt_conn *conn, return BT_GATT_ERR(BT_ATT_ERR_WRITE_REQ_REJECTED); } + addr = net_buf_simple_pull_mem(buf, sizeof(*addr)); + if (addr->type > BT_ADDR_LE_RANDOM) { + LOG_DBG("Invalid address type %u", addr->type); + return BT_GATT_ERR(BT_ATT_ERR_VALUE_NOT_ALLOWED); + } + + adv_sid = net_buf_simple_pull_u8(buf); + if (adv_sid > BT_GAP_SID_MAX) { + LOG_DBG("Invalid adv SID %u", adv_sid); + return BT_GATT_ERR(BT_ATT_ERR_VALUE_NOT_ALLOWED); + } + + broadcast_id = net_buf_simple_pull_le24(buf); + + internal_state = bass_lookup_state(addr->type, adv_sid, broadcast_id); + if (internal_state != NULL) { + LOG_DBG("Adding addr type=0x%02X adv_sid=0x%02X and broadcast_id=0x%06X would " + "result in duplication", + addr->type, adv_sid, broadcast_id); + + return BT_GATT_ERR(BT_ATT_ERR_WRITE_REQ_REJECTED); + } + internal_state = get_free_recv_state(); if (internal_state == NULL) { LOG_DBG("Could not get free receive state"); @@ -670,33 +607,8 @@ static int scan_delegator_add_src(struct bt_conn *conn, state = &internal_state->state; state->src_id = next_src_id(); - state->addr.type = net_buf_simple_pull_u8(buf); - if (state->addr.type > BT_ADDR_LE_RANDOM) { - LOG_DBG("Invalid address type %u", state->addr.type); - ret = BT_GATT_ERR(BT_ATT_ERR_VALUE_NOT_ALLOWED); - goto unlock_return; - } - - addr = net_buf_simple_pull_mem(buf, sizeof(*addr)); - bt_addr_copy(&state->addr.a, addr); - - state->adv_sid = net_buf_simple_pull_u8(buf); - if (state->adv_sid > BT_GAP_SID_MAX) { - LOG_DBG("Invalid adv SID %u", state->adv_sid); - ret = BT_GATT_ERR(BT_ATT_ERR_VALUE_NOT_ALLOWED); - goto unlock_return; - } - - broadcast_id = net_buf_simple_pull_le24(buf); - - if (bass_source_is_duplicate(broadcast_id, state->adv_sid, state->addr.type)) { - LOG_DBG("Adding broadcast_id=0x%06X, adv_sid=0x%02X, and addr.type=0x%02X would " - "result in duplication", state->broadcast_id, state->adv_sid, - state->addr.type); - ret = BT_GATT_ERR(BT_ATT_ERR_WRITE_REQ_REJECTED); - goto unlock_return; - } - + bt_addr_le_copy(&state->addr, addr); + state->adv_sid = adv_sid; state->broadcast_id = broadcast_id; pa_sync = net_buf_simple_pull_u8(buf); @@ -1139,7 +1051,6 @@ static int scan_delegator_rem_src(struct bt_conn *conn, { struct bass_recv_state_internal *internal_state; struct bt_bap_scan_delegator_recv_state *state; - bool bis_sync_was_requested = false; uint8_t src_id; int err; @@ -1166,38 +1077,40 @@ static int scan_delegator_rem_src(struct bt_conn *conn, state = &internal_state->state; - /* If conn == NULL then it's a local operation and we do not need to ask the application */ - if (conn != NULL) { - - if (scan_delegator_cbs != NULL && scan_delegator_cbs->remove_source != NULL) { - err = scan_delegator_cbs->remove_source(conn, src_id); - if (err != 0) { - LOG_DBG("Remove Source rejected with reason 0x%02x", err); - err = k_mutex_unlock(&internal_state->mutex); - __ASSERT(err == 0, "Failed to unlock mutex: %d", err); - return BT_GATT_ERR(BT_ATT_ERR_WRITE_REQ_REJECTED); - } - } - - if (state->pa_sync_state == BT_BAP_PA_STATE_INFO_REQ || - state->pa_sync_state == BT_BAP_PA_STATE_SYNCED) { - /* Terminate PA sync */ - err = pa_sync_term_request(conn, &internal_state->state); - if (err != 0) { - LOG_DBG("PA sync term from %p was rejected with reason %d", - (void *)conn, err); - err = k_mutex_unlock(&internal_state->mutex); - __ASSERT(err == 0, "Failed to unlock mutex: %d", err); - return BT_GATT_ERR(BT_ATT_ERR_WRITE_REQ_REJECTED); - } - } + if (state->pa_sync_state == BT_BAP_PA_STATE_SYNCED) { + LOG_DBG("Cannot remove source ID 0x%02x while PA is synced", state->src_id); + err = k_mutex_unlock(&internal_state->mutex); + __ASSERT(err == 0, "Failed to unlock mutex: %d", err); + /* We shouldn't return a success here, but the Test Spec requires it at the moment, + * Errata to fix this: https://bluetooth.atlassian.net/browse/ES-28445 + */ + return BT_GATT_ERR(BT_ATT_ERR_SUCCESS); } for (uint8_t i = 0U; i < state->num_subgroups; i++) { if (internal_state->requested_bis_sync[i] != 0U && internal_state->state.subgroups[i].bis_sync != 0U) { - bis_sync_was_requested = true; - break; + LOG_DBG("Cannot remove source ID 0x%02x while BIS is synced", + state->src_id); + err = k_mutex_unlock(&internal_state->mutex); + __ASSERT(err == 0, "Failed to unlock mutex: %d", err); + /* We shouldn't return a success here, but the Test Spec requires it at the + * moment, Errata to fix this: + * https://bluetooth.atlassian.net/browse/ES-28445 + */ + return BT_GATT_ERR(BT_ATT_ERR_SUCCESS); + } + } + + /* If conn == NULL then it's a local operation and we do not need to ask the application */ + if (conn != NULL && scan_delegator_cbs != NULL && + scan_delegator_cbs->remove_source != NULL) { + err = scan_delegator_cbs->remove_source(conn, src_id); + if (err != 0) { + LOG_DBG("Remove Source rejected with reason 0x%02x", err); + err = k_mutex_unlock(&internal_state->mutex); + __ASSERT(err == 0, "Failed to unlock mutex: %d", err); + return BT_GATT_ERR(BT_ATT_ERR_WRITE_REQ_REJECTED); } } @@ -1205,7 +1118,6 @@ static int scan_delegator_rem_src(struct bt_conn *conn, internal_state->index, src_id); internal_state->active = false; - internal_state->pa_sync = NULL; (void)memset(&internal_state->state, 0, sizeof(internal_state->state)); (void)memset(internal_state->broadcast_code, 0, sizeof(internal_state->broadcast_code)); @@ -1217,10 +1129,6 @@ static int scan_delegator_rem_src(struct bt_conn *conn, err = k_mutex_unlock(&internal_state->mutex); __ASSERT(err == 0, "Failed to unlock mutex: %d", err); - if (bis_sync_was_requested) { - bis_sync_request_updated(conn, internal_state); - } - /* app callback */ receive_state_updated(conn, internal_state); @@ -1454,18 +1362,6 @@ int bt_bap_scan_delegator_register(struct bt_bap_scan_delegator_cb *cb) #endif /* CONFIG_BT_BAP_SCAN_DELEGATOR_RECV_STATE_COUNT > 2 */ #endif /* CONFIG_BT_BAP_SCAN_DELEGATOR_RECV_STATE_COUNT > 1 */ - if (!atomic_test_and_set_bit(scan_delegator_flags, - SCAN_DELEGATOR_FLAG_REGISTERED_PA_SYNC_CB)) { - err = bt_le_per_adv_sync_cb_register(&pa_sync_cb); - if (err) { - atomic_clear_bit(scan_delegator_flags, - SCAN_DELEGATOR_FLAG_REGISTERED_PA_SYNC_CB); - atomic_clear_bit(scan_delegator_flags, - SCAN_DELEGATOR_FLAG_REGISTERED_SCAN_DELIGATOR); - return err; - } - } - for (size_t i = 0; i < ARRAY_SIZE(scan_delegator.recv_states); i++) { struct bass_recv_state_internal *internal_state = &scan_delegator.recv_states[i]; @@ -1652,6 +1548,12 @@ static bool valid_bt_bap_scan_delegator_add_src_param( return false; } + if (!IN_RANGE(param->pa_state, BT_BAP_PA_STATE_NOT_SYNCED, BT_BAP_PA_STATE_NO_PAST)) { + LOG_DBG("Invalid PA state: %d", param->pa_state); + + return false; + } + for (uint8_t i = 0U; i < param->num_subgroups; i++) { const struct bt_bap_bass_subgroup *subgroup = ¶m->subgroups[i]; @@ -1677,23 +1579,19 @@ int bt_bap_scan_delegator_add_src(const struct bt_bap_scan_delegator_add_src_par { struct bass_recv_state_internal *internal_state = NULL; struct bt_bap_scan_delegator_recv_state *state; - struct bt_le_per_adv_sync *pa_sync; int err; CHECKIF(!valid_bt_bap_scan_delegator_add_src_param(param)) { return -EINVAL; } - pa_sync = bt_le_per_adv_sync_lookup_addr(¶m->addr, param->sid); - - if (pa_sync != NULL) { - internal_state = bass_lookup_pa_sync(pa_sync); - if (internal_state != NULL) { - LOG_DBG("PA Sync already in a receive state with src_id %u", - internal_state->state.src_id); + internal_state = bass_lookup_state(param->addr.type, param->sid, param->broadcast_id); + if (internal_state != NULL) { + LOG_DBG("Adding addr.type=0x%02X adv_sid=0x%02X and broadcast_id=0x%06X would " + "result in duplication", + param->addr.type, param->sid, param->broadcast_id); - return -EALREADY; - } + return -EALREADY; } internal_state = get_free_recv_state(); @@ -1703,15 +1601,23 @@ int bt_bap_scan_delegator_add_src(const struct bt_bap_scan_delegator_add_src_par return -ENOMEM; } + err = k_mutex_lock(&internal_state->mutex, SCAN_DELEGATOR_BUF_SEM_TIMEOUT); + if (err != 0) { + LOG_DBG("Failed to lock mutex: %d", err); + + return -EBUSY; + } + state = &internal_state->state; state->src_id = next_src_id(); bt_addr_le_copy(&state->addr, ¶m->addr); state->adv_sid = param->sid; state->broadcast_id = param->broadcast_id; - state->pa_sync_state = - pa_sync == NULL ? BT_BAP_PA_STATE_NOT_SYNCED : BT_BAP_PA_STATE_SYNCED; + state->pa_sync_state = param->pa_state; + state->encrypt_state = param->encrypt_state; state->num_subgroups = param->num_subgroups; + if (state->num_subgroups > 0U) { (void)memcpy(state->subgroups, param->subgroups, sizeof(state->subgroups)); @@ -1719,15 +1625,7 @@ int bt_bap_scan_delegator_add_src(const struct bt_bap_scan_delegator_add_src_par (void)memset(state->subgroups, 0, sizeof(state->subgroups)); } - err = k_mutex_lock(&internal_state->mutex, SCAN_DELEGATOR_BUF_SEM_TIMEOUT); - if (err != 0) { - LOG_DBG("Failed to lock mutex: %d", err); - - return -EBUSY; - } - internal_state->active = true; - internal_state->pa_sync = pa_sync; /* Set all requested_bis_sync to BT_BAP_BIS_SYNC_NO_PREF, as no * Broadcast Assistant has set any requests yet diff --git a/subsys/bluetooth/audio/bap_unicast_client.c b/subsys/bluetooth/audio/bap_unicast_client.c index 429431ada433..d0d9001548dc 100644 --- a/subsys/bluetooth/audio/bap_unicast_client.c +++ b/subsys/bluetooth/audio/bap_unicast_client.c @@ -327,8 +327,8 @@ static void unicast_client_ep_iso_connected(struct bt_bap_ep *ep) ep->unicast_group->has_been_connected = true; } - if (ep->state != BT_BAP_EP_STATE_ENABLING) { - LOG_DBG("endpoint not in enabling state: %s", bt_bap_ep_state_str(ep->state)); + if (ep->state != BT_BAP_EP_STATE_QOS_CONFIGURED && ep->state != BT_BAP_EP_STATE_ENABLING) { + LOG_DBG("endpoint in invalid state: %s", bt_bap_ep_state_str(ep->state)); return; } diff --git a/tests/bluetooth/tester/src/audio/btp/btp_ascs.h b/tests/bluetooth/tester/src/audio/btp/btp_ascs.h index 50d3291f76e1..706e6cad1875 100644 --- a/tests/bluetooth/tester/src/audio/btp/btp_ascs.h +++ b/tests/bluetooth/tester/src/audio/btp/btp_ascs.h @@ -117,5 +117,20 @@ struct btp_ascs_ase_state_changed_ev { uint8_t state; } __packed; +#define BTP_ASCS_EV_CIS_CONNECTED 0x83 +struct btp_ascs_cis_connected_ev { + bt_addr_le_t address; + uint8_t ase_id; + uint8_t cis_id; +} __packed; + +#define BTP_ASCS_EV_CIS_DISCONNECTED 0x84 +struct btp_ascs_cis_disconnected_ev { + bt_addr_le_t address; + uint8_t ase_id; + uint8_t cis_id; + uint8_t reason; +} __packed; + #define BTP_ASCS_STATUS_SUCCESS 0x00 #define BTP_ASCS_STATUS_FAILED 0x01 diff --git a/tests/bluetooth/tester/src/audio/btp/btp_bap.h b/tests/bluetooth/tester/src/audio/btp/btp_bap.h index 90544cdcbd0d..a9f045b02975 100644 --- a/tests/bluetooth/tester/src/audio/btp/btp_bap.h +++ b/tests/bluetooth/tester/src/audio/btp/btp_bap.h @@ -202,6 +202,20 @@ struct btp_bap_broadcast_source_setup_v2_rp { uint32_t gap_settings; } __packed; +#define BTP_BAP_SCAN_DELEGATOR_ADD_SRC 0x1a +struct btp_bap_scan_delegator_add_src_cmd { + bt_addr_le_t broadcaster_address; + uint8_t advertiser_sid; + uint8_t broadcast_id[BT_AUDIO_BROADCAST_ID_SIZE]; + uint8_t pa_sync_state; + uint8_t big_encryption; + uint8_t num_subgroups; + uint8_t subgroups[]; +} __packed; +struct btp_bap_scan_delegator_add_src_rp { + uint8_t src_id; +} __packed; + /* BAP events */ #define BTP_BAP_EV_DISCOVERY_COMPLETED 0x80 struct btp_bap_discovery_completed_ev { diff --git a/tests/bluetooth/tester/src/audio/btp_bap.c b/tests/bluetooth/tester/src/audio/btp_bap.c index cf9baeec5c3f..c860d7341c6f 100644 --- a/tests/bluetooth/tester/src/audio/btp_bap.c +++ b/tests/bluetooth/tester/src/audio/btp_bap.c @@ -474,6 +474,11 @@ static const struct btp_handler bap_handlers[] = { .expect_len = sizeof(struct btp_bap_send_past_cmd), .func = btp_bap_broadcast_assistant_send_past, }, + { + .opcode = BTP_BAP_SCAN_DELEGATOR_ADD_SRC, + .expect_len = BTP_HANDLER_LENGTH_VARIABLE, + .func = btp_bap_scan_delegator_add_src, + }, #endif /* CONFIG_BT_BAP_BROADCAST_SOURCE || CONFIG_BT_BAP_BROADCAST_SINK */ }; diff --git a/tests/bluetooth/tester/src/audio/btp_bap_broadcast.c b/tests/bluetooth/tester/src/audio/btp_bap_broadcast.c index 3733ab36440a..d301fac6c31f 100644 --- a/tests/bluetooth/tester/src/audio/btp_bap_broadcast.c +++ b/tests/bluetooth/tester/src/audio/btp_bap_broadcast.c @@ -1833,6 +1833,71 @@ uint8_t btp_bap_broadcast_assistant_send_past(const void *cmd, uint16_t cmd_len, return BTP_STATUS_SUCCESS; } +uint8_t btp_bap_scan_delegator_add_src(const void *cmd, uint16_t cmd_len, void *rsp, + uint16_t *rsp_len) +{ + const struct btp_bap_scan_delegator_add_src_cmd *cp = cmd; + struct btp_bap_scan_delegator_add_src_rp *rp = rsp; + struct bt_bap_scan_delegator_add_src_param param = {0}; + struct net_buf_simple buf; + int err; + + if (cmd_len < sizeof(*cp)) { + return BTP_STATUS_FAILED; + } + + if (cp->num_subgroups > CONFIG_BT_BAP_BASS_MAX_SUBGROUPS) { + return BTP_STATUS_FAILED; + } + + if (cp->big_encryption > BT_BAP_BIG_ENC_STATE_BAD_CODE) { + return BTP_STATUS_FAILED; + } + + bt_addr_le_copy(¶m.addr, &cp->broadcaster_address); + param.sid = cp->advertiser_sid; + param.pa_state = (enum bt_bap_pa_state)cp->pa_sync_state; + param.encrypt_state = (enum bt_bap_big_enc_state)cp->big_encryption; + param.broadcast_id = sys_get_le24(cp->broadcast_id); + param.num_subgroups = cp->num_subgroups; + + net_buf_simple_init_with_data(&buf, (void *)cp->subgroups, cmd_len - sizeof(*cp)); + + for (uint8_t i = 0; i < param.num_subgroups; i++) { + struct bt_bap_bass_subgroup *subgroup = ¶m.subgroups[i]; + + /* If remaining data is less than the necessary subgroup fields, return failed */ + if (buf.len < sizeof(subgroup->bis_sync) + sizeof(subgroup->metadata_len)) { + return BTP_STATUS_FAILED; + } + + subgroup->bis_sync = net_buf_simple_pull_le32(&buf); + subgroup->metadata_len = net_buf_simple_pull_u8(&buf); + + if (subgroup->metadata_len > sizeof(subgroup->metadata) || + subgroup->metadata_len > buf.len) { + return BTP_STATUS_FAILED; + } + + memcpy(subgroup->metadata, net_buf_simple_pull_mem(&buf, subgroup->metadata_len), + subgroup->metadata_len); + } + + if (buf.len != 0U) { + return BTP_STATUS_FAILED; + } + + err = bt_bap_scan_delegator_add_src(¶m); + if (err < 0) { + return BTP_STATUS_VAL(err); + } + + rp->src_id = (uint8_t)err; + *rsp_len = sizeof(*rp); + + return BTP_STATUS_SUCCESS; +} + static bool broadcast_inited; int btp_bap_broadcast_init(void) diff --git a/tests/bluetooth/tester/src/audio/btp_bap_broadcast.h b/tests/bluetooth/tester/src/audio/btp_bap_broadcast.h index 3f4ef82b256c..a5fea384c45d 100644 --- a/tests/bluetooth/tester/src/audio/btp_bap_broadcast.h +++ b/tests/bluetooth/tester/src/audio/btp_bap_broadcast.h @@ -120,3 +120,5 @@ uint8_t btp_bap_broadcast_assistant_set_broadcast_code(const void *cmd, uint16_t void *rsp, uint16_t *rsp_len); uint8_t btp_bap_broadcast_assistant_send_past(const void *cmd, uint16_t cmd_len, void *rsp, uint16_t *rsp_len); +uint8_t btp_bap_scan_delegator_add_src(const void *cmd, uint16_t cmd_len, + void *rsp, uint16_t *rsp_len); diff --git a/tests/bluetooth/tester/src/audio/btp_bap_unicast.c b/tests/bluetooth/tester/src/audio/btp_bap_unicast.c index c06795e8267a..449ac8076cae 100644 --- a/tests/bluetooth/tester/src/audio/btp_bap_unicast.c +++ b/tests/bluetooth/tester/src/audio/btp_bap_unicast.c @@ -6,13 +6,18 @@ * SPDX-License-Identifier: Apache-2.0 */ +#include #include +#include #include +#include #include #include #include #include +#include +#include #include #include #include @@ -168,6 +173,37 @@ static void btp_send_ascs_ase_state_changed_ev(struct bt_conn *conn, uint8_t ase tester_event(BTP_SERVICE_ID_ASCS, BTP_ASCS_EV_ASE_STATE_CHANGED, &ev, sizeof(ev)); } +static void btp_send_ascs_cis_connected_ev(const struct bt_bap_stream *stream) +{ + struct btp_bap_unicast_stream *u_stream; + struct btp_ascs_cis_connected_ev ev; + + u_stream = stream_bap_to_unicast(stream); + __ASSERT(u_stream != NULL, "Failed to get unicast_stream from %p", stream); + + bt_addr_le_copy(&ev.address, bt_conn_get_dst(stream->conn)); + ev.ase_id = u_stream->ase_id; + ev.cis_id = u_stream->cis_id; + + tester_event(BTP_SERVICE_ID_ASCS, BTP_ASCS_EV_CIS_CONNECTED, &ev, sizeof(ev)); +} + +static void btp_send_ascs_cis_disconnected_ev(const struct bt_bap_stream *stream, uint8_t reason) +{ + struct btp_bap_unicast_stream *u_stream; + struct btp_ascs_cis_disconnected_ev ev; + + u_stream = stream_bap_to_unicast(stream); + __ASSERT(u_stream != NULL, "Failed to get unicast_stream from %p", stream); + + bt_addr_le_copy(&ev.address, bt_conn_get_dst(stream->conn)); + ev.ase_id = u_stream->ase_id; + ev.cis_id = u_stream->cis_id; + ev.reason = reason; + + tester_event(BTP_SERVICE_ID_ASCS, BTP_ASCS_EV_CIS_DISCONNECTED, &ev, sizeof(ev)); +} + static void btp_send_ascs_operation_completed_ev(struct bt_conn *conn, uint8_t ase_id, uint8_t opcode, uint8_t status) { @@ -524,22 +560,41 @@ static void stream_qos_set_cb(struct bt_bap_stream *stream) static void stream_enabled_cb(struct bt_bap_stream *stream) { - struct bt_bap_ep_info info; - struct bt_conn_info conn_info; + const bool iso_connected = + stream->iso == NULL ? false : stream->iso->state == BT_ISO_STATE_CONNECTED; int err; LOG_DBG("Enabled stream %p", stream); - (void)bt_bap_ep_get_info(stream->ep, &info); - (void)bt_conn_get_info(stream->conn, &conn_info); - if (conn_info.role == BT_HCI_ROLE_PERIPHERAL && info.dir == BT_AUDIO_DIR_SINK) { - /* Automatically do the receiver start ready operation */ - /* TODO: This should ideally be done by the upper tester */ - err = bt_bap_stream_start(stream); - if (err != 0) { - LOG_DBG("Failed to start stream %p", stream); + if (iso_connected) { + struct bt_bap_ep_info ep_info; + struct bt_conn_info conn_info; - return; + err = bt_bap_ep_get_info(stream->ep, &ep_info); + __ASSERT(err == 0, "Failed to get ISO chan info: %d", err); + err = bt_conn_get_info(stream->conn, &conn_info); + __ASSERT(err == 0, "Failed to get ISO chan info: %d", err); + + /* If we are central and the endpoint is a source or if we are a peripheral and the + * endpoint is a sink, then we can start it, else the remote device needs to start + * the endpoint + */ + const bool start_stream = + (IS_ENABLED(CONFIG_BT_CENTRAL) && conn_info.role == BT_HCI_ROLE_CENTRAL && + ep_info.dir == BT_AUDIO_DIR_SOURCE) || + (IS_ENABLED(CONFIG_BT_PERIPHERAL) && + conn_info.role == BT_HCI_ROLE_PERIPHERAL && + ep_info.dir == BT_AUDIO_DIR_SINK); + + if (start_stream) { + /* Automatically do the receiver start ready operation */ + /* TODO: This should ideally be done by the upper tester */ + err = bt_bap_stream_start(stream); + if (err != 0) { + LOG_DBG("Failed to start stream %p", stream); + + return; + } } } @@ -654,12 +709,16 @@ static void stream_connected_cb(struct bt_bap_stream *stream) } if (ep_info.dir == BT_AUDIO_DIR_SOURCE) { - /* Automatically do the receiver start ready operation for source ASEs as - * the client - */ - err = bt_bap_stream_start(stream); - if (err != 0) { - LOG_ERR("Failed to start stream %p", stream); + if (ep_info.state == BT_BAP_EP_STATE_ENABLING) { + /* Automatically do the receiver start ready operation for source + * ASEs as the client when in the enabling state. + * The CIS may be connected in the QoS Configured state as well, in + * which case we should wait until we enter the enabling state. + */ + err = bt_bap_stream_start(stream); + if (err != 0) { + LOG_ERR("Failed to start stream %p", stream); + } } } else { struct btp_bap_unicast_stream *u_stream = stream_bap_to_unicast(stream); @@ -669,6 +728,15 @@ static void stream_connected_cb(struct bt_bap_stream *stream) BTP_ASCS_STATUS_SUCCESS); } } + + btp_send_ascs_cis_connected_ev(stream); +} + +static void stream_disconnected_cb(struct bt_bap_stream *stream, uint8_t reason) +{ + LOG_DBG("Disconnected stream %p: 0x%02X", stream, reason); + + btp_send_ascs_cis_disconnected_ev(stream, reason); } static void stream_stopped_cb(struct bt_bap_stream *stream, uint8_t reason) @@ -749,6 +817,7 @@ static struct bt_bap_stream_ops stream_ops = { .recv = stream_recv_cb, .sent = btp_bap_audio_stream_sent_cb, .connected = stream_connected_cb, + .disconnected = stream_disconnected_cb, }; struct btp_bap_unicast_stream *btp_bap_unicast_stream_alloc( 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 5d7d3ab7db8e..e9a35e0773c2 100644 --- a/tests/bsim/bluetooth/audio/src/bap_broadcast_assistant_test.c +++ b/tests/bsim/bluetooth/audio/src/bap_broadcast_assistant_test.c @@ -502,7 +502,7 @@ static void test_bass_add_source(void) printk("Source added\n"); } -static void test_bass_mod_source(uint32_t bis_sync) +static void test_bass_mod_source(bool pa_sync, uint32_t bis_sync) { int err; struct bt_bap_broadcast_assistant_mod_src_param mod_src_param = { 0 }; @@ -515,7 +515,7 @@ static void test_bass_mod_source(uint32_t bis_sync) UNSET_FLAG(flag_recv_state_updated); mod_src_param.src_id = recv_state.src_id; mod_src_param.num_subgroups = 1; - mod_src_param.pa_sync = true; + mod_src_param.pa_sync = pa_sync; mod_src_param.subgroups = &subgroup; mod_src_param.pa_interval = g_broadcaster_info.interval; subgroup.bis_sync = bis_sync; @@ -542,14 +542,16 @@ static void test_bass_mod_source(uint32_t bis_sync) WAIT_FOR_AND_CLEAR_FLAG(flag_recv_state_updated); } - if (recv_state.pa_sync_state != BT_BAP_PA_STATE_SYNCED) { - FAIL("Unexpected PA sync state: %d\n", recv_state.pa_sync_state); - return; - } + if (pa_sync) { + if (recv_state.pa_sync_state != BT_BAP_PA_STATE_SYNCED) { + FAIL("Unexpected PA sync state: %d\n", recv_state.pa_sync_state); + return; + } - if (recv_state.encrypt_state != BT_BAP_BIG_ENC_STATE_NO_ENC) { - FAIL("Unexpected BIG encryption state: %d\n", recv_state.pa_sync_state); - return; + if (recv_state.encrypt_state != BT_BAP_BIG_ENC_STATE_NO_ENC) { + FAIL("Unexpected BIG encryption state: %d\n", recv_state.pa_sync_state); + return; + } } if (recv_state.num_subgroups != mod_src_param.num_subgroups) { @@ -563,19 +565,15 @@ static void test_bass_mod_source(uint32_t bis_sync) WAIT_FOR_AND_CLEAR_FLAG(flag_recv_state_updated); } - remote_bis_sync = recv_state.subgroups[0].bis_sync; - if (subgroup.bis_sync == 0) { - if (remote_bis_sync != 0U) { - FAIL("Unexpected BIS sync value: %u\n", remote_bis_sync); - return; - } - } else { + if (bis_sync != 0) { + remote_bis_sync = recv_state.subgroups[0].bis_sync; printk("Waiting for BIS sync\n"); if (remote_bis_sync == 0U && recv_state.encrypt_state == BT_BAP_BIG_ENC_STATE_NO_ENC) { - /* Wait for another notification, which will either request a broadcast code - * for encrypted broadcasts, or have the BIS sync values set + /* Wait for another notification, which will either request a + * broadcast code for encrypted broadcasts, or have the BIS sync + * values set */ printk("Waiting for another receive state update with BIS sync\n"); WAIT_FOR_AND_CLEAR_FLAG(flag_recv_state_updated); @@ -770,14 +768,15 @@ static void test_main_client_sync(void) test_bass_scan_stop(); test_bass_create_pa_sync(); test_bass_add_source(); - test_bass_mod_source(0); + test_bass_mod_source(true, 0); test_bass_mod_source_long_meta(); - test_bass_mod_source(BT_ISO_BIS_INDEX_BIT(1) | BT_ISO_BIS_INDEX_BIT(2)); + test_bass_mod_source(true, BT_ISO_BIS_INDEX_BIT(1) | BT_ISO_BIS_INDEX_BIT(2)); test_bass_broadcast_code(BROADCAST_CODE); printk("Waiting for receive state with BIS sync\n"); WAIT_FOR_FLAG(flag_recv_state_updated_with_bis_sync); + test_bass_mod_source(false, 0); test_bass_remove_source(); PASS("BAP Broadcast Assistant Client Sync Passed\n"); @@ -797,11 +796,12 @@ static void test_main_client_sync_incorrect_code(void) test_bass_scan_stop(); test_bass_create_pa_sync(); test_bass_add_source(); - test_bass_mod_source(BT_ISO_BIS_INDEX_BIT(1)); + test_bass_mod_source(true, BT_ISO_BIS_INDEX_BIT(1)); WAIT_FOR_FLAG(flag_broadcast_code_requested); test_bass_broadcast_code(INCORRECT_BROADCAST_CODE); WAIT_FOR_FLAG(flag_incorrect_broadcast_code); + test_bass_mod_source(false, 0); test_bass_remove_source(); PASS("BAP Broadcast Assistant Client Sync Passed\n"); @@ -825,6 +825,7 @@ static void test_main_server_sync_client_rem(void) WAIT_FOR_FLAG(flag_recv_state_updated_with_bis_sync); printk("Attempting to remove source for the first time\n"); + test_bass_mod_source(false, 0); test_bass_remove_source(); WAIT_FOR_FLAG(flag_remove_source_rejected); 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 c8f728fa96af..b979464a39a5 100644 --- a/tests/bsim/bluetooth/audio/src/bap_scan_delegator_test.c +++ b/tests/bsim/bluetooth/audio/src/bap_scan_delegator_test.c @@ -123,6 +123,7 @@ static void pa_timer_handler(struct k_work *work) if (state->recv_state != NULL) { enum bt_bap_pa_state pa_state; + int err; if (state->recv_state->pa_sync_state == BT_BAP_PA_STATE_INFO_REQ) { pa_state = BT_BAP_PA_STATE_NO_PAST; @@ -130,8 +131,11 @@ static void pa_timer_handler(struct k_work *work) pa_state = BT_BAP_PA_STATE_FAILED; } - bt_bap_scan_delegator_set_pa_state(state->recv_state->src_id, - pa_state); + err = bt_bap_scan_delegator_set_pa_state(state->recv_state->src_id, pa_state); + if (err != 0) { + FAIL("Could not set PA sync state: %d\n", err); + return; + } } FAIL("PA timeout\n"); @@ -209,6 +213,8 @@ static int pa_sync_term(struct sync_state *state) printk("Deleting PA sync\n"); + UNSET_FLAG(flag_pa_terminated); + err = bt_le_per_adv_sync_delete(state->pa_sync); if (err != 0) { FAIL("Could not delete per adv sync: %d\n", err); @@ -217,6 +223,8 @@ static int pa_sync_term(struct sync_state *state) state->pa_sync = NULL; } + WAIT_FOR_FLAG(flag_pa_terminated); + return err; } @@ -286,7 +294,8 @@ static int pa_sync_req_cb(struct bt_conn *conn, err = bt_bap_scan_delegator_set_pa_state(state->recv_state->src_id, BT_BAP_PA_STATE_INFO_REQ); if (err != 0) { - printk("Failed to set INFO_REQ state: %d", err); + FAIL("Could not set PA sync state: %d\n", err); + return err; } } } else { @@ -426,6 +435,16 @@ static void pa_synced_cb(struct bt_le_per_adv_sync *sync, return; } + if (state->recv_state != NULL) { + int err; + + err = bt_bap_scan_delegator_set_pa_state(state->src_id, BT_BAP_PA_STATE_SYNCED); + if (err != 0) { + FAIL("Could not set PA sync state: %d\n", err); + return; + } + } + k_work_cancel_delayable(&state->pa_timer); SET_FLAG(flag_pa_synced); @@ -444,6 +463,16 @@ static void pa_term_cb(struct bt_le_per_adv_sync *sync, return; } + if (state->recv_state != NULL) { + int err; + + err = bt_bap_scan_delegator_set_pa_state(state->src_id, BT_BAP_PA_STATE_NOT_SYNCED); + if (err != 0) { + FAIL("Could not set PA sync state: %d\n", err); + return; + } + } + k_work_cancel_delayable(&state->pa_timer); SET_FLAG(flag_pa_terminated); @@ -541,6 +570,7 @@ static int add_source(struct sync_state *state) bt_addr_le_copy(¶m.addr, &sync_info.addr); param.sid = sync_info.sid; + param.pa_state = BT_BAP_PA_STATE_SYNCED; param.encrypt_state = BT_BAP_BIG_ENC_STATE_NO_ENC; param.broadcast_id = g_broadcast_id; param.num_subgroups = 1U; @@ -657,6 +687,8 @@ static int remove_source(struct sync_state *state) return err; } + state->recv_state = NULL; + WAIT_FOR_FLAG(flag_recv_state_updated); return 0; @@ -680,13 +712,48 @@ static void remove_all_sources(void) printk("[%zu]: Source removed with id %u\n", i, state->src_id); + } + } +} - printk("Terminating PA sync\n"); - err = pa_sync_term(state); - if (err) { - FAIL("[%zu]: PA sync term failed (err %d)\n", err); - return; - } +static void terminate_all_pa(void) +{ + for (size_t i = 0U; i < ARRAY_SIZE(sync_states); i++) { + int err; + struct sync_state *state = &sync_states[i]; + + if (state->pa_sync == NULL) { + continue; + } + + err = pa_sync_term(state); + if (err != 0) { + FAIL("[%zu]: PA sync term failed (err %d)\n", i, err); + return; + } + } +} + +static void set_bis_sync_state(struct sync_state *state, + uint32_t bis_sync_req[CONFIG_BT_BAP_BASS_MAX_SUBGROUPS]) +{ + int err; + + err = bt_bap_scan_delegator_set_bis_sync_state(state->src_id, bis_sync_req); + if (err != 0) { + FAIL("Could not set BIS sync state: %d\n", err); + return; + } +} + +static void set_all_bis_sync_states(uint32_t bis_sync_req[CONFIG_BT_BAP_BASS_MAX_SUBGROUPS]) +{ + for (size_t i = 0U; i < ARRAY_SIZE(sync_states); i++) { + struct sync_state *state = &sync_states[i]; + + if (state->recv_state != NULL) { + printk("[%zu]: Setting BIS sync state\n", i); + set_bis_sync_state(state, bis_sync_req); } } } @@ -886,7 +953,12 @@ static void test_main_server_sync_server_rem(void) /* Set the BIS sync state */ sync_all_broadcasts(); - /* Remote all sources, causing PA sync term request to trigger */ + uint32_t bis_sync_req[CONFIG_BT_BAP_BASS_MAX_SUBGROUPS] = {0}; + + set_all_bis_sync_states(bis_sync_req); + terminate_all_pa(); + + /* Remove all sources, causing PA sync term request to trigger */ remove_all_sources(); /* Wait for PA sync to be terminated */ diff --git a/tests/bsim/bluetooth/tester/src/bsim_btp.c b/tests/bsim/bluetooth/tester/src/bsim_btp.c index 19b196d1fe62..eaeaa427a70e 100644 --- a/tests/bsim/bluetooth/tester/src/bsim_btp.c +++ b/tests/bsim/bluetooth/tester/src/bsim_btp.c @@ -1041,6 +1041,10 @@ static bool is_valid_ascs_packet_len(const struct btp_hdr *hdr, struct net_buf_s return buf_simple->len == 0U; case BTP_ASCS_EV_ASE_STATE_CHANGED: return buf_simple->len == sizeof(struct btp_ascs_ase_state_changed_ev); + case BTP_ASCS_EV_CIS_CONNECTED: + return buf_simple->len == sizeof(struct btp_ascs_cis_connected_ev); + case BTP_ASCS_EV_CIS_DISCONNECTED: + return buf_simple->len == sizeof(struct btp_ascs_cis_disconnected_ev); default: LOG_ERR("Unhandled opcode 0x%02X", hdr->opcode); return false;