Bluetooth: Audio: CAP: babblesim test for broadcast reception start
Add a babblesim test for the broadcast reception start procedure from the CAP commander Signed-off-by: Andries Kruithof <andries.kruithof@nordicsemi.no>
This commit is contained in:
parent
9ae5352372
commit
811387600a
4 changed files with 758 additions and 18 deletions
|
@ -36,12 +36,12 @@
|
||||||
#if defined(CONFIG_BT_BAP_BROADCAST_SINK)
|
#if defined(CONFIG_BT_BAP_BROADCAST_SINK)
|
||||||
extern enum bst_result_t bst_result;
|
extern enum bst_result_t bst_result;
|
||||||
|
|
||||||
CREATE_FLAG(broadcaster_found);
|
CREATE_FLAG(flag_broadcaster_found);
|
||||||
CREATE_FLAG(flag_base_received);
|
CREATE_FLAG(flag_base_received);
|
||||||
CREATE_FLAG(flag_base_metadata_updated);
|
CREATE_FLAG(flag_base_metadata_updated);
|
||||||
CREATE_FLAG(pa_synced);
|
CREATE_FLAG(flag_pa_synced);
|
||||||
CREATE_FLAG(flag_syncable);
|
CREATE_FLAG(flag_syncable);
|
||||||
CREATE_FLAG(pa_sync_lost);
|
CREATE_FLAG(flag_pa_sync_lost);
|
||||||
CREATE_FLAG(flag_received);
|
CREATE_FLAG(flag_received);
|
||||||
CREATE_FLAG(flag_pa_request);
|
CREATE_FLAG(flag_pa_request);
|
||||||
CREATE_FLAG(flag_bis_sync_requested);
|
CREATE_FLAG(flag_bis_sync_requested);
|
||||||
|
@ -265,7 +265,7 @@ static bool scan_check_and_sync_broadcast(struct bt_data *data, void *user_data)
|
||||||
struct bt_uuid_16 adv_uuid;
|
struct bt_uuid_16 adv_uuid;
|
||||||
uint32_t broadcast_id;
|
uint32_t broadcast_id;
|
||||||
|
|
||||||
if (TEST_FLAG(broadcaster_found)) {
|
if (TEST_FLAG(flag_broadcaster_found)) {
|
||||||
/* no-op*/
|
/* no-op*/
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -293,7 +293,7 @@ static bool scan_check_and_sync_broadcast(struct bt_data *data, void *user_data)
|
||||||
printk("Found broadcaster with ID 0x%06X and addr %s and sid 0x%02X\n", broadcast_id,
|
printk("Found broadcaster with ID 0x%06X and addr %s and sid 0x%02X\n", broadcast_id,
|
||||||
le_addr, info->sid);
|
le_addr, info->sid);
|
||||||
|
|
||||||
SET_FLAG(broadcaster_found);
|
SET_FLAG(flag_broadcaster_found);
|
||||||
|
|
||||||
/* Store info for PA sync parameters */
|
/* Store info for PA sync parameters */
|
||||||
memcpy(&broadcaster_info, info, sizeof(broadcaster_info));
|
memcpy(&broadcaster_info, info, sizeof(broadcaster_info));
|
||||||
|
@ -322,7 +322,7 @@ static void bap_pa_sync_synced_cb(struct bt_le_per_adv_sync *sync,
|
||||||
printk("PA sync %p synced for broadcast sink with broadcast ID 0x%06X\n", sync,
|
printk("PA sync %p synced for broadcast sink with broadcast ID 0x%06X\n", sync,
|
||||||
broadcaster_broadcast_id);
|
broadcaster_broadcast_id);
|
||||||
|
|
||||||
SET_FLAG(pa_synced);
|
SET_FLAG(flag_pa_synced);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -333,7 +333,7 @@ static void bap_pa_sync_terminated_cb(struct bt_le_per_adv_sync *sync,
|
||||||
printk("PA sync %p lost with reason %u\n", sync, info->reason);
|
printk("PA sync %p lost with reason %u\n", sync, info->reason);
|
||||||
pa_sync = NULL;
|
pa_sync = NULL;
|
||||||
|
|
||||||
SET_FLAG(pa_sync_lost);
|
SET_FLAG(flag_pa_sync_lost);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -640,9 +640,9 @@ static int init(void)
|
||||||
bt_le_per_adv_sync_cb_register(&bap_pa_sync_cb);
|
bt_le_per_adv_sync_cb_register(&bap_pa_sync_cb);
|
||||||
bt_le_scan_cb_register(&bap_scan_cb);
|
bt_le_scan_cb_register(&bap_scan_cb);
|
||||||
|
|
||||||
UNSET_FLAG(broadcaster_found);
|
UNSET_FLAG(flag_broadcaster_found);
|
||||||
UNSET_FLAG(flag_base_received);
|
UNSET_FLAG(flag_base_received);
|
||||||
UNSET_FLAG(pa_synced);
|
UNSET_FLAG(flag_pa_synced);
|
||||||
|
|
||||||
for (size_t i = 0U; i < ARRAY_SIZE(streams); i++) {
|
for (size_t i = 0U; i < ARRAY_SIZE(streams); i++) {
|
||||||
streams[i] = bap_stream_from_audio_test_stream(&broadcast_sink_streams[i]);
|
streams[i] = bap_stream_from_audio_test_stream(&broadcast_sink_streams[i]);
|
||||||
|
@ -710,7 +710,7 @@ static void test_scan_and_pa_sync(void)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
WAIT_FOR_FLAG(broadcaster_found);
|
WAIT_FOR_FLAG(flag_broadcaster_found);
|
||||||
|
|
||||||
printk("Broadcast source found, stopping scan\n");
|
printk("Broadcast source found, stopping scan\n");
|
||||||
err = bt_le_scan_stop();
|
err = bt_le_scan_stop();
|
||||||
|
@ -728,7 +728,7 @@ static void test_scan_and_pa_sync(void)
|
||||||
}
|
}
|
||||||
|
|
||||||
printk("Waiting for PA sync\n");
|
printk("Waiting for PA sync\n");
|
||||||
WAIT_FOR_FLAG(pa_synced);
|
WAIT_FOR_FLAG(flag_pa_synced);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void test_broadcast_sink_create(void)
|
static void test_broadcast_sink_create(void)
|
||||||
|
@ -976,7 +976,7 @@ static void test_main(void)
|
||||||
* either way will work.
|
* either way will work.
|
||||||
*/
|
*/
|
||||||
printk("Waiting for PA disconnected\n");
|
printk("Waiting for PA disconnected\n");
|
||||||
WAIT_FOR_FLAG(pa_sync_lost);
|
WAIT_FOR_FLAG(flag_pa_sync_lost);
|
||||||
|
|
||||||
printk("Waiting for streams to be stopped\n");
|
printk("Waiting for streams to be stopped\n");
|
||||||
for (size_t i = 0U; i < ARRAY_SIZE(streams); i++) {
|
for (size_t i = 0U; i < ARRAY_SIZE(streams); i++) {
|
||||||
|
|
|
@ -46,11 +46,15 @@ extern enum bst_result_t bst_result;
|
||||||
#define SOURCE_CONTEXT (BT_AUDIO_CONTEXT_TYPE_UNSPECIFIED | BT_AUDIO_CONTEXT_TYPE_NOTIFICATIONS)
|
#define SOURCE_CONTEXT (BT_AUDIO_CONTEXT_TYPE_UNSPECIFIED | BT_AUDIO_CONTEXT_TYPE_NOTIFICATIONS)
|
||||||
|
|
||||||
CREATE_FLAG(flag_broadcaster_found);
|
CREATE_FLAG(flag_broadcaster_found);
|
||||||
|
CREATE_FLAG(flag_broadcast_code);
|
||||||
CREATE_FLAG(flag_base_received);
|
CREATE_FLAG(flag_base_received);
|
||||||
CREATE_FLAG(flag_pa_synced);
|
CREATE_FLAG(flag_pa_synced);
|
||||||
CREATE_FLAG(flag_syncable);
|
CREATE_FLAG(flag_syncable);
|
||||||
CREATE_FLAG(flag_received);
|
CREATE_FLAG(flag_received);
|
||||||
CREATE_FLAG(flag_pa_sync_lost);
|
CREATE_FLAG(flag_pa_sync_lost);
|
||||||
|
CREATE_FLAG(flag_pa_request);
|
||||||
|
CREATE_FLAG(flag_bis_sync_requested);
|
||||||
|
CREATE_FLAG(flag_base_metadata_updated);
|
||||||
|
|
||||||
static struct bt_bap_broadcast_sink *g_broadcast_sink;
|
static struct bt_bap_broadcast_sink *g_broadcast_sink;
|
||||||
static struct bt_le_scan_recv_info broadcaster_info;
|
static struct bt_le_scan_recv_info broadcaster_info;
|
||||||
|
@ -58,6 +62,9 @@ static bt_addr_le_t broadcaster_addr;
|
||||||
static struct bt_le_per_adv_sync *pa_sync;
|
static struct bt_le_per_adv_sync *pa_sync;
|
||||||
static uint32_t broadcaster_broadcast_id;
|
static uint32_t broadcaster_broadcast_id;
|
||||||
static struct audio_test_stream broadcast_sink_streams[CONFIG_BT_BAP_BROADCAST_SNK_STREAM_COUNT];
|
static struct audio_test_stream broadcast_sink_streams[CONFIG_BT_BAP_BROADCAST_SNK_STREAM_COUNT];
|
||||||
|
static struct bt_le_ext_adv *ext_adv;
|
||||||
|
static uint32_t requested_bis_sync;
|
||||||
|
static const struct bt_bap_scan_delegator_recv_state *req_recv_state;
|
||||||
|
|
||||||
static const struct bt_audio_codec_cap codec_cap = BT_AUDIO_CODEC_CAP_LC3(
|
static const struct bt_audio_codec_cap codec_cap = BT_AUDIO_CODEC_CAP_LC3(
|
||||||
BT_AUDIO_CODEC_CAP_FREQ_ANY, BT_AUDIO_CODEC_CAP_DURATION_ANY,
|
BT_AUDIO_CODEC_CAP_FREQ_ANY, BT_AUDIO_CODEC_CAP_DURATION_ANY,
|
||||||
|
@ -110,6 +117,8 @@ static bool subgroup_data_func_cb(struct bt_data *data, void *user_data)
|
||||||
|
|
||||||
static bool valid_subgroup_metadata_cb(const struct bt_bap_base_subgroup *subgroup, void *user_data)
|
static bool valid_subgroup_metadata_cb(const struct bt_bap_base_subgroup *subgroup, void *user_data)
|
||||||
{
|
{
|
||||||
|
static uint8_t metadata[CONFIG_BT_AUDIO_CODEC_CFG_MAX_METADATA_SIZE];
|
||||||
|
static size_t metadata_size;
|
||||||
bool stream_context_found = false;
|
bool stream_context_found = false;
|
||||||
uint8_t *meta;
|
uint8_t *meta;
|
||||||
int ret;
|
int ret;
|
||||||
|
@ -120,6 +129,14 @@ static bool valid_subgroup_metadata_cb(const struct bt_bap_base_subgroup *subgro
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (TEST_FLAG(flag_base_received) &&
|
||||||
|
((size_t)ret != metadata_size || memcmp(meta, metadata, metadata_size) != 0)) {
|
||||||
|
printk("Metadata updated\n");
|
||||||
|
SET_FLAG(flag_base_metadata_updated);
|
||||||
|
}
|
||||||
|
|
||||||
|
metadata_size = (size_t)ret;
|
||||||
|
|
||||||
ret = bt_audio_data_parse(meta, (size_t)ret, subgroup_data_func_cb, &stream_context_found);
|
ret = bt_audio_data_parse(meta, (size_t)ret, subgroup_data_func_cb, &stream_context_found);
|
||||||
if (ret != 0 && ret != -ECANCELED) {
|
if (ret != 0 && ret != -ECANCELED) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -139,10 +156,6 @@ static void base_recv_cb(struct bt_bap_broadcast_sink *sink, const struct bt_bap
|
||||||
uint32_t base_bis_index_bitfield = 0U;
|
uint32_t base_bis_index_bitfield = 0U;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (TEST_FLAG(flag_base_received)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = bt_bap_base_get_subgroup_count(base);
|
ret = bt_bap_base_get_subgroup_count(base);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
FAIL("Failed to get subgroup count: %d\n", ret);
|
FAIL("Failed to get subgroup count: %d\n", ret);
|
||||||
|
@ -286,7 +299,7 @@ static void recv_cb(struct bt_bap_stream *stream, const struct bt_iso_recv_info
|
||||||
{
|
{
|
||||||
struct audio_test_stream *test_stream = audio_test_stream_from_bap_stream(stream);
|
struct audio_test_stream *test_stream = audio_test_stream_from_bap_stream(stream);
|
||||||
|
|
||||||
if ((test_stream->rx_cnt % 100U) == 0U) {
|
if ((test_stream->rx_cnt % 50U) == 0U) {
|
||||||
printk("[%zu]: Incoming audio on stream %p len %u and ts %u\n", test_stream->rx_cnt,
|
printk("[%zu]: Incoming audio on stream %p len %u and ts %u\n", test_stream->rx_cnt,
|
||||||
stream, buf->len, info->ts);
|
stream, buf->len, info->ts);
|
||||||
}
|
}
|
||||||
|
@ -359,6 +372,77 @@ static struct bt_bap_stream_ops unicast_stream_ops = {
|
||||||
.enabled = unicast_stream_enabled_cb,
|
.enabled = unicast_stream_enabled_cb,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
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 -EALREADY;
|
||||||
|
}
|
||||||
|
|
||||||
|
printk("Sync request\n");
|
||||||
|
req_recv_state = recv_state;
|
||||||
|
|
||||||
|
bt_addr_le_copy(&broadcaster_addr, &req_recv_state->addr);
|
||||||
|
broadcaster_info.sid = req_recv_state->adv_sid;
|
||||||
|
broadcaster_info.interval = pa_interval;
|
||||||
|
|
||||||
|
SET_FLAG(flag_pa_request);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int pa_sync_term_req_cb(struct bt_conn *conn,
|
||||||
|
const struct bt_bap_scan_delegator_recv_state *recv_state)
|
||||||
|
{
|
||||||
|
if (pa_sync == NULL || recv_state->pa_sync_state == BT_BAP_PA_STATE_NOT_SYNCED) {
|
||||||
|
return -EALREADY;
|
||||||
|
}
|
||||||
|
|
||||||
|
req_recv_state = recv_state;
|
||||||
|
|
||||||
|
UNSET_FLAG(flag_pa_request);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
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[CONFIG_BT_BAP_BASS_MAX_SUBGROUPS])
|
||||||
|
{
|
||||||
|
/* We only care about a single subgroup in this test */
|
||||||
|
requested_bis_sync = bis_sync_req[0];
|
||||||
|
broadcaster_broadcast_id = recv_state->broadcast_id;
|
||||||
|
if (bis_sync_req[0] != 0) {
|
||||||
|
SET_FLAG(flag_bis_sync_requested);
|
||||||
|
} else {
|
||||||
|
UNSET_FLAG(flag_bis_sync_requested);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
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])
|
||||||
|
{
|
||||||
|
printk("Broadcast code received for %p\n", recv_state);
|
||||||
|
|
||||||
|
req_recv_state = recv_state;
|
||||||
|
|
||||||
|
SET_FLAG(flag_broadcast_code);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct bt_bap_scan_delegator_cb scan_delegator_cbs = {
|
||||||
|
.pa_sync_req = pa_sync_req_cb,
|
||||||
|
.pa_sync_term_req = pa_sync_term_req_cb,
|
||||||
|
.bis_sync_req = bis_sync_req_cb,
|
||||||
|
.broadcast_code = broadcast_code_cb,
|
||||||
|
};
|
||||||
|
|
||||||
/* TODO: Expand with CAP service data */
|
/* TODO: Expand with CAP service data */
|
||||||
static const struct bt_data cap_acceptor_ad[] = {
|
static const struct bt_data cap_acceptor_ad[] = {
|
||||||
BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)),
|
BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)),
|
||||||
|
@ -558,6 +642,35 @@ static int set_supported_contexts(void)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void test_start_adv(void)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
|
||||||
|
/* Create a connectable non-scannable advertising set */
|
||||||
|
err = bt_le_ext_adv_create(BT_LE_ADV_CONN_ONE_TIME, NULL, &ext_adv);
|
||||||
|
if (err != 0) {
|
||||||
|
FAIL("Failed to create advertising set (err %d)\n", err);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Add cap acceptor advertising data */
|
||||||
|
err = bt_le_ext_adv_set_data(ext_adv, cap_acceptor_ad, ARRAY_SIZE(cap_acceptor_ad), NULL,
|
||||||
|
0);
|
||||||
|
if (err != 0) {
|
||||||
|
FAIL("Failed to set advertising data (err %d)\n", err);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = bt_le_ext_adv_start(ext_adv, BT_LE_EXT_ADV_START_DEFAULT);
|
||||||
|
if (err != 0) {
|
||||||
|
FAIL("Failed to start advertising set (err %d)\n", err);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void set_available_contexts(void)
|
static void set_available_contexts(void)
|
||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
|
@ -644,6 +757,7 @@ static void init(void)
|
||||||
FAIL("Advertising failed to start (err %d)\n", err);
|
FAIL("Advertising failed to start (err %d)\n", err);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
test_start_adv();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IS_ENABLED(CONFIG_BT_BAP_BROADCAST_SINK)) {
|
if (IS_ENABLED(CONFIG_BT_BAP_BROADCAST_SINK)) {
|
||||||
|
@ -662,10 +776,15 @@ static void init(void)
|
||||||
bt_bap_broadcast_sink_register_cb(&broadcast_sink_cbs);
|
bt_bap_broadcast_sink_register_cb(&broadcast_sink_cbs);
|
||||||
bt_le_per_adv_sync_cb_register(&bap_pa_sync_cb);
|
bt_le_per_adv_sync_cb_register(&bap_pa_sync_cb);
|
||||||
bt_le_scan_cb_register(&bap_scan_cb);
|
bt_le_scan_cb_register(&bap_scan_cb);
|
||||||
|
bt_bap_scan_delegator_register_cb(&scan_delegator_cbs);
|
||||||
|
|
||||||
UNSET_FLAG(flag_broadcaster_found);
|
UNSET_FLAG(flag_broadcaster_found);
|
||||||
|
UNSET_FLAG(flag_broadcast_code);
|
||||||
UNSET_FLAG(flag_base_received);
|
UNSET_FLAG(flag_base_received);
|
||||||
UNSET_FLAG(flag_pa_synced);
|
UNSET_FLAG(flag_pa_synced);
|
||||||
|
UNSET_FLAG(flag_pa_request);
|
||||||
|
UNSET_FLAG(flag_received);
|
||||||
|
UNSET_FLAG(flag_base_metadata_updated);
|
||||||
|
|
||||||
for (size_t i = 0U; i < ARRAY_SIZE(broadcast_sink_streams); i++) {
|
for (size_t i = 0U; i < ARRAY_SIZE(broadcast_sink_streams); i++) {
|
||||||
bt_cap_stream_ops_register(
|
bt_cap_stream_ops_register(
|
||||||
|
@ -899,6 +1018,93 @@ static void test_cap_acceptor_broadcast(void)
|
||||||
PASS("CAP acceptor broadcast passed\n");
|
PASS("CAP acceptor broadcast passed\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void test_cap_acceptor_broadcast_reception(void)
|
||||||
|
{
|
||||||
|
static struct bt_bap_stream *bap_streams[ARRAY_SIZE(broadcast_sink_streams)];
|
||||||
|
size_t stream_count;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
init();
|
||||||
|
|
||||||
|
WAIT_FOR_FLAG(flag_pa_request);
|
||||||
|
|
||||||
|
err = pa_sync_create();
|
||||||
|
if (err != 0) {
|
||||||
|
FAIL("Could not create Broadcast PA sync: %d\n", err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
printk("Waiting for PA sync\n");
|
||||||
|
WAIT_FOR_FLAG(flag_pa_synced);
|
||||||
|
|
||||||
|
err = bt_bap_broadcast_sink_create(pa_sync, broadcaster_broadcast_id, &g_broadcast_sink);
|
||||||
|
if (err != 0) {
|
||||||
|
FAIL("Unable to create the sink: %d\n", err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (req_recv_state->num_subgroups == 0) {
|
||||||
|
FAIL("Number of subgroups is 0");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
printk("Broadcast source PA synced, waiting for BASE\n");
|
||||||
|
WAIT_FOR_FLAG(flag_base_received);
|
||||||
|
printk("BASE received\n");
|
||||||
|
|
||||||
|
WAIT_FOR_FLAG(flag_syncable);
|
||||||
|
|
||||||
|
for (size_t i = 0U; i < ARRAY_SIZE(broadcast_sink_streams); i++) {
|
||||||
|
bap_streams[i] = bap_stream_from_audio_test_stream(&broadcast_sink_streams[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
printk("Syncing the sink\n");
|
||||||
|
stream_count = 0;
|
||||||
|
for (int i = 1; i < BT_ISO_MAX_GROUP_ISO_COUNT; i++) {
|
||||||
|
if ((bis_index_bitfield & BIT(i)) != 0) {
|
||||||
|
stream_count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err = bt_bap_broadcast_sink_sync(g_broadcast_sink, bis_index_bitfield, bap_streams, NULL);
|
||||||
|
if (err != 0) {
|
||||||
|
FAIL("Unable to sync the sink: %d\n", err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Wait for all to be started */
|
||||||
|
printk("Waiting for %zu streams to be started\n", stream_count);
|
||||||
|
for (size_t i = 0U; i < stream_count; i++) {
|
||||||
|
k_sem_take(&sem_broadcast_started, K_FOREVER);
|
||||||
|
}
|
||||||
|
|
||||||
|
printk("Waiting for data\n");
|
||||||
|
WAIT_FOR_FLAG(flag_received);
|
||||||
|
|
||||||
|
backchannel_sync_send_all(); /* let others know we have received some data */
|
||||||
|
|
||||||
|
printk("Waiting for meta update\n");
|
||||||
|
WAIT_FOR_FLAG(flag_base_metadata_updated);
|
||||||
|
|
||||||
|
backchannel_sync_send_all(); /* let others know we have received a metadata update */
|
||||||
|
|
||||||
|
backchannel_sync_send_all(); /* let broadcaster know we can stop the source */
|
||||||
|
|
||||||
|
/* The order of PA sync lost and BIG Sync lost is irrelevant
|
||||||
|
* and depend on timeout parameters. We just wait for PA first, but
|
||||||
|
* either way will work.
|
||||||
|
*/
|
||||||
|
printk("Waiting for PA disconnected\n");
|
||||||
|
WAIT_FOR_FLAG(flag_pa_sync_lost);
|
||||||
|
|
||||||
|
printk("Waiting for %zu streams to be stopped\n", stream_count);
|
||||||
|
for (size_t i = 0U; i < stream_count; i++) {
|
||||||
|
k_sem_take(&sem_broadcast_stopped, K_FOREVER);
|
||||||
|
}
|
||||||
|
|
||||||
|
PASS("CAP acceptor broadcast reception passed\n");
|
||||||
|
}
|
||||||
|
|
||||||
static void test_cap_acceptor_capture_and_render(void)
|
static void test_cap_acceptor_capture_and_render(void)
|
||||||
{
|
{
|
||||||
init();
|
init();
|
||||||
|
@ -927,6 +1133,12 @@ static const struct bst_test_instance test_cap_acceptor[] = {
|
||||||
.test_tick_f = test_tick,
|
.test_tick_f = test_tick,
|
||||||
.test_main_f = test_cap_acceptor_broadcast,
|
.test_main_f = test_cap_acceptor_broadcast,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
.test_id = "cap_acceptor_broadcast_reception",
|
||||||
|
.test_pre_init_f = test_init,
|
||||||
|
.test_tick_f = test_tick,
|
||||||
|
.test_main_f = test_cap_acceptor_broadcast_reception,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
.test_id = "cap_acceptor_capture_and_render",
|
.test_id = "cap_acceptor_capture_and_render",
|
||||||
.test_pre_init_f = test_init,
|
.test_pre_init_f = test_init,
|
||||||
|
|
|
@ -39,10 +39,20 @@ extern enum bst_result_t bst_result;
|
||||||
static struct bt_conn *connected_conns[CONFIG_BT_MAX_CONN];
|
static struct bt_conn *connected_conns[CONFIG_BT_MAX_CONN];
|
||||||
static volatile size_t connected_conn_cnt;
|
static volatile size_t connected_conn_cnt;
|
||||||
|
|
||||||
|
static struct bt_le_scan_recv_info broadcaster_info;
|
||||||
|
static bt_addr_le_t broadcaster_addr;
|
||||||
|
static struct bt_le_per_adv_sync *g_pa_sync;
|
||||||
|
static uint32_t broadcaster_broadcast_id;
|
||||||
|
|
||||||
|
static uint8_t received_base[UINT8_MAX];
|
||||||
|
static uint8_t received_base_size;
|
||||||
|
|
||||||
static struct k_sem sem_disconnected;
|
static struct k_sem sem_disconnected;
|
||||||
static struct k_sem sem_cas_discovered;
|
static struct k_sem sem_cas_discovered;
|
||||||
static struct k_sem sem_vcs_discovered;
|
static struct k_sem sem_vcs_discovered;
|
||||||
static struct k_sem sem_mics_discovered;
|
static struct k_sem sem_mics_discovered;
|
||||||
|
static struct k_sem sem_bass_discovered;
|
||||||
|
|
||||||
CREATE_FLAG(flag_mtu_exchanged);
|
CREATE_FLAG(flag_mtu_exchanged);
|
||||||
CREATE_FLAG(flag_volume_changed);
|
CREATE_FLAG(flag_volume_changed);
|
||||||
CREATE_FLAG(flag_volume_mute_changed);
|
CREATE_FLAG(flag_volume_mute_changed);
|
||||||
|
@ -50,6 +60,14 @@ CREATE_FLAG(flag_volume_offset_changed);
|
||||||
CREATE_FLAG(flag_microphone_mute_changed);
|
CREATE_FLAG(flag_microphone_mute_changed);
|
||||||
CREATE_FLAG(flag_microphone_gain_changed);
|
CREATE_FLAG(flag_microphone_gain_changed);
|
||||||
|
|
||||||
|
CREATE_FLAG(flag_broadcast_reception_start);
|
||||||
|
CREATE_FLAG(flag_broadcaster_found);
|
||||||
|
CREATE_FLAG(flag_base_received);
|
||||||
|
CREATE_FLAG(flag_recv_state_updated_with_bis_sync);
|
||||||
|
CREATE_FLAG(flag_pa_synced);
|
||||||
|
CREATE_FLAG(flag_pa_sync_lost);
|
||||||
|
CREATE_FLAG(flag_syncable);
|
||||||
|
|
||||||
static void cap_discovery_complete_cb(struct bt_conn *conn, int err,
|
static void cap_discovery_complete_cb(struct bt_conn *conn, int err,
|
||||||
const struct bt_csip_set_coordinator_set_member *member,
|
const struct bt_csip_set_coordinator_set_member *member,
|
||||||
const struct bt_csip_set_coordinator_csis_inst *csis_inst)
|
const struct bt_csip_set_coordinator_csis_inst *csis_inst)
|
||||||
|
@ -133,6 +151,18 @@ static void cap_microphone_gain_changed_cb(struct bt_conn *conn, int err)
|
||||||
#endif /* CONFIG_BT_MICP_MIC_CTLR_AICS */
|
#endif /* CONFIG_BT_MICP_MIC_CTLR_AICS */
|
||||||
#endif /* CONFIG_BT_MICP_MIC_CTLR */
|
#endif /* CONFIG_BT_MICP_MIC_CTLR */
|
||||||
|
|
||||||
|
#if defined(CONFIG_BT_BAP_BROADCAST_ASSISTANT)
|
||||||
|
static void cap_broadcast_reception_start_cb(struct bt_conn *conn, int err)
|
||||||
|
{
|
||||||
|
if (err != 0) {
|
||||||
|
FAIL("Failed to perform broadcast reception start for conn %p: %d\n", conn, err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SET_FLAG(flag_broadcast_reception_start);
|
||||||
|
}
|
||||||
|
#endif /* CONFIG_BT_BAP_BROADCAST_ASSISTANT*/
|
||||||
|
|
||||||
static struct bt_cap_commander_cb cap_cb = {
|
static struct bt_cap_commander_cb cap_cb = {
|
||||||
.discovery_complete = cap_discovery_complete_cb,
|
.discovery_complete = cap_discovery_complete_cb,
|
||||||
#if defined(CONFIG_BT_VCP_VOL_CTLR)
|
#if defined(CONFIG_BT_VCP_VOL_CTLR)
|
||||||
|
@ -148,6 +178,9 @@ static struct bt_cap_commander_cb cap_cb = {
|
||||||
.microphone_gain_changed = cap_microphone_gain_changed_cb,
|
.microphone_gain_changed = cap_microphone_gain_changed_cb,
|
||||||
#endif /* CONFIG_BT_MICP_MIC_CTLR_AICS */
|
#endif /* CONFIG_BT_MICP_MIC_CTLR_AICS */
|
||||||
#endif /* CONFIG_BT_MICP_MIC_CTLR */
|
#endif /* CONFIG_BT_MICP_MIC_CTLR */
|
||||||
|
#if defined(CONFIG_BT_BAP_BROADCAST_ASSISTANT)
|
||||||
|
.broadcast_reception_start = cap_broadcast_reception_start_cb,
|
||||||
|
#endif /* CONFIG_BT_BAP_BROADCAST_ASSISTANT*/
|
||||||
};
|
};
|
||||||
|
|
||||||
static void cap_vcp_discover_cb(struct bt_vcp_vol_ctlr *vol_ctlr, int err, uint8_t vocs_count,
|
static void cap_vcp_discover_cb(struct bt_vcp_vol_ctlr *vol_ctlr, int err, uint8_t vocs_count,
|
||||||
|
@ -210,6 +243,267 @@ static void cap_disconnected_cb(struct bt_conn *conn, uint8_t reason)
|
||||||
k_sem_give(&sem_disconnected);
|
k_sem_give(&sem_disconnected);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 {
|
||||||
|
uint32_t interval_ms;
|
||||||
|
uint32_t timeout;
|
||||||
|
|
||||||
|
/* Add retries and convert to unit in 10's of ms */
|
||||||
|
interval_ms = BT_GAP_PER_ADV_INTERVAL_TO_MS(pa_interval);
|
||||||
|
timeout = (interval_ms * PA_SYNC_INTERVAL_TO_TIMEOUT_RATIO) / 10;
|
||||||
|
|
||||||
|
/* Enforce restraints */
|
||||||
|
pa_timeout = CLAMP(timeout, BT_GAP_PER_ADV_MIN_TIMEOUT, BT_GAP_PER_ADV_MAX_TIMEOUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
return pa_timeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int pa_sync_create(void)
|
||||||
|
{
|
||||||
|
struct bt_le_per_adv_sync_param create_params = {0};
|
||||||
|
int err;
|
||||||
|
|
||||||
|
bt_addr_le_copy(&create_params.addr, &broadcaster_addr);
|
||||||
|
create_params.options = 0;
|
||||||
|
create_params.sid = broadcaster_info.sid;
|
||||||
|
create_params.skip = PA_SYNC_SKIP;
|
||||||
|
create_params.timeout = interval_to_sync_timeout(broadcaster_info.interval);
|
||||||
|
|
||||||
|
err = bt_le_per_adv_sync_create(&create_params, &g_pa_sync);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool scan_check_and_sync_broadcast(struct bt_data *data, void *user_data)
|
||||||
|
{
|
||||||
|
const struct bt_le_scan_recv_info *info = user_data;
|
||||||
|
char le_addr[BT_ADDR_LE_STR_LEN];
|
||||||
|
struct bt_uuid_16 adv_uuid;
|
||||||
|
uint32_t broadcast_id;
|
||||||
|
|
||||||
|
if (TEST_FLAG(flag_broadcaster_found)) {
|
||||||
|
/* no-op*/
|
||||||
|
printk("NO OP\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
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)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
broadcast_id = sys_get_le24(data->data + BT_UUID_SIZE_16);
|
||||||
|
|
||||||
|
bt_addr_le_to_str(info->addr, le_addr, sizeof(le_addr));
|
||||||
|
|
||||||
|
printk("Found broadcaster with ID 0x%06X and addr %s and sid 0x%02X\n", broadcast_id,
|
||||||
|
le_addr, info->sid);
|
||||||
|
printk("Adv type %02X interval %u", info->adv_type, info->interval);
|
||||||
|
|
||||||
|
SET_FLAG(flag_broadcaster_found);
|
||||||
|
|
||||||
|
/* Store info for PA sync parameters */
|
||||||
|
memcpy(&broadcaster_info, info, sizeof(broadcaster_info));
|
||||||
|
bt_addr_le_copy(&broadcaster_addr, info->addr);
|
||||||
|
broadcaster_broadcast_id = broadcast_id;
|
||||||
|
|
||||||
|
/* Stop parsing */
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void broadcast_scan_recv(const struct bt_le_scan_recv_info *info, struct net_buf_simple *ad)
|
||||||
|
{
|
||||||
|
if (info->interval != 0U) {
|
||||||
|
bt_data_parse(ad, scan_check_and_sync_broadcast, (void *)info);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct bt_le_scan_cb bap_scan_cb = {
|
||||||
|
.recv = broadcast_scan_recv,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void bap_pa_sync_synced_cb(struct bt_le_per_adv_sync *sync,
|
||||||
|
struct bt_le_per_adv_sync_synced_info *info)
|
||||||
|
{
|
||||||
|
if (sync == g_pa_sync) {
|
||||||
|
printk("PA sync %p synced for broadcast sink with broadcast ID 0x%06X\n", sync,
|
||||||
|
broadcaster_broadcast_id);
|
||||||
|
SET_FLAG(flag_pa_synced);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void bap_pa_sync_terminated_cb(struct bt_le_per_adv_sync *sync,
|
||||||
|
const struct bt_le_per_adv_sync_term_info *info)
|
||||||
|
{
|
||||||
|
if (sync == g_pa_sync) {
|
||||||
|
printk("CAP commander test PA sync %p lost with reason %u\n", sync, info->reason);
|
||||||
|
g_pa_sync = NULL;
|
||||||
|
|
||||||
|
SET_FLAG(flag_pa_sync_lost);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool base_store(struct bt_data *data, void *user_data)
|
||||||
|
{
|
||||||
|
const struct bt_bap_base *base = bt_bap_base_get_base_from_ad(data);
|
||||||
|
uint8_t base_size;
|
||||||
|
int base_subgroup_count;
|
||||||
|
|
||||||
|
/* Base is NULL if the data does not contain a valid BASE */
|
||||||
|
if (base == NULL) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Can not fit all the received subgroups with the size CONFIG_BT_BAP_BASS_MAX_SUBGROUPS */
|
||||||
|
base_subgroup_count = bt_bap_base_get_subgroup_count(base);
|
||||||
|
if (base_subgroup_count < 0 || base_subgroup_count > CONFIG_BT_BAP_BASS_MAX_SUBGROUPS) {
|
||||||
|
printk("Got invalid subgroup count: %d\n", base_subgroup_count);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
base_size = data->data_len - BT_UUID_SIZE_16; /* the BASE comes after the UUID */
|
||||||
|
|
||||||
|
/* Compare BASE and copy if different */
|
||||||
|
if (base_size != received_base_size || memcmp(base, received_base, base_size) != 0) {
|
||||||
|
(void)memcpy(received_base, base, base_size);
|
||||||
|
received_base_size = base_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Stop parsing */
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pa_recv(struct bt_le_per_adv_sync *sync,
|
||||||
|
const struct bt_le_per_adv_sync_recv_info *info, struct net_buf_simple *buf)
|
||||||
|
{
|
||||||
|
if (TEST_FLAG(flag_base_received)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bt_data_parse(buf, base_store, NULL);
|
||||||
|
SET_FLAG(flag_base_received);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct bt_le_per_adv_sync_cb bap_pa_sync_cb = {
|
||||||
|
.synced = bap_pa_sync_synced_cb,
|
||||||
|
.term = bap_pa_sync_terminated_cb,
|
||||||
|
.recv = pa_recv,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void bap_broadcast_assistant_discover_cb(struct bt_conn *conn, int err,
|
||||||
|
uint8_t recv_state_count)
|
||||||
|
{
|
||||||
|
if (err == 0) {
|
||||||
|
printk("BASS discover done with %u recv states\n", recv_state_count);
|
||||||
|
} else {
|
||||||
|
printk("BASS discover failed (%d)\n", err);
|
||||||
|
}
|
||||||
|
|
||||||
|
k_sem_give(&sem_bass_discovered);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void bap_broadcast_assistant_add_src_cb(struct bt_conn *conn, int err)
|
||||||
|
{
|
||||||
|
if (err == 0) {
|
||||||
|
printk("BASS add source successful\n");
|
||||||
|
} else {
|
||||||
|
printk("BASS add source failed (%d)\n", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool metadata_entry(struct bt_data *data, void *user_data)
|
||||||
|
{
|
||||||
|
char metadata[CONFIG_BT_AUDIO_CODEC_CFG_MAX_METADATA_SIZE];
|
||||||
|
|
||||||
|
(void)bin2hex(data->data, data->data_len, metadata, sizeof(metadata));
|
||||||
|
|
||||||
|
printk("\t\tMetadata length %u, type %u, data: %s\n", data->data_len, data->type, metadata);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
bap_broadcast_assistant_recv_state_cb(struct bt_conn *conn, int err,
|
||||||
|
const struct bt_bap_scan_delegator_recv_state *state)
|
||||||
|
{
|
||||||
|
char le_addr[BT_ADDR_LE_STR_LEN];
|
||||||
|
char bad_code[BT_AUDIO_BROADCAST_CODE_SIZE * 2 + 1];
|
||||||
|
|
||||||
|
if (err != 0) {
|
||||||
|
FAIL("BASS recv state read failed (%d)\n", err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state == NULL) {
|
||||||
|
/* Empty receive state */
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bt_addr_le_to_str(&state->addr, le_addr, sizeof(le_addr));
|
||||||
|
(void)bin2hex(state->bad_code, BT_AUDIO_BROADCAST_CODE_SIZE, bad_code, sizeof(bad_code));
|
||||||
|
printk("BASS recv state: src_id %u, addr %s, sid %u, sync_state %u, "
|
||||||
|
"encrypt_state %u%s%s\n",
|
||||||
|
state->src_id, le_addr, state->adv_sid, state->pa_sync_state, state->encrypt_state,
|
||||||
|
state->encrypt_state == BT_BAP_BIG_ENC_STATE_BAD_CODE ? ", bad code" : "", bad_code);
|
||||||
|
|
||||||
|
if (state->encrypt_state == BT_BAP_BIG_ENC_STATE_BAD_CODE) {
|
||||||
|
FAIL("Encryption state is BT_BAP_BIG_ENC_STATE_BAD_CODE");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (uint8_t i = 0; i < state->num_subgroups; i++) {
|
||||||
|
const struct bt_bap_bass_subgroup *subgroup = &state->subgroups[i];
|
||||||
|
struct net_buf_simple buf;
|
||||||
|
|
||||||
|
printk("\t[%d]: BIS sync %u, metadata_len %u\n", i, subgroup->bis_sync,
|
||||||
|
subgroup->metadata_len);
|
||||||
|
|
||||||
|
net_buf_simple_init_with_data(&buf, (void *)subgroup->metadata,
|
||||||
|
subgroup->metadata_len);
|
||||||
|
bt_data_parse(&buf, metadata_entry, NULL);
|
||||||
|
|
||||||
|
if (subgroup->bis_sync != 0) {
|
||||||
|
SET_FLAG(flag_recv_state_updated_with_bis_sync);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(CONFIG_BT_PER_ADV_SYNC_TRANSFER_SENDER)
|
||||||
|
if (state->pa_sync_state == BT_BAP_PA_STATE_INFO_REQ) {
|
||||||
|
err = bt_le_per_adv_sync_transfer(g_pa_sync, conn, BT_UUID_BASS_VAL);
|
||||||
|
if (err != 0) {
|
||||||
|
FAIL("Could not transfer periodic adv sync: %d\n", err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif /* CONFIG_BT_PER_ADV_SYNC_TRANSFER_SENDER */
|
||||||
|
|
||||||
|
if (state->pa_sync_state == BT_BAP_PA_STATE_SYNCED) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct bt_bap_broadcast_assistant_cb ba_cbs = {
|
||||||
|
.discover = bap_broadcast_assistant_discover_cb,
|
||||||
|
.recv_state = bap_broadcast_assistant_recv_state_cb,
|
||||||
|
.add_src = bap_broadcast_assistant_add_src_cb,
|
||||||
|
};
|
||||||
|
|
||||||
static void init(size_t acceptor_cnt)
|
static void init(size_t acceptor_cnt)
|
||||||
{
|
{
|
||||||
static struct bt_conn_cb conn_cb = {
|
static struct bt_conn_cb conn_cb = {
|
||||||
|
@ -244,10 +538,35 @@ static void init(size_t acceptor_cnt)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err = bt_bap_broadcast_assistant_register_cb(&ba_cbs);
|
||||||
|
if (err != 0) {
|
||||||
|
FAIL("Failed to register broadcast assistant callbacks (err %d)\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bt_le_per_adv_sync_cb_register(&bap_pa_sync_cb);
|
||||||
|
bt_le_scan_cb_register(&bap_scan_cb);
|
||||||
|
|
||||||
k_sem_init(&sem_disconnected, 0, acceptor_cnt);
|
k_sem_init(&sem_disconnected, 0, acceptor_cnt);
|
||||||
k_sem_init(&sem_cas_discovered, 0, acceptor_cnt);
|
k_sem_init(&sem_cas_discovered, 0, acceptor_cnt);
|
||||||
|
k_sem_init(&sem_bass_discovered, 0, acceptor_cnt);
|
||||||
k_sem_init(&sem_vcs_discovered, 0, acceptor_cnt);
|
k_sem_init(&sem_vcs_discovered, 0, acceptor_cnt);
|
||||||
k_sem_init(&sem_mics_discovered, 0, acceptor_cnt);
|
k_sem_init(&sem_mics_discovered, 0, acceptor_cnt);
|
||||||
|
|
||||||
|
UNSET_FLAG(flag_mtu_exchanged);
|
||||||
|
UNSET_FLAG(flag_volume_changed);
|
||||||
|
UNSET_FLAG(flag_volume_mute_changed);
|
||||||
|
UNSET_FLAG(flag_volume_offset_changed);
|
||||||
|
UNSET_FLAG(flag_microphone_mute_changed);
|
||||||
|
UNSET_FLAG(flag_microphone_gain_changed);
|
||||||
|
|
||||||
|
UNSET_FLAG(flag_broadcast_reception_start);
|
||||||
|
UNSET_FLAG(flag_broadcaster_found);
|
||||||
|
UNSET_FLAG(flag_base_received);
|
||||||
|
UNSET_FLAG(flag_recv_state_updated_with_bis_sync);
|
||||||
|
UNSET_FLAG(flag_pa_synced);
|
||||||
|
UNSET_FLAG(flag_pa_sync_lost);
|
||||||
|
UNSET_FLAG(flag_syncable);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void cap_device_found(const bt_addr_le_t *addr, int8_t rssi, uint8_t type,
|
static void cap_device_found(const bt_addr_le_t *addr, int8_t rssi, uint8_t type,
|
||||||
|
@ -330,10 +649,13 @@ static void disconnect_acl(size_t acceptor_cnt)
|
||||||
for (size_t i = 0U; i < acceptor_cnt; i++) {
|
for (size_t i = 0U; i < acceptor_cnt; i++) {
|
||||||
const int err = k_sem_take(&sem_disconnected, SEM_TIMEOUT);
|
const int err = k_sem_take(&sem_disconnected, SEM_TIMEOUT);
|
||||||
|
|
||||||
if (err != 0) {
|
if (err == 0) {
|
||||||
|
connected_conn_cnt--;
|
||||||
|
} else {
|
||||||
const struct bt_conn *conn = connected_conns[i];
|
const struct bt_conn *conn = connected_conns[i];
|
||||||
|
|
||||||
FAIL("Failed to take sem_disconnected for %p: %d", (void *)conn, err);
|
FAIL("Failed to take sem_disconnected for %p: %d", (void *)conn, err);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -367,6 +689,69 @@ static void discover_cas(size_t acceptor_cnt)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void discover_bass(size_t acceptor_cnt)
|
||||||
|
{
|
||||||
|
k_sem_reset(&sem_bass_discovered);
|
||||||
|
|
||||||
|
if (acceptor_cnt > 1) {
|
||||||
|
FAIL("Current implementation does not support multiple connections for the "
|
||||||
|
"broadcast assistant");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0U; i < acceptor_cnt; i++) {
|
||||||
|
int err;
|
||||||
|
|
||||||
|
err = bt_bap_broadcast_assistant_discover(connected_conns[i]);
|
||||||
|
if (err != 0) {
|
||||||
|
FAIL("Failed to discover BASS on the sink (err %d)\n", err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0U; i < acceptor_cnt; i++) {
|
||||||
|
const int err = k_sem_take(&sem_bass_discovered, SEM_TIMEOUT);
|
||||||
|
|
||||||
|
if (err != 0) {
|
||||||
|
FAIL("Failed to take sem_bass_discovered for %p: %d",
|
||||||
|
(void *)connected_conns[i], err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pa_sync_to_broadcaster(void)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
|
||||||
|
err = bt_le_scan_start(BT_LE_SCAN_ACTIVE, NULL);
|
||||||
|
if (err != 0) {
|
||||||
|
FAIL("Unable to start scan for broadcast sources: %d", err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
printk("Searching for a broadcaster\n");
|
||||||
|
WAIT_FOR_FLAG(flag_broadcaster_found);
|
||||||
|
|
||||||
|
err = bt_le_scan_stop();
|
||||||
|
if (err != 0) {
|
||||||
|
FAIL("bt_le_scan_stop failed with %d\n", err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
printk("Scan stopped, attempting to PA sync to the broadcaster with id 0x%06X\n",
|
||||||
|
broadcaster_broadcast_id);
|
||||||
|
err = pa_sync_create();
|
||||||
|
if (err != 0) {
|
||||||
|
FAIL("Could not create Broadcast PA sync: %d\n", err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
WAIT_FOR_FLAG(flag_pa_synced); /* todo from bap_pa_sync_synced_cb, bap_pa_sync_cb */
|
||||||
|
|
||||||
|
printk("Broadcast source PA synced, waiting for BASE\n");
|
||||||
|
WAIT_FOR_FLAG(flag_base_received);
|
||||||
|
}
|
||||||
|
|
||||||
static void discover_vcs(size_t acceptor_cnt)
|
static void discover_vcs(size_t acceptor_cnt)
|
||||||
{
|
{
|
||||||
k_sem_reset(&sem_vcs_discovered);
|
k_sem_reset(&sem_vcs_discovered);
|
||||||
|
@ -564,6 +949,65 @@ static void test_change_microphone_gain(void)
|
||||||
printk("Microphone gain changed\n");
|
printk("Microphone gain changed\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void test_broadcast_reception_start(size_t acceptor_count)
|
||||||
|
{
|
||||||
|
struct bt_cap_commander_broadcast_reception_start_param reception_start_param = {0};
|
||||||
|
struct bt_cap_commander_broadcast_reception_start_member_param param[CONFIG_BT_MAX_CONN] = {
|
||||||
|
0};
|
||||||
|
int err;
|
||||||
|
|
||||||
|
reception_start_param.type = BT_CAP_SET_TYPE_AD_HOC;
|
||||||
|
reception_start_param.count = acceptor_count;
|
||||||
|
reception_start_param.param = param;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < acceptor_count; i++) {
|
||||||
|
uint32_t bis_sync[CONFIG_BT_BAP_BASS_MAX_SUBGROUPS];
|
||||||
|
size_t num_subgroups;
|
||||||
|
|
||||||
|
reception_start_param.param[i].member.member = connected_conns[i];
|
||||||
|
bt_addr_le_copy(&reception_start_param.param[i].addr, &broadcaster_addr);
|
||||||
|
reception_start_param.param[i].adv_sid = broadcaster_info.sid;
|
||||||
|
reception_start_param.param[i].pa_interval = broadcaster_info.interval;
|
||||||
|
reception_start_param.param[i].broadcast_id = broadcaster_broadcast_id;
|
||||||
|
num_subgroups =
|
||||||
|
bt_bap_base_get_subgroup_count((const struct bt_bap_base *)received_base);
|
||||||
|
err = bt_bap_base_get_bis_indexes((const struct bt_bap_base *)received_base,
|
||||||
|
bis_sync);
|
||||||
|
if (err != 0) {
|
||||||
|
FAIL("Could not populate subgroup information: %d\n", err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
reception_start_param.param[i].num_subgroups = num_subgroups;
|
||||||
|
for (size_t j = 0; j < num_subgroups; j++) {
|
||||||
|
reception_start_param.param[i].subgroups[j].bis_sync = bis_sync[j];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err = bt_cap_commander_broadcast_reception_start(&reception_start_param);
|
||||||
|
if (err != 0) {
|
||||||
|
FAIL("Could not initiate broadcast reception start: %d\n", err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
WAIT_FOR_FLAG(flag_broadcast_reception_start);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_broadcast_reception_stop(size_t acceptor_count)
|
||||||
|
{
|
||||||
|
struct bt_cap_commander_broadcast_reception_stop_param reception_stop_param;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
/* reception stop is not implemented yet, for now the following command will fail*/
|
||||||
|
reception_stop_param.type = BT_CAP_SET_TYPE_AD_HOC;
|
||||||
|
reception_stop_param.members = NULL;
|
||||||
|
reception_stop_param.count = 0U;
|
||||||
|
err = bt_cap_commander_broadcast_reception_stop(&reception_stop_param);
|
||||||
|
if (err != 0) {
|
||||||
|
printk("Command not implemented yet, could not stop broadcast reception %d\n", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void test_main_cap_commander_capture_and_render(void)
|
static void test_main_cap_commander_capture_and_render(void)
|
||||||
{
|
{
|
||||||
const size_t acceptor_cnt = get_dev_cnt() - 1; /* Assume all other devices are acceptors
|
const size_t acceptor_cnt = get_dev_cnt() - 1; /* Assume all other devices are acceptors
|
||||||
|
@ -613,6 +1057,49 @@ static void test_main_cap_commander_capture_and_render(void)
|
||||||
PASS("CAP commander capture and rendering passed\n");
|
PASS("CAP commander capture and rendering passed\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void test_main_cap_commander_broadcast_reception(void)
|
||||||
|
{
|
||||||
|
size_t acceptor_count;
|
||||||
|
|
||||||
|
/* The test consists of N devices
|
||||||
|
* 1 device is the broadcast source
|
||||||
|
* 1 device is the CAP commander
|
||||||
|
* This leaves N - 2 devices for the acceptor
|
||||||
|
*/
|
||||||
|
acceptor_count = get_dev_cnt() - 2;
|
||||||
|
printk("Acceptor count: %d\n", acceptor_count);
|
||||||
|
|
||||||
|
init(acceptor_count);
|
||||||
|
|
||||||
|
for (size_t i = 0U; i < acceptor_count; i++) {
|
||||||
|
scan_and_connect();
|
||||||
|
|
||||||
|
WAIT_FOR_FLAG(flag_mtu_exchanged);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TODO: We should use CSIP to find set members */
|
||||||
|
discover_cas(acceptor_count);
|
||||||
|
discover_bass(acceptor_count);
|
||||||
|
|
||||||
|
pa_sync_to_broadcaster();
|
||||||
|
|
||||||
|
test_broadcast_reception_start(acceptor_count);
|
||||||
|
|
||||||
|
backchannel_sync_wait_any(); /* wait for the acceptor to receive data */
|
||||||
|
|
||||||
|
backchannel_sync_wait_any(); /* wait for the acceptor to receive a metadata update
|
||||||
|
*/
|
||||||
|
|
||||||
|
test_broadcast_reception_stop(acceptor_count);
|
||||||
|
|
||||||
|
backchannel_sync_wait_any(); /* wait for the acceptor to stop reception */
|
||||||
|
|
||||||
|
/* Disconnect all CAP acceptors */
|
||||||
|
disconnect_acl(acceptor_count);
|
||||||
|
|
||||||
|
PASS("Broadcast reception passed\n");
|
||||||
|
}
|
||||||
|
|
||||||
static const struct bst_test_instance test_cap_commander[] = {
|
static const struct bst_test_instance test_cap_commander[] = {
|
||||||
{
|
{
|
||||||
.test_id = "cap_commander_capture_and_render",
|
.test_id = "cap_commander_capture_and_render",
|
||||||
|
@ -620,6 +1107,12 @@ static const struct bst_test_instance test_cap_commander[] = {
|
||||||
.test_tick_f = test_tick,
|
.test_tick_f = test_tick,
|
||||||
.test_main_f = test_main_cap_commander_capture_and_render,
|
.test_main_f = test_main_cap_commander_capture_and_render,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
.test_id = "cap_commander_broadcast_reception",
|
||||||
|
.test_post_init_f = test_init,
|
||||||
|
.test_tick_f = test_tick,
|
||||||
|
.test_main_f = test_main_cap_commander_broadcast_reception,
|
||||||
|
},
|
||||||
BSTEST_END_MARKER,
|
BSTEST_END_MARKER,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
35
tests/bsim/bluetooth/audio/test_scripts/cap_broadcast_reception.sh
Executable file
35
tests/bsim/bluetooth/audio/test_scripts/cap_broadcast_reception.sh
Executable file
|
@ -0,0 +1,35 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
#
|
||||||
|
# Copyright (c) 2024 Nordic Semiconductor ASA
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
SIMULATION_ID="cap_broadcast_reception"
|
||||||
|
VERBOSITY_LEVEL=2
|
||||||
|
NR_OF_DEVICES=3
|
||||||
|
|
||||||
|
source ${ZEPHYR_BASE}/tests/bsim/sh_common.source
|
||||||
|
|
||||||
|
cd ${BSIM_OUT_PATH}/bin
|
||||||
|
|
||||||
|
printf "\n\n======== Running CAP commander broadcast reception start and stop test =========\n\n"
|
||||||
|
|
||||||
|
Execute ./bs_${BOARD_TS}_tests_bsim_bluetooth_audio_prj_conf \
|
||||||
|
-v=${VERBOSITY_LEVEL} -s=${SIMULATION_ID} -d=0 -testid=cap_commander_broadcast_reception \
|
||||||
|
-rs=46 -D=${NR_OF_DEVICES}
|
||||||
|
|
||||||
|
Execute ./bs_${BOARD_TS}_tests_bsim_bluetooth_audio_prj_conf \
|
||||||
|
-v=${VERBOSITY_LEVEL} -s=${SIMULATION_ID} -d=1 -testid=broadcast_source \
|
||||||
|
-rs=23 -D=${NR_OF_DEVICES}
|
||||||
|
|
||||||
|
Execute ./bs_${BOARD_TS}_tests_bsim_bluetooth_audio_prj_conf \
|
||||||
|
-v=${VERBOSITY_LEVEL} -s=${SIMULATION_ID} -d=2 -testid=cap_acceptor_broadcast_reception \
|
||||||
|
-rs=69 -D=${NR_OF_DEVICES}
|
||||||
|
|
||||||
|
# Simulation time should be larger than the WAIT_TIME in common.h
|
||||||
|
Execute ./bs_2G4_phy_v1 -v=${VERBOSITY_LEVEL} -s=${SIMULATION_ID} \
|
||||||
|
-D=${NR_OF_DEVICES} -sim_length=60e6 $@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
wait_for_background_jobs
|
Loading…
Add table
Add a link
Reference in a new issue