| /* |
| * Copyright (c) 2021-2022 Nordic Semiconductor ASA |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #ifdef CONFIG_BT_BAP_SCAN_DELEGATOR |
| #include <zephyr/bluetooth/bluetooth.h> |
| #include <zephyr/bluetooth/byteorder.h> |
| #include <zephyr/bluetooth/audio/audio.h> |
| #include <zephyr/bluetooth/audio/bap.h> |
| #include <zephyr/sys/byteorder.h> |
| #include "common.h" |
| |
| extern enum bst_result_t bst_result; |
| |
| #define SYNC_RETRY_COUNT 6 /* similar to retries for connections */ |
| #define PA_SYNC_SKIP 5 |
| |
| CREATE_FLAG(flag_pa_synced); |
| CREATE_FLAG(flag_pa_terminated); |
| CREATE_FLAG(flag_broadcast_code_received); |
| CREATE_FLAG(flag_recv_state_updated); |
| CREATE_FLAG(flag_bis_sync_requested); |
| CREATE_FLAG(flag_bis_sync_term_requested); |
| static volatile uint32_t g_broadcast_id; |
| |
| struct sync_state { |
| uint8_t src_id; |
| const struct bt_bap_scan_delegator_recv_state *recv_state; |
| bool pa_syncing; |
| struct k_work_delayable pa_timer; |
| struct bt_le_per_adv_sync *pa_sync; |
| uint8_t broadcast_code[BT_AUDIO_BROADCAST_CODE_SIZE]; |
| uint32_t bis_sync_req[BT_BAP_SCAN_DELEGATOR_MAX_SUBGROUPS]; |
| } sync_states[CONFIG_BT_BAP_SCAN_DELEGATOR_RECV_STATE_COUNT]; |
| |
| static struct sync_state *sync_state_get(const struct bt_bap_scan_delegator_recv_state *recv_state) |
| { |
| for (size_t i = 0U; i < ARRAY_SIZE(sync_states); i++) { |
| if (sync_states[i].recv_state == recv_state) { |
| return &sync_states[i]; |
| } |
| } |
| |
| return NULL; |
| } |
| |
| static struct sync_state *sync_state_get_or_new( |
| const struct bt_bap_scan_delegator_recv_state *recv_state) |
| { |
| struct sync_state *free_state = NULL; |
| |
| for (size_t i = 0U; i < ARRAY_SIZE(sync_states); i++) { |
| if (sync_states[i].recv_state == NULL && |
| free_state == NULL) { |
| free_state = &sync_states[i]; |
| |
| if (recv_state == NULL) { |
| return free_state; |
| } |
| } |
| |
| if (sync_states[i].recv_state == recv_state) { |
| return &sync_states[i]; |
| } |
| } |
| |
| return free_state; |
| } |
| |
| static struct sync_state *sync_state_get_by_pa(struct bt_le_per_adv_sync *sync) |
| { |
| for (size_t i = 0U; i < ARRAY_SIZE(sync_states); i++) { |
| if (sync_states[i].pa_sync == sync) { |
| return &sync_states[i]; |
| } |
| } |
| |
| return NULL; |
| } |
| |
| static struct sync_state *sync_state_get_by_src_id(uint8_t src_id) |
| { |
| for (size_t i = 0U; i < ARRAY_SIZE(sync_states); i++) { |
| if (sync_states[i].src_id == src_id) { |
| return &sync_states[i]; |
| } |
| } |
| |
| return NULL; |
| } |
| |
| static uint16_t interval_to_sync_timeout(uint16_t pa_interval) |
| { |
| uint16_t pa_timeout; |
| |
| if (pa_interval == BT_BAP_PA_INTERVAL_UNKNOWN) { |
| /* Use maximum value to maximize chance of success */ |
| pa_timeout = BT_GAP_PER_ADV_MAX_TIMEOUT; |
| } else { |
| /* Ensure that the following calculation does not overflow silently */ |
| __ASSERT(SYNC_RETRY_COUNT < 10, |
| "SYNC_RETRY_COUNT shall be less than 10"); |
| |
| /* Add retries and convert to unit in 10's of ms */ |
| pa_timeout = ((uint32_t)pa_interval * SYNC_RETRY_COUNT) / 10; |
| |
| /* Enforce restraints */ |
| pa_timeout = CLAMP(pa_timeout, BT_GAP_PER_ADV_MIN_TIMEOUT, |
| BT_GAP_PER_ADV_MAX_TIMEOUT); |
| } |
| |
| return pa_timeout; |
| } |
| |
| static void pa_timer_handler(struct k_work *work) |
| { |
| struct k_work_delayable *dwork = k_work_delayable_from_work(work); |
| struct sync_state *state = CONTAINER_OF(dwork, struct sync_state, pa_timer); |
| |
| state->pa_syncing = false; |
| |
| if (state->recv_state != NULL) { |
| enum bt_bap_pa_state pa_state; |
| |
| if (state->recv_state->pa_sync_state == BT_BAP_PA_STATE_INFO_REQ) { |
| pa_state = BT_BAP_PA_STATE_NO_PAST; |
| } else { |
| pa_state = BT_BAP_PA_STATE_FAILED; |
| } |
| |
| bt_bap_scan_delegator_set_pa_state(state->recv_state->src_id, |
| pa_state); |
| } |
| |
| FAIL("PA timeout\n"); |
| } |
| |
| static int pa_sync_past(struct bt_conn *conn, |
| struct sync_state *state, |
| uint16_t pa_interval) |
| { |
| struct bt_le_per_adv_sync_transfer_param param = { 0 }; |
| int err; |
| |
| param.skip = PA_SYNC_SKIP; |
| param.timeout = interval_to_sync_timeout(pa_interval); |
| |
| 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, |
| K_MSEC(param.timeout * 10)); |
| } |
| |
| return err; |
| } |
| |
| static int pa_sync_no_past(struct sync_state *state, |
| uint16_t pa_interval) |
| { |
| const struct bt_bap_scan_delegator_recv_state *recv_state; |
| struct bt_le_per_adv_sync_param param = { 0 }; |
| int err; |
| |
| recv_state = state->recv_state; |
| state->src_id = recv_state->src_id; |
| |
| bt_addr_le_copy(¶m.addr, &recv_state->addr); |
| param.sid = recv_state->adv_sid; |
| param.skip = PA_SYNC_SKIP; |
| param.timeout = interval_to_sync_timeout(pa_interval); |
| |
| /* TODO: Validate that the advertise is broadcasting the same |
| * broadcast_id that the receive state has |
| */ |
| err = bt_le_per_adv_sync_create(¶m, &state->pa_sync); |
| if (err != 0) { |
| printk("Could not sync per adv: %d\n", err); |
| } else { |
| char addr_str[BT_ADDR_LE_STR_LEN]; |
| |
| bt_addr_le_to_str(&recv_state->addr, addr_str, sizeof(addr_str)); |
| printk("PA sync pending for addr %s\n", addr_str); |
| state->pa_syncing = true; |
| k_work_init_delayable(&state->pa_timer, pa_timer_handler); |
| (void)k_work_reschedule(&state->pa_timer, |
| K_MSEC(param.timeout * 10)); |
| } |
| |
| return err; |
| } |
| |
| static int pa_sync_term(struct sync_state *state) |
| { |
| int err; |
| |
| (void)k_work_cancel_delayable(&state->pa_timer); |
| |
| if (state->pa_sync == NULL) { |
| return -1; |
| } |
| |
| printk("Deleting PA sync\n"); |
| |
| err = bt_le_per_adv_sync_delete(state->pa_sync); |
| if (err != 0) { |
| FAIL("Could not delete per adv sync: %d\n", err); |
| } else { |
| state->pa_syncing = false; |
| state->pa_sync = NULL; |
| } |
| |
| return err; |
| } |
| |
| static void recv_state_updated_cb(struct bt_conn *conn, |
| const struct bt_bap_scan_delegator_recv_state *recv_state) |
| { |
| struct sync_state *state; |
| |
| printk("Receive state with ID %u updated\n", recv_state->src_id); |
| |
| state = sync_state_get_by_src_id(recv_state->src_id); |
| if (state == NULL) { |
| FAIL("Could not get state"); |
| 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); |
| return; |
| } |
| } else { |
| state->recv_state = recv_state; |
| } |
| |
| SET_FLAG(flag_recv_state_updated); |
| } |
| |
| static int pa_sync_req_cb(struct bt_conn *conn, |
| const struct bt_bap_scan_delegator_recv_state *recv_state, |
| bool past_avail, uint16_t pa_interval) |
| { |
| struct sync_state *state; |
| int err; |
| |
| printk("PA Sync request: past_avail %u, pa_interval 0x%04x\n: %p", |
| past_avail, pa_interval, recv_state); |
| |
| state = sync_state_get_or_new(recv_state); |
| if (state == NULL) { |
| FAIL("Could not get state"); |
| return -1; |
| } |
| |
| state->recv_state = recv_state; |
| state->src_id = recv_state->src_id; |
| |
| if (recv_state->pa_sync_state == BT_BAP_PA_STATE_SYNCED || |
| recv_state->pa_sync_state == BT_BAP_PA_STATE_INFO_REQ) { |
| /* Already syncing */ |
| /* TODO: Terminate existing sync and then sync to new?*/ |
| return -1; |
| } |
| |
| if (past_avail) { |
| err = pa_sync_past(conn, state, pa_interval); |
| } else { |
| err = pa_sync_no_past(state, pa_interval); |
| } |
| |
| return err; |
| } |
| |
| static int pa_sync_term_req_cb(struct bt_conn *conn, |
| const struct bt_bap_scan_delegator_recv_state *recv_state) |
| { |
| struct sync_state *state; |
| |
| printk("PA Sync term request for %p\n", recv_state); |
| |
| state = sync_state_get(recv_state); |
| if (state == NULL) { |
| FAIL("Could not get state\n"); |
| return -1; |
| } |
| |
| return pa_sync_term(state); |
| } |
| |
| static void broadcast_code_cb(struct bt_conn *conn, |
| const struct bt_bap_scan_delegator_recv_state *recv_state, |
| const uint8_t broadcast_code[BT_AUDIO_BROADCAST_CODE_SIZE]) |
| { |
| struct sync_state *state; |
| |
| printk("Broadcast code received for %p\n", recv_state); |
| |
| state = sync_state_get(recv_state); |
| if (state == NULL) { |
| FAIL("Could not get state\n"); |
| return; |
| } |
| |
| (void)memcpy(state->broadcast_code, broadcast_code, BT_AUDIO_BROADCAST_CODE_SIZE); |
| |
| SET_FLAG(flag_broadcast_code_received); |
| } |
| |
| static int bis_sync_req_cb(struct bt_conn *conn, |
| const struct bt_bap_scan_delegator_recv_state *recv_state, |
| const uint32_t bis_sync_req[BT_BAP_SCAN_DELEGATOR_MAX_SUBGROUPS]) |
| { |
| struct sync_state *state; |
| bool sync_bis; |
| |
| printk("BIS sync request received for %p\n", recv_state); |
| for (int i = 0; i < BT_BAP_SCAN_DELEGATOR_MAX_SUBGROUPS; i++) { |
| if (bis_sync_req[i]) { |
| sync_bis = true; |
| } |
| |
| printk(" [%d]: 0x%08x\n", i, bis_sync_req[i]); |
| } |
| |
| state = sync_state_get(recv_state); |
| if (state == NULL) { |
| FAIL("Could not get state\n"); |
| return -1; |
| } |
| |
| (void)memcpy(state->bis_sync_req, bis_sync_req, |
| sizeof(state->bis_sync_req)); |
| |
| if (sync_bis) { |
| SET_FLAG(flag_bis_sync_requested); |
| } else { |
| SET_FLAG(flag_bis_sync_term_requested); |
| } |
| |
| return 0; |
| } |
| |
| static struct bt_bap_scan_delegator_cb scan_delegator_cb = { |
| .recv_state_updated = recv_state_updated_cb, |
| .pa_sync_req = pa_sync_req_cb, |
| .pa_sync_term_req = pa_sync_term_req_cb, |
| .broadcast_code = broadcast_code_cb, |
| .bis_sync_req = bis_sync_req_cb, |
| }; |
| |
| static void pa_synced_cb(struct bt_le_per_adv_sync *sync, |
| struct bt_le_per_adv_sync_synced_info *info) |
| { |
| struct sync_state *state; |
| |
| printk("PA %p synced\n", sync); |
| |
| state = sync_state_get_by_pa(sync); |
| if (state == NULL) { |
| FAIL("Could not get sync state from PA sync %p\n", sync); |
| return; |
| } |
| |
| k_work_cancel_delayable(&state->pa_timer); |
| |
| SET_FLAG(flag_pa_synced); |
| } |
| |
| static void pa_term_cb(struct bt_le_per_adv_sync *sync, |
| const struct bt_le_per_adv_sync_term_info *info) |
| { |
| struct sync_state *state; |
| |
| printk("PA %p sync terminated\n", sync); |
| |
| state = sync_state_get_by_pa(sync); |
| if (state == NULL) { |
| FAIL("Could not get sync state from PA sync %p\n", sync); |
| return; |
| } |
| |
| k_work_cancel_delayable(&state->pa_timer); |
| |
| SET_FLAG(flag_pa_terminated); |
| } |
| |
| static struct bt_le_per_adv_sync_cb pa_sync_cb = { |
| .synced = pa_synced_cb, |
| .term = pa_term_cb, |
| }; |
| |
| static bool broadcast_source_found(struct bt_data *data, void *user_data) |
| { |
| struct bt_le_per_adv_sync_param sync_create_param = { 0 }; |
| const struct bt_le_scan_recv_info *info = user_data; |
| char addr_str[BT_ADDR_LE_STR_LEN]; |
| struct bt_uuid_16 adv_uuid; |
| struct sync_state *state; |
| int err; |
| |
| |
| if (data->type != BT_DATA_SVC_DATA16) { |
| return true; |
| } |
| |
| if (data->data_len < BT_UUID_SIZE_16 + BT_AUDIO_BROADCAST_ID_SIZE) { |
| return true; |
| } |
| |
| if (!bt_uuid_create(&adv_uuid.uuid, data->data, BT_UUID_SIZE_16)) { |
| return true; |
| } |
| |
| if (bt_uuid_cmp(&adv_uuid.uuid, BT_UUID_BROADCAST_AUDIO) != 0) { |
| return true; |
| } |
| |
| g_broadcast_id = sys_get_le24(data->data + BT_UUID_SIZE_16); |
| bt_addr_le_to_str(info->addr, addr_str, sizeof(addr_str)); |
| |
| printk("Found BAP broadcast source with address %s and ID 0x%06X\n", |
| addr_str, g_broadcast_id); |
| |
| state = sync_state_get_or_new(NULL); |
| if (state == NULL) { |
| FAIL("Failed to get sync state"); |
| return true; |
| } |
| |
| printk("Creating Periodic Advertising Sync\n"); |
| bt_addr_le_copy(&sync_create_param.addr, info->addr); |
| sync_create_param.sid = info->sid; |
| sync_create_param.timeout = 0xa; |
| |
| err = bt_le_per_adv_sync_create(&sync_create_param, |
| &state->pa_sync); |
| if (err != 0) { |
| FAIL("Could not create PA sync (err %d)\n", err); |
| return true; |
| } |
| |
| state->pa_syncing = true; |
| |
| return false; |
| } |
| |
| static void scan_recv_cb(const struct bt_le_scan_recv_info *info, |
| struct net_buf_simple *ad) |
| { |
| /* We are only interested in non-connectable periodic advertisers */ |
| if ((info->adv_props & BT_GAP_ADV_PROP_CONNECTABLE) != 0 || |
| info->interval == 0) { |
| return; |
| } |
| |
| bt_data_parse(ad, broadcast_source_found, (void *)info); |
| } |
| |
| static struct bt_le_scan_cb scan_cb = { |
| .recv = scan_recv_cb |
| }; |
| |
| static int add_source(struct sync_state *state) |
| { |
| struct bt_bap_scan_delegator_add_src_param param; |
| int res; |
| |
| UNSET_FLAG(flag_recv_state_updated); |
| |
| param.pa_sync = state->pa_sync; |
| param.encrypt_state = BT_BAP_BIG_ENC_STATE_NO_ENC; |
| param.broadcast_id = g_broadcast_id; |
| param.num_subgroups = 1U; |
| |
| for (uint8_t i = 0U; i < param.num_subgroups; i++) { |
| struct bt_bap_scan_delegator_subgroup *subgroup_param = ¶m.subgroups[i]; |
| |
| subgroup_param->bis_sync = BT_BAP_BIS_SYNC_NO_PREF; |
| subgroup_param->metadata_len = 0U; |
| (void)memset(subgroup_param->metadata, 0, sizeof(subgroup_param->metadata)); |
| } |
| |
| res = bt_bap_scan_delegator_add_src(¶m); |
| if (res < 0) { |
| return res; |
| } |
| |
| state->src_id = (uint8_t)res; |
| |
| WAIT_FOR_FLAG(flag_recv_state_updated); |
| |
| return res; |
| } |
| |
| static void add_all_sources(void) |
| { |
| for (size_t i = 0U; i < ARRAY_SIZE(sync_states); i++) { |
| struct sync_state *state = &sync_states[i]; |
| |
| if (state->pa_sync != NULL) { |
| int res; |
| |
| printk("[%zu]: Adding source\n", i); |
| |
| res = add_source(state); |
| if (res < 0) { |
| FAIL("[%zu]: Add source failed (err %d)\n", i, res); |
| return; |
| } |
| |
| printk("[%zu]: Source added with id %u\n", |
| i, state->src_id); |
| } |
| } |
| } |
| |
| static int mod_source(struct sync_state *state) |
| { |
| const uint16_t pref_context = BT_AUDIO_CONTEXT_TYPE_CONVERSATIONAL; |
| struct bt_bap_scan_delegator_mod_src_param param; |
| uint8_t pref_context_metadata[4] = { |
| 0x03, /* length of the type and value */ |
| BT_AUDIO_METADATA_TYPE_PREF_CONTEXT, |
| BT_BYTES_LIST_LE16(pref_context), |
| }; |
| int err; |
| |
| UNSET_FLAG(flag_recv_state_updated); |
| |
| param.src_id = state->src_id; |
| param.encrypt_state = BT_BAP_BIG_ENC_STATE_NO_ENC; |
| param.broadcast_id = g_broadcast_id; |
| param.num_subgroups = 1U; |
| |
| for (uint8_t i = 0U; i < param.num_subgroups; i++) { |
| struct bt_bap_scan_delegator_subgroup *subgroup_param = ¶m.subgroups[i]; |
| |
| subgroup_param->bis_sync = 0U; |
| subgroup_param->metadata_len = sizeof(pref_context_metadata); |
| (void)memcpy(subgroup_param->metadata, pref_context_metadata, |
| subgroup_param->metadata_len); |
| } |
| |
| err = bt_bap_scan_delegator_mod_src(¶m); |
| if (err < 0) { |
| return err; |
| } |
| |
| WAIT_FOR_FLAG(flag_recv_state_updated); |
| |
| return 0; |
| } |
| |
| static void mod_all_sources(void) |
| { |
| for (size_t i = 0U; i < ARRAY_SIZE(sync_states); i++) { |
| struct sync_state *state = &sync_states[i]; |
| |
| if (state->pa_sync != NULL) { |
| int err; |
| |
| printk("[%zu]: Modifying source\n", i); |
| |
| err = mod_source(state); |
| if (err < 0) { |
| FAIL("[%zu]: Modify source failed (err %d)\n", i, err); |
| return; |
| } |
| |
| printk("[%zu]: Source id modifed %u\n", |
| i, state->src_id); |
| } |
| } |
| } |
| |
| static int remove_source(struct sync_state *state) |
| { |
| int err; |
| |
| UNSET_FLAG(flag_recv_state_updated); |
| |
| /* We don't actually need to sync to the BIG/BISes */ |
| err = bt_bap_scan_delegator_rem_src(state->src_id); |
| if (err) { |
| return err; |
| } |
| |
| WAIT_FOR_FLAG(flag_recv_state_updated); |
| |
| return 0; |
| } |
| |
| static void remove_all_sources(void) |
| { |
| for (size_t i = 0U; i < ARRAY_SIZE(sync_states); i++) { |
| struct sync_state *state = &sync_states[i]; |
| |
| if (state->recv_state != NULL) { |
| int err; |
| |
| printk("[%zu]: Removing source\n", i); |
| |
| err = remove_source(state); |
| if (err) { |
| FAIL("[%zu]: Remove source failed (err %d)\n", err); |
| return; |
| } |
| |
| printk("[%zu]: Source removed with id %u\n", |
| i, state->src_id); |
| } |
| } |
| } |
| |
| static int sync_broadcast(struct sync_state *state) |
| { |
| int err; |
| |
| UNSET_FLAG(flag_recv_state_updated); |
| |
| /* We don't actually need to sync to the BIG/BISes */ |
| err = bt_bap_scan_delegator_set_bis_sync_state(state->src_id, |
| (uint32_t []){ BIT(1) }); |
| if (err) { |
| return err; |
| } |
| |
| WAIT_FOR_FLAG(flag_recv_state_updated); |
| |
| return 0; |
| } |
| |
| static void sync_all_broadcasts(void) |
| { |
| for (size_t i = 0U; i < ARRAY_SIZE(sync_states); i++) { |
| struct sync_state *state = &sync_states[i]; |
| |
| if (state->pa_sync != NULL) { |
| int res; |
| |
| printk("[%zu]: Setting broadcast sync state\n", i); |
| |
| res = sync_broadcast(state); |
| if (res < 0) { |
| FAIL("[%zu]: Broadcast sync state set failed (err %d)\n", i, res); |
| return; |
| } |
| |
| printk("[%zu]: Broadcast sync state set\n", i); |
| } |
| } |
| } |
| |
| static int common_init(void) |
| { |
| int err; |
| |
| err = bt_enable(NULL); |
| if (err) { |
| FAIL("Bluetooth init failed (err %d)\n", err); |
| return err; |
| } |
| |
| printk("Bluetooth initialized\n"); |
| |
| bt_bap_scan_delegator_register_cb(&scan_delegator_cb); |
| bt_le_per_adv_sync_cb_register(&pa_sync_cb); |
| |
| err = bt_le_adv_start(BT_LE_ADV_CONN_NAME, ad, AD_SIZE, NULL, 0); |
| if (err) { |
| FAIL("Advertising failed to start (err %d)\n", err); |
| return err; |
| } |
| |
| printk("Advertising successfully started\n"); |
| |
| WAIT_FOR_FLAG(flag_connected); |
| |
| return 0; |
| } |
| |
| static void test_main_client_sync(void) |
| { |
| int err; |
| |
| err = common_init(); |
| if (err) { |
| FAIL("common init failed (err %d)\n", err); |
| return; |
| } |
| |
| /* Wait for broadcast assistant to request us to sync to PA */ |
| WAIT_FOR_FLAG(flag_pa_synced); |
| |
| /* Wait for broadcast assistant to send us broadcast code */ |
| WAIT_FOR_FLAG(flag_broadcast_code_received); |
| |
| /* Mod all sources by modifying the metadata */ |
| mod_all_sources(); |
| |
| /* Wait for broadcast assistant to tell us to BIS sync */ |
| WAIT_FOR_FLAG(flag_bis_sync_requested); |
| |
| /* Set the BIS sync state */ |
| sync_all_broadcasts(); |
| |
| /* Wait for broadcast assistant to remove source and terminate PA sync */ |
| WAIT_FOR_FLAG(flag_pa_terminated); |
| |
| PASS("BAP Scan Delegator Client Sync passed\n"); |
| } |
| |
| static void test_main_server_sync_client_rem(void) |
| { |
| int err; |
| |
| err = common_init(); |
| if (err) { |
| FAIL("common init failed (err %d)\n", err); |
| return; |
| } |
| |
| bt_le_scan_cb_register(&scan_cb); |
| |
| err = bt_le_scan_start(BT_LE_SCAN_PASSIVE, NULL); |
| if (err != 0) { |
| FAIL("Could not start scan (%d)", err); |
| return; |
| } |
| |
| /* Wait for PA to sync */ |
| WAIT_FOR_FLAG(flag_pa_synced); |
| |
| /* Add PAs as receive state sources */ |
| add_all_sources(); |
| |
| /* Wait for broadcast assistant to send us broadcast code */ |
| WAIT_FOR_FLAG(flag_broadcast_code_received); |
| |
| /* Mod all sources by modifying the metadata */ |
| mod_all_sources(); |
| |
| /* Set the BIS sync state */ |
| sync_all_broadcasts(); |
| |
| /* For for client to remove source and thus terminate the PA */ |
| WAIT_FOR_FLAG(flag_pa_terminated); |
| |
| PASS("BAP Scan Delegator Server Sync Client Remove passed\n"); |
| } |
| |
| static void test_main_server_sync_server_rem(void) |
| { |
| int err; |
| |
| err = common_init(); |
| if (err) { |
| FAIL("common init failed (err %d)\n", err); |
| return; |
| } |
| |
| bt_le_scan_cb_register(&scan_cb); |
| |
| err = bt_le_scan_start(BT_LE_SCAN_PASSIVE, NULL); |
| if (err != 0) { |
| FAIL("Could not start scan (%d)", err); |
| return; |
| } |
| |
| /* Wait for PA to sync */ |
| WAIT_FOR_FLAG(flag_pa_synced); |
| |
| /* Add PAs as receive state sources */ |
| add_all_sources(); |
| |
| /* Wait for broadcast assistant to send us broadcast code */ |
| WAIT_FOR_FLAG(flag_broadcast_code_received); |
| |
| /* Mod all sources by modifying the metadata */ |
| mod_all_sources(); |
| |
| /* Set the BIS sync state */ |
| sync_all_broadcasts(); |
| |
| /* Remote all sources, causing PA sync term request to trigger */ |
| remove_all_sources(); |
| |
| /* Wait for PA sync to be terminated */ |
| WAIT_FOR_FLAG(flag_pa_terminated); |
| |
| PASS("BAP Scan Delegator Server Sync Server Remove passed\n"); |
| } |
| |
| static const struct bst_test_instance test_scan_delegator[] = { |
| { |
| .test_id = "bap_scan_delegator_client_sync", |
| .test_post_init_f = test_init, |
| .test_tick_f = test_tick, |
| .test_main_f = test_main_client_sync, |
| }, |
| { |
| .test_id = "bap_scan_delegator_server_sync_client_rem", |
| .test_post_init_f = test_init, |
| .test_tick_f = test_tick, |
| .test_main_f = test_main_server_sync_client_rem, |
| }, |
| { |
| .test_id = "bap_scan_delegator_server_sync_server_rem", |
| .test_post_init_f = test_init, |
| .test_tick_f = test_tick, |
| .test_main_f = test_main_server_sync_server_rem, |
| }, |
| BSTEST_END_MARKER |
| }; |
| |
| struct bst_test_list *test_scan_delegator_install(struct bst_test_list *tests) |
| { |
| return bst_add_tests(tests, test_scan_delegator); |
| } |
| #else |
| struct bst_test_list *test_scan_delegator_install(struct bst_test_list *tests) |
| { |
| return tests; |
| } |
| |
| #endif /* CONFIG_BT_BAP_SCAN_DELEGATOR */ |