tests bsim: Change folder structure

Bsim won't be limited anymore to BT tests.
In preparation for adding more tests in network areas
swap the tests/bluetooth/bsim with tests/bsim/bluetooth

There is no other changes in this commit beyond that.

Signed-off-by: Alberto Escolar Piedras <alberto.escolar.piedras@nordicsemi.no>
This commit is contained in:
Alberto Escolar Piedras 2023-03-17 15:29:24 +01:00 committed by Carles Cufí
commit f27c0b4905
500 changed files with 222 additions and 222 deletions

View file

@ -0,0 +1,15 @@
This folder contains tests meant to be run with BabbleSim's physical layer
simulation, and therefore cannot be run directly from twister
The compile.sh and run_parallel.sh scripts are used by the CI system to build
the needed images and execute these tests in batch.
You can also run them manually if desired, but be sure to call them setting
the variables they expect. For example, from Zephyr's root folder, you can run:
WORK_DIR=${ZEPHYR_BASE}/bsim_bt_out tests/bsim/bluetooth/compile.sh
RESULTS_FILE=${ZEPHYR_BASE}/myresults.xml SEARCH_PATH=tests/bsim/bluetooth tests/bsim/bluetooth/run_parallel.sh
Or to run only a specific subset, e.g. host advertising tests:
WORK_DIR=${ZEPHYR_BASE}/bsim_bt_out tests/bsim/bluetooth/host/compile.sh
RESULTS_FILE=${ZEPHYR_BASE}/myresults.xml SEARCH_PATH=tests/bsim/bluetooth/host/adv tests/bsim/bluetooth/run_parallel.sh

View file

@ -0,0 +1,91 @@
#!/usr/bin/env bash
# Copyright 2018 Oticon A/S
# SPDX-License-Identifier: Apache-2.0
# Compile with all permutations of a given set of KConfigs
# Specifically for going through possible combinations of
# optional control procedures
#set -x #uncomment this line for debugging
# set DEBUG_PERMUTATE to 'true' for extra debug output
DEBUG_PERMUTATE=false
: "${BSIM_OUT_PATH:?BSIM_OUT_PATH must be defined}"
: "${BSIM_COMPONENTS_PATH:?BSIM_COMPONENTS_PATH must be defined}"
: "${ZEPHYR_BASE:?ZEPHYR_BASE must be set to point to the zephyr root\
directory}"
WORK_DIR="${WORK_DIR:-${ZEPHYR_BASE}/bsim_bt_out}"
BOARD="${BOARD:-nrf52_bsim}"
BOARD_ROOT="${BOARD_ROOT:-${ZEPHYR_BASE}}"
mkdir -p ${WORK_DIR}
source ${ZEPHYR_BASE}/tests/bsim/bluetooth/compile.source
declare -a list=(
"CONFIG_BT_CENTRAL="
"CONFIG_BT_PERIPHERAL="
"CONFIG_BT_CTLR_PER_INIT_FEAT_XCHG="
"CONFIG_BT_DATA_LEN_UPDATE="
"CONFIG_BT_PHY_UPDATE="
"CONFIG_BT_CTLR_MIN_USED_CHAN="
"CONFIG_BT_CTLR_LE_PING="
"CONFIG_BT_CTLR_LE_ENC="
"CONFIG_BT_CTLR_CONN_PARAM_REQ="
)
perm_compile() {
local -a results=()
# We set a unique exe-name, so that we don't overwrite the executables
# created by the compile scripts since that may mess up other tests
# We also delete the executable to avoid having artifacts from
# a previous run
local exe_name="bs_nrf52_bsim_tests_kconfig_perm"
local executable_name=${exe_name}
local executable_name=${BSIM_OUT_PATH}/bin/$executable_name
rm -f ${executable_name}
let idx=$2
for (( j = 0; j < $1; j++ )); do
if (( idx % 2 )); then
results=("${results[@]}" "${list[$j]}n")
else
results=("${results[@]}" "${list[$j]}y")
fi
let idx\>\>=1
done
printf '%s\n' "${results[@]}" > $3
if test "$DEBUG_PERMUTATE" = "true"; then
echo "Compile with config overlay:"
cat $3
fi
local app=tests/bsim/bluetooth/edtt_ble_test_app/hci_test_app
local conf_file=prj_dut_llcp.conf
local conf_overlay=$3
compile
if [ ! -f ${executable_name} ]; then
compile_failures=$(expr $compile_failures + 1)
fi
}
let n=${#list[@]}
temp_conf_file=$(mktemp -p ${WORK_DIR})
# compile_failures will be equal to the number of failed compilations
let compile_failures=0
for (( i = 0; i < 2**n; i++ )); do
## don't compile for CENTRAL=n AND PERIPHERAL=n
if (( (i & 0x3) != 0x3 )); then
perm_compile $n $i ${temp_conf_file}
fi
done
# We set exit code based on type of failure
# 0 means all configurations compiled w/o error
trap "{ rm "${temp_conf_file}" ; exit 255; }" SIGINT
trap "{ rm "${temp_conf_file}" ; exit 254; }" SIGTERM
trap "{ rm "${temp_conf_file}" ; exit 253; }" ERR
trap "{ rm "${temp_conf_file}" ; exit ${compile_failures}; }" EXIT

View file

@ -0,0 +1,22 @@
# SPDX-License-Identifier: Apache-2.0
cmake_minimum_required(VERSION 3.20.0)
if (NOT DEFINED ENV{BSIM_COMPONENTS_PATH})
message(FATAL_ERROR "This test requires the BabbleSim simulator. Please set\
the environment variable BSIM_COMPONENTS_PATH to point to its components \
folder. More information can be found in\
https://babblesim.github.io/folder_structure_and_env.html")
endif()
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(bsim_test_audio)
FILE(GLOB app_sources src/*.c)
target_sources(app PRIVATE ${app_sources} )
zephyr_include_directories(
$ENV{BSIM_COMPONENTS_PATH}/libUtilv1/src/
$ENV{BSIM_COMPONENTS_PATH}/libPhyComv1/src/
${ZEPHYR_BASE}/subsys/bluetooth/host/audio/
)

View file

@ -0,0 +1,14 @@
# Copyright (c) 2022 Nordic Semiconductor ASA
# SPDX-License-Identifier: Apache-2.0
# Workaround for pretending that the controller
# supports CIS as the host expects the controller
# to support these features.
config BT_LL_SW_SPLIT
select BT_CTLR_SYNC_TRANSFER_RECEIVER_SUPPORT
select BT_CTLR_SYNC_TRANSFER_SENDER_SUPPORT
menu "Zephyr Kernel"
source "Kconfig.zephyr"
endmenu

View file

@ -0,0 +1,5 @@
Zephyr test application which uses the simulated boards test hooks.
Can be compiled targeting the *_bsim boards.
This application will, based on the command line arguments, select one of
testcases which are compiled with it.

View file

@ -0,0 +1,25 @@
#!/usr/bin/env bash
# Copyright 2023 Nordic Semiconductor ASA
# SPDX-License-Identifier: Apache-2.0
# Compile all the applications needed by the bsim tests
#set -x #uncomment this line for debugging
set -ue
: "${BSIM_OUT_PATH:?BSIM_OUT_PATH must be defined}"
: "${BSIM_COMPONENTS_PATH:?BSIM_COMPONENTS_PATH must be defined}"
: "${ZEPHYR_BASE:?ZEPHYR_BASE must be set to point to the zephyr root\
directory}"
WORK_DIR="${WORK_DIR:-${ZEPHYR_BASE}/bsim_out}"
BOARD="${BOARD:-nrf52_bsim}"
BOARD_ROOT="${BOARD_ROOT:-${ZEPHYR_BASE}}"
mkdir -p ${WORK_DIR}
source ${ZEPHYR_BASE}/tests/bsim/bluetooth/compile.source
app=tests/bsim/bluetooth/audio compile
wait_for_background_jobs

View file

@ -0,0 +1,169 @@
CONFIG_TEST=y
CONFIG_BT_TESTING=y
CONFIG_CONSOLE_HANDLER=y
CONFIG_USERSPACE=n
CONFIG_NO_OPTIMIZATIONS=y
CONFIG_BT=y
CONFIG_BT_CENTRAL=y
CONFIG_BT_PERIPHERAL=y
CONFIG_BT_DEVICE_NAME="bsim_test_audio"
CONFIG_BT_L2CAP_TX_BUF_COUNT=20
CONFIG_BT_MAX_CONN=5
CONFIG_BT_MAX_PAIRED=5
CONFIG_BT_GATT_DYNAMIC_DB=y
CONFIG_BT_SMP=y
# Needed for extended advertising
CONFIG_BT_EXT_ADV_LEGACY_SUPPORT=y
CONFIG_BT_AUDIO=y
CONFIG_BT_BAP_UNICAST_SERVER=y
CONFIG_BT_BAP_UNICAST_CLIENT=y
CONFIG_BT_BAP_UNICAST_CLIENT_GROUP_STREAM_COUNT=2
CONFIG_BT_BAP_BROADCAST_SOURCE=y
CONFIG_BT_BAP_BROADCAST_SINK=y
# Only 1 stream support by controller at this point
CONFIG_BT_BAP_BROADCAST_SRC_STREAM_COUNT=1
CONFIG_BT_BAP_BROADCAST_SRC_SUBGROUP_COUNT=1
# Only 1 stream support by controller at this point
CONFIG_BT_BAP_BROADCAST_SNK_SUBGROUP_COUNT=1
CONFIG_BT_BAP_BROADCAST_SNK_STREAM_COUNT=1
CONFIG_BT_ISO_TX_BUF_COUNT=4
CONFIG_BT_ISO_MAX_CHAN=2
# Needed for Periodic Advertising Sync Transfer
CONFIG_BT_PER_ADV_SYNC_TRANSFER_RECEIVER=y
CONFIG_BT_PER_ADV_SYNC_TRANSFER_SENDER=y
# Volume Offset Control Service
CONFIG_BT_VOCS_MAX_INSTANCE_COUNT=2
CONFIG_BT_VOCS_CLIENT_MAX_INSTANCE_COUNT=2
# Audio Input Control Service
CONFIG_BT_AICS_MAX_INSTANCE_COUNT=4
CONFIG_BT_AICS_CLIENT_MAX_INSTANCE_COUNT=4
#Volume Control
CONFIG_BT_VCP_VOL_REND=y
CONFIG_BT_VCP_VOL_REND_VOCS_INSTANCE_COUNT=2
CONFIG_BT_VCP_VOL_REND_AICS_INSTANCE_COUNT=2
CONFIG_BT_VCP_VOL_CTLR=y
CONFIG_BT_VCP_VOL_CTLR_MAX_VOCS_INST=2
CONFIG_BT_VCP_VOL_CTLR_MAX_AICS_INST=2
CONFIG_BT_MICP_MIC_DEV=y
CONFIG_BT_MICP_MIC_DEV_AICS_INSTANCE_COUNT=2
CONFIG_BT_MICP_MIC_CTLR=y
CONFIG_BT_MICP_MIC_CTLR_MAX_AICS_INST=2
# Coordinated Set Identification
CONFIG_BT_CSIP_SET_MEMBER=y
CONFIG_BT_CSIP_SET_MEMBER_TEST_SAMPLE_DATA=y
CONFIG_BT_CSIP_SET_COORDINATOR=y
CONFIG_BT_CSIP_SET_COORDINATOR_TEST_SAMPLE_DATA=y
# Telephone bearer service
CONFIG_BT_TBS=y
CONFIG_BT_TBS_CLIENT=y
# Media control
CONFIG_MCTL=y
CONFIG_MCTL_LOCAL_PLAYER_CONTROL=y
CONFIG_MCTL_LOCAL_PLAYER_LOCAL_CONTROL=y
CONFIG_MCTL_LOCAL_PLAYER_REMOTE_CONTROL=y
CONFIG_MCTL_REMOTE_PLAYER_CONTROL=y
CONFIG_MCTL_REMOTE_PLAYER_CONTROL_OBJECTS=y
CONFIG_UTF8=y
# Media player
CONFIG_BT_MPL=y
CONFIG_BT_MPL_OBJECTS=y
CONFIG_BT_MPL_MAX_OBJ_SIZE=600
# Media control
CONFIG_BT_MCS=y
CONFIG_BT_MCC=y
CONFIG_BT_MCC_OTS=y
CONFIG_BT_MCC_SHELL=y
CONFIG_BT_MCC_TOTAL_OBJ_CONTENT_MEM=4096
# Immediate Alert
CONFIG_BT_IAS=y
CONFIG_BT_IAS_CLIENT=y
# Object Transfer
CONFIG_BT_OTS=y
CONFIG_BT_OTS_SECONDARY_SVC=y
CONFIG_BT_OTS_MAX_OBJ_CNT=0x30
CONFIG_BT_OTS_CLIENT=y
# Broadcast Audio Scan Service and client
CONFIG_BT_BAP_SCAN_DELEGATOR=y
CONFIG_BT_BAP_BROADCAST_ASSISTANT=y
# Hearing Access
CONFIG_BT_HAS=y
CONFIG_BT_HAS_CLIENT=y
# Common Audio Profile
CONFIG_BT_CAP_ACCEPTOR=y
CONFIG_BT_CAP_ACCEPTOR_SET_MEMBER=y
CONFIG_BT_CAP_INITIATOR=y
# DEBUGGING
CONFIG_BT_DEBUG_LOG=y
CONFIG_BT_VCP_VOL_REND_LOG_LEVEL_DBG=y
CONFIG_BT_VCP_VOL_CTLR_LOG_LEVEL_DBG=y
CONFIG_BT_AICS_LOG_LEVEL_DBG=y
CONFIG_BT_AICS_CLIENT_LOG_LEVEL_DBG=y
CONFIG_BT_VOCS_LOG_LEVEL_DBG=y
CONFIG_BT_VOCS_CLIENT_LOG_LEVEL_DBG=y
CONFIG_BT_MICP_MIC_DEV_LOG_LEVEL_DBG=y
CONFIG_BT_MICP_MIC_CTLR_LOG_LEVEL_DBG=y
CONFIG_BT_MPL_LOG_LEVEL_DBG=y
CONFIG_BT_TBS_LOG_LEVEL_DBG=y
CONFIG_BT_TBS_CLIENT_LOG_LEVEL_DBG=y
CONFIG_BT_MCS_LOG_LEVEL_DBG=y
CONFIG_BT_MCC_LOG_LEVEL_DBG=y
CONFIG_BT_OTS_LOG_LEVEL_DBG=y
CONFIG_BT_OTS_CLIENT_LOG_LEVEL_DBG=y
CONFIG_MCTL_LOG_LEVEL_DBG=y
CONFIG_BT_CSIP_SET_MEMBER_LOG_LEVEL_DBG=y
CONFIG_BT_CSIP_SET_COORDINATOR_LOG_LEVEL_DBG=y
CONFIG_BT_CSIP_SET_MEMBER_CRYPTO_LOG_LEVEL_DBG=y
CONFIG_BT_BAP_UNICAST_CLIENT_LOG_LEVEL_DBG=y
CONFIG_BT_BAP_UNICAST_SERVER_LOG_LEVEL_DBG=y
CONFIG_BT_BAP_BROADCAST_SINK_LOG_LEVEL_DBG=y
CONFIG_BT_BAP_BROADCAST_SOURCE_LOG_LEVEL_DBG=y
CONFIG_BT_ASCS_LOG_LEVEL_DBG=y
CONFIG_BT_PACS_LOG_LEVEL_DBG=y
CONFIG_BT_BAP_STREAM_LOG_LEVEL_DBG=y
CONFIG_BT_BAP_ISO_LOG_LEVEL_DBG=y
CONFIG_BT_AUDIO_CODEC_LOG_LEVEL_DBG=y
CONFIG_BT_BAP_SCAN_DELEGATOR_LOG_LEVEL_DBG=y
CONFIG_BT_BAP_BROADCAST_ASSISTANT_LOG_LEVEL_DBG=y
CONFIG_BT_HAS_LOG_LEVEL_DBG=y
CONFIG_BT_HAS_CLIENT_LOG_LEVEL_DBG=y
CONFIG_BT_CAP_ACCEPTOR_LOG_LEVEL_DBG=y
CONFIG_BT_DEBUG_CAP_INITIATOR=y
# LOGGING
CONFIG_TEST_LOGGING_DEFAULTS=n
CONFIG_LOG_MODE_IMMEDIATE=y
# Controller Broadcast ISO configs
CONFIG_BT_CTLR_ADV_ISO=y
CONFIG_BT_CTLR_SYNC_ISO=y
CONFIG_BT_CTLR_SCAN_DATA_LEN_MAX=255
# Supports the highest SDU size required by any BAP LC3 presets (155)
CONFIG_BT_CTLR_ISO_TX_BUFFER_SIZE=155
CONFIG_BT_CTLR_ADV_DATA_LEN_MAX=191
CONFIG_BT_CTLR_ADV_ISO_STREAM_COUNT=1
CONFIG_BT_CTLR_SYNC_ISO_STREAM_MAX=1
# Controller Connected ISO configs
CONFIG_BT_CTLR_CENTRAL_ISO=y
CONFIG_BT_CTLR_PERIPHERAL_ISO=y
CONFIG_BT_CTLR_ISO_TX_BUFFERS=3

View file

@ -0,0 +1,492 @@
/*
* Copyright (c) 2021-2023 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifdef CONFIG_BT_BAP_BROADCAST_ASSISTANT
#include <zephyr/bluetooth/bluetooth.h>
#include <zephyr/bluetooth/hci.h>
#include <zephyr/bluetooth/audio/bap.h>
#include "../../../../../subsys/bluetooth/host/hci_core.h"
#include "common.h"
extern enum bst_result_t bst_result;
/* BASS variables */
static volatile bool g_mtu_exchanged;
static volatile bool g_discovery_complete;
static volatile bool g_write_complete;
static volatile bool g_cb;
static volatile bool g_broadcaster_found;
static volatile bool g_pa_synced;
static volatile bool g_state_synced;
static volatile uint8_t g_src_id;
static volatile uint32_t g_broadcast_id;
static volatile bool g_cb;
/* Broadcaster variables */
static bt_addr_le_t g_broadcaster_addr;
static struct bt_le_scan_recv_info g_broadcaster_info;
static struct bt_le_per_adv_sync *g_pa_sync;
static const char *phy2str(uint8_t phy)
{
switch (phy) {
case 0: return "No packets";
case BT_GAP_LE_PHY_1M: return "LE 1M";
case BT_GAP_LE_PHY_2M: return "LE 2M";
case BT_GAP_LE_PHY_CODED: return "LE Coded";
default: return "Unknown";
}
}
static void bap_broadcast_assistant_discover_cb(struct bt_conn *conn, int err,
uint8_t recv_state_count)
{
if (err != 0) {
FAIL("BASS discover failed (%d)\n", err);
return;
}
printk("BASS discover done with %u recv states\n", recv_state_count);
g_discovery_complete = true;
}
static void bap_broadcast_assistant_scan_cb(const struct bt_le_scan_recv_info *info,
uint32_t broadcast_id)
{
char le_addr[BT_ADDR_LE_STR_LEN];
bt_addr_le_to_str(info->addr, le_addr, sizeof(le_addr));
printk("Scan Recv: [DEVICE]: %s, broadcast_id %u, "
"interval (ms) %u), SID 0x%x, RSSI %i\n",
le_addr, broadcast_id, info->interval * 5 / 4,
info->sid, info->rssi);
(void)memcpy(&g_broadcaster_info, info, sizeof(g_broadcaster_info));
bt_addr_le_copy(&g_broadcaster_addr, info->addr);
g_broadcast_id = broadcast_id;
g_broadcaster_found = true;
}
static bool metadata_entry(struct bt_data *data, void *user_data)
{
char metadata[512];
(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[33];
if (err != 0) {
FAIL("BASS recv state read failed (%d)\n", err);
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);
for (int i = 0; i < state->num_subgroups; i++) {
const struct bt_bap_scan_delegator_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 (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;
}
}
g_state_synced = state->pa_sync_state == BT_BAP_PA_STATE_SYNCED;
g_src_id = state->src_id;
g_cb = true;
}
static void bap_broadcast_assistant_recv_state_removed_cb(struct bt_conn *conn, int err,
uint8_t src_id)
{
if (err != 0) {
FAIL("BASS recv state removed failed (%d)\n", err);
return;
}
printk("BASS recv state %u removed\n", src_id);
g_cb = true;
}
static void bap_broadcast_assistant_scan_start_cb(struct bt_conn *conn, int err)
{
if (err != 0) {
FAIL("BASS scan start failed (%d)\n", err);
return;
}
printk("BASS scan start successful\n");
g_write_complete = true;
}
static void bap_broadcast_assistant_scan_stop_cb(struct bt_conn *conn, int err)
{
if (err != 0) {
FAIL("BASS scan stop failed (%d)\n", err);
return;
}
printk("BASS scan stop successful\n");
g_write_complete = true;
}
static void bap_broadcast_assistant_add_src_cb(struct bt_conn *conn, int err)
{
if (err != 0) {
FAIL("BASS add source failed (%d)\n", err);
return;
}
printk("BASS add source successful\n");
g_write_complete = true;
}
static void bap_broadcast_assistant_mod_src_cb(struct bt_conn *conn, int err)
{
if (err != 0) {
FAIL("BASS modify source failed (%d)\n", err);
return;
}
printk("BASS modify source successful\n");
g_write_complete = true;
}
static void bap_broadcast_assistant_broadcast_code_cb(struct bt_conn *conn, int err)
{
if (err != 0) {
FAIL("BASS broadcast code failed (%d)\n", err);
return;
}
printk("BASS broadcast code successful\n");
g_write_complete = true;
}
static void bap_broadcast_assistant_rem_src_cb(struct bt_conn *conn, int err)
{
if (err != 0) {
FAIL("BASS remove source failed (%d)\n", err);
return;
}
printk("BASS remove source successful\n");
g_write_complete = true;
}
static struct bt_bap_broadcast_assistant_cb broadcast_assistant_cbs = {
.discover = bap_broadcast_assistant_discover_cb,
.scan = bap_broadcast_assistant_scan_cb,
.recv_state = bap_broadcast_assistant_recv_state_cb,
.recv_state_removed = bap_broadcast_assistant_recv_state_removed_cb,
.scan_start = bap_broadcast_assistant_scan_start_cb,
.scan_stop = bap_broadcast_assistant_scan_stop_cb,
.add_src = bap_broadcast_assistant_add_src_cb,
.mod_src = bap_broadcast_assistant_mod_src_cb,
.broadcast_code = bap_broadcast_assistant_broadcast_code_cb,
.rem_src = bap_broadcast_assistant_rem_src_cb,
};
static void att_mtu_updated(struct bt_conn *conn, uint16_t tx, uint16_t rx)
{
g_mtu_exchanged = true;
}
static struct bt_gatt_cb gatt_callbacks = {
.att_mtu_updated = att_mtu_updated,
};
static void sync_cb(struct bt_le_per_adv_sync *sync,
struct bt_le_per_adv_sync_synced_info *info)
{
char le_addr[BT_ADDR_LE_STR_LEN];
bt_addr_le_to_str(info->addr, le_addr, sizeof(le_addr));
printk("PER_ADV_SYNC[%u]: [DEVICE]: %s synced, "
"Interval 0x%04x (%u ms), PHY %s\n",
bt_le_per_adv_sync_get_index(sync), le_addr, info->interval,
info->interval * 5 / 4, phy2str(info->phy));
g_pa_synced = true;
}
static void term_cb(struct bt_le_per_adv_sync *sync,
const struct bt_le_per_adv_sync_term_info *info)
{
char le_addr[BT_ADDR_LE_STR_LEN];
bt_addr_le_to_str(info->addr, le_addr, sizeof(le_addr));
printk("PER_ADV_SYNC[%u]: [DEVICE]: %s sync terminated\n",
bt_le_per_adv_sync_get_index(sync), le_addr);
g_pa_synced = false;
}
static struct bt_le_per_adv_sync_cb sync_callbacks = {
.synced = sync_cb,
.term = term_cb,
};
static void test_exchange_mtu(void)
{
WAIT_FOR_COND(g_mtu_exchanged);
printk("MTU exchanged\n");
}
static void test_bass_discover(void)
{
int err;
printk("Discovering BASS\n");
err = bt_bap_broadcast_assistant_discover(default_conn);
if (err != 0) {
FAIL("Failed to discover BASS %d\n", err);
return;
}
WAIT_FOR_COND(g_discovery_complete);
printk("Discovery complete\n");
}
static void test_bass_scan_start(void)
{
int err;
printk("Starting scan\n");
g_write_complete = false;
err = bt_bap_broadcast_assistant_scan_start(default_conn, true);
if (err != 0) {
FAIL("Could not write scan start to BASS (err %d)\n", err);
return;
}
WAIT_FOR_COND(g_write_complete && g_broadcaster_found);
printk("Scan started\n");
}
static void test_bass_scan_stop(void)
{
int err;
printk("Stopping scan\n");
g_write_complete = false;
err = bt_bap_broadcast_assistant_scan_stop(default_conn);
if (err != 0) {
FAIL("Could not write scan stop to BASS (err %d)\n", err);
return;
}
WAIT_FOR_COND(g_write_complete);
printk("Scan stopped\n");
}
static void test_bass_create_pa_sync(void)
{
int err;
struct bt_le_per_adv_sync_param sync_create_param = { 0 };
printk("Creating Periodic Advertising Sync...\n");
bt_addr_le_copy(&sync_create_param.addr, &g_broadcaster_addr);
sync_create_param.sid = g_broadcaster_info.sid;
sync_create_param.timeout = 0xa;
err = bt_le_per_adv_sync_create(&sync_create_param, &g_pa_sync);
if (err != 0) {
FAIL("Could not create PA syncs (err %d)\n", err);
return;
}
WAIT_FOR_COND(g_pa_synced);
printk("PA synced\n");
}
static void test_bass_add_source(void)
{
int err;
struct bt_bap_broadcast_assistant_add_src_param add_src_param = { 0 };
struct bt_bap_scan_delegator_subgroup subgroup = { 0 };
printk("Adding source\n");
g_cb = g_write_complete = false;
bt_addr_le_copy(&add_src_param.addr, &g_broadcaster_addr);
add_src_param.adv_sid = g_broadcaster_info.sid;
add_src_param.num_subgroups = 1;
add_src_param.pa_interval = g_broadcaster_info.interval;
add_src_param.pa_sync = false;
add_src_param.broadcast_id = g_broadcast_id;
add_src_param.subgroups = &subgroup;
subgroup.bis_sync = 0;
subgroup.metadata_len = 0;
err = bt_bap_broadcast_assistant_add_src(default_conn, &add_src_param);
if (err != 0) {
FAIL("Could not add source (err %d)\n", err);
return;
}
WAIT_FOR_COND(g_cb && g_write_complete);
printk("Source added\n");
}
static void test_bass_mod_source(void)
{
int err;
struct bt_bap_broadcast_assistant_mod_src_param mod_src_param = { 0 };
struct bt_bap_scan_delegator_subgroup subgroup = { 0 };
printk("Modify source\n");
g_cb = g_write_complete = false;
mod_src_param.src_id = g_src_id;
mod_src_param.num_subgroups = 1;
mod_src_param.pa_sync = true;
mod_src_param.subgroups = &subgroup;
mod_src_param.pa_interval = g_broadcaster_info.interval;
subgroup.bis_sync = 0;
subgroup.metadata_len = 0;
err = bt_bap_broadcast_assistant_mod_src(default_conn, &mod_src_param);
if (err != 0) {
FAIL("Could not modify source (err %d)\n", err);
return;
}
WAIT_FOR_COND(g_cb && g_write_complete);
printk("Source added, waiting for server to PA sync\n");
WAIT_FOR_COND(g_state_synced)
printk("Server PA synced\n");
}
static void test_bass_broadcast_code(void)
{
uint8_t broadcast_code[BT_AUDIO_BROADCAST_CODE_SIZE];
int err;
for (int i = 0; i < ARRAY_SIZE(broadcast_code); i++) {
broadcast_code[i] = i;
}
printk("Adding broadcast code\n");
g_write_complete = false;
err = bt_bap_broadcast_assistant_set_broadcast_code(default_conn, g_src_id, broadcast_code);
if (err != 0) {
FAIL("Could not add broadcast code (err %d)\n", err);
return;
}
WAIT_FOR_COND(g_write_complete);
printk("Broadcast code added\n");
}
static void test_bass_remove_source(void)
{
int err;
printk("Removing source\n");
g_cb = g_write_complete = false;
err = bt_bap_broadcast_assistant_rem_src(default_conn, g_src_id);
if (err != 0) {
FAIL("Could not remove source (err %d)\n", err);
return;
}
WAIT_FOR_COND(g_cb && g_write_complete);
printk("Source removed\n");
}
static void test_main(void)
{
int err;
err = bt_enable(NULL);
if (err != 0) {
FAIL("Bluetooth enable failed (err %d)\n", err);
return;
}
bt_gatt_cb_register(&gatt_callbacks);
bt_bap_broadcast_assistant_register_cb(&broadcast_assistant_cbs);
bt_le_per_adv_sync_cb_register(&sync_callbacks);
printk("Starting scan\n");
err = bt_le_scan_start(BT_LE_SCAN_PASSIVE, device_found);
if (err != 0) {
FAIL("Scanning failed to start (err %d)\n", err);
return;
}
printk("Scanning successfully started\n");
WAIT_FOR_FLAG(flag_connected);
test_exchange_mtu();
test_bass_discover();
test_bass_scan_start();
test_bass_scan_stop();
test_bass_create_pa_sync();
test_bass_add_source();
test_bass_mod_source();
test_bass_broadcast_code();
test_bass_remove_source();
PASS("BAP broadcast assistant Passed\n");
}
static const struct bst_test_instance test_bass[] = {
{
.test_id = "bap_broadcast_assistant",
.test_post_init_f = test_init,
.test_tick_f = test_tick,
.test_main_f = test_main
},
BSTEST_END_MARKER
};
struct bst_test_list *test_bap_broadcast_assistant_install(struct bst_test_list *tests)
{
return bst_add_tests(tests, test_bass);
}
#else
struct bst_test_list *test_bap_broadcast_assistant_install(struct bst_test_list *tests)
{
return tests;
}
#endif /* CONFIG_BT_BAP_BROADCAST_ASSISTANT */

View file

@ -0,0 +1,92 @@
/*
* Copyright (c) 2021-2022 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifdef CONFIG_BT_BAP_SCAN_DELEGATOR
#include <zephyr/bluetooth/audio/bap.h>
#include "common.h"
extern enum bst_result_t bst_result;
static volatile bool g_cb;
static volatile bool g_pa_synced;
static void pa_synced(struct bt_bap_scan_delegator_recv_state *recv_state,
const struct bt_le_per_adv_sync_synced_info *info)
{
printk("Receive state %p synced\n", recv_state);
g_pa_synced = true;
}
static void pa_term(struct bt_bap_scan_delegator_recv_state *recv_state,
const struct bt_le_per_adv_sync_term_info *info)
{
printk("Receive state %p sync terminated\n", recv_state);
g_pa_synced = false;
}
static void pa_recv(struct bt_bap_scan_delegator_recv_state *recv_state,
const struct bt_le_per_adv_sync_recv_info *info,
struct net_buf_simple *buf)
{
printk("Receive state %p received data\n", recv_state);
}
static struct bt_bap_scan_delegator_cb scan_delegator_cb = {
.pa_synced = pa_synced,
.pa_term = pa_term,
.pa_recv = pa_recv
};
static void test_main(void)
{
int err;
err = bt_enable(NULL);
if (err) {
FAIL("Bluetooth init failed (err %d)\n", err);
return;
}
printk("Bluetooth initialized\n");
bt_bap_scan_delegator_register_cb(&scan_delegator_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;
}
printk("Advertising successfully started\n");
WAIT_FOR_FLAG(flag_connected);
WAIT_FOR_COND(g_pa_synced);
PASS("BAP Scan Delegator passed\n");
}
static const struct bst_test_instance test_scan_delegator[] = {
{
.test_id = "bap_scan_delegator",
.test_post_init_f = test_init,
.test_tick_f = test_tick,
.test_main_f = test_main
},
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 */

View file

@ -0,0 +1,607 @@
/*
* Copyright (c) 2021-2023 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#if defined(CONFIG_BT_BAP_UNICAST_CLIENT)
#include <zephyr/bluetooth/bluetooth.h>
#include <zephyr/bluetooth/audio/audio.h>
#include <zephyr/bluetooth/audio/bap.h>
#include <zephyr/bluetooth/audio/bap_lc3_preset.h>
#include <zephyr/bluetooth/audio/pacs.h>
#include "common.h"
#include "bap_unicast_common.h"
extern enum bst_result_t bst_result;
static struct bt_bap_stream g_streams[CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SNK_COUNT];
static struct bt_bap_ep *g_sinks[CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SNK_COUNT];
/* Mandatory support preset by both client and server */
static struct bt_bap_lc3_preset preset_16_2_1 = BT_BAP_LC3_UNICAST_PRESET_16_2_1(
BT_AUDIO_LOCATION_FRONT_LEFT, BT_AUDIO_CONTEXT_TYPE_UNSPECIFIED);
CREATE_FLAG(flag_mtu_exchanged);
CREATE_FLAG(flag_sink_discovered);
CREATE_FLAG(flag_stream_codec_configured);
static atomic_t flag_stream_qos_configured;
CREATE_FLAG(flag_stream_enabled);
CREATE_FLAG(flag_stream_started);
CREATE_FLAG(flag_stream_released);
CREATE_FLAG(flag_operation_success);
static void stream_configured(struct bt_bap_stream *stream,
const struct bt_codec_qos_pref *pref)
{
printk("Configured stream %p\n", stream);
/* TODO: The preference should be used/taken into account when
* setting the QoS
*/
SET_FLAG(flag_stream_codec_configured);
}
static void stream_qos_set(struct bt_bap_stream *stream)
{
printk("QoS set stream %p\n", stream);
atomic_inc(&flag_stream_qos_configured);
}
static void stream_enabled(struct bt_bap_stream *stream)
{
printk("Enabled stream %p\n", stream);
SET_FLAG(flag_stream_enabled);
}
static void stream_started(struct bt_bap_stream *stream)
{
printk("Started stream %p\n", stream);
SET_FLAG(flag_stream_started);
}
static void stream_metadata_updated(struct bt_bap_stream *stream)
{
printk("Metadata updated stream %p\n", stream);
}
static void stream_disabled(struct bt_bap_stream *stream)
{
printk("Disabled stream %p\n", stream);
}
static void stream_stopped(struct bt_bap_stream *stream, uint8_t reason)
{
printk("Stopped stream %p with reason 0x%02X\n", stream, reason);
}
static void stream_released(struct bt_bap_stream *stream)
{
printk("Released stream %p\n", stream);
SET_FLAG(flag_stream_released);
}
static struct bt_bap_stream_ops stream_ops = {
.configured = stream_configured,
.qos_set = stream_qos_set,
.enabled = stream_enabled,
.started = stream_started,
.metadata_updated = stream_metadata_updated,
.disabled = stream_disabled,
.stopped = stream_stopped,
.released = stream_released,
};
static void unicast_client_location_cb(struct bt_conn *conn,
enum bt_audio_dir dir,
enum bt_audio_location loc)
{
printk("dir %u loc %X\n", dir, loc);
}
static void available_contexts_cb(struct bt_conn *conn,
enum bt_audio_context snk_ctx,
enum bt_audio_context src_ctx)
{
printk("snk ctx %u src ctx %u\n", snk_ctx, src_ctx);
}
static void config_cb(struct bt_bap_stream *stream, enum bt_bap_ascs_rsp_code rsp_code,
enum bt_bap_ascs_reason reason)
{
printk("stream %p config operation rsp_code %u reason %u\n", stream, rsp_code, reason);
if (rsp_code == BT_BAP_ASCS_RSP_CODE_SUCCESS) {
SET_FLAG(flag_operation_success);
}
}
static void qos_cb(struct bt_bap_stream *stream, enum bt_bap_ascs_rsp_code rsp_code,
enum bt_bap_ascs_reason reason)
{
printk("stream %p qos operation rsp_code %u reason %u\n", stream, rsp_code, reason);
if (rsp_code == BT_BAP_ASCS_RSP_CODE_SUCCESS) {
SET_FLAG(flag_operation_success);
}
}
static void enable_cb(struct bt_bap_stream *stream, enum bt_bap_ascs_rsp_code rsp_code,
enum bt_bap_ascs_reason reason)
{
printk("stream %p enable operation rsp_code %u reason %u\n", stream, rsp_code, reason);
if (rsp_code == BT_BAP_ASCS_RSP_CODE_SUCCESS) {
SET_FLAG(flag_operation_success);
}
}
static void start_cb(struct bt_bap_stream *stream, enum bt_bap_ascs_rsp_code rsp_code,
enum bt_bap_ascs_reason reason)
{
printk("stream %p start operation rsp_code %u reason %u\n", stream, rsp_code, reason);
if (rsp_code == BT_BAP_ASCS_RSP_CODE_SUCCESS) {
SET_FLAG(flag_operation_success);
}
}
static void stop_cb(struct bt_bap_stream *stream, enum bt_bap_ascs_rsp_code rsp_code,
enum bt_bap_ascs_reason reason)
{
printk("stream %p stop operation rsp_code %u reason %u\n", stream, rsp_code, reason);
if (rsp_code == BT_BAP_ASCS_RSP_CODE_SUCCESS) {
SET_FLAG(flag_operation_success);
}
}
static void disable_cb(struct bt_bap_stream *stream, enum bt_bap_ascs_rsp_code rsp_code,
enum bt_bap_ascs_reason reason)
{
printk("stream %p disable operation rsp_code %u reason %u\n", stream, rsp_code, reason);
if (rsp_code == BT_BAP_ASCS_RSP_CODE_SUCCESS) {
SET_FLAG(flag_operation_success);
}
}
static void metadata_cb(struct bt_bap_stream *stream, enum bt_bap_ascs_rsp_code rsp_code,
enum bt_bap_ascs_reason reason)
{
printk("stream %p metadata operation rsp_code %u reason %u\n", stream, rsp_code, reason);
if (rsp_code == BT_BAP_ASCS_RSP_CODE_SUCCESS) {
SET_FLAG(flag_operation_success);
}
}
static void release_cb(struct bt_bap_stream *stream, enum bt_bap_ascs_rsp_code rsp_code,
enum bt_bap_ascs_reason reason)
{
printk("stream %p release operation rsp_code %u reason %u\n", stream, rsp_code, reason);
if (rsp_code == BT_BAP_ASCS_RSP_CODE_SUCCESS) {
SET_FLAG(flag_operation_success);
}
}
const struct bt_bap_unicast_client_cb unicast_client_cbs = {
.location = unicast_client_location_cb,
.available_contexts = available_contexts_cb,
.config = config_cb,
.qos = qos_cb,
.enable = enable_cb,
.start = start_cb,
.stop = stop_cb,
.disable = disable_cb,
.metadata = metadata_cb,
.release = release_cb,
};
static void add_remote_sink(struct bt_bap_ep *ep, uint8_t index)
{
printk("Sink #%u: ep %p\n", index, ep);
g_sinks[index] = ep;
}
static void print_remote_codec(struct bt_codec *codec, int index, enum bt_audio_dir dir)
{
printk("#%u: codec %p dir 0x%02x\n", index, codec, dir);
print_codec(codec);
}
static void discover_sink_cb(struct bt_conn *conn, struct bt_codec *codec, struct bt_bap_ep *ep,
struct bt_bap_unicast_client_discover_params *params)
{
static bool codec_found;
static bool endpoint_found;
if (params->err != 0) {
FAIL("Discovery failed: %d\n", params->err);
return;
}
if (codec != NULL) {
print_remote_codec(codec, params->num_caps, params->dir);
codec_found = true;
return;
}
if (ep != NULL) {
if (params->dir == BT_AUDIO_DIR_SINK) {
add_remote_sink(ep, params->num_eps);
endpoint_found = true;
} else {
FAIL("Invalid param dir: %u\n", params->dir);
}
return;
}
printk("Discover complete\n");
(void)memset(params, 0, sizeof(*params));
if (endpoint_found && codec_found) {
SET_FLAG(flag_sink_discovered);
} else {
FAIL("Did not discover endpoint and codec\n");
}
}
static void att_mtu_updated(struct bt_conn *conn, uint16_t tx, uint16_t rx)
{
printk("MTU exchanged\n");
SET_FLAG(flag_mtu_exchanged);
}
static struct bt_gatt_cb gatt_callbacks = {
.att_mtu_updated = att_mtu_updated,
};
static void init(void)
{
int err;
err = bt_enable(NULL);
if (err != 0) {
FAIL("Bluetooth enable failed (err %d)\n", err);
return;
}
for (size_t i = 0; i < ARRAY_SIZE(g_streams); i++) {
g_streams[i].ops = &stream_ops;
}
bt_gatt_cb_register(&gatt_callbacks);
err = bt_bap_unicast_client_register_cb(&unicast_client_cbs);
if (err != 0) {
FAIL("Failed to register client callbacks: %d", err);
return;
}
}
static void scan_and_connect(void)
{
int err;
err = bt_le_scan_start(BT_LE_SCAN_PASSIVE, device_found);
if (err != 0) {
FAIL("Scanning failed to start (err %d)\n", err);
return;
}
printk("Scanning successfully started\n");
WAIT_FOR_FLAG(flag_connected);
}
static void exchange_mtu(void)
{
WAIT_FOR_FLAG(flag_mtu_exchanged);
}
static void discover_sink(void)
{
static struct bt_bap_unicast_client_discover_params params;
int err;
params.func = discover_sink_cb;
params.dir = BT_AUDIO_DIR_SINK;
err = bt_bap_unicast_client_discover(default_conn, &params);
if (err != 0) {
printk("Failed to discover sink: %d\n", err);
return;
}
WAIT_FOR_FLAG(flag_sink_discovered);
}
static int codec_configure_stream(struct bt_bap_stream *stream, struct bt_bap_ep *ep)
{
int err;
UNSET_FLAG(flag_stream_codec_configured);
UNSET_FLAG(flag_operation_success);
err = bt_bap_stream_config(default_conn, stream, ep,
&preset_16_2_1.codec);
if (err != 0) {
FAIL("Could not configure stream: %d\n", err);
return err;
}
WAIT_FOR_FLAG(flag_stream_codec_configured);
WAIT_FOR_FLAG(flag_operation_success);
return 0;
}
static void codec_configure_streams(size_t stream_cnt)
{
for (size_t i = 0U; i < stream_cnt; i++) {
struct bt_bap_stream *stream = &g_streams[i];
int err;
if (g_sinks[i] == NULL) {
break;
}
err = codec_configure_stream(stream, g_sinks[i]);
if (err != 0) {
FAIL("Unable to configure stream[%zu]: %d", i, err);
return;
}
}
}
static void qos_configure_streams(struct bt_bap_unicast_group *unicast_group,
size_t stream_cnt)
{
int err;
UNSET_FLAG(flag_stream_qos_configured);
err = bt_bap_stream_qos(default_conn, unicast_group);
if (err != 0) {
FAIL("Unable to QoS configure streams: %d", err);
return;
}
while (atomic_get(&flag_stream_qos_configured) != stream_cnt) {
(void)k_sleep(K_MSEC(1));
}
}
static int enable_stream(struct bt_bap_stream *stream)
{
int err;
UNSET_FLAG(flag_stream_enabled);
err = bt_bap_stream_enable(stream, NULL, 0);
if (err != 0) {
FAIL("Could not enable stream: %d\n", err);
return err;
}
WAIT_FOR_FLAG(flag_stream_enabled);
return 0;
}
static void enable_streams(size_t stream_cnt)
{
for (size_t i = 0U; i < stream_cnt; i++) {
struct bt_bap_stream *stream = &g_streams[i];
int err;
err = enable_stream(stream);
if (err != 0) {
FAIL("Unable to enable stream[%zu]: %d",
i, err);
return;
}
}
}
static int start_stream(struct bt_bap_stream *stream)
{
int err;
UNSET_FLAG(flag_stream_started);
err = bt_bap_stream_start(stream);
if (err != 0) {
FAIL("Could not start stream: %d\n", err);
return err;
}
WAIT_FOR_FLAG(flag_stream_started);
return 0;
}
static void start_streams(size_t stream_cnt)
{
for (size_t i = 0U; i < 1; i++) {
struct bt_bap_stream *stream = &g_streams[i];
int err;
err = start_stream(stream);
if (err != 0) {
FAIL("Unable to start stream[%zu]: %d", i, err);
return;
}
}
}
static size_t release_streams(size_t stream_cnt)
{
for (size_t i = 0; i < stream_cnt; i++) {
int err;
if (g_sinks[i] == NULL) {
break;
}
UNSET_FLAG(flag_operation_success);
UNSET_FLAG(flag_stream_released);
err = bt_bap_stream_release(&g_streams[i]);
if (err != 0) {
FAIL("Unable to release stream[%zu]: %d", i, err);
return 0;
}
WAIT_FOR_FLAG(flag_operation_success);
WAIT_FOR_FLAG(flag_stream_released);
}
return stream_cnt;
}
static size_t create_unicast_group(struct bt_bap_unicast_group **unicast_group)
{
struct bt_bap_unicast_group_stream_pair_param pair_params[ARRAY_SIZE(g_streams)];
struct bt_bap_unicast_group_stream_param stream_params[ARRAY_SIZE(g_streams)];
struct bt_bap_unicast_group_param param;
size_t stream_cnt = 0;
int err;
for (stream_cnt = 0U;
stream_cnt < MIN(ARRAY_SIZE(g_sinks), ARRAY_SIZE(g_streams));
stream_cnt++) {
if (g_sinks[stream_cnt] == NULL) {
break;
}
stream_params[stream_cnt].stream = &g_streams[stream_cnt];
stream_params[stream_cnt].qos = &preset_16_2_1.qos;
pair_params[stream_cnt].rx_param = NULL;
pair_params[stream_cnt].tx_param = &stream_params[stream_cnt];
}
if (stream_cnt == 0U) {
FAIL("No streams added to group");
return 0;
}
param.params = pair_params;
param.params_count = stream_cnt;
param.packing = BT_ISO_PACKING_SEQUENTIAL;
/* Require controller support for CIGs */
printk("Creating unicast group\n");
err = bt_bap_unicast_group_create(&param, unicast_group);
if (err != 0) {
FAIL("Unable to create unicast group: %d", err);
return 0;
}
return stream_cnt;
}
static void delete_unicast_group(struct bt_bap_unicast_group *unicast_group)
{
int err;
/* Require controller support for CIGs */
err = bt_bap_unicast_group_delete(unicast_group);
if (err != 0) {
FAIL("Unable to delete unicast group: %d", err);
return;
}
}
static void test_main(void)
{
const unsigned int iterations = 3;
init();
scan_and_connect();
exchange_mtu();
discover_sink();
/* Run the stream setup multiple time to ensure states are properly
* set and reset
*/
for (unsigned int i = 0U; i < iterations; i++) {
struct bt_bap_unicast_group *unicast_group;
size_t stream_cnt;
printk("\n########### Running iteration #%u\n\n", i);
printk("Creating unicast group\n");
stream_cnt = create_unicast_group(&unicast_group);
printk("Codec configuring streams\n");
codec_configure_streams(stream_cnt);
printk("QoS configuring streams\n");
qos_configure_streams(unicast_group, stream_cnt);
printk("Enabling streams\n");
enable_streams(stream_cnt);
printk("Starting streams\n");
start_streams(stream_cnt);
printk("Releasing streams\n");
release_streams(stream_cnt);
/* Test removing streams from group after creation */
printk("Deleting unicast group\n");
delete_unicast_group(unicast_group);
unicast_group = NULL;
}
PASS("Unicast client passed\n");
}
static const struct bst_test_instance test_unicast_client[] = {
{
.test_id = "unicast_client",
.test_post_init_f = test_init,
.test_tick_f = test_tick,
.test_main_f = test_main
},
BSTEST_END_MARKER
};
struct bst_test_list *test_unicast_client_install(struct bst_test_list *tests)
{
return bst_add_tests(tests, test_unicast_client);
}
#else /* !(CONFIG_BT_BAP_UNICAST_CLIENT) */
struct bst_test_list *test_unicast_client_install(struct bst_test_list *tests)
{
return tests;
}
#endif /* CONFIG_BT_BAP_UNICAST_CLIENT */

View file

@ -0,0 +1,49 @@
/*
* Copyright (c) 2021-2023 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "common.h"
#include "bap_unicast_common.h"
void print_hex(const uint8_t *ptr, size_t len)
{
while (len-- != 0) {
printk("%02x", *ptr++);
}
}
void print_codec(const struct bt_codec *codec)
{
printk("codec 0x%02x cid 0x%04x vid 0x%04x count %u\n",
codec->id, codec->cid, codec->vid, codec->data_count);
for (uint8_t i = 0; i < codec->data_count; i++) {
printk("data #%u: type 0x%02x len %u\n",
i, codec->data[i].data.type,
codec->data[i].data.data_len);
print_hex(codec->data[i].data.data,
codec->data[i].data.data_len -
sizeof(codec->data[i].data.type));
printk("\n");
}
for (uint8_t i = 0; i < codec->meta_count; i++) {
printk("meta #%u: type 0x%02x len %u\n",
i, codec->meta[i].data.type,
codec->meta[i].data.data_len);
print_hex(codec->meta[i].data.data,
codec->meta[i].data.data_len -
sizeof(codec->meta[i].data.type));
printk("\n");
}
}
void print_qos(const struct bt_codec_qos *qos)
{
printk("QoS: interval %u framing 0x%02x phy 0x%02x sdu %u "
"rtn %u latency %u pd %u\n",
qos->interval, qos->framing, qos->phy, qos->sdu,
qos->rtn, qos->latency, qos->pd);
}

View file

@ -0,0 +1,19 @@
/**
* Common functions and helpers for unicast audio BSIM audio tests
*
* Copyright (c) 2021-2023 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef ZEPHYR_TEST_BSIM_BT_AUDIO_TEST_UNICAST_COMMON_
#define ZEPHYR_TEST_BSIM_BT_AUDIO_TEST_UNICAST_COMMON_
#include <zephyr/bluetooth/bluetooth.h>
#include <zephyr/bluetooth/audio/audio.h>
void print_hex(const uint8_t *ptr, size_t len);
void print_codec(const struct bt_codec *codec);
void print_qos(const struct bt_codec_qos *qos);
#endif /* ZEPHYR_TEST_BSIM_BT_AUDIO_TEST_UNICAST_COMMON_ */

View file

@ -0,0 +1,375 @@
/*
* Copyright (c) 2021-2023 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#if defined(CONFIG_BT_BAP_UNICAST_SERVER)
#include <zephyr/bluetooth/bluetooth.h>
#include <zephyr/bluetooth/audio/audio.h>
#include <zephyr/bluetooth/audio/bap.h>
#include <zephyr/bluetooth/audio/pacs.h>
#include "common.h"
#include "bap_unicast_common.h"
extern enum bst_result_t bst_result;
#define CHANNEL_COUNT_1 BIT(0)
static struct bt_codec lc3_codec =
BT_CODEC_LC3(BT_CODEC_LC3_FREQ_16KHZ, BT_CODEC_LC3_DURATION_10, CHANNEL_COUNT_1, 40u, 40u,
1u, (BT_AUDIO_CONTEXT_TYPE_CONVERSATIONAL | BT_AUDIO_CONTEXT_TYPE_MEDIA));
static struct bt_bap_stream streams[CONFIG_BT_ASCS_ASE_SNK_COUNT + CONFIG_BT_ASCS_ASE_SRC_COUNT];
static const struct bt_codec_qos_pref qos_pref =
BT_CODEC_QOS_PREF(true, BT_GAP_LE_PHY_2M, 0x02, 10, 40000, 40000, 40000, 40000);
/* TODO: Expand with BAP data */
static const struct bt_data unicast_server_ad[] = {
BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)),
BT_DATA_BYTES(BT_DATA_UUID16_ALL, BT_UUID_16_ENCODE(BT_UUID_ASCS_VAL)),
};
CREATE_FLAG(flag_stream_configured);
static void print_ase_info(struct bt_bap_ep *ep, void *user_data)
{
struct bt_bap_ep_info info;
bt_bap_ep_get_info(ep, &info);
printk("ASE info: id %u state %u dir %u\n", info.id, info.state, info.dir);
}
static struct bt_bap_stream *stream_alloc(void)
{
for (size_t i = 0; i < ARRAY_SIZE(streams); i++) {
struct bt_bap_stream *stream = &streams[i];
if (!stream->conn) {
return stream;
}
}
return NULL;
}
static int lc3_config(struct bt_conn *conn, const struct bt_bap_ep *ep, enum bt_audio_dir dir,
const struct bt_codec *codec, struct bt_bap_stream **stream,
struct bt_codec_qos_pref *const pref)
{
printk("ASE Codec Config: conn %p ep %p dir %u\n", conn, ep, dir);
print_codec(codec);
*stream = stream_alloc();
if (*stream == NULL) {
printk("No streams available\n");
return -ENOMEM;
}
printk("ASE Codec Config stream %p\n", *stream);
bt_bap_unicast_server_foreach_ep(conn, print_ase_info, NULL);
SET_FLAG(flag_stream_configured);
*pref = qos_pref;
return 0;
}
static int lc3_reconfig(struct bt_bap_stream *stream, enum bt_audio_dir dir,
const struct bt_codec *codec, struct bt_codec_qos_pref *const pref)
{
printk("ASE Codec Reconfig: stream %p\n", stream);
print_codec(codec);
/* We only support one QoS at the moment, reject changes */
return -ENOEXEC;
}
static int lc3_qos(struct bt_bap_stream *stream, const struct bt_codec_qos *qos)
{
printk("QoS: stream %p qos %p\n", stream, qos);
print_qos(qos);
return 0;
}
static int lc3_enable(struct bt_bap_stream *stream, const struct bt_codec_data *meta,
size_t meta_count)
{
printk("Enable: stream %p meta_count %zu\n", stream, meta_count);
return 0;
}
static int lc3_start(struct bt_bap_stream *stream)
{
printk("Start: stream %p\n", stream);
return 0;
}
static bool valid_metadata_type(uint8_t type, uint8_t len)
{
switch (type) {
case BT_AUDIO_METADATA_TYPE_PREF_CONTEXT:
case BT_AUDIO_METADATA_TYPE_STREAM_CONTEXT:
if (len != 2) {
return false;
}
return true;
case BT_AUDIO_METADATA_TYPE_STREAM_LANG:
if (len != 3) {
return false;
}
return true;
case BT_AUDIO_METADATA_TYPE_PARENTAL_RATING:
if (len != 1) {
return false;
}
return true;
case BT_AUDIO_METADATA_TYPE_EXTENDED: /* 1 - 255 octets */
case BT_AUDIO_METADATA_TYPE_VENDOR: /* 1 - 255 octets */
if (len < 1) {
return false;
}
return true;
case BT_AUDIO_METADATA_TYPE_CCID_LIST: /* 2 - 254 octets */
if (len < 2) {
return false;
}
return true;
case BT_AUDIO_METADATA_TYPE_PROGRAM_INFO: /* 0 - 255 octets */
case BT_AUDIO_METADATA_TYPE_PROGRAM_INFO_URI: /* 0 - 255 octets */
return true;
default:
return false;
}
}
static int lc3_metadata(struct bt_bap_stream *stream, const struct bt_codec_data *meta,
size_t meta_count)
{
printk("Metadata: stream %p meta_count %zu\n", stream, meta_count);
for (size_t i = 0; i < meta_count; i++) {
if (!valid_metadata_type(meta->data.type, meta->data.data_len)) {
printk("Invalid metadata type %u or length %u\n", meta->data.type,
meta->data.data_len);
return -EINVAL;
}
}
return 0;
}
static int lc3_disable(struct bt_bap_stream *stream)
{
printk("Disable: stream %p\n", stream);
return 0;
}
static int lc3_stop(struct bt_bap_stream *stream)
{
printk("Stop: stream %p\n", stream);
return 0;
}
static int lc3_release(struct bt_bap_stream *stream)
{
printk("Release: stream %p\n", stream);
return 0;
}
static const struct bt_bap_unicast_server_cb unicast_server_cb = {
.config = lc3_config,
.reconfig = lc3_reconfig,
.qos = lc3_qos,
.enable = lc3_enable,
.start = lc3_start,
.metadata = lc3_metadata,
.disable = lc3_disable,
.stop = lc3_stop,
.release = lc3_release,
};
static void stream_enabled_cb(struct bt_bap_stream *stream)
{
struct bt_bap_ep_info ep_info;
int err;
printk("Enabled: stream %p\n", stream);
err = bt_bap_ep_get_info(stream->ep, &ep_info);
if (err != 0) {
FAIL("Failed to get ep info: %d\n", err);
return;
}
if (ep_info.dir == BT_AUDIO_DIR_SINK) {
/* Automatically do the receiver start ready operation */
err = bt_bap_stream_start(stream);
if (err != 0) {
FAIL("Failed to start stream: %d\n", err);
return;
}
}
}
static void stream_recv(struct bt_bap_stream *stream, const struct bt_iso_recv_info *info,
struct net_buf *buf)
{
printk("Incoming audio on stream %p len %u\n", stream, buf->len);
}
static struct bt_bap_stream_ops stream_ops = {
.enabled = stream_enabled_cb,
.recv = stream_recv
};
static void init(void)
{
static struct bt_pacs_cap cap = {
.codec = &lc3_codec,
};
int err;
err = bt_enable(NULL);
if (err != 0) {
FAIL("Bluetooth enable failed (err %d)\n", err);
return;
}
printk("Bluetooth initialized\n");
bt_bap_unicast_server_register_cb(&unicast_server_cb);
err = bt_pacs_cap_register(BT_AUDIO_DIR_SINK, &cap);
if (err != 0) {
FAIL("Failed to register capabilities: %d", err);
return;
}
for (size_t i = 0; i < ARRAY_SIZE(streams); i++) {
bt_bap_stream_cb_register(&streams[i], &stream_ops);
}
err = bt_le_adv_start(BT_LE_ADV_CONN_NAME, unicast_server_ad, ARRAY_SIZE(unicast_server_ad),
NULL, 0);
if (err != 0) {
FAIL("Advertising failed to start (err %d)\n", err);
return;
}
}
static void set_location(void)
{
int err;
if (IS_ENABLED(CONFIG_BT_PAC_SNK_LOC)) {
err = bt_pacs_set_location(BT_AUDIO_DIR_SINK, BT_AUDIO_LOCATION_FRONT_CENTER);
if (err != 0) {
FAIL("Failed to set sink location (err %d)\n", err);
return;
}
}
if (IS_ENABLED(CONFIG_BT_PAC_SRC_LOC)) {
err = bt_pacs_set_location(BT_AUDIO_DIR_SOURCE, (BT_AUDIO_LOCATION_FRONT_LEFT |
BT_AUDIO_LOCATION_FRONT_RIGHT));
if (err != 0) {
FAIL("Failed to set source location (err %d)\n", err);
return;
}
}
printk("Location successfully set\n");
}
static void set_available_contexts(void)
{
int err;
err = bt_pacs_set_supported_contexts(BT_AUDIO_DIR_SINK,
BT_AUDIO_CONTEXT_TYPE_MEDIA |
BT_AUDIO_CONTEXT_TYPE_CONVERSATIONAL);
if (IS_ENABLED(CONFIG_BT_PAC_SNK) && err != 0) {
FAIL("Failed to set sink supported contexts (err %d)\n", err);
return;
}
err = bt_pacs_set_available_contexts(BT_AUDIO_DIR_SINK,
BT_AUDIO_CONTEXT_TYPE_MEDIA |
BT_AUDIO_CONTEXT_TYPE_CONVERSATIONAL);
if (IS_ENABLED(CONFIG_BT_PAC_SNK) && err != 0) {
FAIL("Failed to set sink available contexts (err %d)\n", err);
return;
}
err = bt_pacs_set_supported_contexts(BT_AUDIO_DIR_SOURCE,
BT_AUDIO_CONTEXT_TYPE_NOTIFICATIONS);
if (IS_ENABLED(CONFIG_BT_PAC_SRC) && err != 0) {
FAIL("Failed to set source supported contexts (err %d)\n", err);
return;
}
err = bt_pacs_set_available_contexts(BT_AUDIO_DIR_SOURCE,
BT_AUDIO_CONTEXT_TYPE_NOTIFICATIONS);
if (IS_ENABLED(CONFIG_BT_PAC_SRC) && err != 0) {
FAIL("Failed to set source available contexts (err %d)\n", err);
return;
}
printk("Available contexts successfully set\n");
}
static void test_main(void)
{
init();
set_location();
set_available_contexts();
/* TODO: When babblesim supports ISO, wait for audio stream to pass */
WAIT_FOR_FLAG(flag_connected);
WAIT_FOR_FLAG(flag_stream_configured);
PASS("Unicast server passed\n");
}
static const struct bst_test_instance test_unicast_server[] = {{.test_id = "unicast_server",
.test_post_init_f = test_init,
.test_tick_f = test_tick,
.test_main_f = test_main},
BSTEST_END_MARKER};
struct bst_test_list *test_unicast_server_install(struct bst_test_list *tests)
{
return bst_add_tests(tests, test_unicast_server);
}
#else /* !(CONFIG_BT_BAP_UNICAST_SERVER) */
struct bst_test_list *test_unicast_server_install(struct bst_test_list *tests)
{
return tests;
}
#endif /* CONFIG_BT_BAP_UNICAST_SERVER */

View file

@ -0,0 +1,94 @@
/*
* Copyright (c) 2021 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/bluetooth/bluetooth.h>
#include "common.h"
extern enum bst_result_t bst_result;
/* TODO: Deprecate in favor of broadcast_source_test */
#define BROADCAST_ID_ENCODE(broadcast_id) \
(((broadcast_id) >> 0) & 0xFF), \
(((broadcast_id) >> 8) & 0xFF), \
(((broadcast_id) >> 16) & 0xFF)
static void test_main(void)
{
int err;
uint32_t broadcast_id = 1234;
struct bt_le_ext_adv *adv;
struct bt_data ad[2] = {
BT_DATA_BYTES(BT_DATA_FLAGS, BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR),
BT_DATA_BYTES(BT_DATA_SVC_DATA16,
BT_UUID_16_ENCODE(BT_UUID_BROADCAST_AUDIO_VAL),
BROADCAST_ID_ENCODE(broadcast_id))
};
err = bt_enable(NULL);
if (err) {
FAIL("Bluetooth init failed (err %d)\n", err);
return;
}
printk("Bluetooth initialized\n");
/* Create a non-connectable non-scannable advertising set */
err = bt_le_ext_adv_create(BT_LE_EXT_ADV_NCONN_NAME, NULL, &adv);
if (err) {
FAIL("Failed to create advertising set (err %d)\n", err);
return;
}
/* Set periodic advertising parameters */
err = bt_le_per_adv_set_param(adv, BT_LE_PER_ADV_DEFAULT);
if (err) {
FAIL("Failed to set periodic advertising parameters (err %d)\n", err);
return;
}
/* Set adv data */
err = bt_le_ext_adv_set_data(adv, ad, ARRAY_SIZE(ad), NULL, 0);
if (err) {
FAIL("Failed to set advertising data (err %d)\n", err);
return;
}
/* Enable Periodic Advertising */
err = bt_le_per_adv_start(adv);
if (err) {
FAIL("Failed to enable periodic advertising (err %d)\n", err);
return;
}
/* Start extended advertising */
err = bt_le_ext_adv_start(adv, BT_LE_EXT_ADV_START_DEFAULT);
if (err) {
FAIL("Failed to start extended advertising (err %d)\n", err);
return;
}
printk("Advertising successfully started\n");
k_sleep(K_SECONDS(10));
PASS("BASS broadcaster passed\n");
}
static const struct bst_test_instance test_bass_broadcaster[] = {
{
.test_id = "bass_broadcaster",
.test_post_init_f = test_init,
.test_tick_f = test_tick,
.test_main_f = test_main
},
BSTEST_END_MARKER
};
struct bst_test_list *test_bass_broadcaster_install(struct bst_test_list *tests)
{
return bst_add_tests(tests, test_bass_broadcaster);
}

View file

@ -0,0 +1,332 @@
/*
* Copyright (c) 2021-2023 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#if defined(CONFIG_BT_BAP_BROADCAST_SINK)
#include <zephyr/bluetooth/bluetooth.h>
#include <zephyr/bluetooth/audio/audio.h>
#include <zephyr/bluetooth/audio/bap.h>
#include <zephyr/bluetooth/audio/bap_lc3_preset.h>
#include <zephyr/bluetooth/audio/pacs.h>
#include "common.h"
extern enum bst_result_t bst_result;
CREATE_FLAG(broadcaster_found);
CREATE_FLAG(base_received);
CREATE_FLAG(flag_base_metadata_updated);
CREATE_FLAG(pa_synced);
CREATE_FLAG(flag_syncable);
CREATE_FLAG(pa_sync_lost);
CREATE_FLAG(flag_received);
static struct bt_bap_broadcast_sink *g_sink;
static struct bt_bap_stream broadcast_sink_streams[CONFIG_BT_BAP_BROADCAST_SNK_STREAM_COUNT];
static struct bt_bap_stream *streams[ARRAY_SIZE(broadcast_sink_streams)];
static struct bt_bap_lc3_preset preset_16_2_1 = BT_BAP_LC3_BROADCAST_PRESET_16_2_1(
BT_AUDIO_LOCATION_FRONT_LEFT, BT_AUDIO_CONTEXT_TYPE_UNSPECIFIED);
static K_SEM_DEFINE(sem_started, 0U, ARRAY_SIZE(streams));
static K_SEM_DEFINE(sem_stopped, 0U, ARRAY_SIZE(streams));
static struct bt_codec_data metadata[CONFIG_BT_CODEC_MAX_METADATA_COUNT];
/* Create a mask for the maximum BIS we can sync to using the number of streams
* we have. We add an additional 1 since the bis indexes start from 1 and not
* 0.
*/
static const uint32_t bis_index_mask = BIT_MASK(ARRAY_SIZE(streams) + 1U);
static uint32_t bis_index_bitfield;
static bool scan_recv_cb(const struct bt_le_scan_recv_info *info,
struct net_buf_simple *ad,
uint32_t broadcast_id)
{
SET_FLAG(broadcaster_found);
return true;
}
static void scan_term_cb(int err)
{
if (err != 0) {
FAIL("Scan terminated with error: %d", err);
}
}
static void pa_synced_cb(struct bt_bap_broadcast_sink *sink,
struct bt_le_per_adv_sync *sync,
uint32_t broadcast_id)
{
if (g_sink != NULL) {
FAIL("Unexpected PA sync");
return;
}
printk("PA synced for broadcast sink %p with broadcast ID 0x%06X\n",
sink, broadcast_id);
g_sink = sink;
SET_FLAG(pa_synced);
}
static void base_recv_cb(struct bt_bap_broadcast_sink *sink, const struct bt_bap_base *base)
{
uint32_t base_bis_index_bitfield = 0U;
if (TEST_FLAG(base_received)) {
if (base->subgroup_count > 0 &&
memcmp(metadata, base->subgroups[0].codec.meta,
sizeof(base->subgroups[0].codec.meta)) != 0) {
(void)memcpy(metadata, base->subgroups[0].codec.meta,
sizeof(base->subgroups[0].codec.meta));
SET_FLAG(flag_base_metadata_updated);
}
return;
}
printk("Received BASE with %u subgroups from broadcast sink %p\n",
base->subgroup_count, sink);
for (size_t i = 0U; i < base->subgroup_count; i++) {
for (size_t j = 0U; j < base->subgroups[i].bis_count; j++) {
const uint8_t index = base->subgroups[i].bis_data[j].index;
base_bis_index_bitfield |= BIT(index);
}
}
bis_index_bitfield = base_bis_index_bitfield & bis_index_mask;
SET_FLAG(base_received);
}
static void syncable_cb(struct bt_bap_broadcast_sink *sink, bool encrypted)
{
printk("Broadcast sink %p syncable with%s encryption\n",
sink, encrypted ? "" : "out");
SET_FLAG(flag_syncable);
}
static void pa_sync_lost_cb(struct bt_bap_broadcast_sink *sink)
{
if (g_sink == NULL) {
FAIL("Unexpected PA sync lost");
return;
}
if (TEST_FLAG(pa_sync_lost)) {
return;
}
printk("Sink %p disconnected\n", sink);
g_sink = NULL;
SET_FLAG(pa_sync_lost);
}
static struct bt_bap_broadcast_sink_cb broadcast_sink_cbs = {
.scan_recv = scan_recv_cb,
.scan_term = scan_term_cb,
.base_recv = base_recv_cb,
.pa_synced = pa_synced_cb,
.syncable = syncable_cb,
.pa_sync_lost = pa_sync_lost_cb
};
static struct bt_pacs_cap cap = {
.codec = &preset_16_2_1.codec,
};
static void started_cb(struct bt_bap_stream *stream)
{
printk("Stream %p started\n", stream);
k_sem_give(&sem_started);
}
static void stopped_cb(struct bt_bap_stream *stream, uint8_t reason)
{
printk("Stream %p stopped with reason 0x%02X\n", stream, reason);
k_sem_give(&sem_stopped);
}
static void recv_cb(struct bt_bap_stream *stream,
const struct bt_iso_recv_info *info,
struct net_buf *buf)
{
SET_FLAG(flag_received);
}
static struct bt_bap_stream_ops stream_ops = {
.started = started_cb,
.stopped = stopped_cb,
.recv = recv_cb
};
static int init(void)
{
int err;
err = bt_enable(NULL);
if (err) {
FAIL("Bluetooth enable failed (err %d)\n", err);
return err;
}
printk("Bluetooth initialized\n");
err = bt_pacs_cap_register(BT_AUDIO_DIR_SINK, &cap);
if (err) {
FAIL("Capability register failed (err %d)\n", err);
return err;
}
bt_bap_broadcast_sink_register_cb(&broadcast_sink_cbs);
UNSET_FLAG(broadcaster_found);
UNSET_FLAG(base_received);
UNSET_FLAG(pa_synced);
for (size_t i = 0U; i < ARRAY_SIZE(streams); i++) {
streams[i] = &broadcast_sink_streams[i];
bt_bap_stream_cb_register(streams[i], &stream_ops);
}
return 0;
}
static void test_common(void)
{
int err;
err = init();
if (err) {
FAIL("Init failed (err %d)\n", err);
return;
}
printk("Scanning for broadcast sources\n");
err = bt_bap_broadcast_sink_scan_start(BT_LE_SCAN_ACTIVE);
if (err != 0) {
FAIL("Unable to start scan for broadcast sources: %d", err);
return;
}
WAIT_FOR_FLAG(broadcaster_found);
printk("Broadcast source found, waiting for PA sync\n");
WAIT_FOR_FLAG(pa_synced);
printk("Broadcast source PA synced, waiting for BASE\n");
WAIT_FOR_FLAG(base_received);
printk("BASE received\n");
printk("Waiting for BIG syncable\n");
WAIT_FOR_FLAG(flag_syncable);
printk("Syncing the sink\n");
err = bt_bap_broadcast_sink_sync(g_sink, bis_index_bitfield, streams, NULL);
if (err != 0) {
FAIL("Unable to sync the sink: %d\n", err);
return;
}
/* Wait for all to be started */
printk("Waiting for streams to be started\n");
for (size_t i = 0U; i < ARRAY_SIZE(streams); i++) {
k_sem_take(&sem_started, K_FOREVER);
}
printk("Waiting for data\n");
WAIT_FOR_FLAG(flag_received);
/* Ensure that we also see the metadata update */
printk("Waiting for metadata update\n");
WAIT_FOR_FLAG(flag_base_metadata_updated)
}
static void test_main(void)
{
test_common();
/* 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(pa_sync_lost);
printk("Waiting for streams to be stopped\n");
for (size_t i = 0U; i < ARRAY_SIZE(streams); i++) {
k_sem_take(&sem_stopped, K_FOREVER);
}
PASS("Broadcast sink passed\n");
}
static void test_sink_disconnect(void)
{
int err;
test_common();
err = bt_bap_broadcast_sink_stop(g_sink);
if (err != 0) {
FAIL("Unable to stop sink: %d", err);
return;
}
printk("Waiting for streams to be stopped\n");
for (size_t i = 0U; i < ARRAY_SIZE(streams); i++) {
k_sem_take(&sem_stopped, K_FOREVER);
}
err = bt_bap_broadcast_sink_delete(g_sink);
if (err != 0) {
FAIL("Unable to delete sink: %d", err);
return;
}
/* No "sync lost" event is generated when we initialized the disconnect */
g_sink = NULL;
PASS("Broadcast sink disconnect passed\n");
}
static const struct bst_test_instance test_broadcast_sink[] = {
{
.test_id = "broadcast_sink",
.test_post_init_f = test_init,
.test_tick_f = test_tick,
.test_main_f = test_main
},
{
.test_id = "broadcast_sink_disconnect",
.test_post_init_f = test_init,
.test_tick_f = test_tick,
.test_main_f = test_sink_disconnect
},
BSTEST_END_MARKER
};
struct bst_test_list *test_broadcast_sink_install(struct bst_test_list *tests)
{
return bst_add_tests(tests, test_broadcast_sink);
}
#else /* !CONFIG_BT_BAP_BROADCAST_SINK */
struct bst_test_list *test_broadcast_sink_install(struct bst_test_list *tests)
{
return tests;
}
#endif /* CONFIG_BT_BAP_BROADCAST_SINK */

View file

@ -0,0 +1,387 @@
/*
* Copyright (c) 2021-2023 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#if defined(CONFIG_BT_BAP_BROADCAST_SOURCE)
#include <zephyr/bluetooth/bluetooth.h>
#include <zephyr/bluetooth/audio/audio.h>
#include <zephyr/bluetooth/audio/bap.h>
#include <zephyr/bluetooth/audio/bap_lc3_preset.h>
#include "common.h"
/* When BROADCAST_ENQUEUE_COUNT > 1 we can enqueue enough buffers to ensure that
* the controller is never idle
*/
#define BROADCAST_ENQUEUE_COUNT 2U
#define TOTAL_BUF_NEEDED (BROADCAST_ENQUEUE_COUNT * CONFIG_BT_BAP_BROADCAST_SRC_STREAM_COUNT)
BUILD_ASSERT(CONFIG_BT_ISO_TX_BUF_COUNT >= TOTAL_BUF_NEEDED,
"CONFIG_BT_ISO_TX_BUF_COUNT should be at least "
"BROADCAST_ENQUEUE_COUNT * CONFIG_BT_BAP_BROADCAST_SRC_STREAM_COUNT");
NET_BUF_POOL_FIXED_DEFINE(tx_pool,
TOTAL_BUF_NEEDED,
BT_ISO_SDU_BUF_SIZE(CONFIG_BT_ISO_TX_MTU), 8, NULL);
extern enum bst_result_t bst_result;
static struct bt_bap_stream broadcast_source_streams[CONFIG_BT_BAP_BROADCAST_SRC_STREAM_COUNT];
static struct bt_bap_stream *streams[ARRAY_SIZE(broadcast_source_streams)];
static struct bt_bap_lc3_preset preset_16_2_1 = BT_BAP_LC3_BROADCAST_PRESET_16_2_1(
BT_AUDIO_LOCATION_FRONT_LEFT, BT_AUDIO_CONTEXT_TYPE_UNSPECIFIED);
static struct bt_bap_lc3_preset preset_16_2_2 = BT_BAP_LC3_BROADCAST_PRESET_16_2_2(
BT_AUDIO_LOCATION_FRONT_LEFT, BT_AUDIO_CONTEXT_TYPE_UNSPECIFIED);
CREATE_FLAG(flag_stopping);
static K_SEM_DEFINE(sem_started, 0U, ARRAY_SIZE(streams));
static K_SEM_DEFINE(sem_stopped, 0U, ARRAY_SIZE(streams));
static void started_cb(struct bt_bap_stream *stream)
{
printk("Stream %p started\n", stream);
k_sem_give(&sem_started);
}
static void stopped_cb(struct bt_bap_stream *stream, uint8_t reason)
{
printk("Stream %p stopped with reason 0x%02X\n", stream, reason);
k_sem_give(&sem_stopped);
}
static void sent_cb(struct bt_bap_stream *stream)
{
static uint8_t mock_data[CONFIG_BT_ISO_TX_MTU];
static bool mock_data_initialized;
static uint16_t seq_num;
struct net_buf *buf;
int ret;
if (TEST_FLAG(flag_stopping)) {
return;
}
if (!mock_data_initialized) {
for (size_t i = 0U; i < ARRAY_SIZE(mock_data); i++) {
/* Initialize mock data */
mock_data[i] = (uint8_t)i;
}
mock_data_initialized = true;
}
buf = net_buf_alloc(&tx_pool, K_FOREVER);
if (buf == NULL) {
printk("Could not allocate buffer when sending on %p\n",
stream);
return;
}
net_buf_reserve(buf, BT_ISO_CHAN_SEND_RESERVE);
/* Use preset_16_2_1 as that is the config we end up using */
net_buf_add_mem(buf, mock_data, preset_16_2_1.qos.sdu);
ret = bt_bap_stream_send(stream, buf, seq_num++,
BT_ISO_TIMESTAMP_NONE);
if (ret < 0) {
/* This will end broadcasting on this stream. */
printk("Unable to broadcast data on %p: %d\n", stream, ret);
net_buf_unref(buf);
return;
}
}
static struct bt_bap_stream_ops stream_ops = {
.started = started_cb,
.stopped = stopped_cb,
.sent = sent_cb
};
static int setup_broadcast_source(struct bt_bap_broadcast_source **source)
{
struct bt_codec_data bis_codec_data = BT_CODEC_DATA(BT_CODEC_CONFIG_LC3_FREQ,
BT_CODEC_CONFIG_LC3_FREQ_16KHZ);
struct bt_bap_broadcast_source_stream_param
stream_params[ARRAY_SIZE(broadcast_source_streams)];
struct bt_bap_broadcast_source_subgroup_param
subgroup_params[CONFIG_BT_BAP_BROADCAST_SRC_SUBGROUP_COUNT];
struct bt_bap_broadcast_source_create_param create_param;
int err;
(void)memset(broadcast_source_streams, 0,
sizeof(broadcast_source_streams));
for (size_t i = 0; i < ARRAY_SIZE(stream_params); i++) {
stream_params[i].stream = &broadcast_source_streams[i];
bt_bap_stream_cb_register(stream_params[i].stream,
&stream_ops);
stream_params[i].data_count = 1U;
stream_params[i].data = &bis_codec_data;
}
for (size_t i = 0U; i < ARRAY_SIZE(subgroup_params); i++) {
subgroup_params[i].params_count = 1U;
subgroup_params[i].params = &stream_params[i];
subgroup_params[i].codec = &preset_16_2_1.codec;
}
create_param.params_count = ARRAY_SIZE(subgroup_params);
create_param.params = subgroup_params;
create_param.qos = &preset_16_2_2.qos;
create_param.packing = BT_ISO_PACKING_SEQUENTIAL;
create_param.encryption = false;
printk("Creating broadcast source with %zu subgroups and %zu streams\n",
ARRAY_SIZE(subgroup_params), ARRAY_SIZE(stream_params));
err = bt_bap_broadcast_source_create(&create_param, source);
if (err != 0) {
printk("Unable to create broadcast source: %d\n", err);
return err;
}
return 0;
}
static int setup_extended_adv(struct bt_bap_broadcast_source *source, struct bt_le_ext_adv **adv)
{
/* Broadcast Audio Streaming Endpoint advertising data */
NET_BUF_SIMPLE_DEFINE(ad_buf,
BT_UUID_SIZE_16 + BT_AUDIO_BROADCAST_ID_SIZE);
NET_BUF_SIMPLE_DEFINE(base_buf, 128);
struct bt_data ext_ad;
struct bt_data per_ad;
uint32_t broadcast_id;
int err;
/* Create a non-connectable non-scannable advertising set */
err = bt_le_ext_adv_create(BT_LE_EXT_ADV_NCONN_NAME, NULL, adv);
if (err != 0) {
printk("Unable to create extended advertising set: %d\n", err);
return err;
}
/* Set periodic advertising parameters */
err = bt_le_per_adv_set_param(*adv, BT_LE_PER_ADV_DEFAULT);
if (err) {
printk("Failed to set periodic advertising parameters: %d\n",
err);
return err;
}
err = bt_bap_broadcast_source_get_id(source, &broadcast_id);
if (err != 0) {
printk("Unable to get broadcast ID: %d\n", err);
return err;
}
/* Setup extended advertising data */
net_buf_simple_add_le16(&ad_buf, BT_UUID_BROADCAST_AUDIO_VAL);
net_buf_simple_add_le24(&ad_buf, broadcast_id);
ext_ad.type = BT_DATA_SVC_DATA16;
ext_ad.data_len = ad_buf.len;
ext_ad.data = ad_buf.data;
err = bt_le_ext_adv_set_data(*adv, &ext_ad, 1, NULL, 0);
if (err != 0) {
printk("Failed to set extended advertising data: %d\n", err);
return err;
}
/* Setup periodic advertising data */
err = bt_bap_broadcast_source_get_base(source, &base_buf);
if (err != 0) {
printk("Failed to get encoded BASE: %d\n", err);
return err;
}
per_ad.type = BT_DATA_SVC_DATA16;
per_ad.data_len = base_buf.len;
per_ad.data = base_buf.data;
err = bt_le_per_adv_set_data(*adv, &per_ad, 1);
if (err != 0) {
printk("Failed to set periodic advertising data: %d\n", err);
return err;
}
/* Start extended advertising */
err = bt_le_ext_adv_start(*adv, BT_LE_EXT_ADV_START_DEFAULT);
if (err) {
printk("Failed to start extended advertising: %d\n", err);
return err;
}
/* Enable Periodic Advertising */
err = bt_le_per_adv_start(*adv);
if (err) {
printk("Failed to enable periodic advertising: %d\n", err);
return err;
}
return 0;
}
static int stop_extended_adv(struct bt_le_ext_adv *adv)
{
int err;
err = bt_le_per_adv_stop(adv);
if (err) {
printk("Failed to stop periodic advertising: %d\n", err);
return err;
}
err = bt_le_ext_adv_stop(adv);
if (err) {
printk("Failed to stop extended advertising: %d\n", err);
return err;
}
err = bt_le_ext_adv_delete(adv);
if (err) {
printk("Failed to delete extended advertising: %d\n", err);
return err;
}
return 0;
}
static void test_main(void)
{
struct bt_codec_data new_metadata[1] =
BT_CODEC_LC3_CONFIG_META(BT_AUDIO_CONTEXT_TYPE_ALERTS);
struct bt_bap_broadcast_source *source;
struct bt_le_ext_adv *adv;
int err;
err = bt_enable(NULL);
if (err) {
FAIL("Bluetooth init failed (err %d)\n", err);
return;
}
printk("Bluetooth initialized\n");
err = setup_broadcast_source(&source);
if (err != 0) {
FAIL("Unable to setup broadcast source: %d\n", err);
return;
}
err = setup_extended_adv(source, &adv);
if (err != 0) {
FAIL("Failed to setup extended advertising: %d\n", err);
return;
}
printk("Reconfiguring broadcast source\n");
err = bt_bap_broadcast_source_reconfig(source, &preset_16_2_1.codec, &preset_16_2_1.qos);
if (err != 0) {
FAIL("Unable to reconfigure broadcast source: %d\n", err);
return;
}
printk("Starting broadcast source\n");
err = bt_bap_broadcast_source_start(source, adv);
if (err != 0) {
FAIL("Unable to start broadcast source: %d\n", err);
return;
}
/* Wait for all to be started */
printk("Waiting for streams to be started\n");
for (size_t i = 0U; i < ARRAY_SIZE(streams); i++) {
k_sem_take(&sem_started, K_FOREVER);
}
/* Initialize sending */
for (size_t i = 0U; i < ARRAY_SIZE(streams); i++) {
for (unsigned int j = 0U; j < BROADCAST_ENQUEUE_COUNT; j++) {
sent_cb(streams[i]);
}
}
/* Keeping running for a little while */
k_sleep(K_SECONDS(15));
/* Update metadata while streaming */
printk("Updating metadata\n");
err = bt_bap_broadcast_source_update_metadata(source, new_metadata,
ARRAY_SIZE(new_metadata));
if (err != 0) {
FAIL("Failed to update metadata broadcast source: %d", err);
return;
}
/* Keeping running for a little while */
k_sleep(K_SECONDS(5));
printk("Stopping broadcast source\n");
SET_FLAG(flag_stopping);
err = bt_bap_broadcast_source_stop(source);
if (err != 0) {
FAIL("Unable to stop broadcast source: %d\n", err);
return;
}
/* Wait for all to be stopped */
printk("Waiting for streams to be stopped\n");
for (size_t i = 0U; i < ARRAY_SIZE(streams); i++) {
k_sem_take(&sem_stopped, K_FOREVER);
}
printk("Deleting broadcast source\n");
err = bt_bap_broadcast_source_delete(source);
if (err != 0) {
FAIL("Unable to delete broadcast source: %d\n", err);
return;
}
source = NULL;
err = stop_extended_adv(adv);
if (err != 0) {
FAIL("Unable to stop extended advertising: %d\n", err);
return;
}
adv = NULL;
/* Recreate broadcast source to verify that it's possible */
printk("Recreating broadcast source\n");
err = setup_broadcast_source(&source);
if (err != 0) {
FAIL("Unable to setup broadcast source: %d\n", err);
return;
}
printk("Deleting broadcast source\n");
err = bt_bap_broadcast_source_delete(source);
if (err != 0) {
FAIL("Unable to delete broadcast source: %d\n", err);
return;
}
source = NULL;
PASS("Broadcast source passed\n");
}
static const struct bst_test_instance test_broadcast_source[] = {
{
.test_id = "broadcast_source",
.test_post_init_f = test_init,
.test_tick_f = test_tick,
.test_main_f = test_main
},
BSTEST_END_MARKER
};
struct bst_test_list *test_broadcast_source_install(struct bst_test_list *tests)
{
return bst_add_tests(tests, test_broadcast_source);
}
#else /* CONFIG_BT_BAP_BROADCAST_SOURCE */
struct bst_test_list *test_broadcast_source_install(struct bst_test_list *tests)
{
return tests;
}
#endif /* CONFIG_BT_BAP_BROADCAST_SOURCE */

View file

@ -0,0 +1,359 @@
/*
* Copyright (c) 2022-2023 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#if defined(CONFIG_BT_CAP_ACCEPTOR)
#include <zephyr/bluetooth/bluetooth.h>
#include <zephyr/bluetooth/audio/bap_lc3_preset.h>
#include <zephyr/bluetooth/audio/cap.h>
#include <zephyr/bluetooth/audio/pacs.h>
#include "common.h"
#include "bap_unicast_common.h"
extern enum bst_result_t bst_result;
CREATE_FLAG(flag_broadcaster_found);
CREATE_FLAG(flag_base_received);
CREATE_FLAG(flag_pa_synced);
CREATE_FLAG(flag_syncable);
CREATE_FLAG(flag_received);
CREATE_FLAG(flag_pa_sync_lost);
static struct bt_bap_broadcast_sink *g_broadcast_sink;
static struct bt_cap_stream broadcast_sink_streams[CONFIG_BT_BAP_BROADCAST_SNK_STREAM_COUNT];
static struct bt_bap_lc3_preset broadcast_preset_16_2_1 = BT_BAP_LC3_BROADCAST_PRESET_16_2_1(
BT_AUDIO_LOCATION_FRONT_LEFT, BT_AUDIO_CONTEXT_TYPE_UNSPECIFIED);
static K_SEM_DEFINE(sem_broadcast_started, 0U, ARRAY_SIZE(broadcast_sink_streams));
static K_SEM_DEFINE(sem_broadcast_stopped, 0U, ARRAY_SIZE(broadcast_sink_streams));
/* Create a mask for the maximum BIS we can sync to using the number of
* broadcast_sink_streams we have. We add an additional 1 since the bis indexes
* start from 1 and not 0.
*/
static const uint32_t bis_index_mask = BIT_MASK(ARRAY_SIZE(broadcast_sink_streams) + 1U);
static uint32_t bis_index_bitfield;
static bool scan_recv_cb(const struct bt_le_scan_recv_info *info,
struct net_buf_simple *ad,
uint32_t broadcast_id)
{
SET_FLAG(flag_broadcaster_found);
return true;
}
static void scan_term_cb(int err)
{
if (err != 0) {
FAIL("Scan terminated with error: %d", err);
}
}
static void pa_synced_cb(struct bt_bap_broadcast_sink *sink,
struct bt_le_per_adv_sync *sync,
uint32_t broadcast_id)
{
if (g_broadcast_sink != NULL) {
FAIL("Unexpected PA sync");
return;
}
printk("PA synced for broadcast sink %p with broadcast ID 0x%06X\n",
sink, broadcast_id);
g_broadcast_sink = sink;
SET_FLAG(flag_pa_synced);
}
static bool valid_subgroup_metadata(const struct bt_bap_base_subgroup *subgroup)
{
bool stream_context_found = false;
for (size_t j = 0U; j < subgroup->codec.meta_count; j++) {
const struct bt_data *metadata = &subgroup->codec.meta[j].data;
if (metadata->type == BT_AUDIO_METADATA_TYPE_STREAM_CONTEXT) {
if (metadata->data_len != 2) { /* Stream context size */
printk("Subgroup has invalid streaming context length: %u\n",
metadata->data_len);
return false;
}
stream_context_found = true;
break;
}
}
if (!stream_context_found) {
printk("Subgroup did not have streaming context\n");
return false;
}
return true;
}
static void base_recv_cb(struct bt_bap_broadcast_sink *sink, const struct bt_bap_base *base)
{
uint32_t base_bis_index_bitfield = 0U;
if (TEST_FLAG(flag_base_received)) {
return;
}
printk("Received BASE with %u subgroups from broadcast sink %p\n",
base->subgroup_count, sink);
if (base->subgroup_count == 0) {
FAIL("base->subgroup_count was 0");
return;
}
for (size_t i = 0U; i < base->subgroup_count; i++) {
const struct bt_bap_base_subgroup *subgroup = &base->subgroups[i];
for (size_t j = 0U; j < subgroup->bis_count; j++) {
const uint8_t index = subgroup->bis_data[j].index;
base_bis_index_bitfield |= BIT(index);
}
if (!valid_subgroup_metadata(subgroup)) {
FAIL("Subgroup[%zu] has invalid metadata\n", i);
return;
}
}
bis_index_bitfield = base_bis_index_bitfield & bis_index_mask;
SET_FLAG(flag_base_received);
}
static void syncable_cb(struct bt_bap_broadcast_sink *sink, bool encrypted)
{
printk("Broadcast sink %p syncable with%s encryption\n",
sink, encrypted ? "" : "out");
SET_FLAG(flag_syncable);
}
static void pa_sync_lost_cb(struct bt_bap_broadcast_sink *sink)
{
if (g_broadcast_sink == NULL) {
FAIL("Unexpected PA sync lost");
return;
}
printk("Sink %p disconnected\n", sink);
SET_FLAG(flag_pa_sync_lost);
g_broadcast_sink = NULL;
}
static struct bt_bap_broadcast_sink_cb broadcast_sink_cbs = {
.scan_recv = scan_recv_cb,
.scan_term = scan_term_cb,
.base_recv = base_recv_cb,
.pa_synced = pa_synced_cb,
.syncable = syncable_cb,
.pa_sync_lost = pa_sync_lost_cb
};
static void started_cb(struct bt_bap_stream *stream)
{
printk("Stream %p started\n", stream);
k_sem_give(&sem_broadcast_started);
}
static void stopped_cb(struct bt_bap_stream *stream, uint8_t reason)
{
printk("Stream %p stopped with reason 0x%02X\n", stream, reason);
k_sem_give(&sem_broadcast_stopped);
}
static void recv_cb(struct bt_bap_stream *stream, const struct bt_iso_recv_info *info,
struct net_buf *buf)
{
SET_FLAG(flag_received);
}
static struct bt_bap_stream_ops broadcast_stream_ops = {
.started = started_cb, .stopped = stopped_cb, .recv = recv_cb};
/* TODO: Expand with CAP service data */
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_UUID16_ALL, BT_UUID_16_ENCODE(BT_UUID_CAS_VAL)),
};
static struct bt_csip_set_member_svc_inst *csip_set_member;
static void init(void)
{
struct bt_csip_set_member_register_param csip_set_member_param = {
.set_size = 3,
.rank = 1,
.lockable = true,
/* Using the CSIP_SET_MEMBER test sample SIRK */
.set_sirk = { 0xcd, 0xcc, 0x72, 0xdd, 0x86, 0x8c, 0xcd, 0xce,
0x22, 0xfd, 0xa1, 0x21, 0x09, 0x7d, 0x7d, 0x45 },
};
int err;
err = bt_enable(NULL);
if (err != 0) {
FAIL("Bluetooth enable failed (err %d)\n", err);
return;
}
printk("Bluetooth initialized\n");
if (IS_ENABLED(CONFIG_BT_CAP_ACCEPTOR_SET_MEMBER)) {
err = bt_cap_acceptor_register(&csip_set_member_param,
&csip_set_member);
if (err != 0) {
FAIL("CAP acceptor failed to register (err %d)\n", err);
return;
}
}
if (IS_ENABLED(CONFIG_BT_BAP_UNICAST_SERVER)) {
err = bt_le_adv_start(BT_LE_ADV_CONN_NAME, cap_acceptor_ad,
ARRAY_SIZE(cap_acceptor_ad), NULL, 0);
if (err != 0) {
FAIL("Advertising failed to start (err %d)\n", err);
return;
}
}
if (IS_ENABLED(CONFIG_BT_BAP_BROADCAST_SINK)) {
static struct bt_pacs_cap cap = {
.codec = &broadcast_preset_16_2_1.codec,
};
err = bt_pacs_cap_register(BT_AUDIO_DIR_SINK, &cap);
if (err != 0) {
FAIL("Broadcast capability register failed (err %d)\n",
err);
return;
}
bt_bap_broadcast_sink_register_cb(&broadcast_sink_cbs);
UNSET_FLAG(flag_broadcaster_found);
UNSET_FLAG(flag_base_received);
UNSET_FLAG(flag_pa_synced);
for (size_t i = 0U; i < ARRAY_SIZE(broadcast_sink_streams); i++) {
bt_cap_stream_ops_register(&broadcast_sink_streams[i],
&broadcast_stream_ops);
}
}
}
static void test_cap_acceptor_unicast(void)
{
init();
/* TODO: When babblesim supports ISO, wait for audio stream to pass */
WAIT_FOR_FLAG(flag_connected);
PASS("CAP acceptor unicast passed\n");
}
static void test_cap_acceptor_broadcast(void)
{
static struct bt_bap_stream *bap_streams[ARRAY_SIZE(broadcast_sink_streams)];
int err;
init();
printk("Scanning for broadcast sources\n");
err = bt_bap_broadcast_sink_scan_start(BT_LE_SCAN_ACTIVE);
if (err != 0) {
FAIL("Unable to start scan for broadcast sources: %d", err);
return;
}
WAIT_FOR_FLAG(flag_broadcaster_found);
printk("Broadcast source found, waiting for PA sync\n");
WAIT_FOR_FLAG(flag_pa_synced);
printk("Broadcast source PA synced, waiting for BASE\n");
WAIT_FOR_FLAG(flag_base_received);
printk("BASE received\n");
printk("Waiting for BIG syncable\n");
WAIT_FOR_FLAG(flag_syncable);
for (size_t i = 0U; i < ARRAY_SIZE(broadcast_sink_streams); i++) {
bap_streams[i] = &broadcast_sink_streams[i].bap_stream;
}
printk("Syncing the sink\n");
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 broadcast_sink_streams to be started\n");
for (size_t i = 0U; i < ARRAY_SIZE(broadcast_sink_streams); i++) {
k_sem_take(&sem_broadcast_started, K_FOREVER);
}
printk("Waiting for data\n");
WAIT_FOR_FLAG(flag_received);
/* 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 streams to be stopped\n");
for (size_t i = 0U; i < ARRAY_SIZE(broadcast_sink_streams); i++) {
k_sem_take(&sem_broadcast_stopped, K_FOREVER);
}
PASS("CAP acceptor broadcast passed\n");
}
static const struct bst_test_instance test_cap_acceptor[] = {
{
.test_id = "cap_acceptor_unicast",
.test_post_init_f = test_init,
.test_tick_f = test_tick,
.test_main_f = test_cap_acceptor_unicast
},
{
.test_id = "cap_acceptor_broadcast",
.test_post_init_f = test_init,
.test_tick_f = test_tick,
.test_main_f = test_cap_acceptor_broadcast
},
BSTEST_END_MARKER
};
struct bst_test_list *test_cap_acceptor_install(struct bst_test_list *tests)
{
return bst_add_tests(tests, test_cap_acceptor);
}
#else /* !(CONFIG_BT_CAP_ACCEPTOR) */
struct bst_test_list *test_cap_acceptor_install(struct bst_test_list *tests)
{
return tests;
}
#endif /* CONFIG_BT_CAP_ACCEPTOR */

View file

@ -0,0 +1,486 @@
/*
* Copyright (c) 2022-2023 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#if defined(CONFIG_BT_CAP_INITIATOR)
#include <zephyr/bluetooth/bluetooth.h>
#include <zephyr/bluetooth/audio/bap_lc3_preset.h>
#include <zephyr/bluetooth/audio/cap.h>
#include "common.h"
#include "bap_unicast_common.h"
/* When BROADCAST_ENQUEUE_COUNT > 1 we can enqueue enough buffers to ensure that
* the controller is never idle
*/
#define BROADCAST_ENQUEUE_COUNT 2U
#define TOTAL_BUF_NEEDED (BROADCAST_ENQUEUE_COUNT * CONFIG_BT_BAP_BROADCAST_SRC_STREAM_COUNT)
BUILD_ASSERT(CONFIG_BT_ISO_TX_BUF_COUNT >= TOTAL_BUF_NEEDED,
"CONFIG_BT_ISO_TX_BUF_COUNT should be at least "
"BROADCAST_ENQUEUE_COUNT * CONFIG_BT_BAP_BROADCAST_SRC_STREAM_COUNT");
NET_BUF_POOL_FIXED_DEFINE(tx_pool,
TOTAL_BUF_NEEDED,
BT_ISO_SDU_BUF_SIZE(CONFIG_BT_ISO_TX_MTU), 8, NULL);
extern enum bst_result_t bst_result;
static struct bt_cap_stream broadcast_source_streams[CONFIG_BT_BAP_BROADCAST_SRC_STREAM_COUNT];
static struct bt_cap_stream *broadcast_streams[ARRAY_SIZE(broadcast_source_streams)];
static struct bt_bap_lc3_preset broadcast_preset_16_2_1 = BT_BAP_LC3_BROADCAST_PRESET_16_2_1(
BT_AUDIO_LOCATION_FRONT_LEFT, BT_AUDIO_CONTEXT_TYPE_MEDIA);
static K_SEM_DEFINE(sem_broadcast_started, 0U, ARRAY_SIZE(broadcast_streams));
static K_SEM_DEFINE(sem_broadcast_stopped, 0U, ARRAY_SIZE(broadcast_streams));
CREATE_FLAG(flag_discovered);
CREATE_FLAG(flag_mtu_exchanged);
CREATE_FLAG(flag_broadcast_stopping);
static void broadcast_started_cb(struct bt_bap_stream *stream)
{
printk("Stream %p started\n", stream);
k_sem_give(&sem_broadcast_started);
}
static void broadcast_stopped_cb(struct bt_bap_stream *stream, uint8_t reason)
{
printk("Stream %p stopped with reason 0x%02X\n", stream, reason);
k_sem_give(&sem_broadcast_stopped);
}
static void broadcast_sent_cb(struct bt_bap_stream *stream)
{
static uint8_t mock_data[CONFIG_BT_ISO_TX_MTU];
static bool mock_data_initialized;
static uint32_t seq_num;
struct net_buf *buf;
int ret;
if (broadcast_preset_16_2_1.qos.sdu > CONFIG_BT_ISO_TX_MTU) {
FAIL("Invalid SDU %u for the MTU: %d",
broadcast_preset_16_2_1.qos.sdu, CONFIG_BT_ISO_TX_MTU);
return;
}
if (TEST_FLAG(flag_broadcast_stopping)) {
return;
}
if (!mock_data_initialized) {
for (size_t i = 0U; i < ARRAY_SIZE(mock_data); i++) {
/* Initialize mock data */
mock_data[i] = (uint8_t)i;
}
mock_data_initialized = true;
}
buf = net_buf_alloc(&tx_pool, K_FOREVER);
if (buf == NULL) {
printk("Could not allocate buffer when sending on %p\n",
stream);
return;
}
net_buf_reserve(buf, BT_ISO_CHAN_SEND_RESERVE);
net_buf_add_mem(buf, mock_data, broadcast_preset_16_2_1.qos.sdu);
ret = bt_bap_stream_send(stream, buf, seq_num++, BT_ISO_TIMESTAMP_NONE);
if (ret < 0) {
/* This will end broadcasting on this stream. */
printk("Unable to broadcast data on %p: %d\n", stream, ret);
net_buf_unref(buf);
return;
}
}
static struct bt_bap_stream_ops broadcast_stream_ops = {.started = broadcast_started_cb,
.stopped = broadcast_stopped_cb,
.sent = broadcast_sent_cb};
static void cap_discovery_complete_cb(struct bt_conn *conn, int err,
const struct bt_csip_set_coordinator_csis_inst *csis_inst)
{
if (err != 0) {
FAIL("Failed to discover CAS: %d", err);
return;
}
if (IS_ENABLED(CONFIG_BT_CAP_ACCEPTOR_SET_MEMBER)) {
if (csis_inst == NULL) {
FAIL("Failed to discover CAS CSIS");
return;
}
printk("Found CAS with CSIS %p\n", csis_inst);
} else {
printk("Found CAS\n");
}
SET_FLAG(flag_discovered);
}
static struct bt_cap_initiator_cb cap_cb = {
.unicast_discovery_complete = cap_discovery_complete_cb
};
static void att_mtu_updated(struct bt_conn *conn, uint16_t tx, uint16_t rx)
{
printk("MTU exchanged\n");
SET_FLAG(flag_mtu_exchanged);
}
static struct bt_gatt_cb gatt_callbacks = {
.att_mtu_updated = att_mtu_updated,
};
static void init(void)
{
int err;
err = bt_enable(NULL);
if (err != 0) {
FAIL("Bluetooth enable failed (err %d)\n", err);
return;
}
if (IS_ENABLED(CONFIG_BT_BAP_UNICAST_CLIENT)) {
bt_gatt_cb_register(&gatt_callbacks);
err = bt_cap_initiator_register_cb(&cap_cb);
if (err != 0) {
FAIL("Failed to register CAP callbacks (err %d)\n", err);
return;
}
(void)memset(broadcast_source_streams, 0,
sizeof(broadcast_source_streams));
for (size_t i = 0; i < ARRAY_SIZE(broadcast_streams); i++) {
broadcast_streams[i] = &broadcast_source_streams[i];
bt_cap_stream_ops_register(broadcast_streams[i],
&broadcast_stream_ops);
}
}
}
static void scan_and_connect(void)
{
int err;
err = bt_le_scan_start(BT_LE_SCAN_PASSIVE, device_found);
if (err != 0) {
FAIL("Scanning failed to start (err %d)\n", err);
return;
}
printk("Scanning successfully started\n");
WAIT_FOR_FLAG(flag_connected);
}
static void discover_cas(void)
{
int err;
UNSET_FLAG(flag_discovered);
err = bt_cap_initiator_unicast_discover(default_conn);
if (err != 0) {
printk("Failed to discover CAS: %d\n", err);
return;
}
WAIT_FOR_FLAG(flag_discovered);
}
static void test_cap_initiator_unicast(void)
{
init();
scan_and_connect();
WAIT_FOR_FLAG(flag_mtu_exchanged);
discover_cas();
PASS("CAP initiator unicast passed\n");
}
static int setup_extended_adv(struct bt_le_ext_adv **adv)
{
int err;
/* Create a non-connectable non-scannable advertising set */
err = bt_le_ext_adv_create(BT_LE_EXT_ADV_NCONN_NAME, NULL, adv);
if (err != 0) {
printk("Unable to create extended advertising set: %d\n", err);
return err;
}
/* Set periodic advertising parameters */
err = bt_le_per_adv_set_param(*adv, BT_LE_PER_ADV_DEFAULT);
if (err) {
printk("Failed to set periodic advertising parameters: %d\n",
err);
return err;
}
return 0;
}
static int setup_extended_adv_data(struct bt_cap_broadcast_source *source,
struct bt_le_ext_adv *adv)
{
/* Broadcast Audio Streaming Endpoint advertising data */
NET_BUF_SIMPLE_DEFINE(ad_buf,
BT_UUID_SIZE_16 + BT_AUDIO_BROADCAST_ID_SIZE);
NET_BUF_SIMPLE_DEFINE(base_buf, 128);
struct bt_data ext_ad;
struct bt_data per_ad;
uint32_t broadcast_id;
int err;
err = bt_cap_initiator_broadcast_get_id(source, &broadcast_id);
if (err != 0) {
printk("Unable to get broadcast ID: %d\n", err);
return err;
}
/* Setup extended advertising data */
net_buf_simple_add_le16(&ad_buf, BT_UUID_BROADCAST_AUDIO_VAL);
net_buf_simple_add_le24(&ad_buf, broadcast_id);
ext_ad.type = BT_DATA_SVC_DATA16;
ext_ad.data_len = ad_buf.len + sizeof(ext_ad.type);
ext_ad.data = ad_buf.data;
err = bt_le_ext_adv_set_data(adv, &ext_ad, 1, NULL, 0);
if (err != 0) {
printk("Failed to set extended advertising data: %d\n", err);
return err;
}
/* Setup periodic advertising data */
err = bt_cap_initiator_broadcast_get_base(source, &base_buf);
if (err != 0) {
printk("Failed to get encoded BASE: %d\n", err);
return err;
}
per_ad.type = BT_DATA_SVC_DATA16;
per_ad.data_len = base_buf.len;
per_ad.data = base_buf.data;
err = bt_le_per_adv_set_data(adv, &per_ad, 1);
if (err != 0) {
printk("Failed to set periodic advertising data: %d\n", err);
return err;
}
return 0;
}
static int start_extended_adv(struct bt_le_ext_adv *adv)
{
int err;
/* Start extended advertising */
err = bt_le_ext_adv_start(adv, BT_LE_EXT_ADV_START_DEFAULT);
if (err) {
printk("Failed to start extended advertising: %d\n", err);
return err;
}
/* Enable Periodic Advertising */
err = bt_le_per_adv_start(adv);
if (err) {
printk("Failed to enable periodic advertising: %d\n", err);
return err;
}
return 0;
}
static int stop_and_delete_extended_adv(struct bt_le_ext_adv *adv)
{
int err;
/* Stop extended advertising */
err = bt_le_per_adv_stop(adv);
if (err) {
printk("Failed to stop periodic advertising: %d\n", err);
return err;
}
err = bt_le_ext_adv_stop(adv);
if (err) {
printk("Failed to stop extended advertising: %d\n", err);
return err;
}
err = bt_le_ext_adv_delete(adv);
if (err) {
printk("Failed to delete extended advertising: %d\n", err);
return err;
}
return 0;
}
static void test_cap_initiator_broadcast(void)
{
struct bt_codec_data bis_codec_data = BT_CODEC_DATA(BT_CODEC_CONFIG_LC3_FREQ,
BT_CODEC_CONFIG_LC3_FREQ_16KHZ);
const uint16_t mock_ccid = 0x1234;
const struct bt_codec_data new_metadata[] = {
BT_CODEC_DATA(BT_AUDIO_METADATA_TYPE_STREAM_CONTEXT,
(BT_AUDIO_CONTEXT_TYPE_MEDIA & 0xFFU),
((BT_AUDIO_CONTEXT_TYPE_MEDIA >> 8) & 0xFFU)),
BT_CODEC_DATA(BT_AUDIO_METADATA_TYPE_CCID_LIST,
(mock_ccid & 0xFFU),
((mock_ccid >> 8) & 0xFFU)),
};
struct bt_cap_initiator_broadcast_stream_param
stream_params[ARRAY_SIZE(broadcast_source_streams)];
struct bt_cap_initiator_broadcast_subgroup_param subgroup_param;
struct bt_cap_initiator_broadcast_create_param create_param;
struct bt_cap_broadcast_source *broadcast_source;
struct bt_le_ext_adv *adv;
int err;
(void)memset(broadcast_source_streams, 0,
sizeof(broadcast_source_streams));
for (size_t i = 0; i < ARRAY_SIZE(broadcast_streams); i++) {
stream_params[i].stream = &broadcast_source_streams[i];
bt_cap_stream_ops_register(stream_params[i].stream,
&broadcast_stream_ops);
stream_params[i].data_count = 1U;
stream_params[i].data = &bis_codec_data;
}
subgroup_param.stream_count = ARRAY_SIZE(broadcast_streams);
subgroup_param.stream_params = stream_params;
subgroup_param.codec = &broadcast_preset_16_2_1.codec;
create_param.subgroup_count = 1U;
create_param.subgroup_params = &subgroup_param;
create_param.qos = &broadcast_preset_16_2_1.qos;
create_param.packing = BT_ISO_PACKING_SEQUENTIAL;
create_param.encryption = false;
init();
printk("Creating broadcast source with %zu broadcast_streams\n",
ARRAY_SIZE(broadcast_streams));
err = setup_extended_adv(&adv);
if (err != 0) {
FAIL("Unable to setup extended advertiser: %d\n", err);
return;
}
err = bt_cap_initiator_broadcast_audio_start(&create_param, adv,
&broadcast_source);
if (err != 0) {
FAIL("Unable to start broadcast source: %d\n", err);
return;
}
err = setup_extended_adv_data(broadcast_source, adv);
if (err != 0) {
FAIL("Unable to setup extended advertising data: %d\n", err);
return;
}
err = start_extended_adv(adv);
if (err != 0) {
FAIL("Unable to start extended advertiser: %d\n", err);
return;
}
/* Wait for all to be started */
printk("Waiting for broadcast_streams to be started\n");
for (size_t i = 0U; i < ARRAY_SIZE(broadcast_streams); i++) {
k_sem_take(&sem_broadcast_started, K_FOREVER);
}
/* Initialize sending */
for (size_t i = 0U; i < ARRAY_SIZE(broadcast_streams); i++) {
for (unsigned int j = 0U; j < BROADCAST_ENQUEUE_COUNT; j++) {
broadcast_sent_cb(&broadcast_streams[i]->bap_stream);
}
}
/* Keeping running for a little while */
k_sleep(K_SECONDS(5));
err = bt_cap_initiator_broadcast_audio_update(broadcast_source,
new_metadata,
ARRAY_SIZE(new_metadata));
if (err != 0) {
FAIL("Failed to update broadcast source metadata: %d\n", err);
return;
}
/* Keeping running for a little while */
k_sleep(K_SECONDS(5));
err = bt_cap_initiator_broadcast_audio_stop(broadcast_source);
if (err != 0) {
FAIL("Failed to stop broadcast source: %d\n", err);
return;
}
/* Wait for all to be stopped */
printk("Waiting for broadcast_streams to be stopped\n");
for (size_t i = 0U; i < ARRAY_SIZE(broadcast_streams); i++) {
k_sem_take(&sem_broadcast_stopped, K_FOREVER);
}
err = bt_cap_initiator_broadcast_audio_delete(broadcast_source);
if (err != 0) {
FAIL("Failed to stop broadcast source: %d\n", err);
return;
}
broadcast_source = NULL;
err = stop_and_delete_extended_adv(adv);
if (err != 0) {
FAIL("Failed to stop and delete extended advertising: %d\n", err);
return;
}
adv = NULL;
PASS("CAP initiator broadcast passed\n");
}
static const struct bst_test_instance test_cap_initiator[] = {
#if defined(CONFIG_BT_BAP_UNICAST_CLIENT)
{.test_id = "cap_initiator_unicast",
.test_post_init_f = test_init,
.test_tick_f = test_tick,
.test_main_f = test_cap_initiator_unicast},
#endif /* CONFIG_BT_BAP_UNICAST_CLIENT */
#if defined(CONFIG_BT_BAP_BROADCAST_SOURCE)
{.test_id = "cap_initiator_broadcast",
.test_post_init_f = test_init,
.test_tick_f = test_tick,
.test_main_f = test_cap_initiator_broadcast},
#endif /* CONFIG_BT_BAP_BROADCAST_SOURCE */
BSTEST_END_MARKER};
struct bst_test_list *test_cap_initiator_install(struct bst_test_list *tests)
{
return bst_add_tests(tests, test_cap_initiator);
}
#else /* !(CONFIG_BT_CAP_INITIATOR) */
struct bst_test_list *test_cap_initiator_install(struct bst_test_list *tests)
{
return tests;
}
#endif /* CONFIG_BT_CAP_INITIATOR */

View file

@ -0,0 +1,110 @@
/*
* Copyright (c) 2019 Bose Corporation
* Copyright (c) 2020-2021 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "common.h"
extern enum bst_result_t bst_result;
struct bt_conn *default_conn;
atomic_t flag_connected;
const struct bt_data ad[AD_SIZE] = {
BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR))
};
void device_found(const bt_addr_le_t *addr, int8_t rssi, uint8_t type,
struct net_buf_simple *ad)
{
char addr_str[BT_ADDR_LE_STR_LEN];
int err;
if (default_conn) {
return;
}
/* We're only interested in connectable events */
if (type != BT_HCI_ADV_IND && type != BT_HCI_ADV_DIRECT_IND) {
return;
}
bt_addr_le_to_str(addr, addr_str, sizeof(addr_str));
printk("Device found: %s (RSSI %d)\n", addr_str, rssi);
/* connect only to devices in close proximity */
if (rssi < -70) {
FAIL("RSSI too low");
return;
}
printk("Stopping scan\n");
if (bt_le_scan_stop()) {
FAIL("Could not stop scan");
return;
}
err = bt_conn_le_create(addr, BT_CONN_LE_CREATE_CONN,
BT_LE_CONN_PARAM_DEFAULT, &default_conn);
if (err) {
FAIL("Could not connect to peer: %d", err);
}
}
static void connected(struct bt_conn *conn, uint8_t err)
{
char addr[BT_ADDR_LE_STR_LEN];
(void)bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
if (default_conn == NULL) {
default_conn = bt_conn_ref(conn);
}
if (err != 0) {
bt_conn_unref(default_conn);
default_conn = NULL;
FAIL("Failed to connect to %s (%u)\n", addr, err);
return;
}
printk("Connected to %s\n", addr);
SET_FLAG(flag_connected);
}
void disconnected(struct bt_conn *conn, uint8_t reason)
{
char addr[BT_ADDR_LE_STR_LEN];
if (conn != default_conn) {
return;
}
bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
printk("Disconnected: %s (reason %u)\n", addr, reason);
bt_conn_unref(default_conn);
default_conn = NULL;
UNSET_FLAG(flag_connected);
}
BT_CONN_CB_DEFINE(conn_callbacks) = {
.connected = connected,
.disconnected = disconnected,
};
void test_tick(bs_time_t HW_device_time)
{
if (bst_result != Passed) {
FAIL("test failed (not passed after %i seconds)\n", WAIT_SECONDS);
}
}
void test_init(void)
{
bst_ticker_set_next_tick_absolute(WAIT_TIME);
bst_result = In_progress;
}

View file

@ -0,0 +1,68 @@
/**
* Common functions and helpers for BSIM audio tests
*
* Copyright (c) 2019 Bose Corporation
* Copyright (c) 2020-2022 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef ZEPHYR_TEST_BSIM_BT_AUDIO_TEST_
#define ZEPHYR_TEST_BSIM_BT_AUDIO_TEST_
#include <zephyr/kernel.h>
#include "bs_types.h"
#include "bs_tracing.h"
#include "time_machine.h"
#include "bstests.h"
#include <zephyr/types.h>
#include <stddef.h>
#include <errno.h>
#include <zephyr/sys_clock.h>
#include <zephyr/bluetooth/bluetooth.h>
#include <zephyr/bluetooth/hci.h>
#include <zephyr/bluetooth/conn.h>
#include <zephyr/bluetooth/uuid.h>
#include <zephyr/bluetooth/gatt.h>
#define WAIT_SECONDS 30 /* seconds */
#define WAIT_TIME (WAIT_SECONDS * USEC_PER_SEC) /* microseconds*/
#define WAIT_FOR_COND(cond) while (!(cond)) { k_sleep(K_MSEC(1)); }
#define CREATE_FLAG(flag) static atomic_t flag = (atomic_t)false
#define SET_FLAG(flag) (void)atomic_set(&flag, (atomic_t)true)
#define UNSET_FLAG(flag) (void)atomic_clear(&flag)
#define TEST_FLAG(flag) (atomic_get(&flag) == (atomic_t)true)
#define WAIT_FOR_FLAG(flag) \
while (!(bool)atomic_get(&flag)) { \
(void)k_sleep(K_MSEC(1)); \
}
#define FAIL(...) \
do { \
bst_result = Failed; \
bs_trace_error_time_line(__VA_ARGS__); \
} while (0)
#define PASS(...) \
do { \
bst_result = Passed; \
bs_trace_info_time(1, "PASSED: " __VA_ARGS__); \
} while (0)
#define AD_SIZE 1
extern const struct bt_data ad[AD_SIZE];
extern struct bt_conn *default_conn;
extern atomic_t flag_connected;
void device_found(const bt_addr_le_t *addr, int8_t rssi, uint8_t type,
struct net_buf_simple *ad);
void disconnected(struct bt_conn *conn, uint8_t reason);
void test_tick(bs_time_t HW_device_time);
void test_init(void);
#endif /* ZEPHYR_TEST_BSIM_BT_AUDIO_TEST_ */

View file

@ -0,0 +1,411 @@
/*
* Copyright (c) 2019 Bose Corporation
* Copyright (c) 2020-2022 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifdef CONFIG_BT_CSIP_SET_COORDINATOR
#include <zephyr/bluetooth/addr.h>
#include <zephyr/bluetooth/audio/csip.h>
#include "common.h"
extern enum bst_result_t bst_result;
static volatile bool discovered;
static volatile bool members_discovered;
static volatile bool set_locked;
static volatile bool set_unlocked;
static volatile bool ordered_access_locked;
static volatile bool ordered_access_unlocked;
static const struct bt_csip_set_coordinator_csis_inst *inst;
static uint8_t members_found;
static struct k_work_delayable discover_members_timer;
static bt_addr_le_t addr_found[CONFIG_BT_MAX_CONN];
static struct bt_conn *conns[CONFIG_BT_MAX_CONN];
static const struct bt_csip_set_coordinator_set_member *set_members[CONFIG_BT_MAX_CONN];
static void csip_set_coordinator_lock_set_cb(int err);
static void csip_set_coordinator_lock_release_cb(int err)
{
printk("%s\n", __func__);
if (err != 0) {
FAIL("Release sets failed (%d)\n", err);
return;
}
set_unlocked = true;
}
static void csip_set_coordinator_lock_set_cb(int err)
{
printk("%s\n", __func__);
if (err != 0) {
FAIL("Lock sets failed (%d)\n", err);
return;
}
set_locked = true;
}
static void csip_discover_cb(struct bt_conn *conn,
const struct bt_csip_set_coordinator_set_member *member,
int err, size_t set_count)
{
uint8_t conn_index;
printk("%s\n", __func__);
if (err != 0 || set_count == 0U) {
FAIL("Discover failed (%d)\n", err);
return;
}
conn_index = bt_conn_index(conn);
inst = &member->insts[0];
set_members[conn_index] = member;
discovered = true;
}
static void csip_lock_changed_cb(struct bt_csip_set_coordinator_csis_inst *inst,
bool locked)
{
printk("Inst %p %s\n", inst, locked ? "locked" : "released");
}
static void csip_set_coordinator_ordered_access_cb(
const struct bt_csip_set_coordinator_set_info *set_info, int err,
bool locked, struct bt_csip_set_coordinator_set_member *member)
{
if (err) {
FAIL("Ordered access failed with err %d\n", err);
} else if (locked) {
printk("Ordered access procedure locked member %p\n", member);
ordered_access_locked = true;
} else {
printk("Ordered access procedure finished\n");
ordered_access_unlocked = true;
}
}
static struct bt_csip_set_coordinator_cb cbs = {
.lock_set = csip_set_coordinator_lock_set_cb,
.release_set = csip_set_coordinator_lock_release_cb,
.discover = csip_discover_cb,
.lock_changed = csip_lock_changed_cb,
.ordered_access = csip_set_coordinator_ordered_access_cb
};
static bool csip_set_coordinator_oap_cb(const struct bt_csip_set_coordinator_set_info *set_info,
struct bt_csip_set_coordinator_set_member *members[],
size_t count)
{
for (size_t i = 0; i < count; i++) {
printk("Ordered access for members[%zu]: %p\n", i, members[i]);
}
return true;
}
static bool is_discovered(const bt_addr_le_t *addr)
{
for (int i = 0; i < members_found; i++) {
if (bt_addr_le_eq(addr, &addr_found[i])) {
return true;
}
}
return false;
}
static bool csip_found(struct bt_data *data, void *user_data)
{
if (bt_csip_set_coordinator_is_set_member(inst->info.set_sirk, data)) {
const bt_addr_le_t *addr = user_data;
char addr_str[BT_ADDR_LE_STR_LEN];
bt_addr_le_to_str(addr, addr_str, sizeof(addr_str));
printk("Found CSIP advertiser with address %s\n", addr_str);
if (is_discovered(addr)) {
printk("Set member already found\n");
/* Stop parsing */
return false;
}
bt_addr_le_copy(&addr_found[members_found++], addr);
printk("Found member (%u / %u)\n",
members_found, inst->info.set_size);
/* Stop parsing */
return false;
}
/* Continue parsing */
return true;
}
static void csip_set_coordinator_scan_recv(const struct bt_le_scan_recv_info *info,
struct net_buf_simple *ad)
{
/* We're only interested in connectable events */
if (info->adv_props & BT_GAP_ADV_PROP_CONNECTABLE) {
if (inst == NULL) {
/* Scanning for the first device */
if (members_found == 0) {
bt_addr_le_copy(&addr_found[members_found++],
info->addr);
}
} else { /* Scanning for set members */
bt_data_parse(ad, csip_found, (void *)info->addr);
}
}
}
static struct bt_le_scan_cb csip_set_coordinator_scan_callbacks = {
.recv = csip_set_coordinator_scan_recv
};
static void discover_members_timer_handler(struct k_work *work)
{
FAIL("Could not find all members (%u / %u)\n",
members_found, inst->info.set_size);
}
static void ordered_access(const struct bt_csip_set_coordinator_set_member **members,
size_t count, bool expect_locked)
{
int err;
printk("Performing ordered access, expecting %s\n",
expect_locked ? "locked" : "unlocked");
if (expect_locked) {
ordered_access_locked = false;
} else {
ordered_access_unlocked = false;
}
err = bt_csip_set_coordinator_ordered_access(members, count,
&inst->info,
csip_set_coordinator_oap_cb);
if (err != 0) {
FAIL("Failed to do CSIP set coordinator ordered access (%d)",
err);
return;
}
if (expect_locked) {
WAIT_FOR_COND(ordered_access_locked);
} else {
WAIT_FOR_COND(ordered_access_unlocked);
}
}
static void test_main(void)
{
int err;
char addr[BT_ADDR_LE_STR_LEN];
const struct bt_csip_set_coordinator_set_member *locked_members[CONFIG_BT_MAX_CONN];
uint8_t connected_member_count = 0;
err = bt_enable(NULL);
if (err != 0) {
FAIL("Bluetooth init failed (err %d)\n", err);
return;
}
printk("Audio Client: Bluetooth initialized\n");
bt_csip_set_coordinator_register_cb(&cbs);
k_work_init_delayable(&discover_members_timer,
discover_members_timer_handler);
bt_le_scan_cb_register(&csip_set_coordinator_scan_callbacks);
err = bt_le_scan_start(BT_LE_SCAN_PASSIVE, NULL);
if (err != 0) {
FAIL("Scanning failed to start (err %d)\n", err);
return;
}
printk("Scanning successfully started\n");
WAIT_FOR_COND(members_found == 1);
printk("Stopping scan\n");
err = bt_le_scan_stop();
if (err != 0) {
FAIL("Could not stop scan");
return;
}
bt_addr_le_to_str(&addr_found[0], addr, sizeof(addr));
err = bt_conn_le_create(&addr_found[0], BT_CONN_LE_CREATE_CONN,
BT_LE_CONN_PARAM_DEFAULT, &conns[0]);
if (err != 0) {
FAIL("Failed to connect to %s: %d\n", err);
return;
}
printk("Connecting to %s\n", addr);
WAIT_FOR_FLAG(flag_connected);
connected_member_count++;
err = bt_csip_set_coordinator_discover(conns[0]);
if (err != 0) {
FAIL("Failed to initialize set coordinator for connection %d\n",
err);
return;
}
WAIT_FOR_COND(discovered);
err = bt_le_scan_start(BT_LE_SCAN_ACTIVE, NULL);
if (err != 0) {
FAIL("Could not start scan: %d", err);
return;
}
err = k_work_reschedule(&discover_members_timer,
BT_CSIP_SET_COORDINATOR_DISCOVER_TIMER_VALUE);
if (err < 0) { /* Can return 0, 1 and 2 for success */
FAIL("Could not schedule discover_members_timer %d", err);
return;
}
WAIT_FOR_COND(members_found == inst->info.set_size);
(void)k_work_cancel_delayable(&discover_members_timer);
err = bt_le_scan_stop();
if (err != 0) {
FAIL("Scanning failed to stop (err %d)\n", err);
return;
}
for (uint8_t i = 1; i < members_found; i++) {
bt_addr_le_to_str(&addr_found[i], addr, sizeof(addr));
UNSET_FLAG(flag_connected);
printk("Connecting to member[%d] (%s)", i, addr);
err = bt_conn_le_create(&addr_found[i],
BT_CONN_LE_CREATE_CONN,
BT_LE_CONN_PARAM_DEFAULT,
&conns[i]);
if (err != 0) {
FAIL("Failed to connect to %s: %d\n", addr, err);
return;
}
printk("Connected to %s\n", addr);
WAIT_FOR_FLAG(flag_connected);
connected_member_count++;
discovered = false;
printk("Doing discovery on member[%u]", i);
err = bt_csip_set_coordinator_discover(conns[i]);
if (err != 0) {
FAIL("Failed to initialize set coordinator for connection %d\n",
err);
return;
}
WAIT_FOR_COND(discovered);
}
for (size_t i = 0; i < ARRAY_SIZE(locked_members); i++) {
locked_members[i] = set_members[i];
}
ordered_access(locked_members, connected_member_count, false);
printk("Locking set\n");
err = bt_csip_set_coordinator_lock(locked_members, connected_member_count,
&inst->info);
if (err != 0) {
FAIL("Failed to do set coordinator lock (%d)", err);
return;
}
WAIT_FOR_COND(set_locked);
ordered_access(locked_members, connected_member_count, true);
k_sleep(K_MSEC(1000)); /* Simulate doing stuff */
printk("Releasing set\n");
err = bt_csip_set_coordinator_release(locked_members, connected_member_count,
&inst->info);
if (err != 0) {
FAIL("Failed to do set coordinator release (%d)", err);
return;
}
WAIT_FOR_COND(set_unlocked);
ordered_access(locked_members, connected_member_count, false);
/* Lock and unlock again */
set_locked = false;
set_unlocked = false;
printk("Locking set\n");
err = bt_csip_set_coordinator_lock(locked_members, connected_member_count,
&inst->info);
if (err != 0) {
FAIL("Failed to do set coordinator lock (%d)", err);
return;
}
WAIT_FOR_COND(set_locked);
k_sleep(K_MSEC(1000)); /* Simulate doing stuff */
printk("Releasing set\n");
err = bt_csip_set_coordinator_release(locked_members, connected_member_count,
&inst->info);
if (err != 0) {
FAIL("Failed to do set coordinator release (%d)", err);
return;
}
WAIT_FOR_COND(set_unlocked);
for (uint8_t i = 0; i < members_found; i++) {
printk("Disconnecting member[%u] (%s)", i, addr);
err = bt_conn_disconnect(conns[i],
BT_HCI_ERR_REMOTE_USER_TERM_CONN);
(void)memset(&set_members[i], 0, sizeof(set_members[i]));
if (err != 0) {
FAIL("Failed to do disconnect\n", err);
return;
}
}
PASS("All members disconnected\n");
}
static const struct bst_test_instance test_connect[] = {
{
.test_id = "csip_set_coordinator",
.test_post_init_f = test_init,
.test_tick_f = test_tick,
.test_main_f = test_main
},
BSTEST_END_MARKER
};
struct bst_test_list *test_csip_set_coordinator_install(struct bst_test_list *tests)
{
return bst_add_tests(tests, test_connect);
}
#else
struct bst_test_list *test_csip_set_coordinator_install(struct bst_test_list *tests)
{
return tests;
}
#endif /* CONFIG_BT_CSIP_SET_COORDINATOR */

View file

@ -0,0 +1,198 @@
/*
* Copyright (c) 2019 Bose Corporation
* Copyright (c) 2020-2022 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifdef CONFIG_BT_CSIP_SET_MEMBER
#include <zephyr/bluetooth/audio/csip.h>
#include "common.h"
static struct bt_csip_set_member_svc_inst *svc_inst;
extern enum bst_result_t bst_result;
static volatile bool g_locked;
static uint8_t sirk_read_req_rsp = BT_CSIP_READ_SIRK_REQ_RSP_ACCEPT;
struct bt_csip_set_member_register_param param = {
.set_size = 3,
.rank = 1,
.lockable = true,
/* Using the CSIS test sample SIRK */
.set_sirk = { 0xcd, 0xcc, 0x72, 0xdd, 0x86, 0x8c, 0xcd, 0xce,
0x22, 0xfd, 0xa1, 0x21, 0x09, 0x7d, 0x7d, 0x45 },
};
static void csip_disconnected(struct bt_conn *conn, uint8_t reason)
{
printk("Disconnected (reason %u)\n", reason);
if (reason == BT_HCI_ERR_REMOTE_USER_TERM_CONN) {
PASS("Client successfully disconnected\n");
} else {
FAIL("Client disconnected unexpectedly (0x%02x)\n", reason);
}
}
static void csip_lock_changed_cb(struct bt_conn *conn,
struct bt_csip_set_member_svc_inst *svc_inst,
bool locked)
{
printk("Client %p %s the lock\n", conn, locked ? "locked" : "released");
g_locked = locked;
}
static uint8_t sirk_read_req_cb(struct bt_conn *conn,
struct bt_csip_set_member_svc_inst *svc_inst)
{
return sirk_read_req_rsp;
}
static struct bt_csip_set_member_cb csip_cbs = {
.lock_changed = csip_lock_changed_cb,
.sirk_read_req = sirk_read_req_cb,
};
static void bt_ready(int err)
{
uint8_t rsi[BT_CSIP_RSI_SIZE];
struct bt_data ad[] = {
BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)),
BT_CSIP_DATA_RSI(rsi),
};
if (err != 0) {
FAIL("Bluetooth init failed (err %d)\n", err);
return;
}
printk("Audio Server: Bluetooth initialized\n");
param.cb = &csip_cbs;
err = bt_csip_set_member_register(&param, &svc_inst);
if (err != 0) {
FAIL("Could not register CSIP (err %d)\n", err);
return;
}
err = bt_csip_set_member_generate_rsi(svc_inst, rsi);
if (err != 0) {
FAIL("Failed to generate RSI (err %d)\n", err);
return;
}
err = bt_le_adv_start(BT_LE_ADV_CONN_NAME, ad, ARRAY_SIZE(ad), NULL, 0);
if (err != 0) {
FAIL("Advertising failed to start (err %d)\n", err);
}
}
static struct bt_conn_cb conn_callbacks = {
.disconnected = csip_disconnected,
};
static void test_main(void)
{
int err;
err = bt_enable(bt_ready);
if (err != 0) {
FAIL("Bluetooth init failed (err %d)\n", err);
return;
}
bt_conn_cb_register(&conn_callbacks);
}
static void test_force_release(void)
{
int err;
err = bt_enable(bt_ready);
if (err != 0) {
FAIL("Bluetooth init failed (err %d)\n", err);
return;
}
bt_conn_cb_register(&conn_callbacks);
WAIT_FOR_COND(g_locked);
printk("Force releasing set\n");
bt_csip_set_member_lock(svc_inst, false, true);
}
static void test_csip_enc(void)
{
printk("Running %s\n", __func__);
sirk_read_req_rsp = BT_CSIP_READ_SIRK_REQ_RSP_ACCEPT_ENC;
test_main();
}
static void test_args(int argc, char *argv[])
{
for (size_t argn = 0; argn < argc; argn++) {
const char *arg = argv[argn];
if (strcmp(arg, "size") == 0) {
param.set_size = strtol(argv[++argn], NULL, 10);
} else if (strcmp(arg, "rank") == 0) {
param.rank = strtol(argv[++argn], NULL, 10);
} else if (strcmp(arg, "not-lockable") == 0) {
param.lockable = false;
} else if (strcmp(arg, "sirk") == 0) {
size_t len;
argn++;
len = hex2bin(argv[argn], strlen(argv[argn]),
param.set_sirk, sizeof(param.set_sirk));
if (len == 0) {
FAIL("Could not parse SIRK");
return;
}
} else {
FAIL("Invalid arg: %s", arg);
}
}
}
static const struct bst_test_instance test_connect[] = {
{
.test_id = "csip_set_member",
.test_post_init_f = test_init,
.test_tick_f = test_tick,
.test_main_f = test_main,
.test_args_f = test_args,
},
{
.test_id = "csip_set_member_release",
.test_post_init_f = test_init,
.test_tick_f = test_tick,
.test_main_f = test_force_release,
.test_args_f = test_args,
},
{
.test_id = "csip_set_member_enc",
.test_post_init_f = test_init,
.test_tick_f = test_tick,
.test_main_f = test_csip_enc,
.test_args_f = test_args,
},
BSTEST_END_MARKER
};
struct bst_test_list *test_csip_set_member_install(struct bst_test_list *tests)
{
return bst_add_tests(tests, test_connect);
}
#else
struct bst_test_list *test_csip_set_member_install(struct bst_test_list *tests)
{
return tests;
}
#endif /* CONFIG_BT_CSIP_SET_MEMBER */

View file

@ -0,0 +1,251 @@
/*
* Copyright (c) 2022 Codecoup
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifdef CONFIG_BT_HAS_CLIENT
#include <zephyr/bluetooth/audio/has.h>
#include "common.h"
extern enum bst_result_t bst_result;
extern const char *test_preset_name_1;
extern const char *test_preset_name_5;
extern const uint8_t test_preset_index_1;
extern const uint8_t test_preset_index_5;
extern const enum bt_has_properties test_preset_properties;
CREATE_FLAG(g_service_discovered);
CREATE_FLAG(g_preset_switched);
CREATE_FLAG(g_preset_1_found);
CREATE_FLAG(g_preset_5_found);
static struct bt_has *g_has;
static uint8_t g_active_index;
static void discover_cb(struct bt_conn *conn, int err, struct bt_has *has,
enum bt_has_hearing_aid_type type, enum bt_has_capabilities caps)
{
if (err) {
FAIL("Failed to discover HAS (err %d)\n", err);
return;
}
printk("HAS discovered type %d caps %d\n", type, caps);
g_has = has;
SET_FLAG(g_service_discovered);
}
static void preset_switch_cb(struct bt_has *has, int err, uint8_t index)
{
if (err != 0) {
return;
}
printk("Active preset index %d\n", index);
SET_FLAG(g_preset_switched);
g_active_index = index;
}
static void check_preset_record(const struct bt_has_preset_record *record,
enum bt_has_properties expected_properties,
const char *expected_name)
{
if (record->properties != expected_properties || strcmp(record->name, expected_name)) {
FAIL("mismatch 0x%02x %s vs 0x%02x %s expected\n",
record->properties, record->name, expected_properties, expected_name);
}
}
static void preset_read_rsp_cb(struct bt_has *has, int err,
const struct bt_has_preset_record *record, bool is_last)
{
if (err) {
FAIL("%s: err %d\n", __func__, err);
return;
}
if (record->index == test_preset_index_1) {
SET_FLAG(g_preset_1_found);
check_preset_record(record, test_preset_properties, test_preset_name_1);
} else if (record->index == test_preset_index_5) {
SET_FLAG(g_preset_5_found);
check_preset_record(record, test_preset_properties, test_preset_name_5);
} else {
FAIL("unexpected index 0x%02x", record->index);
}
}
static const struct bt_has_client_cb has_cb = {
.discover = discover_cb,
.preset_switch = preset_switch_cb,
.preset_read_rsp = preset_read_rsp_cb,
};
static bool test_preset_switch(uint8_t index)
{
int err;
UNSET_FLAG(g_preset_switched);
err = bt_has_client_preset_set(g_has, index, false);
if (err < 0) {
printk("%s (err %d)\n", __func__, err);
return false;
}
WAIT_FOR_COND(g_preset_switched);
return g_active_index == index;
}
static bool test_preset_next(uint8_t active_index_expected)
{
int err;
UNSET_FLAG(g_preset_switched);
err = bt_has_client_preset_next(g_has, false);
if (err < 0) {
printk("%s (err %d)\n", __func__, err);
return false;
}
WAIT_FOR_COND(g_preset_switched);
return g_active_index == active_index_expected;
}
static bool test_preset_prev(uint8_t active_index_expected)
{
int err;
UNSET_FLAG(g_preset_switched);
err = bt_has_client_preset_prev(g_has, false);
if (err < 0) {
printk("%s (err %d)\n", __func__, err);
return false;
}
WAIT_FOR_COND(g_preset_switched);
return g_active_index == active_index_expected;
}
static void test_main(void)
{
int err;
err = bt_enable(NULL);
if (err < 0) {
FAIL("Bluetooth discover failed (err %d)\n", err);
return;
}
printk("Bluetooth initialized\n");
err = bt_has_client_cb_register(&has_cb);
if (err < 0) {
FAIL("Failed to register callbacks (err %d)\n", err);
return;
}
err = bt_le_scan_start(BT_LE_SCAN_PASSIVE, device_found);
if (err < 0) {
FAIL("Scanning failed to start (err %d)\n", err);
return;
}
printk("Scanning successfully started\n");
WAIT_FOR_FLAG(flag_connected);
err = bt_has_client_discover(default_conn);
if (err < 0) {
FAIL("Failed to discover HAS (err %d)\n", err);
return;
}
WAIT_FOR_COND(g_service_discovered);
WAIT_FOR_COND(g_preset_switched);
err = bt_has_client_presets_read(g_has, BT_HAS_PRESET_INDEX_FIRST, 255);
if (err < 0) {
FAIL("Failed to read presets (err %d)\n", err);
return;
}
WAIT_FOR_COND(g_preset_1_found);
WAIT_FOR_COND(g_preset_5_found);
if (!test_preset_switch(test_preset_index_1)) {
FAIL("Failed to switch preset %d\n", test_preset_index_1);
return;
}
if (!test_preset_switch(test_preset_index_5)) {
FAIL("Failed to switch preset %d\n", test_preset_index_5);
return;
}
if (!test_preset_next(test_preset_index_1)) {
FAIL("Failed to set next preset %d\n", test_preset_index_1);
return;
}
if (!test_preset_next(test_preset_index_5)) {
FAIL("Failed to set next preset %d\n", test_preset_index_5);
return;
}
if (!test_preset_next(test_preset_index_1)) {
FAIL("Failed to set next preset %d\n", test_preset_index_1);
return;
}
if (!test_preset_prev(test_preset_index_5)) {
FAIL("Failed to set previous preset %d\n", test_preset_index_5);
return;
}
if (!test_preset_prev(test_preset_index_1)) {
FAIL("Failed to set previous preset %d\n", test_preset_index_1);
return;
}
if (!test_preset_prev(test_preset_index_5)) {
FAIL("Failed to set previous preset %d\n", test_preset_index_5);
return;
}
PASS("HAS main PASS\n");
}
static const struct bst_test_instance test_has[] = {
{
.test_id = "has_client",
.test_post_init_f = test_init,
.test_tick_f = test_tick,
.test_main_f = test_main,
},
BSTEST_END_MARKER
};
struct bst_test_list *test_has_client_install(struct bst_test_list *tests)
{
return bst_add_tests(tests, test_has);
}
#else
struct bst_test_list *test_has_client_install(struct bst_test_list *tests)
{
return tests;
}
#endif /* CONFIG_BT_HAS_CLIENT */

View file

@ -0,0 +1,107 @@
/*
* Copyright (c) 2022 Codecoup
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifdef CONFIG_BT_HAS
#include <zephyr/bluetooth/gatt.h>
#include <zephyr/bluetooth/audio/has.h>
#include "common.h"
extern enum bst_result_t bst_result;
const uint8_t test_preset_index_1 = 0x01;
const uint8_t test_preset_index_5 = 0x05;
const char *test_preset_name_1 = "test_preset_name_1";
const char *test_preset_name_5 = "test_preset_name_5";
const enum bt_has_properties test_preset_properties = BT_HAS_PROP_AVAILABLE;
static int preset_select(uint8_t index, bool sync)
{
return 0;
}
static const struct bt_has_preset_ops preset_ops = {
.select = preset_select,
};
static void test_main(void)
{
struct bt_has_register_param has_param = {0};
struct bt_has_preset_register_param preset_param;
int err;
err = bt_enable(NULL);
if (err) {
FAIL("Bluetooth enable failed (err %d)\n", err);
return;
}
printk("Bluetooth initialized\n");
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;
}
printk("Advertising successfully started\n");
has_param.type = BT_HAS_HEARING_AID_TYPE_MONAURAL;
err = bt_has_register(&has_param);
if (err) {
FAIL("HAS register failed (err %d)\n", err);
return;
}
preset_param.index = test_preset_index_5;
preset_param.properties = test_preset_properties;
preset_param.name = test_preset_name_5;
preset_param.ops = &preset_ops,
err = bt_has_preset_register(&preset_param);
if (err) {
FAIL("Preset register failed (err %d)\n", err);
return;
}
preset_param.index = test_preset_index_1;
preset_param.properties = test_preset_properties;
preset_param.name = test_preset_name_1;
err = bt_has_preset_register(&preset_param);
if (err) {
FAIL("Preset register failed (err %d)\n", err);
return;
}
printk("Presets registered\n");
PASS("HAS passed\n");
}
static const struct bst_test_instance test_has[] = {
{
.test_id = "has",
.test_post_init_f = test_init,
.test_tick_f = test_tick,
.test_main_f = test_main,
},
BSTEST_END_MARKER,
};
struct bst_test_list *test_has_install(struct bst_test_list *tests)
{
return bst_add_tests(tests, test_has);
}
#else
struct bst_test_list *test_has_install(struct bst_test_list *tests)
{
return tests;
}
#endif /* CONFIG_BT_HAS */

View file

@ -0,0 +1,137 @@
/*
* Copyright (c) 2022 Codecoup
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdint.h>
#ifdef CONFIG_BT_IAS_CLIENT
#include "zephyr/bluetooth/services/ias.h"
#include "common.h"
extern enum bst_result_t bst_result;
CREATE_FLAG(g_service_discovered);
static void discover_cb(struct bt_conn *conn, int err)
{
if (err) {
FAIL("Failed to discover IAS (err %d)\n", err);
return;
}
printk("IAS discovered\n");
SET_FLAG(g_service_discovered);
}
static const struct bt_ias_client_cb ias_client_cb = {
.discover = discover_cb,
};
static void test_alert_high(struct bt_conn *conn)
{
int err;
err = bt_ias_client_alert_write(conn, BT_IAS_ALERT_LVL_HIGH_ALERT);
if (err == 0) {
printk("High alert sent\n");
} else {
FAIL("Failed to send high alert\n");
}
}
static void test_alert_mild(struct bt_conn *conn)
{
int err;
err = bt_ias_client_alert_write(conn, BT_IAS_ALERT_LVL_MILD_ALERT);
if (err == 0) {
printk("Mild alert sent\n");
} else {
FAIL("Failed to send mild alert\n");
}
}
static void test_alert_stop(struct bt_conn *conn)
{
int err;
err = bt_ias_client_alert_write(conn, BT_IAS_ALERT_LVL_NO_ALERT);
if (err == 0) {
printk("Stop alert sent\n");
} else {
FAIL("Failed to send no alert\n");
}
}
static void test_main(void)
{
int err;
err = bt_enable(NULL);
if (err < 0) {
FAIL("Bluetooth discover failed (err %d)\n", err);
return;
}
printk("Bluetooth initialized\n");
err = bt_ias_client_cb_register(&ias_client_cb);
if (err < 0) {
FAIL("Failed to register callbacks (err %d)\n", err);
return;
}
err = bt_le_scan_start(BT_LE_SCAN_PASSIVE, device_found);
if (err < 0) {
FAIL("Scanning failed to start (err %d)\n", err);
return;
}
printk("Scanning successfully started\n");
WAIT_FOR_FLAG(flag_connected);
err = bt_ias_discover(default_conn);
if (err < 0) {
FAIL("Failed to discover IAS (err %d)\n", err);
return;
}
WAIT_FOR_FLAG(g_service_discovered);
/* Set alert levels with a delay to let the server handle any changes it want */
test_alert_high(default_conn);
k_sleep(K_SECONDS(1));
test_alert_mild(default_conn);
k_sleep(K_SECONDS(1));
test_alert_stop(default_conn);
k_sleep(K_SECONDS(1));
PASS("IAS client PASS\n");
}
static const struct bst_test_instance test_ias[] = {
{
.test_id = "ias_client",
.test_post_init_f = test_init,
.test_tick_f = test_tick,
.test_main_f = test_main,
},
BSTEST_END_MARKER
};
struct bst_test_list *test_ias_client_install(struct bst_test_list *tests)
{
return bst_add_tests(tests, test_ias);
}
#else
struct bst_test_list *test_ias_client_install(struct bst_test_list *tests)
{
return tests;
}
#endif /* CONFIG_BT_IAS_CLIENT */

View file

@ -0,0 +1,106 @@
/*
* Copyright (c) 2022 Codecoup
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdint.h>
#ifdef CONFIG_BT_IAS
#include <stddef.h>
#include <errno.h>
#include <zephyr/types.h>
#include <zephyr/bluetooth/bluetooth.h>
#include <zephyr/bluetooth/hci.h>
#include <zephyr/bluetooth/conn.h>
#include <zephyr/bluetooth/uuid.h>
#include <zephyr/bluetooth/gatt.h>
#include <zephyr/bluetooth/services/ias.h>
#include "common.h"
extern enum bst_result_t bst_result;
CREATE_FLAG(g_high_alert_received);
CREATE_FLAG(g_mild_alert_received);
CREATE_FLAG(g_stop_alert_received);
static void high_alert_cb(void)
{
SET_FLAG(g_high_alert_received);
}
static void mild_alert_cb(void)
{
SET_FLAG(g_mild_alert_received);
}
static void no_alert_cb(void)
{
SET_FLAG(g_stop_alert_received);
}
BT_IAS_CB_DEFINE(ias_callbacks) = {
.high_alert = high_alert_cb,
.mild_alert = mild_alert_cb,
.no_alert = no_alert_cb,
};
static void test_main(void)
{
int err;
err = bt_enable(NULL);
if (err != 0) {
FAIL("Bluetooth init failed (err %d)\n", err);
return;
}
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;
}
printk("Advertising successfully started\n");
WAIT_FOR_FLAG(flag_connected);
WAIT_FOR_FLAG(g_high_alert_received);
printk("High alert received\n");
err = bt_ias_local_alert_stop();
if (err != 0) {
FAIL("Failed to locally stop alert: %d\n", err);
return;
}
WAIT_FOR_FLAG(g_stop_alert_received);
WAIT_FOR_FLAG(g_mild_alert_received);
printk("Mild alert received\n");
WAIT_FOR_FLAG(g_stop_alert_received);
printk("Stop alert received\n");
PASS("IAS test passed\n");
}
static const struct bst_test_instance test_ias[] = {
{
.test_id = "ias",
.test_post_init_f = test_init,
.test_tick_f = test_tick,
.test_main_f = test_main,
},
BSTEST_END_MARKER
};
struct bst_test_list *test_ias_install(struct bst_test_list *tests)
{
return bst_add_tests(tests, test_ias);
}
#endif /* CONFIG_BT_IAS */

View file

@ -0,0 +1,65 @@
/*
* Copyright (c) 2020-2022 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "bstests.h"
extern struct bst_test_list *test_vcp_install(struct bst_test_list *tests);
extern struct bst_test_list *test_vcp_vol_ctlr_install(struct bst_test_list *tests);
extern struct bst_test_list *test_micp_install(struct bst_test_list *tests);
extern struct bst_test_list *test_micp_mic_ctlr_install(struct bst_test_list *tests);
extern struct bst_test_list *test_csip_set_member_install(struct bst_test_list *tests);
extern struct bst_test_list *test_csip_set_coordinator_install(struct bst_test_list *tests);
extern struct bst_test_list *test_tbs_install(struct bst_test_list *tests);
extern struct bst_test_list *test_tbs_client_install(struct bst_test_list *tests);
extern struct bst_test_list *test_mcs_install(struct bst_test_list *tests);
extern struct bst_test_list *test_mcc_install(struct bst_test_list *tests);
extern struct bst_test_list *test_media_controller_install(struct bst_test_list *tests);
extern struct bst_test_list *test_unicast_client_install(struct bst_test_list *tests);
extern struct bst_test_list *test_unicast_server_install(struct bst_test_list *tests);
extern struct bst_test_list *test_broadcast_source_install(struct bst_test_list *tests);
extern struct bst_test_list *test_broadcast_sink_install(struct bst_test_list *tests);
extern struct bst_test_list *test_scan_delegator_install(struct bst_test_list *tests);
extern struct bst_test_list *test_bap_broadcast_assistant_install(struct bst_test_list *tests);
extern struct bst_test_list *test_bass_broadcaster_install(struct bst_test_list *tests);
extern struct bst_test_list *test_cap_acceptor_install(struct bst_test_list *tests);
extern struct bst_test_list *test_cap_initiator_install(struct bst_test_list *tests);
extern struct bst_test_list *test_has_install(struct bst_test_list *tests);
extern struct bst_test_list *test_has_client_install(struct bst_test_list *tests);
extern struct bst_test_list *test_ias_install(struct bst_test_list *tests);
extern struct bst_test_list *test_ias_client_install(struct bst_test_list *tests);
bst_test_install_t test_installers[] = {
test_vcp_install,
test_vcp_vol_ctlr_install,
test_micp_install,
test_micp_mic_ctlr_install,
test_csip_set_member_install,
test_csip_set_coordinator_install,
test_tbs_install,
test_tbs_client_install,
test_mcs_install,
test_mcc_install,
test_media_controller_install,
test_unicast_client_install,
test_unicast_server_install,
test_broadcast_source_install,
test_broadcast_sink_install,
test_scan_delegator_install,
test_bap_broadcast_assistant_install,
test_bass_broadcaster_install,
test_cap_acceptor_install,
test_cap_initiator_install,
test_has_install,
test_has_client_install,
test_ias_install,
test_ias_client_install,
NULL
};
void main(void)
{
bst_main();
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,81 @@
/*
* Copyright (c) 2019 - 2021 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifdef CONFIG_BT_MCS
#include <zephyr/bluetooth/audio/media_proxy.h>
#include "common.h"
extern enum bst_result_t bst_result;
/* Callback after Bluetoot initialization attempt */
static void bt_ready(int err)
{
if (err) {
FAIL("Bluetooth init failed (err %d)\n", err);
return;
}
printk("Bluetooth initialized\n");
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;
}
printk("Advertising successfully started\n");
}
static void test_main(void)
{
int err;
printk("Media Control Server test application. Board: %s\n", CONFIG_BOARD);
/* Initialize media player */
err = media_proxy_pl_init();
if (err) {
FAIL("Initializing MPL failed (err %d)", err);
return;
}
/* Initialize Bluetooth, get connected */
err = bt_enable(bt_ready);
if (err) {
FAIL("Bluetooth init failed (err %d)\n", err);
return;
}
WAIT_FOR_FLAG(flag_connected);
PASS("MCS passed\n");
}
static const struct bst_test_instance test_mcs[] = {
{
.test_id = "mcs",
.test_post_init_f = test_init,
.test_tick_f = test_tick,
.test_main_f = test_main
},
BSTEST_END_MARKER
};
struct bst_test_list *test_mcs_install(struct bst_test_list *tests)
{
return bst_add_tests(tests, test_mcs);
}
#else
struct bst_test_list *test_mcs_install(struct bst_test_list *tests)
{
return tests;
}
#endif /* CONFIG_BT_MCS */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,458 @@
/*
* Copyright (c) 2021-2022 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifdef CONFIG_BT_MICP_MIC_CTLR
#include <zephyr/bluetooth/bluetooth.h>
#include <zephyr/bluetooth/audio/micp.h>
#include "common.h"
#define AICS_DESC_SIZE 64
extern enum bst_result_t bst_result;
static struct bt_micp_mic_ctlr *mic_ctlr;
static struct bt_micp_included micp_included;
static volatile bool g_bt_init;
static volatile bool g_discovery_complete;
static volatile bool g_write_complete;
static volatile uint8_t g_mute;
static volatile uint8_t g_aics_count;
static volatile int8_t g_aics_gain;
static volatile uint8_t g_aics_input_mute;
static volatile uint8_t g_aics_mode;
static volatile uint8_t g_aics_input_type;
static volatile uint8_t g_aics_units;
static volatile uint8_t g_aics_gain_max;
static volatile uint8_t g_aics_gain_min;
static volatile bool g_aics_active = true;
static char g_aics_desc[AICS_DESC_SIZE];
static volatile bool g_cb;
static void aics_state_cb(struct bt_aics *inst, int err, int8_t gain,
uint8_t mute, uint8_t mode)
{
if (err != 0) {
FAIL("AICS state cb err (%d)", err);
return;
}
g_aics_gain = gain;
g_aics_input_mute = mute;
g_aics_mode = mode;
g_cb = true;
}
static void aics_gain_setting_cb(struct bt_aics *inst, int err, uint8_t units,
int8_t minimum, int8_t maximum)
{
if (err != 0) {
FAIL("AICS gain setting cb err (%d)", err);
return;
}
g_aics_units = units;
g_aics_gain_min = minimum;
g_aics_gain_max = maximum;
g_cb = true;
}
static void aics_input_type_cb(struct bt_aics *inst, int err,
uint8_t input_type)
{
if (err != 0) {
FAIL("AICS input type cb err (%d)", err);
return;
}
g_aics_input_type = input_type;
g_cb = true;
}
static void aics_status_cb(struct bt_aics *inst, int err, bool active)
{
if (err != 0) {
FAIL("AICS status cb err (%d)", err);
return;
}
g_aics_active = active;
g_cb = true;
}
static void aics_description_cb(struct bt_aics *inst, int err,
char *description)
{
if (err != 0) {
FAIL("AICS description cb err (%d)", err);
return;
}
if (strlen(description) > sizeof(g_aics_desc) - 1) {
printk("Warning: AICS description (%zu) is larger than buffer (%zu)\n",
strlen(description), sizeof(g_aics_desc) - 1);
}
strncpy(g_aics_desc, description, sizeof(g_aics_desc) - 1);
g_aics_desc[sizeof(g_aics_desc) - 1] = '\0';
g_cb = true;
}
static void aics_write_cb(struct bt_aics *inst, int err)
{
if (err != 0) {
FAIL("AICS write failed (%d)\n", err);
return;
}
g_write_complete = true;
}
static void micp_mic_ctlr_discover_cb(struct bt_micp_mic_ctlr *mic_ctlr,
int err,
uint8_t aics_count)
{
if (err != 0) {
FAIL("MICS could not be discovered (%d)\n", err);
return;
}
g_aics_count = aics_count;
g_discovery_complete = true;
}
static void micp_mic_ctlr_mute_written_cb(struct bt_micp_mic_ctlr *mic_ctlr,
int err)
{
if (err != 0) {
FAIL("mic_ctlr mute write failed (%d)\n", err);
return;
}
g_write_complete = true;
}
static void micp_mic_ctlr_unmute_written_cb(struct bt_micp_mic_ctlr *mic_ctlr,
int err)
{
if (err != 0) {
FAIL("mic_ctlr unmute write failed (%d)\n", err);
return;
}
g_write_complete = true;
}
static void micp_mic_ctlr_mute_cb(struct bt_micp_mic_ctlr *mic_ctlr, int err,
uint8_t mute)
{
if (err != 0) {
FAIL("mic_ctlr mute read failed (%d)\n", err);
return;
}
g_mute = mute;
g_cb = true;
}
static struct bt_micp_mic_ctlr_cb micp_mic_ctlr_cbs = {
.discover = micp_mic_ctlr_discover_cb,
.mute = micp_mic_ctlr_mute_cb,
.mute_written = micp_mic_ctlr_mute_written_cb,
.unmute_written = micp_mic_ctlr_unmute_written_cb,
.aics_cb = {
.state = aics_state_cb,
.gain_setting = aics_gain_setting_cb,
.type = aics_input_type_cb,
.status = aics_status_cb,
.description = aics_description_cb,
.set_gain = aics_write_cb,
.unmute = aics_write_cb,
.mute = aics_write_cb,
.set_manual_mode = aics_write_cb,
.set_auto_mode = aics_write_cb,
}
};
static void bt_ready(int err)
{
if (err != 0) {
FAIL("Bluetooth discover failed (err %d)\n", err);
return;
}
g_bt_init = true;
}
static int test_aics(void)
{
int err;
int8_t expected_gain;
uint8_t expected_input_mute;
uint8_t expected_mode;
uint8_t expected_input_type;
char expected_aics_desc[AICS_DESC_SIZE];
struct bt_conn *cached_conn;
printk("Getting AICS client conn\n");
err = bt_aics_client_conn_get(micp_included.aics[0], &cached_conn);
if (err != 0) {
FAIL("Could not get AICS client conn (err %d)\n", err);
return err;
}
if (cached_conn != default_conn) {
FAIL("Cached conn was not the conn used to discover");
return -ENOTCONN;
}
printk("Getting AICS state\n");
g_cb = false;
err = bt_aics_state_get(micp_included.aics[0]);
if (err != 0) {
FAIL("Could not get AICS state (err %d)\n", err);
return err;
}
WAIT_FOR_COND(g_cb);
printk("AICS state get\n");
printk("Getting AICS gain setting\n");
g_cb = false;
err = bt_aics_gain_setting_get(micp_included.aics[0]);
if (err != 0) {
FAIL("Could not get AICS gain setting (err %d)\n", err);
return err;
}
WAIT_FOR_COND(g_cb);
printk("AICS gain setting get\n");
printk("Getting AICS input type\n");
expected_input_type = BT_AICS_INPUT_TYPE_UNSPECIFIED;
g_cb = false;
err = bt_aics_type_get(micp_included.aics[0]);
if (err != 0) {
FAIL("Could not get AICS input type (err %d)\n", err);
return err;
}
/* Expect and wait for input_type from init */
WAIT_FOR_COND(g_cb && expected_input_type == g_aics_input_type);
printk("AICS input type get\n");
printk("Getting AICS status\n");
g_cb = false;
err = bt_aics_status_get(micp_included.aics[0]);
if (err != 0) {
FAIL("Could not get AICS status (err %d)\n", err);
return err;
}
WAIT_FOR_COND(g_cb);
printk("AICS status get\n");
printk("Getting AICS description\n");
g_cb = false;
err = bt_aics_description_get(micp_included.aics[0]);
if (err != 0) {
FAIL("Could not get AICS description (err %d)\n", err);
return err;
}
WAIT_FOR_COND(g_cb);
printk("AICS description get\n");
printk("Setting AICS mute\n");
expected_input_mute = BT_AICS_STATE_MUTED;
g_write_complete = g_cb = false;
err = bt_aics_mute(micp_included.aics[0]);
if (err != 0) {
FAIL("Could not set AICS mute (err %d)\n", err);
return err;
}
WAIT_FOR_COND(g_aics_input_mute == expected_input_mute &&
g_cb && g_write_complete);
printk("AICS mute set\n");
printk("Setting AICS unmute\n");
expected_input_mute = BT_AICS_STATE_UNMUTED;
g_write_complete = g_cb = false;
err = bt_aics_unmute(micp_included.aics[0]);
if (err != 0) {
FAIL("Could not set AICS unmute (err %d)\n", err);
return err;
}
WAIT_FOR_COND(g_aics_input_mute == expected_input_mute &&
g_cb && g_write_complete);
printk("AICS unmute set\n");
printk("Setting AICS auto mode\n");
expected_mode = BT_AICS_MODE_AUTO;
g_write_complete = g_cb = false;
err = bt_aics_automatic_gain_set(micp_included.aics[0]);
if (err != 0) {
FAIL("Could not set AICS auto mode (err %d)\n", err);
return err;
}
WAIT_FOR_COND(g_aics_mode == expected_mode && g_cb && g_write_complete);
printk("AICS auto mode set\n");
printk("Setting AICS manual mode\n");
expected_mode = BT_AICS_MODE_MANUAL;
g_write_complete = g_cb = false;
err = bt_aics_manual_gain_set(micp_included.aics[0]);
if (err != 0) {
FAIL("Could not set AICS manual mode (err %d)\n", err);
return err;
}
WAIT_FOR_COND(g_aics_mode == expected_mode && g_cb && g_write_complete);
printk("AICS manual mode set\n");
printk("Setting AICS gain\n");
expected_gain = g_aics_gain_max - 1;
g_write_complete = g_cb = false;
err = bt_aics_gain_set(micp_included.aics[0], expected_gain);
if (err != 0) {
FAIL("Could not set AICS gain (err %d)\n", err);
return err;
}
WAIT_FOR_COND(g_aics_gain == expected_gain && g_cb && g_write_complete);
printk("AICS gain set\n");
printk("Setting AICS Description\n");
strncpy(expected_aics_desc, "New Input Description",
sizeof(expected_aics_desc));
expected_aics_desc[sizeof(expected_aics_desc) - 1] = '\0';
g_cb = false;
err = bt_aics_description_set(micp_included.aics[0],
expected_aics_desc);
if (err != 0) {
FAIL("Could not set AICS Description (err %d)\n", err);
return err;
}
WAIT_FOR_COND(g_cb &&
(strncmp(expected_aics_desc, g_aics_desc,
sizeof(expected_aics_desc)) == 0));
printk("AICS Description set\n");
printk("AICS passed\n");
return 0;
}
static void test_main(void)
{
int err;
uint8_t expected_mute;
struct bt_conn *cached_conn;
err = bt_enable(bt_ready);
if (err != 0) {
FAIL("Bluetooth discover failed (err %d)\n", err);
return;
}
bt_micp_mic_ctlr_cb_register(&micp_mic_ctlr_cbs);
WAIT_FOR_COND(g_bt_init);
err = bt_le_scan_start(BT_LE_SCAN_PASSIVE, device_found);
if (err != 0) {
FAIL("Scanning failed to start (err %d)\n", err);
return;
}
printk("Scanning successfully started\n");
WAIT_FOR_FLAG(flag_connected);
err = bt_micp_mic_ctlr_discover(default_conn, &mic_ctlr);
if (err != 0) {
FAIL("Failed to discover MICS %d", err);
}
WAIT_FOR_COND(g_discovery_complete);
err = bt_micp_mic_ctlr_included_get(mic_ctlr, &micp_included);
if (err != 0) {
FAIL("Failed to get mic_ctlr context (err %d)\n", err);
return;
}
printk("Getting mic_ctlr conn\n");
err = bt_micp_mic_ctlr_conn_get(mic_ctlr, &cached_conn);
if (err != 0) {
FAIL("Failed to get mic_ctlr conn (err %d)\n", err);
return;
}
if (cached_conn != default_conn) {
FAIL("Cached conn was not the conn used to discover");
return;
}
printk("Getting mic_ctlr mute state\n");
g_cb = false;
err = bt_micp_mic_ctlr_mute_get(mic_ctlr);
if (err != 0) {
FAIL("Could not get mic_ctlr mute state (err %d)\n", err);
return;
}
WAIT_FOR_COND(g_cb);
printk("mic_ctlr mute state received\n");
printk("Muting mic_ctlr\n");
expected_mute = 1;
g_write_complete = g_cb = false;
err = bt_micp_mic_ctlr_mute(mic_ctlr);
if (err != 0) {
FAIL("Could not mute mic_ctlr (err %d)\n", err);
return;
}
WAIT_FOR_COND(g_mute == expected_mute && g_cb && g_write_complete);
printk("mic_ctlr muted\n");
printk("Unmuting mic_ctlr\n");
expected_mute = 0;
g_write_complete = g_cb = false;
err = bt_micp_mic_ctlr_unmute(mic_ctlr);
if (err != 0) {
FAIL("Could not unmute mic_ctlr (err %d)\n", err);
return;
}
WAIT_FOR_COND(g_mute == expected_mute && g_cb && g_write_complete);
printk("mic_ctlr unmuted\n");
if (CONFIG_BT_MICP_MIC_CTLR_MAX_AICS_INST > 0 && g_aics_count > 0) {
if (test_aics()) {
return;
}
}
PASS("mic_ctlr Passed\n");
}
static const struct bst_test_instance test_micp[] = {
{
.test_id = "micp_mic_ctlr",
.test_post_init_f = test_init,
.test_tick_f = test_tick,
.test_main_f = test_main
},
BSTEST_END_MARKER
};
struct bst_test_list *test_micp_mic_ctlr_install(struct bst_test_list *tests)
{
return bst_add_tests(tests, test_micp);
}
#else
struct bst_test_list *test_micp_mic_ctlr_install(struct bst_test_list *tests)
{
return tests;
}
#endif /* CONFIG_BT_MICP_MIC_CTLR */

View file

@ -0,0 +1,462 @@
/*
* Copyright (c) 2020-2022 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifdef CONFIG_BT_MICP_MIC_DEV
#include <zephyr/bluetooth/audio/micp.h>
#include "common.h"
extern enum bst_result_t bst_result;
#if defined(CONFIG_BT_AICS)
#define AICS_DESC_SIZE CONFIG_BT_AICS_MAX_INPUT_DESCRIPTION_SIZE
#else
#define AICS_DESC_SIZE 0
#endif /* CONFIG_BT_AICS */
static struct bt_micp_included micp_included;
static volatile uint8_t g_mute;
static volatile int8_t g_aics_gain;
static volatile uint8_t g_aics_input_mute;
static volatile uint8_t g_aics_mode;
static volatile uint8_t g_aics_input_type;
static volatile uint8_t g_aics_units;
static volatile uint8_t g_aics_gain_max;
static volatile uint8_t g_aics_gain_min;
static volatile bool g_aics_active = true;
static char g_aics_desc[AICS_DESC_SIZE];
static volatile bool g_cb;
static void micp_mute_cb(uint8_t mute)
{
g_mute = mute;
g_cb = true;
}
static struct bt_micp_mic_dev_cb micp_cb = {
.mute = micp_mute_cb,
};
#if defined(CONFIG_BT_MICP_MIC_DEV_AICS)
static void aics_state_cb(struct bt_aics *inst, int err, int8_t gain,
uint8_t mute, uint8_t mode)
{
if (err != 0) {
FAIL("AICS state cb err (%d)", err);
return;
}
g_aics_gain = gain;
g_aics_input_mute = mute;
g_aics_mode = mode;
g_cb = true;
}
static void aics_gain_setting_cb(struct bt_aics *inst, int err, uint8_t units,
int8_t minimum, int8_t maximum)
{
if (err != 0) {
FAIL("AICS gain setting cb err (%d)", err);
return;
}
g_aics_units = units;
g_aics_gain_min = minimum;
g_aics_gain_max = maximum;
g_cb = true;
}
static void aics_input_type_cb(struct bt_aics *inst, int err,
uint8_t input_type)
{
if (err != 0) {
FAIL("AICS input type cb err (%d)", err);
return;
}
g_aics_input_type = input_type;
g_cb = true;
}
static void aics_status_cb(struct bt_aics *inst, int err, bool active)
{
if (err != 0) {
FAIL("AICS status cb err (%d)", err);
return;
}
g_aics_active = active;
g_cb = true;
}
static void aics_description_cb(struct bt_aics *inst, int err,
char *description)
{
if (err != 0) {
FAIL("AICS description cb err (%d)", err);
return;
}
strncpy(g_aics_desc, description, sizeof(g_aics_desc) - 1);
g_aics_desc[sizeof(g_aics_desc) - 1] = '\0';
g_cb = true;
}
static struct bt_aics_cb aics_cb = {
.state = aics_state_cb,
.gain_setting = aics_gain_setting_cb,
.type = aics_input_type_cb,
.status = aics_status_cb,
.description = aics_description_cb
};
#endif /* CONFIG_BT_MICP_MIC_DEV_AICS */
static int test_aics_server_only(void)
{
int err;
int8_t expected_gain;
uint8_t expected_input_mute;
uint8_t expected_mode;
uint8_t expected_input_type;
bool expected_aics_active;
char expected_aics_desc[AICS_DESC_SIZE];
printk("Deactivating AICS\n");
expected_aics_active = false;
err = bt_aics_deactivate(micp_included.aics[0]);
if (err != 0) {
FAIL("Could not deactivate AICS (err %d)\n", err);
return err;
}
WAIT_FOR_COND(expected_aics_active == g_aics_active);
printk("AICS deactivated\n");
printk("Activating AICS\n");
expected_aics_active = true;
err = bt_aics_activate(micp_included.aics[0]);
if (err != 0) {
FAIL("Could not activate AICS (err %d)\n", err);
return err;
}
WAIT_FOR_COND(expected_aics_active == g_aics_active);
printk("AICS activated\n");
printk("Getting AICS state\n");
g_cb = false;
err = bt_aics_state_get(micp_included.aics[0]);
if (err != 0) {
FAIL("Could not get AICS state (err %d)\n", err);
return err;
}
WAIT_FOR_COND(g_cb);
printk("AICS state get\n");
printk("Getting AICS gain setting\n");
g_cb = false;
err = bt_aics_gain_setting_get(micp_included.aics[0]);
if (err != 0) {
FAIL("Could not get AICS gain setting (err %d)\n", err);
return err;
}
WAIT_FOR_COND(g_cb);
printk("AICS gain setting get\n");
printk("Getting AICS input type\n");
g_cb = false;
expected_input_type = BT_AICS_INPUT_TYPE_DIGITAL;
err = bt_aics_type_get(micp_included.aics[0]);
if (err != 0) {
FAIL("Could not get AICS input type (err %d)\n", err);
return err;
}
/* Expect and wait for input_type from init */
WAIT_FOR_COND(g_cb && expected_input_type == g_aics_input_type);
printk("AICS input type get\n");
printk("Getting AICS status\n");
g_cb = false;
err = bt_aics_status_get(micp_included.aics[0]);
if (err != 0) {
FAIL("Could not get AICS status (err %d)\n", err);
return err;
}
WAIT_FOR_COND(g_cb);
printk("AICS status get\n");
printk("Getting AICS description\n");
g_cb = false;
err = bt_aics_description_get(micp_included.aics[0]);
if (err != 0) {
FAIL("Could not get AICS description (err %d)\n", err);
return err;
}
WAIT_FOR_COND(g_cb);
printk("AICS description get\n");
printk("Setting AICS mute\n");
g_cb = false;
expected_input_mute = BT_AICS_STATE_MUTED;
err = bt_aics_mute(micp_included.aics[0]);
if (err != 0) {
FAIL("Could not set AICS mute (err %d)\n", err);
return err;
}
WAIT_FOR_COND(g_cb && expected_input_mute == g_aics_input_mute);
printk("AICS mute set\n");
printk("Setting AICS unmute\n");
g_cb = false;
expected_input_mute = BT_AICS_STATE_UNMUTED;
err = bt_aics_unmute(micp_included.aics[0]);
if (err != 0) {
FAIL("Could not set AICS unmute (err %d)\n", err);
return err;
}
WAIT_FOR_COND(g_cb && expected_input_mute == g_aics_input_mute);
printk("AICS unmute set\n");
printk("Setting AICS auto mode\n");
g_cb = false;
expected_mode = BT_AICS_MODE_AUTO;
err = bt_aics_automatic_gain_set(micp_included.aics[0]);
if (err != 0) {
FAIL("Could not set AICS auto mode (err %d)\n", err);
return err;
}
WAIT_FOR_COND(g_cb && expected_mode == g_aics_mode);
printk("AICS auto mode set\n");
printk("Setting AICS manual mode\n");
g_cb = false;
expected_mode = BT_AICS_MODE_MANUAL;
err = bt_aics_manual_gain_set(micp_included.aics[0]);
if (err != 0) {
FAIL("Could not set AICS manual mode (err %d)\n", err);
return err;
}
WAIT_FOR_COND(g_cb && expected_mode == g_aics_mode);
printk("AICS manual mode set\n");
printk("Setting AICS gain\n");
g_cb = false;
expected_gain = g_aics_gain_max - 1;
err = bt_aics_gain_set(micp_included.aics[0], expected_gain);
if (err != 0) {
FAIL("Could not set AICS gain (err %d)\n", err);
return err;
}
WAIT_FOR_COND(g_cb && expected_gain == g_aics_gain);
printk("AICS gain set\n");
printk("Setting AICS Description\n");
g_cb = false;
strncpy(expected_aics_desc, "New Input Description",
sizeof(expected_aics_desc));
expected_aics_desc[sizeof(expected_aics_desc) - 1] = '\0';
err = bt_aics_description_set(micp_included.aics[0], expected_aics_desc);
if (err != 0) {
FAIL("Could not set AICS Description (err %d)\n", err);
return err;
}
WAIT_FOR_COND(g_cb && !strncmp(expected_aics_desc, g_aics_desc,
sizeof(expected_aics_desc)));
printk("AICS Description set\n");
return 0;
}
static void test_mic_dev_only(void)
{
int err;
struct bt_micp_mic_dev_register_param micp_param;
uint8_t expected_mute;
err = bt_enable(NULL);
if (err != 0) {
FAIL("Bluetooth init failed (err %d)\n", err);
return;
}
printk("Bluetooth initialized\n");
(void)memset(&micp_param, 0, sizeof(micp_param));
#if defined(CONFIG_BT_MICP_MIC_DEV_AICS)
char input_desc[CONFIG_BT_MICP_MIC_DEV_AICS_INSTANCE_COUNT][16];
for (int i = 0; i < ARRAY_SIZE(micp_param.aics_param); i++) {
micp_param.aics_param[i].desc_writable = true;
snprintf(input_desc[i], sizeof(input_desc[i]), "Input %d", i + 1);
micp_param.aics_param[i].description = input_desc[i];
micp_param.aics_param[i].type = BT_AICS_INPUT_TYPE_DIGITAL;
micp_param.aics_param[i].status = g_aics_active;
micp_param.aics_param[i].gain_mode = BT_AICS_MODE_MANUAL;
micp_param.aics_param[i].units = 1;
micp_param.aics_param[i].min_gain = 0;
micp_param.aics_param[i].max_gain = 100;
micp_param.aics_param[i].cb = &aics_cb;
}
#endif /* CONFIG_BT_MICP_MIC_DEV_AICS */
micp_param.cb = &micp_cb;
err = bt_micp_mic_dev_register(&micp_param);
if (err != 0) {
FAIL("MICP init failed (err %d)\n", err);
return;
}
if (IS_ENABLED(CONFIG_BT_MICP_MIC_DEV_AICS)) {
err = bt_micp_mic_dev_included_get(&micp_included);
if (err != 0) {
FAIL("MICP get failed (err %d)\n", err);
return;
}
}
printk("MICP initialized\n");
printk("Getting MICP mute\n");
g_cb = false;
err = bt_micp_mic_dev_mute_get();
if (err != 0) {
FAIL("Could not get MICP mute (err %d)\n", err);
return;
}
WAIT_FOR_COND(g_cb);
printk("MICP mute get\n");
printk("Setting MICP mute\n");
expected_mute = BT_MICP_MUTE_MUTED;
err = bt_micp_mic_dev_mute();
if (err != 0) {
FAIL("MICP mute failed (err %d)\n", err);
return;
}
WAIT_FOR_COND(expected_mute == g_mute);
printk("MICP mute set\n");
printk("Setting MICP unmute\n");
expected_mute = BT_MICP_MUTE_UNMUTED;
err = bt_micp_mic_dev_unmute();
if (err != 0) {
FAIL("MICP unmute failed (err %d)\n", err);
return;
}
WAIT_FOR_COND(expected_mute == g_mute);
printk("MICP unmute set\n");
printk("Setting MICP disable\n");
expected_mute = BT_MICP_MUTE_DISABLED;
err = bt_micp_mic_dev_mute_disable();
if (err != 0) {
FAIL("MICP disable failed (err %d)\n", err);
return;
}
WAIT_FOR_COND(expected_mute == g_mute);
printk("MICP disable set\n");
if (CONFIG_BT_MICP_MIC_DEV_AICS_INSTANCE_COUNT > 0) {
if (test_aics_server_only()) {
return;
}
}
PASS("MICP mic_dev passed\n");
}
static void test_main(void)
{
int err;
struct bt_micp_mic_dev_register_param micp_param;
err = bt_enable(NULL);
if (err != 0) {
FAIL("Bluetooth init failed (err %d)\n", err);
return;
}
printk("Bluetooth initialized\n");
(void)memset(&micp_param, 0, sizeof(micp_param));
#if defined(CONFIG_BT_MICP_MIC_DEV_AICS)
char input_desc[CONFIG_BT_MICP_MIC_DEV_AICS_INSTANCE_COUNT][16];
for (int i = 0; i < ARRAY_SIZE(micp_param.aics_param); i++) {
micp_param.aics_param[i].desc_writable = true;
snprintf(input_desc[i], sizeof(input_desc[i]),
"Input %d", i + 1);
micp_param.aics_param[i].description = input_desc[i];
micp_param.aics_param[i].type = BT_AICS_INPUT_TYPE_UNSPECIFIED;
micp_param.aics_param[i].status = g_aics_active;
micp_param.aics_param[i].gain_mode = BT_AICS_MODE_MANUAL;
micp_param.aics_param[i].units = 1;
micp_param.aics_param[i].min_gain = 0;
micp_param.aics_param[i].max_gain = 100;
micp_param.aics_param[i].cb = &aics_cb;
}
#endif /* CONFIG_BT_MICP_MIC_DEV_AICS */
micp_param.cb = &micp_cb;
err = bt_micp_mic_dev_register(&micp_param);
if (err != 0) {
FAIL("MICP init failed (err %d)\n", err);
return;
}
if (IS_ENABLED(CONFIG_BT_MICP_MIC_DEV_AICS)) {
err = bt_micp_mic_dev_included_get(&micp_included);
if (err != 0) {
FAIL("MICP get failed (err %d)\n", err);
return;
}
}
printk("MICP initialized\n");
err = bt_le_adv_start(BT_LE_ADV_CONN_NAME, ad, AD_SIZE, NULL, 0);
if (err != 0) {
FAIL("Advertising failed to start (err %d)\n", err);
return;
}
printk("Advertising successfully started\n");
WAIT_FOR_FLAG(flag_connected);
PASS("MICP mic_dev passed\n");
}
static const struct bst_test_instance test_micp[] = {
{
.test_id = "micp_mic_dev_only",
.test_post_init_f = test_init,
.test_tick_f = test_tick,
.test_main_f = test_mic_dev_only
},
{
.test_id = "micp_mic_dev",
.test_post_init_f = test_init,
.test_tick_f = test_tick,
.test_main_f = test_main
},
BSTEST_END_MARKER
};
struct bst_test_list *test_micp_install(struct bst_test_list *tests)
{
return bst_add_tests(tests, test_micp);
}
#else
struct bst_test_list *test_micp_install(struct bst_test_list *tests)
{
return tests;
}
#endif /* CONFIG_BT_MICP_MIC_DEV */

View file

@ -0,0 +1,267 @@
/*
* Copyright (c) 2019 Bose Corporation
* Copyright (c) 2021 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifdef CONFIG_BT_TBS_CLIENT
#include <zephyr/bluetooth/audio/tbs.h>
#include "common.h"
extern enum bst_result_t bst_result;
static volatile bool bt_init;
static volatile bool discovery_complete;
static volatile bool is_gtbs_found;
static volatile bool read_complete;
static volatile bool call_placed;
static volatile uint8_t call_state;
static volatile uint8_t call_index;
static volatile uint8_t tbs_count;
CREATE_FLAG(ccid_read_flag);
static void tbs_client_call_states_cb(struct bt_conn *conn, int err,
uint8_t index, uint8_t call_count,
const struct bt_tbs_client_call_state *call_states)
{
if (index != 0) {
return;
}
printk("%s\n", __func__);
printk("Index %u\n", index);
if (err != 0) {
FAIL("Call could not read call states (%d)\n", err);
return;
}
call_index = call_states[0].index;
call_state = call_states[0].state;
printk("call index %u - state %u\n", call_index, call_state);
}
static void tbs_client_read_bearer_provider_name(struct bt_conn *conn, int err,
uint8_t index,
const char *value)
{
if (err != 0) {
FAIL("Call could not read bearer name (%d)\n", err);
return;
}
printk("Index %u\n", index);
printk("Bearer name pointer: %p\n", value);
printk("Bearer name: %s\n", value);
read_complete = true;
}
static void tbs_client_discover_cb(struct bt_conn *conn, int err,
uint8_t count, bool gtbs_found)
{
printk("%s\n", __func__);
if (err != 0) {
FAIL("TBS_CLIENT could not be discovered (%d)\n", err);
return;
}
tbs_count = count;
is_gtbs_found = true;
discovery_complete = true;
}
static void tbs_client_read_ccid_cb(struct bt_conn *conn, int err,
uint8_t inst_index, uint32_t value)
{
struct bt_tbs_instance *inst;
if (value > UINT8_MAX) {
FAIL("Invalid CCID: %u", value);
return;
}
printk("Read CCID %u on index %u\n", value, inst_index);
inst = bt_tbs_client_get_by_ccid(conn, (uint8_t)value);
if (inst == NULL) {
FAIL("Could not get instance by CCID: %u", value);
return;
}
SET_FLAG(ccid_read_flag);
}
static const struct bt_tbs_client_cb tbs_client_cbs = {
.discover = tbs_client_discover_cb,
.originate_call = NULL,
.terminate_call = NULL,
.hold_call = NULL,
.accept_call = NULL,
.retrieve_call = NULL,
.bearer_provider_name = tbs_client_read_bearer_provider_name,
.bearer_uci = NULL,
.technology = NULL,
.uri_list = NULL,
.signal_strength = NULL,
.signal_interval = NULL,
.current_calls = NULL,
.ccid = tbs_client_read_ccid_cb,
.status_flags = NULL,
.call_uri = NULL,
.call_state = tbs_client_call_states_cb,
.termination_reason = NULL
};
static void bt_ready(int err)
{
if (err != 0) {
FAIL("Bluetooth discover failed (err %d)\n", err);
return;
}
bt_init = true;
}
static void test_ccid(void)
{
if (is_gtbs_found) {
int err;
UNSET_FLAG(ccid_read_flag);
printk("Reading GTBS CCID\n");
err = bt_tbs_client_read_ccid(default_conn, BT_TBS_GTBS_INDEX);
if (err != 0) {
FAIL("Read GTBS CCID failed (%d)\n", err);
return;
}
WAIT_FOR_FLAG(ccid_read_flag);
}
for (uint8_t i = 0; i < tbs_count; i++) {
int err;
UNSET_FLAG(ccid_read_flag);
printk("Reading bearer CCID on index %u\n", i);
err = bt_tbs_client_read_ccid(default_conn, i);
if (err != 0) {
FAIL("Read bearer CCID failed (%d)\n", err);
return;
}
WAIT_FOR_FLAG(ccid_read_flag);
}
}
static void test_main(void)
{
int err;
int index = 0;
int tbs_client_err;
err = bt_enable(bt_ready);
if (err != 0) {
FAIL("Bluetooth discover failed (err %d)\n", err);
return;
}
bt_tbs_client_register_cb(&tbs_client_cbs);
WAIT_FOR_COND(bt_init);
printk("Audio Server: Bluetooth discovered\n");
err = bt_le_adv_start(BT_LE_ADV_CONN_NAME, ad, AD_SIZE, NULL, 0);
if (err != 0) {
FAIL("Advertising failed to start (err %d)\n", err);
return;
}
printk("Advertising successfully started\n");
WAIT_FOR_COND(flag_connected);
tbs_client_err = bt_tbs_client_discover(default_conn, true);
if (tbs_client_err) {
FAIL("Failed to discover TBS_CLIENT for connection %d", tbs_client_err);
}
WAIT_FOR_COND(discovery_complete);
printk("GTBS %sfound\n", is_gtbs_found ? "" : "not ");
printk("Placing call\n");
err = bt_tbs_client_originate_call(default_conn, 0, "tel:123456789012");
if (err != 0) {
FAIL("Originate call failed (%d)\n", err);
}
/* Call transitions:
* 1) Dialing
* 2) Alerting
* 3) Active
* 4) Remotely Held
*/
printk("Waiting for remotely held\n");
WAIT_FOR_COND(call_state == BT_TBS_CALL_STATE_REMOTELY_HELD);
printk("Holding call\n");
err = bt_tbs_client_hold_call(default_conn, index, call_index);
if (err != 0) {
FAIL("Hold call failed (%d)\n", err);
}
/* Call transitions:
* 1) Locally and remotely held
* 2) Locally held
*/
WAIT_FOR_COND(call_state == BT_TBS_CALL_STATE_LOCALLY_HELD);
printk("Retrieving call\n");
err = bt_tbs_client_retrieve_call(default_conn, index, call_index);
if (err != 0) {
FAIL("Retrieve call failed (%d)\n", err);
}
WAIT_FOR_COND(call_state == BT_TBS_CALL_STATE_ACTIVE);
printk("Reading bearer provider name\n");
err = bt_tbs_client_read_bearer_provider_name(default_conn, index);
if (err != 0) {
FAIL("Read bearer provider name failed (%d)\n", err);
}
test_ccid();
WAIT_FOR_COND(read_complete);
PASS("TBS_CLIENT Passed\n");
}
static const struct bst_test_instance test_tbs_client[] = {
{
.test_id = "tbs_client",
.test_post_init_f = test_init,
.test_tick_f = test_tick,
.test_main_f = test_main
},
BSTEST_END_MARKER
};
struct bst_test_list *test_tbs_client_install(struct bst_test_list *tests)
{
return bst_add_tests(tests, test_tbs_client);
}
#else
struct bst_test_list *test_tbs_client_install(struct bst_test_list *tests)
{
return tests;
}
#endif /* CONFIG_BT_TBS_CLIENT */

View file

@ -0,0 +1,120 @@
/*
* Copyright (c) 2019 Bose Corporation
* Copyright (c) 2021 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifdef CONFIG_BT_TBS
#include <zephyr/bluetooth/audio/tbs.h>
#include "common.h"
extern enum bst_result_t bst_result;
static volatile bool call_placed;
static volatile bool call_held;
static volatile bool call_id;
static void tbs_hold_call_cb(struct bt_conn *conn, uint8_t call_index)
{
if (call_index == call_id) {
call_held = true;
}
}
static bool tbs_originate_call_cb(struct bt_conn *conn, uint8_t call_index,
const char *caller_id)
{
printk("Placing call to remote with id %u to %s\n",
call_index, caller_id);
call_id = call_index;
call_placed = true;
return true;
}
static bool tbs_authorize_cb(struct bt_conn *conn)
{
return conn == default_conn;
}
static struct bt_tbs_cb tbs_cbs = {
.originate_call = tbs_originate_call_cb,
.terminate_call = NULL,
.hold_call = tbs_hold_call_cb,
.accept_call = NULL,
.retrieve_call = NULL,
.join_calls = NULL,
.authorize = tbs_authorize_cb,
};
static void test_main(void)
{
int err;
err = bt_enable(NULL);
if (err != 0) {
printk("Bluetooth init failed (err %d)\n", err);
return;
}
printk("Audio Client: Bluetooth initialized\n");
bt_tbs_register_cb(&tbs_cbs);
err = bt_le_scan_start(BT_LE_SCAN_PASSIVE, device_found);
if (err != 0) {
FAIL("Scanning failed to start (err %d)\n", err);
return;
}
printk("Scanning successfully started\n");
WAIT_FOR_FLAG(flag_connected);
WAIT_FOR_COND(call_placed);
err = bt_tbs_remote_answer(call_id);
if (err != BT_TBS_RESULT_CODE_SUCCESS) {
FAIL("Remote could not answer call: %d\n", err);
return;
}
printk("Remote answered %u\n", call_id);
err = bt_tbs_remote_hold(call_id);
if (err != BT_TBS_RESULT_CODE_SUCCESS) {
FAIL("Remote could not hold call: %d\n", err);
}
printk("Remote held %u\n", call_id);
WAIT_FOR_COND(call_held);
err = bt_tbs_remote_retrieve(call_id);
if (err != BT_TBS_RESULT_CODE_SUCCESS) {
FAIL("Remote could not answer call: %d\n", err);
return;
}
printk("Remote retrieved %u\n", call_id);
PASS("TBS passed\n");
}
static const struct bst_test_instance test_tbs[] = {
{
.test_id = "tbs",
.test_post_init_f = test_init,
.test_tick_f = test_tick,
.test_main_f = test_main
},
BSTEST_END_MARKER
};
struct bst_test_list *test_tbs_install(struct bst_test_list *tests)
{
return bst_add_tests(tests, test_tbs);
}
#else
struct bst_test_list *test_tbs_install(struct bst_test_list *tests)
{
return tests;
}
#endif /* CONFIG_BT_TBS */

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,11 @@
#!/usr/bin/env bash
#
# Copyright (c) 2022 Nordic Semiconductor ASA
#
# SPDX-License-Identifier: Apache-2.0
dir_path=$(dirname "$0")
$dir_path/cap_unicast.sh
$dir_path/cap_broadcast.sh

View file

@ -0,0 +1,48 @@
#!/usr/bin/env bash
#
# Copyright (c) 2020-2022 Nordic Semiconductor ASA
#
# SPDX-License-Identifier: Apache-2.0
SIMULATION_ID="bass"
VERBOSITY_LEVEL=2
PROCESS_IDS=""; EXIT_CODE=0
function Execute(){
if [ ! -f $1 ]; then
echo -e " \e[91m`pwd`/`basename $1` cannot be found (did you forget to\
compile it?)\e[39m"
exit 1
fi
timeout 20 $@ & PROCESS_IDS="$PROCESS_IDS $!"
}
: "${BSIM_OUT_PATH:?BSIM_OUT_PATH must be defined}"
#Give a default value to BOARD if it does not have one yet:
BOARD="${BOARD:-nrf52_bsim}"
cd ${BSIM_OUT_PATH}/bin
printf "\n\n======== Running BASS and BASS client test =========\n\n"
Execute ./bs_${BOARD}_tests_bsim_bluetooth_audio_prj_conf \
-v=${VERBOSITY_LEVEL} -s=${SIMULATION_ID} -d=0 -testid=bap_scan_delegator \
-rs=24
Execute ./bs_${BOARD}_tests_bsim_bluetooth_audio_prj_conf \
-v=${VERBOSITY_LEVEL} -s=${SIMULATION_ID} -d=1 \
-testid=bap_broadcast_assistant -rs=46
Execute ./bs_${BOARD}_tests_bsim_bluetooth_audio_prj_conf \
-v=${VERBOSITY_LEVEL} -s=${SIMULATION_ID} -d=2 -testid=bass_broadcaster -rs=69
# Simulation time should be larger than the WAIT_TIME in common.h
Execute ./bs_2G4_phy_v1 -v=${VERBOSITY_LEVEL} -s=${SIMULATION_ID} -D=3 \
-sim_length=60e6 $@
for PROCESS_ID in $PROCESS_IDS; do
wait $PROCESS_ID || let "EXIT_CODE=$?"
done
exit $EXIT_CODE #the last exit code != 0

View file

@ -0,0 +1,65 @@
#!/usr/bin/env bash
#
# Copyright (c) 2021 Nordic Semiconductor ASA
#
# SPDX-License-Identifier: Apache-2.0
VERBOSITY_LEVEL=2
PROCESS_IDS=""; EXIT_CODE=0
function Execute(){
if [ ! -f $1 ]; then
echo -e " \e[91m`pwd`/`basename $1` cannot be found (did you forget to\
compile it?)\e[39m"
exit 1
fi
timeout 20 $@ & PROCESS_IDS="$PROCESS_IDS $!"
}
: "${BSIM_OUT_PATH:?BSIM_OUT_PATH must be defined}"
#Give a default value to BOARD if it does not have one yet:
BOARD="${BOARD:-nrf52_bsim}"
cd ${BSIM_OUT_PATH}/bin
printf "\n\n======== Broadcaster test =========\n\n"
SIMULATION_ID="broadcaster"
Execute ./bs_${BOARD}_tests_bsim_bluetooth_audio_prj_conf \
-v=${VERBOSITY_LEVEL} -s=${SIMULATION_ID} -d=0 -testid=broadcast_source -rs=23
Execute ./bs_${BOARD}_tests_bsim_bluetooth_audio_prj_conf \
-v=${VERBOSITY_LEVEL} -s=${SIMULATION_ID} -d=1 -testid=broadcast_sink -rs=27
# Simulation time should be larger than the WAIT_TIME in common.h
Execute ./bs_2G4_phy_v1 -v=${VERBOSITY_LEVEL} -s=${SIMULATION_ID} \
-D=2 -sim_length=60e6 $@
for PROCESS_ID in $PROCESS_IDS; do
wait $PROCESS_ID || let "EXIT_CODE=$?"
done
printf "\n\n======== Broadcaster sink disconnect test =========\n\n"
SIMULATION_ID="broadcaster_sink_disconnect"
Execute ./bs_${BOARD}_tests_bsim_bluetooth_audio_prj_conf \
-v=${VERBOSITY_LEVEL} -s=${SIMULATION_ID} -d=0 -testid=broadcast_source -rs=23
Execute ./bs_${BOARD}_tests_bsim_bluetooth_audio_prj_conf \
-v=${VERBOSITY_LEVEL} -s=${SIMULATION_ID} -d=1 \
-testid=broadcast_sink_disconnect -rs=27
# Simulation time should be larger than the WAIT_TIME in common.h
Execute ./bs_2G4_phy_v1 -v=${VERBOSITY_LEVEL} -s=${SIMULATION_ID} \
-D=2 -sim_length=60e6 $@
for PROCESS_ID in $PROCESS_IDS; do
wait $PROCESS_ID || let "EXIT_CODE=$?"
done
exit $EXIT_CODE #the last exit code != 0

View file

@ -0,0 +1,44 @@
#!/usr/bin/env bash
#
# Copyright (c) 2021-2022 Nordic Semiconductor ASA
#
# SPDX-License-Identifier: Apache-2.0
SIMULATION_ID="unicast_audio"
VERBOSITY_LEVEL=2
PROCESS_IDS=""; EXIT_CODE=0
function Execute(){
if [ ! -f $1 ]; then
echo -e " \e[91m`pwd`/`basename $1` cannot be found (did you forget to\
compile it?)\e[39m"
exit 1
fi
timeout 20 $@ & PROCESS_IDS="$PROCESS_IDS $!"
}
: "${BSIM_OUT_PATH:?BSIM_OUT_PATH must be defined}"
#Give a default value to BOARD if it does not have one yet:
BOARD="${BOARD:-nrf52_bsim}"
cd ${BSIM_OUT_PATH}/bin
printf "\n\n======== Unicast Audio test =========\n\n"
Execute ./bs_${BOARD}_tests_bsim_bluetooth_audio_prj_conf \
-v=${VERBOSITY_LEVEL} -s=${SIMULATION_ID} -d=0 -testid=unicast_client -rs=23
Execute ./bs_${BOARD}_tests_bsim_bluetooth_audio_prj_conf \
-v=${VERBOSITY_LEVEL} -s=${SIMULATION_ID} -d=1 -testid=unicast_server -rs=27
# Simulation time should be larger than the WAIT_TIME in common.h
Execute ./bs_2G4_phy_v1 -v=${VERBOSITY_LEVEL} -s=${SIMULATION_ID} \
-D=2 -sim_length=60e6 $@
for PROCESS_ID in $PROCESS_IDS; do
wait $PROCESS_ID || let "EXIT_CODE=$?"
done
exit $EXIT_CODE #the last exit code != 0

View file

@ -0,0 +1,43 @@
#!/usr/bin/env bash
#
# Copyright (c) 2022 Nordic Semiconductor ASA
#
# SPDX-License-Identifier: Apache-2.0
SIMULATION_ID="cap_broadcast"
VERBOSITY_LEVEL=2
PROCESS_IDS=""; EXIT_CODE=0
function Execute(){
if [ ! -f $1 ]; then
echo -e " \e[91m`pwd`/`basename $1` cannot be found (did you forget to\
compile it?)\e[39m"
exit 1
fi
timeout 20 $@ & PROCESS_IDS="$PROCESS_IDS $!"
}
: "${BSIM_OUT_PATH:?BSIM_OUT_PATH must be defined}"
#Give a default value to BOARD if it does not have one yet:
BOARD="${BOARD:-nrf52_bsim}"
cd ${BSIM_OUT_PATH}/bin
printf "\n\n======== Running CAP broadcast test =========\n\n"
Execute ./bs_${BOARD}_tests_bsim_bluetooth_audio_prj_conf \
-v=${VERBOSITY_LEVEL} -s=${SIMULATION_ID} -d=0 -testid=cap_acceptor_broadcast -rs=23
Execute ./bs_${BOARD}_tests_bsim_bluetooth_audio_prj_conf \
-v=${VERBOSITY_LEVEL} -s=${SIMULATION_ID} -d=1 -testid=cap_initiator_broadcast -rs=46
# Simulation time should be larger than the WAIT_TIME in common.h
Execute ./bs_2G4_phy_v1 -v=${VERBOSITY_LEVEL} -s=${SIMULATION_ID} \
-D=2 -sim_length=60e6 $@
for PROCESS_ID in $PROCESS_IDS; do
wait $PROCESS_ID || let "EXIT_CODE=$?"
done
exit $EXIT_CODE #the last exit code != 0

View file

@ -0,0 +1,43 @@
#!/usr/bin/env bash
#
# Copyright (c) 2022 Nordic Semiconductor ASA
#
# SPDX-License-Identifier: Apache-2.0
SIMULATION_ID="cap_unicast"
VERBOSITY_LEVEL=2
PROCESS_IDS=""; EXIT_CODE=0
function Execute(){
if [ ! -f $1 ]; then
echo -e " \e[91m`pwd`/`basename $1` cannot be found (did you forget to\
compile it?)\e[39m"
exit 1
fi
timeout 20 $@ & PROCESS_IDS="$PROCESS_IDS $!"
}
: "${BSIM_OUT_PATH:?BSIM_OUT_PATH must be defined}"
#Give a default value to BOARD if it does not have one yet:
BOARD="${BOARD:-nrf52_bsim}"
cd ${BSIM_OUT_PATH}/bin
printf "\n\n======== Running CAP unicast test =========\n\n"
Execute ./bs_${BOARD}_tests_bsim_bluetooth_audio_prj_conf \
-v=${VERBOSITY_LEVEL} -s=${SIMULATION_ID} -d=0 -testid=cap_acceptor_unicast -rs=23
Execute ./bs_${BOARD}_tests_bsim_bluetooth_audio_prj_conf \
-v=${VERBOSITY_LEVEL} -s=${SIMULATION_ID} -d=1 -testid=cap_initiator_unicast -rs=46
# Simulation time should be larger than the WAIT_TIME in common.h
Execute ./bs_2G4_phy_v1 -v=${VERBOSITY_LEVEL} -s=${SIMULATION_ID} \
-D=2 -sim_length=60e6 $@
for PROCESS_ID in $PROCESS_IDS; do
wait $PROCESS_ID || let "EXIT_CODE=$?"
done
exit $EXIT_CODE #the last exit code != 0

View file

@ -0,0 +1,117 @@
#!/usr/bin/env bash
#
# Copyright (c) 2020-2022 Nordic Semiconductor ASA
#
# SPDX-License-Identifier: Apache-2.0
# Basic CSIP test. A set coordinator connects to multiple set members
# lock thems, unlocks them and disconnects.
VERBOSITY_LEVEL=2
PROCESS_IDS=""; EXIT_CODE=0
function Execute(){
if [ ! -f $1 ]; then
echo -e " \e[91m`pwd`/`basename $1` cannot be found (did you forget to\
compile it?)\e[39m"
exit 1
fi
timeout 20 $@ & PROCESS_IDS="$PROCESS_IDS $!"
}
: "${BSIM_OUT_PATH:?BSIM_OUT_PATH must be defined}"
#Give a default value to BOARD if it does not have one yet:
BOARD="${BOARD:-nrf52_bsim}"
cd ${BSIM_OUT_PATH}/bin
# NORMAL TEST
printf "\n\n======== Running normal test ========\n\n"
SIMULATION_ID="csip"
Execute ./bs_${BOARD}_tests_bsim_bluetooth_audio_prj_conf \
-v=${VERBOSITY_LEVEL} -s=${SIMULATION_ID} -d=0 -testid=csip_set_coordinator \
-RealEncryption=1 -rs=1
Execute ./bs_${BOARD}_tests_bsim_bluetooth_audio_prj_conf \
-v=${VERBOSITY_LEVEL} -s=${SIMULATION_ID} -d=1 -testid=csip_set_member \
-RealEncryption=1 -rs=2 -argstest rank 1
Execute ./bs_${BOARD}_tests_bsim_bluetooth_audio_prj_conf \
-v=${VERBOSITY_LEVEL} -s=${SIMULATION_ID} -d=2 -testid=csip_set_member \
-RealEncryption=1 -rs=3 -argstest rank 2
Execute ./bs_${BOARD}_tests_bsim_bluetooth_audio_prj_conf \
-v=${VERBOSITY_LEVEL} -s=${SIMULATION_ID} -d=3 -testid=csip_set_member \
-RealEncryption=1 -rs=4 -argstest rank 3
# Simulation time should be larger than the WAIT_TIME in common.h
Execute ./bs_2G4_phy_v1 -v=${VERBOSITY_LEVEL} -s=${SIMULATION_ID} \
-D=4 -sim_length=60e6 $@
for PROCESS_ID in $PROCESS_IDS; do
wait $PROCESS_ID || let "EXIT_CODE=$?"
done
PROCESS_IDS="";
# TEST WITH FORCE RELEASE
SIMULATION_ID="csip_forced_release"
printf "\n\n======== Running test with forced release of lock ========\n\n"
Execute ./bs_${BOARD}_tests_bsim_bluetooth_audio_prj_conf \
-v=${VERBOSITY_LEVEL} -s=${SIMULATION_ID} -d=0 -testid=csip_set_coordinator \
-RealEncryption=1 -rs=1
Execute ./bs_${BOARD}_tests_bsim_bluetooth_audio_prj_conf \
-v=${VERBOSITY_LEVEL} -s=${SIMULATION_ID} -d=1 -testid=csip_set_member \
-RealEncryption=1 -rs=2 -argstest rank 1
Execute ./bs_${BOARD}_tests_bsim_bluetooth_audio_prj_conf \
-v=${VERBOSITY_LEVEL} -s=${SIMULATION_ID} -d=2 -testid=csip_set_member \
-RealEncryption=1 -rs=3 -argstest rank 2
Execute ./bs_${BOARD}_tests_bsim_bluetooth_audio_prj_conf \
-v=${VERBOSITY_LEVEL} -s=${SIMULATION_ID} -d=3 -testid=csip_set_member_release \
-RealEncryption=1 -rs=4 -argstest rank 3
# Simulation time should be larger than the WAIT_TIME in common.h
Execute ./bs_2G4_phy_v1 -v=${VERBOSITY_LEVEL} -s=${SIMULATION_ID} \
-D=4 -sim_length=60e6 $@
for PROCESS_ID in $PROCESS_IDS; do
wait $PROCESS_ID || let "EXIT_CODE=$?"
done
# TEST WITH SIRK ENC
SIMULATION_ID="csip_sirk_encrypted"
printf "\n\n======== Running test with SIRK encrypted ========\n\n"
Execute ./bs_${BOARD}_tests_bsim_bluetooth_audio_prj_conf \
-v=${VERBOSITY_LEVEL} -s=${SIMULATION_ID} -d=0 -testid=csip_set_coordinator \
-RealEncryption=1 -rs=1
Execute ./bs_${BOARD}_tests_bsim_bluetooth_audio_prj_conf \
-v=${VERBOSITY_LEVEL} -s=${SIMULATION_ID} -d=1 -testid=csip_set_member_enc \
-RealEncryption=1 -rs=2 -argstest rank 1
Execute ./bs_${BOARD}_tests_bsim_bluetooth_audio_prj_conf \
-v=${VERBOSITY_LEVEL} -s=${SIMULATION_ID} -d=2 -testid=csip_set_member_enc \
-RealEncryption=1 -rs=3 -argstest rank 2
Execute ./bs_${BOARD}_tests_bsim_bluetooth_audio_prj_conf \
-v=${VERBOSITY_LEVEL} -s=${SIMULATION_ID} -d=3 -testid=csip_set_member_enc \
-RealEncryption=1 -rs=4 -argstest rank 3
# Simulation time should be larger than the WAIT_TIME in common.h
Execute ./bs_2G4_phy_v1 -v=${VERBOSITY_LEVEL} -s=${SIMULATION_ID} \
-D=4 -sim_length=60e6 $@
for PROCESS_ID in $PROCESS_IDS; do
wait $PROCESS_ID || let "EXIT_CODE=$?"
done
exit $EXIT_CODE #the last exit code != 0

View file

@ -0,0 +1,42 @@
#!/usr/bin/env bash
#
# Copyright (c) 2022 Codecoup
#
# SPDX-License-Identifier: Apache-2.0
SIMULATION_ID="has"
VERBOSITY_LEVEL=2
PROCESS_IDS=""; EXIT_CODE=0
function Execute(){
if [ ! -f $1 ]; then
echo -e " \e[91m`pwd`/`basename $1` cannot be found (did you forget to\
compile it?)\e[39m"
exit 1
fi
timeout 20 $@ & PROCESS_IDS="$PROCESS_IDS $!"
}
: "${BSIM_OUT_PATH:?BSIM_OUT_PATH must be defined}"
#Give a default value to BOARD if it does not have one yet:
BOARD="${BOARD:-nrf52_bsim}"
cd ${BSIM_OUT_PATH}/bin
printf "\n\n======== Running HAS main (API) test =========\n\n"
Execute ./bs_${BOARD}_tests_bsim_bluetooth_audio_prj_conf \
-v=${VERBOSITY_LEVEL} -s=${SIMULATION_ID} -d=0 -testid=has -rs=24
Execute ./bs_${BOARD}_tests_bsim_bluetooth_audio_prj_conf \
-v=${VERBOSITY_LEVEL} -s=${SIMULATION_ID} -d=1 -testid=has_client -rs=46
Execute ./bs_2G4_phy_v1 -v=${VERBOSITY_LEVEL} -s=${SIMULATION_ID} \
-D=2 -sim_length=60e6 $@
for PROCESS_ID in $PROCESS_IDS; do
wait $PROCESS_ID || let "EXIT_CODE=$?"
done
exit $EXIT_CODE #the last exit code != 0

View file

@ -0,0 +1,43 @@
#!/usr/bin/env bash
#
# Copyright (c) 2022 Codecoup
#
# SPDX-License-Identifier: Apache-2.0
SIMULATION_ID="ias"
VERBOSITY_LEVEL=2
PROCESS_IDS=""; EXIT_CODE=0
function Execute(){
if [ ! -f $1 ]; then
echo -e " \e[91m`pwd`/`basename $1` cannot be found (did you forget to\
compile it?)\e[39m"
exit 1
fi
timeout 20 $@ & PROCESS_IDS="$PROCESS_IDS $!"
}
: "${BSIM_OUT_PATH:?BSIM_OUT_PATH must be defined}"
#Give a default value to BOARD if it does not have one yet:
BOARD="${BOARD:-nrf52_bsim}"
cd ${BSIM_OUT_PATH}/bin
printf "\n\n======== Running IAS main (API) test =========\n\n"
Execute ./bs_${BOARD}_tests_bsim_bluetooth_audio_prj_conf \
-v=${VERBOSITY_LEVEL} -s=${SIMULATION_ID} -d=0 -testid=ias -rs=23
Execute ./bs_${BOARD}_tests_bsim_bluetooth_audio_prj_conf \
-v=${VERBOSITY_LEVEL} -s=${SIMULATION_ID} -d=1 -testid=ias_client -rs=6
# Simulation time should be larger than the WAIT_TIME in common.h
Execute ./bs_2G4_phy_v1 -v=${VERBOSITY_LEVEL} -s=${SIMULATION_ID} \
-D=2 -sim_length=60e6 $@
for PROCESS_ID in $PROCESS_IDS; do
wait $PROCESS_ID || let "EXIT_CODE=$?"
done
exit $EXIT_CODE #the last exit code != 0

View file

@ -0,0 +1,43 @@
#!/usr/bin/env bash
#
# Copyright (c) 2020 Nordic Semiconductor ASA
#
# SPDX-License-Identifier: Apache-2.0
SIMULATION_ID="mcs_mcc"
VERBOSITY_LEVEL=2
PROCESS_IDS=""; EXIT_CODE=0
function Execute(){
if [ ! -f $1 ]; then
echo -e " \e[91m`pwd`/`basename $1` cannot be found (did you forget to\
compile it?)\e[39m"
exit 1
fi
timeout 20 $@ & PROCESS_IDS="$PROCESS_IDS $!"
}
: "${BSIM_OUT_PATH:?BSIM_OUT_PATH must be defined}"
#Give a default value to BOARD if it does not have one yet:
BOARD="${BOARD:-nrf52_bsim}"
cd ${BSIM_OUT_PATH}/bin
printf "\n\n======== Running MCS and MCC test =========\n\n"
Execute ./bs_${BOARD}_tests_bsim_bluetooth_audio_prj_conf \
-v=${VERBOSITY_LEVEL} -s=${SIMULATION_ID} -d=0 -testid=mcc -rs=46
Execute ./bs_${BOARD}_tests_bsim_bluetooth_audio_prj_conf \
-v=${VERBOSITY_LEVEL} -s=${SIMULATION_ID} -d=1 -testid=mcs -rs=23
# Simulation time should be larger than the WAIT_TIME in common.h
Execute ./bs_2G4_phy_v1 -v=${VERBOSITY_LEVEL} -s=${SIMULATION_ID} \
-D=2 -sim_length=60e6 $@
for PROCESS_ID in $PROCESS_IDS; do
wait $PROCESS_ID || let "EXIT_CODE=$?"
done
exit $EXIT_CODE #the last exit code != 0

View file

@ -0,0 +1,56 @@
#!/usr/bin/env bash
#
# Copyright (c) 2021 Nordic Semiconductor ASA
#
# SPDX-License-Identifier: Apache-2.0
SIMULATION_ID="media_controller"
VERBOSITY_LEVEL=2
PROCESS_IDS=""; EXIT_CODE=0
function Execute(){
if [ ! -f $1 ]; then
echo -e " \e[91m`pwd`/`basename $1` cannot be found (did you forget to\
compile it?)\e[39m"
exit 1
fi
timeout 20 $@ & PROCESS_IDS="$PROCESS_IDS $!"
}
: "${BSIM_OUT_PATH:?BSIM_OUT_PATH must be defined}"
#Give a default value to BOARD if it does not have one yet:
BOARD="${BOARD:-nrf52_bsim}"
cd ${BSIM_OUT_PATH}/bin
printf "\n\n======== Running media controller local_player test =========\n\n"
Execute ./bs_${BOARD}_tests_bsim_bluetooth_audio_prj_conf \
-v=${VERBOSITY_LEVEL} -s=${SIMULATION_ID} -d=0 -testid=media_controller_local_player -rs=23
# Simulation time should be larger than the WAIT_TIME in common.h
Execute ./bs_2G4_phy_v1 -v=${VERBOSITY_LEVEL} -s=${SIMULATION_ID} \
-D=1 -sim_length=60e6 $@
for PROCESS_ID in $PROCESS_IDS; do
wait $PROCESS_ID || let "EXIT_CODE=$?"
done
printf "\n\n======== Running media controller remote_player test =========\n\n"
Execute ./bs_${BOARD}_tests_bsim_bluetooth_audio_prj_conf \
-v=${VERBOSITY_LEVEL} -s=${SIMULATION_ID} -d=0 -testid=media_controller_remote_player -rs=46
Execute ./bs_${BOARD}_tests_bsim_bluetooth_audio_prj_conf \
-v=${VERBOSITY_LEVEL} -s=${SIMULATION_ID} -d=1 -testid=media_controller_server -rs=23
# Simulation time should be larger than the WAIT_TIME in common.h
Execute ./bs_2G4_phy_v1 -v=${VERBOSITY_LEVEL} -s=${SIMULATION_ID} \
-D=2 -sim_length=60e6 $@
for PROCESS_ID in $PROCESS_IDS; do
wait $PROCESS_ID || let "EXIT_CODE=$?"
done
exit $EXIT_CODE #the last exit code != 0

View file

@ -0,0 +1,56 @@
#!/usr/bin/env bash
#
# Copyright (c) 2020-2022 Nordic Semiconductor ASA
#
# SPDX-License-Identifier: Apache-2.0
SIMULATION_ID="micp"
VERBOSITY_LEVEL=2
PROCESS_IDS=""; EXIT_CODE=0
function Execute(){
if [ ! -f $1 ]; then
echo -e " \e[91m`pwd`/`basename $1` cannot be found (did you forget to\
compile it?)\e[39m"
exit 1
fi
timeout 20 $@ & PROCESS_IDS="$PROCESS_IDS $!"
}
: "${BSIM_OUT_PATH:?BSIM_OUT_PATH must be defined}"
#Give a default value to BOARD if it does not have one yet:
BOARD="${BOARD:-nrf52_bsim}"
cd ${BSIM_OUT_PATH}/bin
printf "\n\n==== Running MICP Microphone Device Only (API) test ====n\n"
Execute ./bs_${BOARD}_tests_bsim_bluetooth_audio_prj_conf \
-v=${VERBOSITY_LEVEL} -s=${SIMULATION_ID} -d=0 -testid=micp_mic_dev_only -rs=23
# Simulation time should be larger than the WAIT_TIME in common.h
Execute ./bs_2G4_phy_v1 -v=${VERBOSITY_LEVEL} -s=${SIMULATION_ID} \
-D=1 -sim_length=60e6 $@
for PROCESS_ID in $PROCESS_IDS; do
wait $PROCESS_ID || let "EXIT_CODE=$?"
done
printf "\n\n==== Running MICP Microphone Device and MICP Microphone Controller test ====n\n"
Execute ./bs_${BOARD}_tests_bsim_bluetooth_audio_prj_conf \
-v=${VERBOSITY_LEVEL} -s=${SIMULATION_ID} -d=0 -testid=micp_mic_dev -rs=23
Execute ./bs_${BOARD}_tests_bsim_bluetooth_audio_prj_conf \
-v=${VERBOSITY_LEVEL} -s=${SIMULATION_ID} -d=1 -testid=micp_mic_ctlr -rs=46
# Simulation time should be larger than the WAIT_TIME in common.h
Execute ./bs_2G4_phy_v1 -v=${VERBOSITY_LEVEL} -s=${SIMULATION_ID} \
-D=2 -sim_length=60e6 $@
for PROCESS_ID in $PROCESS_IDS; do
wait $PROCESS_ID || let "EXIT_CODE=$?"
done
exit $EXIT_CODE #the last exit code != 0

View file

@ -0,0 +1,41 @@
#!/usr/bin/env bash
#
# Copyright (c) 2019 Bose Corporation
# Copyright (c) 2021 Nordic Semiconductor ASA
#
# SPDX-License-Identifier: Apache-2.0
SIMULATION_ID="tbs_ccp"
VERBOSITY_LEVEL=2
PROCESS_IDS=""; EXIT_CODE=0
function Execute(){
if [ ! -f $1 ]; then
echo -e " \e[91m`pwd`/`basename $1` cannot be found (did you forget to\
compile it?)\e[39m"
exit 1
fi
timeout 20 $@ & PROCESS_IDS="$PROCESS_IDS $!"
}
: "${BSIM_OUT_PATH:?BSIM_OUT_PATH must be defined}"
#Give a default value to BOARD if it does not have one yet:
BOARD="${BOARD:-nrf52_bsim}"
cd ${BSIM_OUT_PATH}/bin
Execute ./bs_${BOARD}_tests_bsim_bluetooth_audio_prj_conf \
-v=${VERBOSITY_LEVEL} -s=${SIMULATION_ID} -d=0 -testid=tbs -rs=23
Execute ./bs_${BOARD}_tests_bsim_bluetooth_audio_prj_conf \
-v=${VERBOSITY_LEVEL} -s=${SIMULATION_ID} -d=1 -testid=tbs_client -rs=6
# Simulation time should be larger than the WAIT_TIME in common.h
Execute ./bs_2G4_phy_v1 -v=${VERBOSITY_LEVEL} -s=${SIMULATION_ID} \
-D=2 -sim_length=60e6 $@
for PROCESS_ID in $PROCESS_IDS; do
wait $PROCESS_ID || let "EXIT_CODE=$?"
done
exit $EXIT_CODE #the last exit code != 0

View file

@ -0,0 +1,56 @@
#!/usr/bin/env bash
#
# Copyright (c) 2020-2021 Nordic Semiconductor ASA
#
# SPDX-License-Identifier: Apache-2.0
SIMULATION_ID="vcp"
VERBOSITY_LEVEL=2
PROCESS_IDS=""; EXIT_CODE=0
function Execute(){
if [ ! -f $1 ]; then
echo -e " \e[91m`pwd`/`basename $1` cannot be found (did you forget to\
compile it?)\e[39m"
exit 1
fi
timeout 20 $@ & PROCESS_IDS="$PROCESS_IDS $!"
}
: "${BSIM_OUT_PATH:?BSIM_OUT_PATH must be defined}"
#Give a default value to BOARD if it does not have one yet:
BOARD="${BOARD:-nrf52_bsim}"
cd ${BSIM_OUT_PATH}/bin
printf "\n\n======== Running VCP Volume Renderer standalone (API) test =========\n\n"
Execute ./bs_${BOARD}_tests_bsim_bluetooth_audio_prj_conf \
-v=${VERBOSITY_LEVEL} -s=${SIMULATION_ID} -d=0 -testid=vcp_vol_rend_standalone -rs=23
# Simulation time should be larger than the WAIT_TIME in common.h
Execute ./bs_2G4_phy_v1 -v=${VERBOSITY_LEVEL} -s=${SIMULATION_ID} \
-D=1 -sim_length=60e6 $@
for PROCESS_ID in $PROCESS_IDS; do
wait $PROCESS_ID || let "EXIT_CODE=$?"
done
printf "\n\n======== Running VCP Volume Renderer and VCP Volume Controller test =========\n\n"
Execute ./bs_${BOARD}_tests_bsim_bluetooth_audio_prj_conf \
-v=${VERBOSITY_LEVEL} -s=${SIMULATION_ID} -d=0 -testid=vcp_vol_rend -rs=23
Execute ./bs_${BOARD}_tests_bsim_bluetooth_audio_prj_conf \
-v=${VERBOSITY_LEVEL} -s=${SIMULATION_ID} -d=1 -testid=vcp_vol_ctlr -rs=46
# Simulation time should be larger than the WAIT_TIME in common.h
Execute ./bs_2G4_phy_v1 -v=${VERBOSITY_LEVEL} -s=${SIMULATION_ID} \
-D=2 -sim_length=60e6 $@
for PROCESS_ID in $PROCESS_IDS; do
wait $PROCESS_ID || let "EXIT_CODE=$?"
done
exit $EXIT_CODE #the last exit code != 0

28
tests/bsim/bluetooth/compile.sh Executable file
View file

@ -0,0 +1,28 @@
#!/usr/bin/env bash
# Copyright 2018 Oticon A/S
# SPDX-License-Identifier: Apache-2.0
# Compile all the applications needed by the bsim tests
#set -x #uncomment this line for debugging
set -ue
: "${BSIM_OUT_PATH:?BSIM_OUT_PATH must be defined}"
: "${BSIM_COMPONENTS_PATH:?BSIM_COMPONENTS_PATH must be defined}"
: "${ZEPHYR_BASE:?ZEPHYR_BASE must be set to point to the zephyr root\
directory}"
WORK_DIR="${WORK_DIR:-${ZEPHYR_BASE}/bsim_out}"
BOARD="${BOARD:-nrf52_bsim}"
BOARD_ROOT="${BOARD_ROOT:-${ZEPHYR_BASE}}"
mkdir -p ${WORK_DIR}
source ${ZEPHYR_BASE}/tests/bsim/bluetooth/sh_common.source
run_in_background ${ZEPHYR_BASE}/tests/bsim/bluetooth/audio/compile.sh
run_in_background ${ZEPHYR_BASE}/tests/bsim/bluetooth/host/compile.sh
run_in_background ${ZEPHYR_BASE}/tests/bsim/bluetooth/ll/compile.sh
run_in_background ${ZEPHYR_BASE}/tests/bsim/bluetooth/mesh/compile.sh
wait_for_background_jobs

View file

@ -0,0 +1,71 @@
# Copyright 2018 Oticon A/S
# SPDX-License-Identifier: Apache-2.0
source ${ZEPHYR_BASE}/tests/bsim/bluetooth/sh_common.source
function print_error_info(){
echo -e "\033[0;31mFailure building ${app} ${conf_file} for ${BOARD}\033[0m\n\
You can rebuild it with\n\
${cmake_cmd[@]} && ninja ${ninja_args}"
}
function _compile(){
: "${app:?app must be defined}"
local app_root="${app_root:-${ZEPHYR_BASE}}"
local conf_file="${conf_file:-prj.conf}"
local conf_overlay="${conf_overlay:-""}"
local cmake_args="${cmake_args:-"-DCONFIG_COVERAGE=y \
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON"}"
local ninja_args="${ninja_args:-""}"
local cc_flags="${cc_flags:-"-Werror"}"
if [ "${conf_overlay}" ]; then
local exe_name="${exe_name:-bs_${BOARD}_${app}_${conf_file}_${conf_overlay}}"
else
local exe_name="${exe_name:-bs_${BOARD}_${app}_${conf_file}}"
fi
local exe_name=${exe_name//\//_}
local exe_name=${exe_name//./_}
local exe_name=${BSIM_OUT_PATH}/bin/$exe_name
local map_file_name=${exe_name}.Tsymbols
if [ "${conf_overlay}" ]; then
local this_dir=${WORK_DIR}/${app}/${conf_file}_${conf_overlay}
else
local this_dir=${WORK_DIR}/${app}/${conf_file}
fi
local modules_arg="${ZEPHYR_MODULES:+-DZEPHYR_MODULES=${ZEPHYR_MODULES}}"
echo "Building $exe_name"
local ret=0
local cmake_cmd=(cmake -GNinja -DBOARD_ROOT=${BOARD_ROOT} -DBOARD=${BOARD} \
-DCONF_FILE=${conf_file} -DOVERLAY_CONFIG=${conf_overlay} \
${modules_arg} \
${cmake_args} -DCMAKE_C_FLAGS=\"${cc_flags}\" ${app_root}/${app})
# Set INCR_BUILD when calling to only do an incremental build
if [ ! -v INCR_BUILD ] || [ ! -d "${this_dir}" ]; then
[ -d "${this_dir}" ] && rm ${this_dir} -rf
mkdir -p ${this_dir} && cd ${this_dir}
${cmake_cmd[@]} &> cmake.out || \
{ ret="$?"; print_error_info ; cat cmake.out && return $ret; }
else
cd ${this_dir}
fi
ninja ${ninja_args} &> ninja.out || \
{ ret="$?"; print_error_info ; cat ninja.out && return $ret; }
cp ${this_dir}/zephyr/zephyr.exe ${exe_name}
nm ${exe_name} | grep -v " [U|w] " | sort | cut -d" " -f1,3 > ${map_file_name}
sed -i "1i $(wc -l ${map_file_name} | cut -d" " -f1)" ${map_file_name}
}
function compile(){
run_in_background _compile
}

View file

@ -0,0 +1,24 @@
# SPDX-License-Identifier: Apache-2.0
cmake_minimum_required(VERSION 3.20.0)
if (NOT DEFINED ENV{BSIM_COMPONENTS_PATH})
message(FATAL_ERROR "This test requires the BabbleSim simulator. Please set \
the environment variable BSIM_COMPONENTS_PATH to point to its \
components folder. More information can be found in \
https://babblesim.github.io/folder_structure_and_env.html")
endif()
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(bsim_test_adv_chain)
target_sources(app PRIVATE
src/main.c
${ZEPHYR_BASE}/samples/bluetooth/broadcaster_multiple/src/broadcaster_multiple.c
${ZEPHYR_BASE}/samples/bluetooth/observer/src/observer.c
)
zephyr_include_directories(
$ENV{BSIM_COMPONENTS_PATH}/libUtilv1/src/
$ENV{BSIM_COMPONENTS_PATH}/libPhyComv1/src/
)

View file

@ -0,0 +1,39 @@
CONFIG_BT=y
CONFIG_BT_BROADCASTER=y
CONFIG_BT_OBSERVER=y
CONFIG_BT_EXT_ADV=y
CONFIG_BT_EXT_ADV_MAX_ADV_SET=2
CONFIG_BT_DEVICE_NAME="Broadcaster Multiple"
# Enable Advertising Data chaining in Zephyr Bluetooth LE Controller
CONFIG_BT_CTLR_ADVANCED_FEATURES=y
CONFIG_BT_CTLR_ADV_DATA_CHAIN=y
# Zephyr Bluetooth LE Controller will need to use chain PDUs when AD data
# length > 191 bytes
# - 31 bytes will use 22 bytes for the default name in this sample plus 9 bytes
# for manufacturer data
# - 191 bytes will use 22 bytes for the default name in this sample plus 169
# bytes for manufacturer data
# - 277 bytes will use 22 bytes for the default name in this sample plus 255
# bytes for manufacturer data
CONFIG_BT_CTLR_ADV_DATA_LEN_MAX=1650
# Increase Advertising PDU buffers to number of advertising sets times the
# number of chain PDUs per advertising set when using Zephyr Bluetooth LE
# Controller
CONFIG_BT_CTLR_ADV_DATA_BUF_MAX=2
# Maximum Extended Scanning buffer size
CONFIG_BT_EXT_SCAN_BUF_SIZE=1650
# Set maximum scan data length for Extended Scanning in Bluetooth LE Controller
CONFIG_BT_CTLR_SCAN_DATA_LEN_MAX=1650
# Zephyr Bluetooth LE Controller needs 16 event buffers to generate Extended
# Advertising Report for receiving the complete 1650 bytes of data
CONFIG_BT_BUF_EVT_RX_COUNT=16
# Increase Zephyr Bluetooth LE Controller Rx buffer to receive complete chain
# of PDUs
CONFIG_BT_CTLR_RX_BUFFERS=9

View file

@ -0,0 +1,208 @@
/*
* Copyright (c) 2022 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stddef.h>
#include <zephyr/kernel.h>
#include <zephyr/sys/printk.h>
#include <zephyr/sys/util.h>
#include <zephyr/bluetooth/bluetooth.h>
#include <zephyr/bluetooth/hci.h>
#include "bs_types.h"
#include "bs_tracing.h"
#include "time_machine.h"
#include "bstests.h"
#define FAIL(...) \
do { \
bst_result = Failed; \
bs_trace_error_time_line(__VA_ARGS__); \
} while (0)
#define PASS(...) \
do { \
bst_result = Passed; \
bs_trace_info_time(1, __VA_ARGS__); \
} while (0)
#define NAME_LEN 30
#define BT_AD_DATA_NAME_SIZE (sizeof(CONFIG_BT_DEVICE_NAME) - 1U + 2U)
#define BT_AD_DATA_MFG_DATA_SIZE (254U + 2U)
#define DATA_LEN MIN((BT_AD_DATA_NAME_SIZE + \
BT_AD_DATA_MFG_DATA_SIZE), \
CONFIG_BT_CTLR_ADV_DATA_LEN_MAX)
static K_SEM_DEFINE(sem_recv, 0, 1);
extern enum bst_result_t bst_result;
static void test_adv_main(void)
{
extern int broadcaster_multiple(void);
int err;
err = broadcaster_multiple();
if (err) {
FAIL("Adv tests failed\n");
bs_trace_silent_exit(err);
return;
}
/* Successfully started advertising multiple sets */
PASS("Adv tests passed\n");
/* Let the scanner receive the reports */
k_sleep(K_SECONDS(10));
}
static bool data_cb(struct bt_data *data, void *user_data)
{
char *name = user_data;
uint8_t len;
switch (data->type) {
case BT_DATA_NAME_SHORTENED:
case BT_DATA_NAME_COMPLETE:
len = MIN(data->data_len, NAME_LEN - 1);
(void)memcpy(name, data->data, len);
name[len] = '\0';
return false;
default:
return true;
}
}
static void scan_recv(const struct bt_le_scan_recv_info *info,
struct net_buf_simple *buf)
{
static uint8_t sid[CONFIG_BT_EXT_ADV_MAX_ADV_SET];
static uint8_t sid_count;
char name[NAME_LEN];
uint8_t data_status;
uint16_t data_len;
data_status = BT_HCI_LE_ADV_EVT_TYPE_DATA_STATUS(info->adv_props);
if (data_status) {
return;
}
data_len = buf->len;
if (data_len != DATA_LEN) {
return;
}
(void)memset(name, 0, sizeof(name));
bt_data_parse(buf, data_cb, name);
if (strcmp(name, CONFIG_BT_DEVICE_NAME)) {
return;
}
for (uint8_t i = 0; i < sid_count; i++) {
if (sid[i] == info->sid) {
return;
}
}
sid[sid_count++] = info->sid;
if (sid_count < CONFIG_BT_EXT_ADV_MAX_ADV_SET) {
return;
}
k_sem_give(&sem_recv);
}
static struct bt_le_scan_cb scan_callbacks = {
.recv = scan_recv,
};
static void test_scan_main(void)
{
extern int observer_start(void);
int err;
err = bt_enable(NULL);
if (err) {
FAIL("Bluetooth init failed\n");
bs_trace_silent_exit(err);
return;
}
bt_le_scan_cb_register(&scan_callbacks);
err = observer_start();
if (err) {
FAIL("Observer start failed\n");
bs_trace_silent_exit(err);
return;
}
/* Let the recv callback verify the reports */
k_sleep(K_SECONDS(10));
err = k_sem_take(&sem_recv, K_NO_WAIT);
if (err) {
FAIL("Scan receive failed\n");
bs_trace_silent_exit(err);
return;
}
PASS("Scan tests passed\n");
bs_trace_silent_exit(0);
}
static void test_adv_chain_init(void)
{
bst_ticker_set_next_tick_absolute(60e6);
bst_result = In_progress;
}
static void test_adv_chain_tick(bs_time_t HW_device_time)
{
bst_result = Failed;
bs_trace_error_line("Test GATT Write finished.\n");
}
static const struct bst_test_instance test_def[] = {
{
.test_id = "adv",
.test_descr = "Central GATT Write",
.test_post_init_f = test_adv_chain_init,
.test_tick_f = test_adv_chain_tick,
.test_main_f = test_adv_main
},
{
.test_id = "scan",
.test_descr = "Peripheral GATT Write",
.test_post_init_f = test_adv_chain_init,
.test_tick_f = test_adv_chain_tick,
.test_main_f = test_scan_main
},
BSTEST_END_MARKER
};
struct bst_test_list *test_adv_chain_install(struct bst_test_list *tests)
{
return bst_add_tests(tests, test_def);
}
bst_test_install_t test_installers[] = {
test_adv_chain_install,
NULL
};
void main(void)
{
bst_main();
}

View file

@ -0,0 +1,39 @@
#!/usr/bin/env bash
# Copyright 2018 Oticon A/S
# SPDX-License-Identifier: Apache-2.0
# Validate Extended Advertising AD Data fragment operation, PDU chaining and
# Extended Scanning of chain PDUs
simulation_id="adv_chain"
verbosity_level=2
process_ids=""; exit_code=0
function Execute(){
if [ ! -f $1 ]; then
echo -e " \e[91m`pwd`/`basename $1` cannot be found (did you forget to\
compile it?)\e[39m"
exit 1
fi
timeout 10 $@ & process_ids="$process_ids $!"
}
: "${BSIM_OUT_PATH:?BSIM_OUT_PATH must be defined}"
#Give a default value to BOARD if it does not have one yet:
BOARD="${BOARD:-nrf52_bsim}"
cd ${BSIM_OUT_PATH}/bin
Execute ./bs_${BOARD}_tests_bsim_bluetooth_host_adv_chain_prj_conf \
-v=${verbosity_level} -s=${simulation_id} -d=0 -testid=adv
Execute ./bs_${BOARD}_tests_bsim_bluetooth_host_adv_chain_prj_conf\
-v=${verbosity_level} -s=${simulation_id} -d=1 -testid=scan
Execute ./bs_2G4_phy_v1 -v=${verbosity_level} -s=${simulation_id} \
-D=2 -sim_length=10e6 $@
for process_id in $process_ids; do
wait $process_id || let "exit_code=$?"
done
exit $exit_code #the last exit code != 0

View file

@ -0,0 +1,26 @@
# SPDX-License-Identifier: Apache-2.0
cmake_minimum_required(VERSION 3.20.0)
if (NOT DEFINED ENV{BSIM_COMPONENTS_PATH})
message(FATAL_ERROR "This test requires the BabbleSim simulator. Please set\
the environment variable BSIM_COMPONENTS_PATH to point to its components \
folder. More information can be found in\
https://babblesim.github.io/folder_structure_and_env.html")
endif()
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(bsim_test_audio)
target_sources(app PRIVATE
src/common.c
src/main.c
src/per_adv_advertiser.c
src/per_adv_syncer.c
)
zephyr_include_directories(
$ENV{BSIM_COMPONENTS_PATH}/libUtilv1/src/
$ENV{BSIM_COMPONENTS_PATH}/libPhyComv1/src/
${ZEPHYR_BASE}/subsys/bluetooth/host/audio/
)

View file

@ -0,0 +1,14 @@
CONFIG_BT=y
CONFIG_BT_DEVICE_NAME="test_per_adv"
CONFIG_BT_PERIPHERAL=y
CONFIG_BT_CENTRAL=y
CONFIG_BT_SMP=y
CONFIG_BT_PRIVACY=y
CONFIG_BT_EXT_ADV=y
CONFIG_BT_EXT_ADV_MAX_ADV_SET=2
CONFIG_BT_CTLR_ADVANCED_FEATURES=y
CONFIG_BT_CTLR_ADV_AUX_SET=2
CONFIG_BT_CTLR_ADV_DATA_BUF_MAX=2
CONFIG_BT_PER_ADV=y
CONFIG_BT_PER_ADV_SYNC=y

View file

@ -0,0 +1,21 @@
CONFIG_BT=y
CONFIG_BT_DEVICE_NAME="test_per_adv"
CONFIG_BT_PERIPHERAL=y
CONFIG_BT_CENTRAL=y
CONFIG_BT_SMP=y
CONFIG_BT_PRIVACY=y
CONFIG_BT_EXT_ADV=y
CONFIG_BT_EXT_ADV_MAX_ADV_SET=2
CONFIG_BT_CTLR_ADVANCED_FEATURES=y
CONFIG_BT_CTLR_ADV_AUX_SET=2
CONFIG_BT_CTLR_ADV_DATA_BUF_MAX=2
CONFIG_BT_CTLR_ADV_AUX_PDU_BACK2BACK=y
CONFIG_BT_CTLR_ADV_SYNC_PDU_BACK2BACK=y
CONFIG_BT_PER_ADV=y
CONFIG_BT_PER_ADV_SYNC=y
CONFIG_BT_PER_ADV_SYNC_BUF_SIZE=1650
CONFIG_BT_CTLR_ADV_DATA_LEN_MAX=1650
CONFIG_BT_CTLR_ADV_DATA_CHAIN=y
CONFIG_BT_CTLR_SCAN_DATA_LEN_MAX=1650

View file

@ -0,0 +1,41 @@
/*
* Copyright (c) 2023 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "common.h"
extern enum bst_result_t bst_result;
void test_tick(bs_time_t HW_device_time)
{
if (bst_result != Passed) {
FAIL("test failed (not passed after %i seconds)\n", WAIT_SECONDS);
}
}
void test_init(void)
{
bst_ticker_set_next_tick_absolute(WAIT_TIME);
bst_result = In_progress;
}
uint8_t mfg_data[254] = {
0xFF, 0xFF, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C,
0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B,
0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A,
0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48,
0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66,
0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75,
0x76, 0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F, 0x80, 0x81, 0x82, 0x83, 0x84,
0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F, 0x90, 0x91, 0x92, 0x93,
0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F, 0xA0, 0xA1, 0xA2,
0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, 0xB0, 0xB1,
0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF, 0xC0,
0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF,
0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE,
0xDF, 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED,
0xEE, 0xEF, 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB};

View file

@ -0,0 +1,59 @@
/**
* Common functions and helpers
*
* Copyright (c) 2023 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef ZEPHYR_TEST_BSIM_BT_AUDIO_TEST_
#define ZEPHYR_TEST_BSIM_BT_AUDIO_TEST_
#include <zephyr/kernel.h>
#include "bs_types.h"
#include "bs_tracing.h"
#include "time_machine.h"
#include "bstests.h"
#include <zephyr/types.h>
#include <stddef.h>
#include <errno.h>
#include <zephyr/sys_clock.h>
#include <zephyr/bluetooth/bluetooth.h>
#include <zephyr/bluetooth/hci.h>
#include <zephyr/bluetooth/conn.h>
#include <zephyr/bluetooth/uuid.h>
#include <zephyr/bluetooth/gatt.h>
#define WAIT_SECONDS 30 /* seconds */
#define WAIT_TIME (WAIT_SECONDS * USEC_PER_SEC) /* microseconds*/
#define CREATE_FLAG(flag) static atomic_t flag = (atomic_t)false
#define SET_FLAG(flag) (void)atomic_set(&flag, (atomic_t)true)
#define UNSET_FLAG(flag) (void)atomic_set(&flag, (atomic_t)false)
#define TEST_FLAG(flag) (atomic_get(&flag) == (atomic_t)true)
#define WAIT_FOR_FLAG(flag) \
while (!(bool)atomic_get(&flag)) { \
(void)k_sleep(K_MSEC(1)); \
}
#define FAIL(...) \
do { \
bst_result = Failed; \
bs_trace_error_time_line(__VA_ARGS__); \
} while (0)
#define PASS(...) \
do { \
bst_result = Passed; \
bs_trace_info_time(1, "PASSED: " __VA_ARGS__); \
} while (0)
void test_tick(bs_time_t HW_device_time);
void test_init(void);
extern uint8_t mfg_data[254];
#endif /* ZEPHYR_TEST_BSIM_BT_AUDIO_TEST_ */

View file

@ -0,0 +1,21 @@
/*
* Copyright (c) 2023 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "bstests.h"
extern struct bst_test_list *test_per_adv_syncer(struct bst_test_list *tests);
extern struct bst_test_list *test_per_adv_advertiser(struct bst_test_list *tests);
bst_test_install_t test_installers[] = {
test_per_adv_syncer,
test_per_adv_advertiser,
NULL
};
void main(void)
{
bst_main();
}

View file

@ -0,0 +1,362 @@
/*
* Copyright (c) 2023 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/kernel.h>
#include "bs_types.h"
#include "bs_tracing.h"
#include "time_machine.h"
#include "bstests.h"
#include <zephyr/types.h>
#include <zephyr/sys/printk.h>
#include <zephyr/bluetooth/bluetooth.h>
#include "common.h"
extern enum bst_result_t bst_result;
static struct bt_conn *g_conn;
CREATE_FLAG(flag_connected);
CREATE_FLAG(flag_bonded);
static void connected(struct bt_conn *conn, uint8_t err)
{
char addr[BT_ADDR_LE_STR_LEN];
bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
if (err != BT_HCI_ERR_SUCCESS) {
FAIL("Failed to connect to %s: %u\n", addr, err);
return;
}
printk("Connected to %s\n", addr);
g_conn = bt_conn_ref(conn);
SET_FLAG(flag_connected);
}
static void disconnected(struct bt_conn *conn, uint8_t reason)
{
char addr[BT_ADDR_LE_STR_LEN];
bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
printk("Disconnected: %s (reason %u)\n", addr, reason);
bt_conn_unref(g_conn);
g_conn = NULL;
}
static struct bt_conn_cb conn_cbs = {
.connected = connected,
.disconnected = disconnected,
};
static void pairing_complete_cb(struct bt_conn *conn, bool bonded)
{
if (conn == g_conn && bonded) {
SET_FLAG(flag_bonded);
}
}
static struct bt_conn_auth_info_cb auto_info_cbs = {
.pairing_complete = pairing_complete_cb,
};
static void common_init(void)
{
int err;
err = bt_enable(NULL);
if (err) {
FAIL("Bluetooth init failed: %d\n", err);
return;
}
printk("Bluetooth initialized\n");
bt_conn_cb_register(&conn_cbs);
bt_conn_auth_info_cb_register(&auto_info_cbs);
}
static void create_per_adv_set(struct bt_le_ext_adv **adv)
{
int err;
printk("Creating extended advertising set...");
err = bt_le_ext_adv_create(BT_LE_EXT_ADV_NCONN_NAME, NULL, adv);
if (err) {
printk("Failed to create advertising set: %d\n", err);
return;
}
printk("done.\n");
printk("Setting periodic advertising parameters...");
err = bt_le_per_adv_set_param(*adv, BT_LE_PER_ADV_DEFAULT);
if (err) {
printk("Failed to set periodic advertising parameters: %d\n",
err);
return;
}
printk("done.\n");
}
static void create_conn_adv_set(struct bt_le_ext_adv **adv)
{
int err;
printk("Creating connectable extended advertising set...");
err = bt_le_ext_adv_create(BT_LE_EXT_ADV_CONN_NAME, NULL, adv);
if (err) {
printk("Failed to create advertising set: %d\n", err);
return;
}
printk("done.\n");
}
static void start_ext_adv_set(struct bt_le_ext_adv *adv)
{
int err;
printk("Starting Extended Advertising...");
err = bt_le_ext_adv_start(adv, BT_LE_EXT_ADV_START_DEFAULT);
if (err) {
printk("Failed to start extended advertising: %d\n", err);
return;
}
printk("done.\n");
}
static void start_per_adv_set(struct bt_le_ext_adv *adv)
{
int err;
printk("Starting periodic advertising...");
err = bt_le_per_adv_start(adv);
if (err) {
printk("Failed to start periodic advertising: %d\n", err);
return;
}
printk("done.\n");
}
#if (CONFIG_BT_CTLR_ADV_DATA_CHAIN)
static void set_per_adv_data(struct bt_le_ext_adv *adv)
{
int err;
const struct bt_data ad[] = {
BT_DATA(BT_DATA_MANUFACTURER_DATA, mfg_data, ARRAY_SIZE(mfg_data)),
BT_DATA(BT_DATA_MANUFACTURER_DATA, mfg_data, ARRAY_SIZE(mfg_data))};
printk("Setting Periodic Advertising Data...");
err = bt_le_per_adv_set_data(adv, ad, ARRAY_SIZE(ad));
if (err) {
printk("Failed to set periodic advertising data: %d\n",
err);
return;
}
printk("done.\n");
}
#endif
static void stop_ext_adv_set(struct bt_le_ext_adv *adv)
{
int err;
printk("Stopping Extended Advertising...");
err = bt_le_ext_adv_stop(adv);
if (err) {
printk("Failed to stop extended advertising: %d\n",
err);
return;
}
printk("done.\n");
}
static void stop_per_adv_set(struct bt_le_ext_adv *adv)
{
int err;
printk("Stopping Periodic Advertising...");
err = bt_le_per_adv_stop(adv);
if (err) {
printk("Failed to stop periodic advertising: %d\n",
err);
return;
}
printk("done.\n");
}
static void delete_adv_set(struct bt_le_ext_adv *adv)
{
int err;
printk("Delete extended advertising set...");
err = bt_le_ext_adv_delete(adv);
if (err) {
printk("Failed Delete extended advertising set: %d\n", err);
return;
}
printk("done.\n");
}
static void main_per_adv_advertiser(void)
{
struct bt_le_ext_adv *per_adv;
common_init();
create_per_adv_set(&per_adv);
start_per_adv_set(per_adv);
start_ext_adv_set(per_adv);
/* Advertise for a bit */
k_sleep(K_SECONDS(10));
stop_per_adv_set(per_adv);
stop_ext_adv_set(per_adv);
delete_adv_set(per_adv);
per_adv = NULL;
PASS("Periodic advertiser passed\n");
}
static void main_per_adv_conn_advertiser(void)
{
struct bt_le_ext_adv *conn_adv;
struct bt_le_ext_adv *per_adv;
common_init();
create_per_adv_set(&per_adv);
create_conn_adv_set(&conn_adv);
start_per_adv_set(per_adv);
start_ext_adv_set(per_adv);
start_ext_adv_set(conn_adv);
WAIT_FOR_FLAG(flag_connected);
/* Advertise for a bit */
k_sleep(K_SECONDS(10));
stop_per_adv_set(per_adv);
stop_ext_adv_set(per_adv);
stop_ext_adv_set(conn_adv);
delete_adv_set(per_adv);
per_adv = NULL;
delete_adv_set(conn_adv);
conn_adv = NULL;
PASS("Periodic advertiser passed\n");
}
static void main_per_adv_conn_privacy_advertiser(void)
{
struct bt_le_ext_adv *conn_adv;
struct bt_le_ext_adv *per_adv;
common_init();
create_conn_adv_set(&conn_adv);
start_ext_adv_set(conn_adv);
WAIT_FOR_FLAG(flag_connected);
WAIT_FOR_FLAG(flag_bonded);
/* Start periodic advertising after bonding so that the scanner gets
* the resolved address
*/
create_per_adv_set(&per_adv);
start_per_adv_set(per_adv);
start_ext_adv_set(per_adv);
/* Advertise for a bit */
k_sleep(K_SECONDS(10));
stop_per_adv_set(per_adv);
stop_ext_adv_set(per_adv);
stop_ext_adv_set(conn_adv);
delete_adv_set(per_adv);
per_adv = NULL;
delete_adv_set(conn_adv);
conn_adv = NULL;
PASS("Periodic advertiser passed\n");
}
static void main_per_adv_long_data_advertiser(void)
{
#if (CONFIG_BT_CTLR_ADV_DATA_CHAIN)
struct bt_le_ext_adv *per_adv;
common_init();
create_per_adv_set(&per_adv);
set_per_adv_data(per_adv);
start_per_adv_set(per_adv);
start_ext_adv_set(per_adv);
/* Advertise for a bit */
k_sleep(K_SECONDS(10));
stop_per_adv_set(per_adv);
stop_ext_adv_set(per_adv);
delete_adv_set(per_adv);
per_adv = NULL;
#endif
PASS("Periodic long data advertiser passed\n");
}
static const struct bst_test_instance per_adv_advertiser[] = {
{
.test_id = "per_adv_advertiser",
.test_descr = "Basic periodic advertising test. "
"Will just start periodic advertising.",
.test_post_init_f = test_init,
.test_tick_f = test_tick,
.test_main_f = main_per_adv_advertiser
},
{
.test_id = "per_adv_conn_advertiser",
.test_descr = "Periodic advertising test with concurrent ACL "
"and PA sync.",
.test_post_init_f = test_init,
.test_tick_f = test_tick,
.test_main_f = main_per_adv_conn_advertiser
},
{
.test_id = "per_adv_conn_privacy_advertiser",
.test_descr = "Periodic advertising test with concurrent ACL "
"with bonding and PA sync.",
.test_post_init_f = test_init,
.test_tick_f = test_tick,
.test_main_f = main_per_adv_conn_privacy_advertiser
},
{
.test_id = "per_adv_long_data_advertiser",
.test_descr = "Periodic advertising test with a longer data length. "
"To test the syncers reassembly of large data packets",
.test_post_init_f = test_init,
.test_tick_f = test_tick,
.test_main_f = main_per_adv_long_data_advertiser
},
BSTEST_END_MARKER
};
struct bst_test_list *test_per_adv_advertiser(struct bst_test_list *tests)
{
return bst_add_tests(tests, per_adv_advertiser);
}

View file

@ -0,0 +1,377 @@
/*
* Copyright (c) 2023 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/kernel.h>
#include "bs_types.h"
#include "bs_tracing.h"
#include "time_machine.h"
#include "bstests.h"
#include <zephyr/types.h>
#include <zephyr/sys/printk.h>
#include <zephyr/bluetooth/bluetooth.h>
#include "common.h"
extern enum bst_result_t bst_result;
static struct bt_conn *g_conn;
static bt_addr_le_t per_addr;
static uint8_t per_sid;
CREATE_FLAG(flag_connected);
CREATE_FLAG(flag_bonded);
CREATE_FLAG(flag_per_adv);
CREATE_FLAG(flag_per_adv_sync);
CREATE_FLAG(flag_per_adv_sync_lost);
CREATE_FLAG(flag_per_adv_recv);
static void connected(struct bt_conn *conn, uint8_t err)
{
char addr[BT_ADDR_LE_STR_LEN];
bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
if (err != BT_HCI_ERR_SUCCESS) {
FAIL("Failed to connect to %s: %u\n", addr, err);
return;
}
printk("Connected to %s\n", addr);
g_conn = bt_conn_ref(conn);
SET_FLAG(flag_connected);
}
static void disconnected(struct bt_conn *conn, uint8_t reason)
{
char addr[BT_ADDR_LE_STR_LEN];
bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
printk("Disconnected: %s (reason %u)\n", addr, reason);
bt_conn_unref(g_conn);
g_conn = NULL;
}
static struct bt_conn_cb conn_cbs = {
.connected = connected,
.disconnected = disconnected,
};
static void pairing_complete_cb(struct bt_conn *conn, bool bonded)
{
if (conn == g_conn && bonded) {
SET_FLAG(flag_bonded);
}
}
static struct bt_conn_auth_info_cb auto_info_cbs = {
.pairing_complete = pairing_complete_cb,
};
static void scan_recv(const struct bt_le_scan_recv_info *info,
struct net_buf_simple *buf)
{
if (!TEST_FLAG(flag_connected) &&
info->adv_props & BT_GAP_ADV_PROP_CONNECTABLE) {
int err;
printk("Stopping scan\n");
err = bt_le_scan_stop();
if (err != 0) {
FAIL("Failed to stop scan: %d", err);
return;
}
err = bt_conn_le_create(info->addr, BT_CONN_LE_CREATE_CONN,
BT_LE_CONN_PARAM_DEFAULT, &g_conn);
if (err != 0) {
FAIL("Could not connect to peer: %d", err);
return;
}
} else if (!TEST_FLAG(flag_per_adv) && info->interval != 0U) {
per_sid = info->sid;
bt_addr_le_copy(&per_addr, info->addr);
SET_FLAG(flag_per_adv);
}
}
static struct bt_le_scan_cb scan_callbacks = {
.recv = scan_recv,
};
static void sync_cb(struct bt_le_per_adv_sync *sync,
struct bt_le_per_adv_sync_synced_info *info)
{
char le_addr[BT_ADDR_LE_STR_LEN];
bt_addr_le_to_str(info->addr, le_addr, sizeof(le_addr));
printk("PER_ADV_SYNC[%u]: [DEVICE]: %s synced, "
"Interval 0x%04x (%u us)\n",
bt_le_per_adv_sync_get_index(sync), le_addr,
info->interval, BT_CONN_INTERVAL_TO_US(info->interval));
SET_FLAG(flag_per_adv_sync);
}
static void term_cb(struct bt_le_per_adv_sync *sync,
const struct bt_le_per_adv_sync_term_info *info)
{
char le_addr[BT_ADDR_LE_STR_LEN];
bt_addr_le_to_str(info->addr, le_addr, sizeof(le_addr));
printk("PER_ADV_SYNC[%u]: [DEVICE]: %s sync terminated\n",
bt_le_per_adv_sync_get_index(sync), le_addr);
SET_FLAG(flag_per_adv_sync_lost);
}
static void recv_cb(struct bt_le_per_adv_sync *recv_sync,
const struct bt_le_per_adv_sync_recv_info *info,
struct net_buf_simple *buf)
{
char le_addr[BT_ADDR_LE_STR_LEN];
uint8_t buf_data_len;
if (TEST_FLAG(flag_per_adv_recv)) {
return;
}
bt_addr_le_to_str(info->addr, le_addr, sizeof(le_addr));
printk("PER_ADV_SYNC[%u]: [DEVICE]: %s advertisment received\n",
bt_le_per_adv_sync_get_index(recv_sync), le_addr);
while (buf->len > 0) {
buf_data_len = (uint8_t)net_buf_simple_pull_le16(buf);
if (buf->data[0] - 1 != sizeof(mfg_data) ||
memcmp(buf->data, mfg_data, sizeof(mfg_data))) {
FAIL("Unexpected adv data received\n");
}
net_buf_simple_pull(buf, ARRAY_SIZE(mfg_data));
}
SET_FLAG(flag_per_adv_recv);
}
static struct bt_le_per_adv_sync_cb sync_callbacks = {
.synced = sync_cb,
.term = term_cb,
.recv = recv_cb,
};
static void common_init(void)
{
int err;
err = bt_enable(NULL);
if (err) {
FAIL("Bluetooth init failed: %d\n", err);
return;
}
bt_le_scan_cb_register(&scan_callbacks);
bt_le_per_adv_sync_cb_register(&sync_callbacks);
bt_conn_cb_register(&conn_cbs);
bt_conn_auth_info_cb_register(&auto_info_cbs);
}
static void start_scan(void)
{
int err;
printk("Start scanning...");
err = bt_le_scan_start(BT_LE_SCAN_ACTIVE, NULL);
if (err) {
FAIL("Failed to start scan: %d\n", err);
return;
}
printk("done.\n");
}
static void create_pa_sync(struct bt_le_per_adv_sync **sync)
{
struct bt_le_per_adv_sync_param sync_create_param = { 0 };
int err;
printk("Creating periodic advertising sync...");
bt_addr_le_copy(&sync_create_param.addr, &per_addr);
sync_create_param.options = 0;
sync_create_param.sid = per_sid;
sync_create_param.skip = 0;
sync_create_param.timeout = 0x0a;
err = bt_le_per_adv_sync_create(&sync_create_param, sync);
if (err) {
FAIL("Failed to create periodic advertising sync: %d\n", err);
return;
}
printk("done.\n");
printk("Waiting for periodic sync...\n");
WAIT_FOR_FLAG(flag_per_adv_sync);
printk("Periodic sync established.\n");
}
static void start_bonding(void)
{
int err;
printk("Setting security...");
err = bt_conn_set_security(g_conn, BT_SECURITY_L2);
if (err) {
FAIL("Failed to set security: %d\n", err);
return;
}
printk("done.\n");
}
static void main_per_adv_syncer(void)
{
struct bt_le_per_adv_sync *sync = NULL;
common_init();
start_scan();
printk("Waiting for periodic advertising...\n");
WAIT_FOR_FLAG(flag_per_adv);
printk("Found periodic advertising.\n");
create_pa_sync(&sync);
printk("Waiting for periodic sync lost...\n");
WAIT_FOR_FLAG(flag_per_adv_sync_lost);
PASS("Periodic advertising syncer passed\n");
}
static void main_per_adv_conn_syncer(void)
{
struct bt_le_per_adv_sync *sync = NULL;
common_init();
start_scan();
printk("Waiting for connection...");
WAIT_FOR_FLAG(flag_connected);
printk("done.\n");
start_scan();
printk("Waiting for periodic advertising...\n");
WAIT_FOR_FLAG(flag_per_adv);
printk("Found periodic advertising.\n");
create_pa_sync(&sync);
printk("Waiting for periodic sync lost...\n");
WAIT_FOR_FLAG(flag_per_adv_sync_lost);
PASS("Periodic advertising syncer passed\n");
}
static void main_per_adv_conn_privacy_syncer(void)
{
struct bt_le_per_adv_sync *sync = NULL;
common_init();
start_scan();
printk("Waiting for connection...");
WAIT_FOR_FLAG(flag_connected);
printk("done.\n");
start_bonding();
printk("Waiting for bonding...");
WAIT_FOR_FLAG(flag_bonded);
printk("done.\n");
start_scan();
printk("Waiting for periodic advertising...\n");
WAIT_FOR_FLAG(flag_per_adv);
printk("Found periodic advertising.\n");
create_pa_sync(&sync);
printk("Waiting for periodic sync lost...\n");
WAIT_FOR_FLAG(flag_per_adv_sync_lost);
PASS("Periodic advertising syncer passed\n");
}
static void main_per_adv_long_data_syncer(void)
{
#if (CONFIG_BT_PER_ADV_SYNC_BUF_SIZE > 0)
struct bt_le_per_adv_sync *sync = NULL;
common_init();
start_scan();
printk("Waiting for periodic advertising...\n");
WAIT_FOR_FLAG(flag_per_adv);
printk("Found periodic advertising.\n");
create_pa_sync(&sync);
printk("Waiting to receive periodic advertisment...\n");
WAIT_FOR_FLAG(flag_per_adv_recv);
printk("Waiting for periodic sync lost...\n");
WAIT_FOR_FLAG(flag_per_adv_sync_lost);
#endif
PASS("Periodic advertising long data syncer passed\n");
}
static const struct bst_test_instance per_adv_syncer[] = {
{
.test_id = "per_adv_syncer",
.test_descr = "Basic periodic advertising sync test. "
"Will just sync to a periodic advertiser.",
.test_post_init_f = test_init,
.test_tick_f = test_tick,
.test_main_f = main_per_adv_syncer
},
{
.test_id = "per_adv_conn_syncer",
.test_descr = "Periodic advertising sync test, but where there "
"is a connection between the advertiser and the "
"syncer.",
.test_post_init_f = test_init,
.test_tick_f = test_tick,
.test_main_f = main_per_adv_conn_syncer
},
{
.test_id = "per_adv_conn_privacy_syncer",
.test_descr = "Periodic advertising sync test, but where "
"advertiser and syncer are bonded and using "
"privacy",
.test_post_init_f = test_init,
.test_tick_f = test_tick,
.test_main_f = main_per_adv_conn_privacy_syncer
},
{
.test_id = "per_adv_long_data_syncer",
.test_descr = "Periodic advertising sync test with larger "
"data length. Test is used to verify that "
"reassembly of long data is handeled correctly.",
.test_post_init_f = test_init,
.test_tick_f = test_tick,
.test_main_f = main_per_adv_long_data_syncer
},
BSTEST_END_MARKER
};
struct bst_test_list *test_per_adv_syncer(struct bst_test_list *tests)
{
return bst_add_tests(tests, per_adv_syncer);
}

View file

@ -0,0 +1,41 @@
#!/usr/bin/env bash
# Copyright (c) 2023 Nordic Semiconductor ASA
# SPDX-License-Identifier: Apache-2.0
# Basic periodic advertising sync test: an advertiser advertises with periodic
# advertising, and a scanner scans for and syncs to the periodic advertising.
simulation_id="per_adv"
verbosity_level=2
process_ids=""; exit_code=0
function Execute(){
if [ ! -f $1 ]; then
echo -e " \e[91m`pwd`/`basename $1` cannot be found (did you forget to\
compile it?)\e[39m"
exit 1
fi
timeout 10 $@ & process_ids="$process_ids $!"
}
: "${BSIM_OUT_PATH:?BSIM_OUT_PATH must be defined}"
#Give a default value to BOARD if it does not have one yet:
BOARD="${BOARD:-nrf52_bsim}"
cd ${BSIM_OUT_PATH}/bin
Execute ./bs_${BOARD}_tests_bsim_bluetooth_host_adv_periodic_prj_conf \
-v=${verbosity_level} -s=${simulation_id} -d=0 -RealEncryption=0 \
-testid=per_adv_advertiser -rs=23
Execute ./bs_${BOARD}_tests_bsim_bluetooth_host_adv_periodic_prj_conf \
-v=${verbosity_level} -s=${simulation_id} -d=1 -RealEncryption=0 \
-testid=per_adv_syncer -rs=6
Execute ./bs_2G4_phy_v1 -v=${verbosity_level} -s=${simulation_id} \
-D=2 -sim_length=20e6 $@
for process_id in $process_ids; do
wait $process_id || let "exit_code=$?"
done
exit $exit_code #the last exit code != 0

View file

@ -0,0 +1,41 @@
#!/usr/bin/env bash
# Copyright (c) 2023 Nordic Semiconductor ASA
# SPDX-License-Identifier: Apache-2.0
# Basic periodic advertising sync test: an advertiser advertises with periodic
# advertising, and a scanner scans for and syncs to the periodic advertising.
simulation_id="per_adv_conn"
verbosity_level=2
process_ids=""; exit_code=0
function Execute(){
if [ ! -f $1 ]; then
echo -e " \e[91m`pwd`/`basename $1` cannot be found (did you forget to\
compile it?)\e[39m"
exit 1
fi
timeout 10 $@ & process_ids="$process_ids $!"
}
: "${BSIM_OUT_PATH:?BSIM_OUT_PATH must be defined}"
#Give a default value to BOARD if it does not have one yet:
BOARD="${BOARD:-nrf52_bsim}"
cd ${BSIM_OUT_PATH}/bin
Execute ./bs_${BOARD}_tests_bsim_bluetooth_host_adv_periodic_prj_conf \
-v=${verbosity_level} -s=${simulation_id} -d=0 -RealEncryption=0 \
-testid=per_adv_conn_advertiser -rs=23
Execute ./bs_${BOARD}_tests_bsim_bluetooth_host_adv_periodic_prj_conf \
-v=${verbosity_level} -s=${simulation_id} -d=1 -RealEncryption=0 \
-testid=per_adv_conn_syncer -rs=6
Execute ./bs_2G4_phy_v1 -v=${verbosity_level} -s=${simulation_id} \
-D=2 -sim_length=20e6 $@
for process_id in $process_ids; do
wait $process_id || let "exit_code=$?"
done
exit $exit_code #the last exit code != 0

View file

@ -0,0 +1,41 @@
#!/usr/bin/env bash
# Copyright (c) 2023 Nordic Semiconductor ASA
# SPDX-License-Identifier: Apache-2.0
# Basic periodic advertising sync test: an advertiser advertises with periodic
# advertising, and a scanner scans for and syncs to the periodic advertising.
simulation_id="per_adv_conn_privacy"
verbosity_level=2
process_ids=""; exit_code=0
function Execute(){
if [ ! -f $1 ]; then
echo -e " \e[91m`pwd`/`basename $1` cannot be found (did you forget to\
compile it?)\e[39m"
exit 1
fi
timeout 10 $@ & process_ids="$process_ids $!"
}
: "${BSIM_OUT_PATH:?BSIM_OUT_PATH must be defined}"
#Give a default value to BOARD if it does not have one yet:
BOARD="${BOARD:-nrf52_bsim}"
cd ${BSIM_OUT_PATH}/bin
Execute ./bs_${BOARD}_tests_bsim_bluetooth_host_adv_periodic_prj_conf \
-v=${verbosity_level} -s=${simulation_id} -d=0 -RealEncryption=0 \
-testid=per_adv_conn_privacy_advertiser -rs=23
Execute ./bs_${BOARD}_tests_bsim_bluetooth_host_adv_periodic_prj_conf \
-v=${verbosity_level} -s=${simulation_id} -d=1 -RealEncryption=0 \
-testid=per_adv_conn_privacy_syncer -rs=6
Execute ./bs_2G4_phy_v1 -v=${verbosity_level} -s=${simulation_id} \
-D=2 -sim_length=20e6 $@
for process_id in $process_ids; do
wait $process_id || let "exit_code=$?"
done
exit $exit_code #the last exit code != 0

View file

@ -0,0 +1,41 @@
#!/usr/bin/env bash
# Copyright (c) 2023 Nordic Semiconductor ASA
# SPDX-License-Identifier: Apache-2.0
# Basic periodic advertising sync test: an advertiser advertises with periodic
# advertising, and a scanner scans for and syncs to the periodic advertising.
simulation_id="per_adv_long_data"
verbosity_level=2
process_ids=""; exit_code=0
function Execute(){
if [ ! -f $1 ]; then
echo -e " \e[91m`pwd`/`basename $1` cannot be found (did you forget to\
compile it?)\e[39m"
exit 1
fi
timeout 10 $@ & process_ids="$process_ids $!"
}
: "${BSIM_OUT_PATH:?BSIM_OUT_PATH must be defined}"
#Give a default value to BOARD if it does not have one yet:
BOARD="${BOARD:-nrf52_bsim}"
cd ${BSIM_OUT_PATH}/bin
Execute ./bs_${BOARD}_tests_bsim_bluetooth_host_adv_periodic_prj_long_data_conf \
-v=${verbosity_level} -s=${simulation_id} -d=0 -RealEncryption=0 \
-testid=per_adv_long_data_advertiser -rs=23
Execute ./bs_${BOARD}_tests_bsim_bluetooth_host_adv_periodic_prj_long_data_conf \
-v=${verbosity_level} -s=${simulation_id} -d=1 -RealEncryption=0 \
-testid=per_adv_long_data_syncer -rs=6
Execute ./bs_2G4_phy_v1 -v=${verbosity_level} -s=${simulation_id} \
-D=2 -sim_length=20e6 $@
for process_id in $process_ids; do
wait $process_id || let "exit_code=$?"
done
exit $exit_code #the last exit code != 0

View file

@ -0,0 +1,25 @@
# SPDX-License-Identifier: Apache-2.0
cmake_minimum_required(VERSION 3.20.0)
if (NOT DEFINED ENV{BSIM_COMPONENTS_PATH})
message(FATAL_ERROR "This test requires the BabbleSim simulator. Please set\
the environment variable BSIM_COMPONENTS_PATH to point to its components \
folder. More information can be found in\
https://babblesim.github.io/folder_structure_and_env.html")
endif()
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(bsim_test_adv_resume)
target_sources(app PRIVATE
src/bs_bt_utils.c
src/dut.c
src/main.c
src/tester.c
)
zephyr_include_directories(
$ENV{BSIM_COMPONENTS_PATH}/libUtilv1/src/
$ENV{BSIM_COMPONENTS_PATH}/libPhyComv1/src/
)

View file

@ -0,0 +1,20 @@
CONFIG_BT=y
CONFIG_BT_PERIPHERAL=y
CONFIG_BT_CENTRAL=y
CONFIG_ASSERT=y
CONFIG_BT_TESTING=y
CONFIG_BT_DEBUG_LOG=y
CONFIG_BT_EXT_ADV=n
CONFIG_BT_PRIVACY=n
CONFIG_BT_SCAN_WITH_IDENTITY=n
CONFIG_BT_LOG_SNIFFER_INFO=y
CONFIG_BT_AUTO_PHY_UPDATE=n
CONFIG_BT_AUTO_DATA_LEN_UPDATE=n
CONFIG_BT_GAP_AUTO_UPDATE_CONN_PARAMS=n
CONFIG_BT_MAX_CONN=2
CONFIG_BT_MAX_PAIRED=2
CONFIG_BT_ID_MAX=2

View file

@ -0,0 +1,18 @@
CONFIG_BT=y
CONFIG_BT_PERIPHERAL=y
CONFIG_BT_CENTRAL=y
CONFIG_ASSERT=y
CONFIG_BT_TESTING=y
CONFIG_BT_DEBUG_LOG=y
CONFIG_BT_EXT_ADV=y
CONFIG_BT_PRIVACY=n
CONFIG_BT_AUTO_PHY_UPDATE=n
CONFIG_BT_AUTO_DATA_LEN_UPDATE=n
CONFIG_BT_GAP_AUTO_UPDATE_CONN_PARAMS=n
CONFIG_BT_MAX_CONN=2
CONFIG_BT_MAX_PAIRED=2
CONFIG_BT_ID_MAX=2

View file

@ -0,0 +1,233 @@
/*
* Copyright (c) 2022 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "bs_bt_utils.h"
#include "argparse.h"
#include "bs_pc_backchannel.h"
BUILD_ASSERT(CONFIG_BT_MAX_PAIRED >= 2, "CONFIG_BT_MAX_PAIRED is too small.");
BUILD_ASSERT(CONFIG_BT_ID_MAX == 2, "CONFIG_BT_ID_MAX should be 2.");
#define BS_SECONDS(dur_sec) ((bs_time_t)dur_sec * USEC_PER_SEC)
#define TEST_TIMEOUT_SIMULATED BS_SECONDS(60)
DEFINE_FLAG(flag_is_connected);
DEFINE_FLAG(flag_test_end);
void test_tick(bs_time_t HW_device_time)
{
bs_trace_debug_time(0, "Simulation ends now.\n");
if (bst_result != Passed) {
bst_result = Failed;
bs_trace_error("Test did not pass before simulation ended.\n");
}
}
void test_init(void)
{
bst_ticker_set_next_tick_absolute(TEST_TIMEOUT_SIMULATED);
bst_result = In_progress;
}
void wait_connected(void)
{
UNSET_FLAG(flag_is_connected);
WAIT_FOR_FLAG(flag_is_connected);
printk("connected\n");
}
void wait_disconnected(void)
{
SET_FLAG(flag_is_connected);
WAIT_FOR_FLAG_UNSET(flag_is_connected);
printk("disconnected\n");
}
static void disconnected(struct bt_conn *conn, uint8_t reason)
{
UNSET_FLAG(flag_is_connected);
}
static void connected(struct bt_conn *conn, uint8_t err)
{
if (err != 0) {
return;
}
SET_FLAG(flag_is_connected);
}
BT_CONN_CB_DEFINE(conn_callbacks) = {
.connected = connected,
.disconnected = disconnected,
};
void bs_bt_utils_setup(void)
{
int err;
UNSET_FLAG(flag_test_end);
err = bt_enable(NULL);
ASSERT(!err, "bt_enable failed.\n");
}
static bt_addr_le_t last_scanned_addr;
static void scan_connect_to_first_result_device_found(const bt_addr_le_t *addr, int8_t rssi,
uint8_t type, struct net_buf_simple *ad)
{
struct bt_conn *conn;
char addr_str[BT_ADDR_LE_STR_LEN];
int err;
/* We're only interested in connectable events */
if (type != BT_HCI_ADV_IND && type != BT_HCI_ADV_DIRECT_IND) {
FAIL("Unexpected advertisement type.");
}
bt_addr_le_to_str(addr, addr_str, sizeof(addr_str));
printk("Got scan result, connecting.. dst %s, RSSI %d\n",
addr_str, rssi);
err = bt_le_scan_stop();
ASSERT(!err, "Err bt_le_scan_stop %d", err);
err = bt_conn_le_create(addr,
BT_CONN_LE_CREATE_CONN, BT_LE_CONN_PARAM_DEFAULT,
&conn);
ASSERT(!err, "Err bt_conn_le_create %d", err);
/* Save address for later comparison */
memcpy(&last_scanned_addr, addr, sizeof(last_scanned_addr));
}
void scan_connect_to_first_result(void)
{
int err;
printk("start scanner\n");
err = bt_le_scan_start(BT_LE_SCAN_PASSIVE,
scan_connect_to_first_result_device_found);
ASSERT(!err, "Err bt_le_scan_start %d", err);
}
static void scan_expect_same_address_device_found(const bt_addr_le_t *addr, int8_t rssi,
uint8_t type, struct net_buf_simple *ad)
{
char addr_str[BT_ADDR_LE_STR_LEN];
char expected_addr_str[BT_ADDR_LE_STR_LEN];
/* We're only interested in connectable events */
if (type != BT_HCI_ADV_IND && type != BT_HCI_ADV_DIRECT_IND) {
FAIL("Unexpected advertisement type.");
}
if (!bt_addr_le_eq(&last_scanned_addr, addr)) {
bt_addr_le_to_str(&last_scanned_addr,
expected_addr_str,
sizeof(expected_addr_str));
bt_addr_le_to_str(addr, addr_str, sizeof(addr_str));
FAIL("Expected advertiser with addr %s, got %s\n",
expected_addr_str, addr_str);
}
PASS("Advertiser used correct address on resume\n");
}
void scan_expect_same_address(void)
{
int err;
printk("start scanner\n");
err = bt_le_scan_start(BT_LE_SCAN_PASSIVE,
scan_expect_same_address_device_found);
ASSERT(!err, "Err bt_le_scan_start %d", err);
}
static void disconnect_device(struct bt_conn *conn, void *data)
{
int err;
/* We only use a single flag to indicate connections. Since this
* function will be called multiple times in a row, we have to set it
* back after it has been unset (in the `disconnected` callback).
*/
SET_FLAG(flag_is_connected);
err = bt_conn_disconnect(conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN);
ASSERT(!err, "Failed to initate disconnect (err %d)", err);
printk("Waiting for disconnection...\n");
WAIT_FOR_FLAG_UNSET(flag_is_connected);
}
void disconnect(void)
{
bt_conn_foreach(BT_CONN_TYPE_LE, disconnect_device, NULL);
}
void advertise_connectable(int id, bool persist)
{
printk("start advertiser\n");
int err;
struct bt_le_adv_param param = {};
param.id = id;
param.interval_min = 0x0020;
param.interval_max = 0x4000;
param.options |= persist ? 0 : BT_LE_ADV_OPT_ONE_TIME;
param.options |= BT_LE_ADV_OPT_CONNECTABLE;
err = bt_le_adv_start(&param, NULL, 0, NULL, 0);
ASSERT(err == 0, "Advertising failed to start (err %d)\n", err);
}
#define CHANNEL_ID 0
#define MSG_SIZE 1
void backchannel_init(uint peer)
{
uint device_number = get_device_nbr();
uint device_numbers[] = { peer };
uint channel_numbers[] = { CHANNEL_ID };
uint *ch;
ch = bs_open_back_channel(device_number, device_numbers,
channel_numbers, ARRAY_SIZE(channel_numbers));
if (!ch) {
FAIL("Unable to open backchannel\n");
}
}
void backchannel_sync_send(void)
{
uint8_t sync_msg[MSG_SIZE] = { get_device_nbr() };
printk("Sending sync\n");
bs_bc_send_msg(CHANNEL_ID, sync_msg, ARRAY_SIZE(sync_msg));
}
void backchannel_sync_wait(void)
{
uint8_t sync_msg[MSG_SIZE];
while (true) {
if (bs_bc_is_msg_received(CHANNEL_ID) > 0) {
bs_bc_receive_msg(CHANNEL_ID, sync_msg,
ARRAY_SIZE(sync_msg));
if (sync_msg[0] != get_device_nbr()) {
/* Received a message from another device, exit */
break;
}
}
k_sleep(K_MSEC(1));
}
printk("Sync received\n");
}

View file

@ -0,0 +1,79 @@
/**
* Common functions and helpers for BSIM ADV tests
*
* Copyright (c) 2022 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "bs_tracing.h"
#include "bs_types.h"
#include "bstests.h"
#include "time_machine.h"
#include "zephyr/sys/__assert.h"
#include <errno.h>
#include <zephyr/bluetooth/bluetooth.h>
#include <zephyr/bluetooth/conn.h>
#include <zephyr/bluetooth/gatt.h>
#include <zephyr/bluetooth/hci.h>
#include <zephyr/bluetooth/uuid.h>
#include <zephyr/kernel.h>
#include <zephyr/types.h>
extern enum bst_result_t bst_result;
#define DECLARE_FLAG(flag) extern atomic_t flag
#define DEFINE_FLAG(flag) atomic_t flag = (atomic_t) false
#define SET_FLAG(flag) (void)atomic_set(&flag, (atomic_t) true)
#define UNSET_FLAG(flag) (void)atomic_set(&flag, (atomic_t) false)
#define WAIT_FOR_FLAG(flag) \
while (!(bool)atomic_get(&flag)) { \
(void)k_sleep(K_MSEC(1)); \
}
#define WAIT_FOR_FLAG_UNSET(flag) \
while ((bool)atomic_get(&flag)) { \
(void)k_sleep(K_MSEC(1)); \
}
#define TAKE_FLAG(flag) \
while (!(bool)atomic_cas(&flag, true, false)) { \
(void)k_sleep(K_MSEC(1)); \
}
#define ASSERT(expr, ...) \
do { \
if (!(expr)) { \
FAIL(__VA_ARGS__); \
} \
} while (0)
DECLARE_FLAG(flag_test_end);
#define FAIL(...) \
SET_FLAG(flag_test_end); \
do { \
bst_result = Failed; \
bs_trace_error_time_line(__VA_ARGS__); \
} while (0)
#define PASS(...) \
SET_FLAG(flag_test_end); \
do { \
bst_result = Passed; \
bs_trace_info_time(1, __VA_ARGS__); \
} while (0)
void test_tick(bs_time_t HW_device_time);
void test_init(void);
void backchannel_init(uint peer);
void backchannel_sync_send(void);
void backchannel_sync_wait(void);
void bs_bt_utils_setup(void);
void wait_connected(void);
void wait_disconnected(void);
void scan_connect_to_first_result(void);
void disconnect(void);
void advertise_connectable(int id, bool persist);
void scan_expect_same_address(void);

View file

@ -0,0 +1,66 @@
/*
* Copyright (c) 2022 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "bs_bt_utils.h"
#include "zephyr/bluetooth/addr.h"
#include "zephyr/bluetooth/bluetooth.h"
#include "zephyr/bluetooth/conn.h"
#include "zephyr/toolchain/gcc.h"
#include <stdint.h>
#include <string.h>
void dut_procedure(void)
{
bs_bt_utils_setup();
printk("DUT start\n");
/* start scanning (using NRPA) */
scan_connect_to_first_result();
advertise_connectable(0, 0);
wait_connected();
printk("DUT is peripheral\n");
/* tester advertises using a new identity
* -> will get detected and connected to by DUT
*/
wait_connected();
printk("DUT is central & peripheral\n");
/* restart advertiser: it will fail because we have run out of contexts.
* But since we pass the `persist` flag, it will start up as soon as a
* peripheral role is disconnected.
*
* We can't start it with the `persist` flag the first time, because adv
* will resume straight after the peripheral's connection completes,
* 'stealing' the last conn context and preventing the scanner from
* establishing a connection.
*/
advertise_connectable(0, 1);
wait_disconnected();
printk("DUT is central\n");
scan_connect_to_first_result();
wait_disconnected();
printk("DUT has no connections\n");
PASS("PASS\n");
}
void dut_procedure_2(void)
{
bs_bt_utils_setup();
printk("DUT start\n");
/* Start a resumable advertiser. */
advertise_connectable(0, true);
PASS("DUT done\n");
}

View file

@ -0,0 +1,61 @@
/*
* Copyright (c) 2022 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "bs_bt_utils.h"
#include "bstests.h"
void dut_procedure(void);
void tester_peripheral_procedure(void);
void tester_central_procedure(void);
void dut_procedure_2(void);
void tester_procedure_2(void);
static const struct bst_test_instance test_to_add[] = {
{
.test_id = "dut",
.test_post_init_f = test_init,
.test_tick_f = test_tick,
.test_main_f = dut_procedure,
},
{
.test_id = "tester_peripheral",
.test_post_init_f = test_init,
.test_tick_f = test_tick,
.test_main_f = tester_peripheral_procedure,
},
{
.test_id = "tester_central",
.test_post_init_f = test_init,
.test_tick_f = test_tick,
.test_main_f = tester_central_procedure,
},
{
.test_id = "dut_2",
.test_post_init_f = test_init,
.test_tick_f = test_tick,
.test_main_f = dut_procedure_2,
},
{
.test_id = "tester_2",
.test_post_init_f = test_init,
.test_tick_f = test_tick,
.test_main_f = tester_procedure_2,
},
BSTEST_END_MARKER,
};
static struct bst_test_list *install(struct bst_test_list *tests)
{
return bst_add_tests(tests, test_to_add);
};
bst_test_install_t test_installers[] = { install, NULL };
void main(void)
{
bst_main();
}

View file

@ -0,0 +1,83 @@
/*
* Copyright (c) 2022 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "bs_bt_utils.h"
#include "zephyr/bluetooth/addr.h"
#include "zephyr/bluetooth/conn.h"
#include <stdint.h>
#include <zephyr/bluetooth/bluetooth.h>
#define TESTER_CENTRAL_ID 1
#define TESTER_PERIPHERAL_ID 2
void tester_central_procedure(void)
{
bs_bt_utils_setup();
backchannel_init(TESTER_PERIPHERAL_ID);
printk("central tester start\n");
/* connect to DUT as central */
scan_connect_to_first_result();
wait_connected();
backchannel_sync_send();
/* DUT is peripheral */
/* wait until DUT connects to peripheral, and that it has disconnected
* from it afterwards.
*/
backchannel_sync_wait();
printk("disconnect central\n");
disconnect();
/* DUT starts advertising again (with scanner's NRPA)
* should give wrong address
*/
scan_expect_same_address();
/* PASS/FAIL is called in the `device_found` callback. */
}
void tester_peripheral_procedure(void)
{
bs_bt_utils_setup();
backchannel_init(TESTER_CENTRAL_ID);
printk("peripheral tester start\n");
/* wait for central to connect to DUT */
backchannel_sync_wait();
/* connect to DUT as peripheral */
advertise_connectable(0, 0);
wait_connected();
/* DUT is central & peripheral */
printk("disconnect peripheral\n");
disconnect();
/* DUT starts scanning again (using NRPA) */
backchannel_sync_send();
PASS("PASS\n");
}
void tester_procedure_2(void)
{
bs_bt_utils_setup();
printk("Tester start\n");
scan_connect_to_first_result();
wait_connected();
/* Verify DUT advertiser was able to resume after the connection was
* established.
*/
scan_expect_same_address();
WAIT_FOR_FLAG(flag_test_end);
}

View file

@ -0,0 +1,16 @@
#!/usr/bin/env bash
# Copyright 2022 Nordic Semiconductor ASA
# SPDX-License-Identifier: Apache-2.0
set -eu
bash_source_dir="$(realpath "$(dirname "${BASH_SOURCE[0]}")")"
# Read variable definitions output by _env.sh
source "${bash_source_dir}/_env.sh"
# Place yourself in the test's root (i.e. ./../)
west build -b nrf52_bsim -d build_test && \
cp build_test/zephyr/zephyr.exe "${test_exe}"
west build -b nrf52_bsim -d build_test_2 -- -DCONF_FILE=prj_2.conf && \
cp build_test_2/zephyr/zephyr.exe "${test_2_exe}"

View file

@ -0,0 +1,13 @@
#!/usr/bin/env bash
# Copyright 2022 Nordic Semiconductor ASA
# SPDX-License-Identifier: Apache-2.0
set -eu
bash_source_dir="$(realpath "$(dirname "${BASH_SOURCE[0]}")")"
: "${BSIM_OUT_PATH:?BSIM_OUT_PATH must be defined}"
test_name="$(basename "$(realpath "$bash_source_dir/..")")"
bsim_bin="${BSIM_OUT_PATH}/bin"
BOARD="${BOARD:-nrf52_bsim}"
test_exe="${bsim_bin}/bs_${BOARD}_tests_bsim_bluetooth_host_adv_resume_prj_conf"
test_2_exe="${bsim_bin}/bs_${BOARD}_tests_bsim_bluetooth_host_adv_resume_prj_2_conf"

View file

@ -0,0 +1,49 @@
#!/usr/bin/env bash
# Copyright 2022 Nordic Semiconductor ASA
# SPDX-License-Identifier: Apache-2.0
set -eu
bash_source_dir="$(realpath "$(dirname "${BASH_SOURCE[0]}")")"
# Read variable definitions output by _env.sh
source "${bash_source_dir}/_env.sh"
simulation_id="${test_name}"
verbosity_level=2
process_ids=""
exit_code=0
function Execute() {
if [ ! -f $1 ]; then
echo -e " \e[91m$(pwd)/$(basename $1) cannot be found (did you forget to\
compile it?)\e[39m"
exit 1
fi
timeout 30 $@ &
process_ids="$process_ids $!"
}
: "${BSIM_OUT_PATH:?BSIM_OUT_PATH must be defined}"
cd ${BSIM_OUT_PATH}/bin
Execute "$test_exe" \
-v=${verbosity_level} -s=${simulation_id} -d=0 -testid=dut -RealEncryption=1
Execute "$test_exe" \
-v=${verbosity_level} -s=${simulation_id} -d=1 -testid=tester_central -RealEncryption=1
Execute "$test_exe" \
-v=${verbosity_level} -s=${simulation_id} -d=2 -testid=tester_peripheral -RealEncryption=1
Execute ./bs_2G4_phy_v1 -v=${verbosity_level} -s=${simulation_id} \
-D=3 -sim_length=10e6 $@
# Uncomment (and comment the other peripheral line) to run DUT under a debugger
# gdb --args "$test_exe" \
# -v=${verbosity_level} -s=${simulation_id} -d=1 -testid=peripheral -RealEncryption=1
for process_id in $process_ids; do
wait $process_id || let "exit_code=$?"
done
exit $exit_code #the last exit code != 0

View file

@ -0,0 +1,42 @@
#!/usr/bin/env bash
# Copyright 2022 Nordic Semiconductor ASA
# SPDX-License-Identifier: Apache-2.0
set -eu
bash_source_dir="$(realpath "$(dirname "${BASH_SOURCE[0]}")")"
# Read variable definitions output by _env.sh
source "${bash_source_dir}/_env.sh"
simulation_id="${test_name}_2"
verbosity_level=2
process_ids=""
exit_code=0
function Execute() {
if [ ! -f $1 ]; then
echo -e " \e[91m$(pwd)/$(basename $1) cannot be found (did you forget to\
compile it?)\e[39m"
exit 1
fi
timeout 30 $@ &
process_ids="$process_ids $!"
}
: "${BSIM_OUT_PATH:?BSIM_OUT_PATH must be defined}"
cd ${BSIM_OUT_PATH}/bin
Execute "$test_2_exe" \
-v=${verbosity_level} -s="${simulation_id}" -d=0 -testid=dut_2 -RealEncryption=1
Execute "$test_2_exe" \
-v=${verbosity_level} -s="${simulation_id}" -d=1 -testid=tester_2 -RealEncryption=1
Execute ./bs_2G4_phy_v1 -v=${verbosity_level} -s="${simulation_id}" \
-D=2 -sim_length=10e6 $@
for process_id in $process_ids; do
wait $process_id || let "exit_code=$?"
done
exit $exit_code #the last exit code != 0

View file

@ -0,0 +1,28 @@
# SPDX-License-Identifier: Apache-2.0
cmake_minimum_required(VERSION 3.20.0)
if (NOT DEFINED ENV{BSIM_COMPONENTS_PATH})
message(FATAL_ERROR "This test requires the BabbleSim simulator. Please set\
the environment variable BSIM_COMPONENTS_PATH to point to its components \
folder. More information can be found in\
https://babblesim.github.io/folder_structure_and_env.html")
endif()
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(bsim_test_host)
target_sources(app PRIVATE
src/common.c
src/main_collision.c
src/main_autoconnect.c
src/main_reconfigure.c
src/main.c
)
zephyr_include_directories(
$ENV{BSIM_COMPONENTS_PATH}/libUtilv1/src/
$ENV{BSIM_COMPONENTS_PATH}/libPhyComv1/src/
)

View file

@ -0,0 +1,16 @@
CONFIG_BT=y
CONFIG_BT_CENTRAL=y
CONFIG_BT_PERIPHERAL=y
CONFIG_BT_SMP=y
CONFIG_BT_L2CAP_DYNAMIC_CHANNEL=y
CONFIG_BT_DEVICE_NAME="EATT test"
CONFIG_BT_EATT=y
CONFIG_BT_L2CAP_ECRED=y
CONFIG_BT_EATT_MAX=5
CONFIG_BT_MAX_CONN=1
CONFIG_BT_TESTING=y
CONFIG_BT_DEBUG_LOG=y
CONFIG_ASSERT=y
CONFIG_BT_L2CAP_TX_MTU=200

View file

@ -0,0 +1,15 @@
CONFIG_BT=y
CONFIG_BT_CENTRAL=y
CONFIG_BT_PERIPHERAL=y
CONFIG_BT_SMP=y
CONFIG_BT_L2CAP_DYNAMIC_CHANNEL=y
CONFIG_BT_DEVICE_NAME="EATT test"
CONFIG_BT_EATT=y
CONFIG_BT_L2CAP_ECRED=y
CONFIG_BT_EATT_MAX=5
CONFIG_BT_MAX_CONN=1
CONFIG_BT_EATT_AUTO_CONNECT=n
CONFIG_BT_TESTING=y
CONFIG_BT_DEBUG_LOG=y
CONFIG_ASSERT=y

View file

@ -0,0 +1,15 @@
CONFIG_BT=y
CONFIG_BT_CENTRAL=y
CONFIG_BT_PERIPHERAL=y
CONFIG_BT_SMP=y
CONFIG_BT_L2CAP_DYNAMIC_CHANNEL=y
CONFIG_BT_DEVICE_NAME="EATT test"
CONFIG_BT_EATT=y
CONFIG_BT_L2CAP_ECRED=y
CONFIG_BT_EATT_MAX=5
CONFIG_BT_MAX_CONN=2
CONFIG_BT_EATT_AUTO_CONNECT=n
CONFIG_BT_TESTING=y
CONFIG_BT_DEBUG_LOG=y
CONFIG_ASSERT=y

View file

@ -0,0 +1,223 @@
/* common.c - Common test code */
/*
* Copyright (c) 2022 Nordic Semiconductor
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "common.h"
#include "argparse.h"
struct bt_conn *default_conn;
static const struct bt_data ad[] = {
BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)),
};
static volatile bool is_connected;
static volatile bool is_encrypted;
static void connected(struct bt_conn *conn, uint8_t conn_err)
{
char addr[BT_ADDR_LE_STR_LEN];
bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
if (conn_err) {
if (default_conn) {
bt_conn_unref(default_conn);
default_conn = NULL;
}
FAIL("Failed to connect to %s (%u)\n", addr, conn_err);
return;
}
if (!default_conn) {
default_conn = bt_conn_ref(conn);
}
printk("Connected: %s\n", addr);
is_connected = true;
}
static void disconnected(struct bt_conn *conn, uint8_t reason)
{
char addr[BT_ADDR_LE_STR_LEN];
bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
printk("Disconnected: %s (reason 0x%02x)\n", addr, reason);
bt_conn_unref(default_conn);
default_conn = NULL;
is_connected = false;
is_encrypted = false;
}
static void security_changed(struct bt_conn *conn, bt_security_t level,
enum bt_security_err security_err)
{
if (security_err == BT_SECURITY_ERR_SUCCESS && level > BT_SECURITY_L1) {
is_encrypted = true;
}
}
BT_CONN_CB_DEFINE(conn_callbacks) = {
.connected = connected,
.disconnected = disconnected,
.security_changed = security_changed,
};
static void device_found(const bt_addr_le_t *addr, int8_t rssi, uint8_t type,
struct net_buf_simple *ad)
{
int err;
err = bt_le_scan_stop();
if (err) {
FAIL("Stop LE scan failed (err %d)\n", err);
}
err = bt_conn_le_create(addr, BT_CONN_LE_CREATE_CONN, BT_LE_CONN_PARAM_DEFAULT,
&default_conn);
if (err) {
FAIL("Create conn failed (err %d)\n", err);
}
printk("Device connected\n");
}
void test_init(void)
{
bst_ticker_set_next_tick_absolute(60e6); /* 60 seconds */
bst_result = In_progress;
}
void test_tick(bs_time_t HW_device_time)
{
if (bst_result != Passed) {
bst_result = Failed;
bs_trace_error_time_line("Test eatt finished.\n");
}
}
void central_setup_and_connect(void)
{
int err;
err = bt_enable(NULL);
if (err) {
FAIL("Can't enable Bluetooth (err %d)\n", err);
}
err = bt_le_scan_start(BT_LE_SCAN_ACTIVE, device_found);
if (err) {
FAIL("Scanning failed to start (err %d)\n", err);
}
while (!is_connected) {
k_sleep(K_MSEC(100));
}
err = bt_conn_set_security(default_conn, BT_SECURITY_L2);
if (err) {
FAIL("Failed to start encryption procedure\n");
}
while (!is_encrypted) {
k_sleep(K_MSEC(100));
}
}
void peripheral_setup_and_connect(void)
{
int err;
err = bt_enable(NULL);
if (err) {
FAIL("Can't enable Bluetooth (err %d)\n", err);
}
err = bt_le_adv_start(BT_LE_ADV_CONN_NAME, ad, ARRAY_SIZE(ad), NULL, 0);
if (err) {
FAIL("Advertising failed to start (err %d)\n", err);
}
while (!is_connected) {
k_sleep(K_MSEC(100));
}
/* Wait for central to start encryption */
while (!is_encrypted) {
k_sleep(K_MSEC(100));
}
}
void wait_for_disconnect(void)
{
while (is_connected) {
k_sleep(K_MSEC(100));
}
}
void disconnect(void)
{
int err;
err = bt_conn_disconnect(default_conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN);
if (err) {
FAIL("Disconnection failed (err %d)\n", err);
}
while (is_connected) {
k_sleep(K_MSEC(100));
}
}
#define CHANNEL_ID 0
#define MSG_SIZE 1
void backchannel_init(void)
{
uint device_number = get_device_nbr();
uint peer_number = device_number ^ 1;
uint device_numbers[] = { peer_number };
uint channel_numbers[] = { CHANNEL_ID };
uint *ch;
ch = bs_open_back_channel(device_number, device_numbers, channel_numbers,
ARRAY_SIZE(channel_numbers));
if (!ch) {
FAIL("Unable to open backchannel\n");
}
}
void backchannel_sync_send(void)
{
uint8_t sync_msg[MSG_SIZE] = { get_device_nbr() };
printk("Sending sync\n");
bs_bc_send_msg(CHANNEL_ID, sync_msg, ARRAY_SIZE(sync_msg));
}
void backchannel_sync_wait(void)
{
uint8_t sync_msg[MSG_SIZE];
while (true) {
if (bs_bc_is_msg_received(CHANNEL_ID) > 0) {
bs_bc_receive_msg(CHANNEL_ID, sync_msg, ARRAY_SIZE(sync_msg));
if (sync_msg[0] != get_device_nbr()) {
/* Received a message from another device, exit */
break;
}
}
k_sleep(K_MSEC(1));
}
printk("Sync received\n");
}

View file

@ -0,0 +1,57 @@
/* common.h - Common test code */
/*
* Copyright (c) 2022 Nordic Semiconductor
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stddef.h>
#include <zephyr/types.h>
#include <zephyr/sys/printk.h>
#include <zephyr/sys/util.h>
#include <zephyr/sys/byteorder.h>
#include <zephyr/bluetooth/bluetooth.h>
#include <zephyr/bluetooth/conn.h>
#include <zephyr/bluetooth/att.h>
#include "bs_types.h"
#include "bs_tracing.h"
#include "bstests.h"
#include "bs_pc_backchannel.h"
extern enum bst_result_t bst_result;
#define CREATE_FLAG(flag) static atomic_t flag = (atomic_t)false
#define SET_FLAG(flag) (void)atomic_set(&flag, (atomic_t)true)
#define WAIT_FOR_FLAG(flag) \
while (!(bool)atomic_get(&flag)) { \
(void)k_sleep(K_MSEC(1)); \
}
#define FAIL(...) \
do { \
bst_result = Failed; \
bs_trace_error_time_line(__VA_ARGS__); \
} while (0)
#define PASS(...) \
do { \
bst_result = Passed; \
bs_trace_info_time(1, __VA_ARGS__); \
} while (0)
extern volatile int num_eatt_channels;
extern struct bt_conn *default_conn;
void central_setup_and_connect(void);
void peripheral_setup_and_connect(void);
void wait_for_disconnect(void);
void disconnect(void);
void test_init(void);
void test_tick(bs_time_t HW_device_time);
void backchannel_init(void);
void backchannel_sync_send(void);
void backchannel_sync_wait(void);

View file

@ -0,0 +1,23 @@
/*
* Copyright (c) 2022 Nordic Semiconductor
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "bstests.h"
extern struct bst_test_list *test_main_collision_install(struct bst_test_list *tests);
extern struct bst_test_list *test_main_autoconnect_install(struct bst_test_list *tests);
extern struct bst_test_list *test_main_reconfigure_install(struct bst_test_list *tests);
bst_test_install_t test_installers[] = {
test_main_collision_install,
test_main_autoconnect_install,
test_main_reconfigure_install,
NULL,
};
void main(void)
{
bst_main();
}

View file

@ -0,0 +1,61 @@
/* main_connect.c - Application main entry point */
/*
* Copyright (c) 2022 Nordic Semiconductor
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "common.h"
static void test_peripheral_main(void)
{
peripheral_setup_and_connect();
while (bt_eatt_count(default_conn) < CONFIG_BT_EATT_MAX) {
k_sleep(K_MSEC(10));
}
/* Do not disconnect until the central also has connected all channels */
k_sleep(K_MSEC(1000));
disconnect();
PASS("EATT Peripheral tests Passed\n");
}
static void test_central_main(void)
{
central_setup_and_connect();
while (bt_eatt_count(default_conn) < CONFIG_BT_EATT_MAX) {
k_sleep(K_MSEC(10));
}
wait_for_disconnect();
PASS("EATT Central tests Passed\n");
}
static const struct bst_test_instance test_def[] = {
{
.test_id = "peripheral_autoconnect",
.test_descr = "Peripheral autoconnect",
.test_post_init_f = test_init,
.test_tick_f = test_tick,
.test_main_f = test_peripheral_main,
},
{
.test_id = "central_autoconnect",
.test_descr = "Central autoconnect",
.test_post_init_f = test_init,
.test_tick_f = test_tick,
.test_main_f = test_central_main,
},
BSTEST_END_MARKER,
};
struct bst_test_list *test_main_autoconnect_install(struct bst_test_list *tests)
{
return bst_add_tests(tests, test_def);
}

View file

@ -0,0 +1,88 @@
/* main_collision.c - Application main entry point */
/*
* Copyright (c) 2022 Nordic Semiconductor
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "common.h"
static void test_peripheral_main(void)
{
int err;
backchannel_init();
peripheral_setup_and_connect();
/*
* we need to sync with peer to ensure that we get collisions
*/
backchannel_sync_send();
backchannel_sync_wait();
err = bt_eatt_connect(default_conn, CONFIG_BT_EATT_MAX);
if (err) {
FAIL("Sending credit based connection request failed (err %d)\n", err);
}
while (bt_eatt_count(default_conn) < CONFIG_BT_EATT_MAX) {
k_sleep(K_MSEC(10));
}
/* Do not disconnect until the central also has connected all channels */
k_sleep(K_MSEC(1000));
disconnect();
PASS("EATT Peripheral tests Passed\n");
}
static void test_central_main(void)
{
int err;
backchannel_init();
central_setup_and_connect();
backchannel_sync_wait();
backchannel_sync_send();
err = bt_eatt_connect(default_conn, CONFIG_BT_EATT_MAX);
if (err) {
FAIL("Sending credit based connection request failed (err %d)\n", err);
}
while (bt_eatt_count(default_conn) < CONFIG_BT_EATT_MAX) {
k_sleep(K_MSEC(10));
}
wait_for_disconnect();
PASS("EATT Central tests Passed\n");
}
static const struct bst_test_instance test_def[] = {
{
.test_id = "peripheral",
.test_descr = "Peripheral Collision",
.test_post_init_f = test_init,
.test_tick_f = test_tick,
.test_main_f = test_peripheral_main,
},
{
.test_id = "central",
.test_descr = "Central Collision",
.test_post_init_f = test_init,
.test_tick_f = test_tick,
.test_main_f = test_central_main,
},
BSTEST_END_MARKER,
};
struct bst_test_list *test_main_collision_install(struct bst_test_list *tests)
{
return bst_add_tests(tests, test_def);
}

View file

@ -0,0 +1,91 @@
/* main_reconfigure.c - Application main entry point */
/*
* Copyright (c) 2022 Nordic Semiconductor
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "common.h"
#include <zephyr/bluetooth/gatt.h>
#define NEW_MTU 100
CREATE_FLAG(flag_reconfigured);
void att_mtu_updated(struct bt_conn *conn, uint16_t tx, uint16_t rx)
{
printk("MTU Updated: tx %d, rx %d\n", tx, rx);
if (rx == NEW_MTU || tx == NEW_MTU) {
SET_FLAG(flag_reconfigured);
}
}
static struct bt_gatt_cb cb = {
.att_mtu_updated = att_mtu_updated,
};
static void test_peripheral_main(void)
{
peripheral_setup_and_connect();
bt_gatt_cb_register(&cb);
while (bt_eatt_count(default_conn) < CONFIG_BT_EATT_MAX) {
k_sleep(K_MSEC(10));
}
WAIT_FOR_FLAG(flag_reconfigured);
disconnect();
PASS("EATT Peripheral tests Passed\n");
}
static void test_central_main(void)
{
int err;
central_setup_and_connect();
bt_gatt_cb_register(&cb);
while (bt_eatt_count(default_conn) < CONFIG_BT_EATT_MAX) {
k_sleep(K_MSEC(10));
}
err = bt_eatt_reconfigure(default_conn, NEW_MTU);
if (err < 0) {
FAIL("Reconfigure failed (%d)\n", err);
}
WAIT_FOR_FLAG(flag_reconfigured);
wait_for_disconnect();
PASS("EATT Central tests Passed\n");
}
static const struct bst_test_instance test_def[] = {
{
.test_id = "peripheral_reconfigure",
.test_descr = "Peripheral reconfigure",
.test_post_init_f = test_init,
.test_tick_f = test_tick,
.test_main_f = test_peripheral_main,
},
{
.test_id = "central_reconfigure",
.test_descr = "Central reconfigure",
.test_post_init_f = test_init,
.test_tick_f = test_tick,
.test_main_f = test_central_main,
},
BSTEST_END_MARKER,
};
struct bst_test_list *test_main_reconfigure_install(struct bst_test_list *tests)
{
return bst_add_tests(tests, test_def);
}

View file

@ -0,0 +1,37 @@
#!/usr/bin/env bash
# Copyright (c) 2022 Nordic Semiconductor
# SPDX-License-Identifier: Apache-2.0
simulation_id="connection"
verbosity_level=2
process_ids=""; exit_code=0
function Execute(){
if [ ! -f $1 ]; then
echo -e " \e[91m`pwd`/`basename $1` cannot be found (did you forget to\
compile it?)\e[39m"
exit 1
fi
timeout 120 $@ & process_ids="$process_ids $!"
}
: "${BSIM_OUT_PATH:?BSIM_OUT_PATH must be defined}"
#Give a default value to BOARD if it does not have one yet:
BOARD="${BOARD:-nrf52_bsim}"
cd ${BSIM_OUT_PATH}/bin
Execute ./bs_${BOARD}_tests_bsim_bluetooth_host_att_eatt_prj_autoconnect_conf \
-v=${verbosity_level} -s=${simulation_id} -d=0 -testid=central_autoconnect
Execute ./bs_${BOARD}_tests_bsim_bluetooth_host_att_eatt_prj_autoconnect_conf \
-v=${verbosity_level} -s=${simulation_id} -d=1 -testid=peripheral_autoconnect
Execute ./bs_2G4_phy_v1 -v=${verbosity_level} -s=${simulation_id} \
-D=2 -sim_length=200e6 $@
for process_id in $process_ids; do
wait $process_id || let "exit_code=$?"
done
exit $exit_code #the last exit code != 0

View file

@ -0,0 +1,37 @@
#!/usr/bin/env bash
# Copyright (c) 2022 Nordic Semiconductor
# SPDX-License-Identifier: Apache-2.0
simulation_id="collision"
verbosity_level=2
process_ids=""; exit_code=0
function Execute(){
if [ ! -f $1 ]; then
echo -e " \e[91m`pwd`/`basename $1` cannot be found (did you forget to\
compile it?)\e[39m"
exit 1
fi
timeout 120 $@ & process_ids="$process_ids $!"
}
: "${BSIM_OUT_PATH:?BSIM_OUT_PATH must be defined}"
#Give a default value to BOARD if it does not have one yet:
BOARD="${BOARD:-nrf52_bsim}"
cd ${BSIM_OUT_PATH}/bin
Execute ./bs_${BOARD}_tests_bsim_bluetooth_host_att_eatt_prj_collision_conf \
-v=${verbosity_level} -s=${simulation_id} -d=0 -testid=central
Execute ./bs_${BOARD}_tests_bsim_bluetooth_host_att_eatt_prj_collision_conf \
-v=${verbosity_level} -s=${simulation_id} -d=1 -testid=peripheral
Execute ./bs_2G4_phy_v1 -v=${verbosity_level} -s=${simulation_id} \
-D=2 -sim_length=200e6 $@
for process_id in $process_ids; do
wait $process_id || let "exit_code=$?"
done
exit $exit_code #the last exit code != 0

View file

@ -0,0 +1,37 @@
#!/usr/bin/env bash
# Copyright (c) 2022 Nordic Semiconductor
# SPDX-License-Identifier: Apache-2.0
simulation_id="multiple_conn"
verbosity_level=2
process_ids=""; exit_code=0
function Execute(){
if [ ! -f $1 ]; then
echo -e " \e[91m`pwd`/`basename $1` cannot be found (did you forget to\
compile it?)\e[39m"
exit 1
fi
timeout 120 $@ & process_ids="$process_ids $!"
}
: "${BSIM_OUT_PATH:?BSIM_OUT_PATH must be defined}"
#Give a default value to BOARD if it does not have one yet:
BOARD="${BOARD:-nrf52_bsim}"
cd ${BSIM_OUT_PATH}/bin
Execute ./bs_${BOARD}_tests_bsim_bluetooth_host_att_eatt_prj_multiple_conn_conf \
-v=${verbosity_level} -s=${simulation_id} -d=0 -testid=central
Execute ./bs_${BOARD}_tests_bsim_bluetooth_host_att_eatt_prj_multiple_conn_conf \
-v=${verbosity_level} -s=${simulation_id} -d=1 -testid=peripheral
Execute ./bs_2G4_phy_v1 -v=${verbosity_level} -s=${simulation_id} \
-D=2 -sim_length=200e6 $@
for process_id in $process_ids; do
wait $process_id || let "exit_code=$?"
done
exit $exit_code #the last exit code != 0

View file

@ -0,0 +1,37 @@
#!/usr/bin/env bash
# Copyright (c) 2022 Nordic Semiconductor
# SPDX-License-Identifier: Apache-2.0
simulation_id="reconfigure"
verbosity_level=2
process_ids=""; exit_code=0
function Execute(){
if [ ! -f $1 ]; then
echo -e " \e[91m`pwd`/`basename $1` cannot be found (did you forget to\
compile it?)\e[39m"
exit 1
fi
timeout 120 $@ & process_ids="$process_ids $!"
}
: "${BSIM_OUT_PATH:?BSIM_OUT_PATH must be defined}"
#Give a default value to BOARD if it does not have one yet:
BOARD="${BOARD:-nrf52_bsim}"
cd ${BSIM_OUT_PATH}/bin
Execute ./bs_${BOARD}_tests_bsim_bluetooth_host_att_eatt_prj_autoconnect_conf \
-v=${verbosity_level} -s=${simulation_id} -d=0 -testid=central_reconfigure
Execute ./bs_${BOARD}_tests_bsim_bluetooth_host_att_eatt_prj_autoconnect_conf \
-v=${verbosity_level} -s=${simulation_id} -d=1 -testid=peripheral_reconfigure
Execute ./bs_2G4_phy_v1 -v=${verbosity_level} -s=${simulation_id} \
-D=2 -sim_length=60e6 $@
for process_id in $process_ids; do
wait $process_id || let "exit_code=$?"
done
exit $exit_code #the last exit code != 0

View file

@ -0,0 +1,21 @@
# SPDX-License-Identifier: Apache-2.0
cmake_minimum_required(VERSION 3.20.0)
if (NOT DEFINED ENV{BSIM_COMPONENTS_PATH})
message(FATAL_ERROR "This test requires the BabbleSim simulator. Please set\
the environment variable BSIM_COMPONENTS_PATH to point to its components \
folder. More information can be found in\
https://babblesim.github.io/folder_structure_and_env.html")
endif()
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(bsim_test_eatt_notif)
FILE(GLOB app_sources src/*.c)
target_sources(app PRIVATE ${app_sources} )
zephyr_include_directories(
$ENV{BSIM_COMPONENTS_PATH}/libUtilv1/src/
$ENV{BSIM_COMPONENTS_PATH}/libPhyComv1/src/
)

View file

@ -0,0 +1,16 @@
CONFIG_BT=y
CONFIG_BT_CENTRAL=y
CONFIG_BT_PERIPHERAL=y
CONFIG_BT_SMP=y
CONFIG_BT_L2CAP_DYNAMIC_CHANNEL=y
CONFIG_BT_DEVICE_NAME="EATT test"
CONFIG_BT_EATT=y
CONFIG_BT_TESTING=y
CONFIG_BT_EATT_AUTO_CONNECT=n
CONFIG_BT_L2CAP_ECRED=y
CONFIG_BT_EATT_MAX=16
CONFIG_BT_MAX_CONN=1
CONFIG_BT_GATT_CLIENT=y
CONFIG_BT_GATT_AUTO_DISCOVER_CCC=y
CONFIG_BT_ATT_PREPARE_COUNT=3
CONFIG_ASSERT=y

View file

@ -0,0 +1,264 @@
/*
* Copyright (c) 2022 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*
* EATT notification reliability test:
* A central acting as a GATT client scans and connects
* to a peripheral acting as a GATT server.
* The GATT client will then attempt to connect a number of CONFIG_BT_EATT_MAX bearers
* over EATT, send notifications, disconnect all bearers and reconnect EATT_BEARERS_TEST
* and send start a transaction with a request, then send a lot of notifications
* before the response is received.
* The test might be expanded by checking that all the notifications all transmitted
* on EATT channels.
*/
#include <zephyr/bluetooth/bluetooth.h>
#include <zephyr/bluetooth/gatt.h>
#include <zephyr/bluetooth/conn.h>
#include <zephyr/bluetooth/att.h>
#include "common.h"
CREATE_FLAG(flag_is_connected);
CREATE_FLAG(flag_discover_complete);
CREATE_FLAG(flag_is_encrypted);
static struct bt_conn *g_conn;
static const struct bt_gatt_attr *local_attr;
static struct bt_uuid *test_svc_uuid = TEST_SERVICE_UUID;
#define NUM_NOTIF 100
#define SAMPLE_DATA 1
#define EATT_BEARERS_TEST 1
volatile int num_eatt_channels;
static void connected(struct bt_conn *conn, uint8_t err)
{
char addr[BT_ADDR_LE_STR_LEN];
bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
if (err != 0) {
FAIL("Failed to connect to %s (%u)\n", addr, err);
return;
}
printk("Connected to %s\n", addr);
SET_FLAG(flag_is_connected);
}
static void disconnected(struct bt_conn *conn, uint8_t reason)
{
char addr[BT_ADDR_LE_STR_LEN];
if (conn != g_conn) {
return;
}
bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
printk("Disconnected: %s (reason 0x%02x)\n", addr, reason);
bt_conn_unref(g_conn);
g_conn = NULL;
UNSET_FLAG(flag_is_connected);
}
static void security_changed(struct bt_conn *conn, bt_security_t level,
enum bt_security_err security_err)
{
if (security_err == BT_SECURITY_ERR_SUCCESS && level > BT_SECURITY_L1) {
SET_FLAG(flag_is_encrypted);
}
}
BT_CONN_CB_DEFINE(conn_callbacks) = {
.connected = connected,
.disconnected = disconnected,
.security_changed = security_changed,
};
void device_found(const bt_addr_le_t *addr, int8_t rssi, uint8_t type,
struct net_buf_simple *ad)
{
char addr_str[BT_ADDR_LE_STR_LEN];
int err;
if (g_conn != NULL) {
return;
}
/* We're only interested in connectable events */
if (type != BT_HCI_ADV_IND && type != BT_HCI_ADV_DIRECT_IND) {
return;
}
bt_addr_le_to_str(addr, addr_str, sizeof(addr_str));
printk("Device found: %s (RSSI %d)\n", addr_str, rssi);
printk("Stopping scan\n");
err = bt_le_scan_stop();
if (err != 0) {
FAIL("Could not stop scan: %d");
return;
}
err = bt_conn_le_create(addr, BT_CONN_LE_CREATE_CONN,
BT_LE_CONN_PARAM_DEFAULT, &g_conn);
if (err != 0) {
FAIL("Could not connect to peer: %d", err);
}
}
void send_notification(void)
{
const uint8_t sample_dat = SAMPLE_DATA;
int err;
do {
err = bt_gatt_notify(g_conn, local_attr, &sample_dat, sizeof(sample_dat));
if (!err) {
return;
} else if (err != -ENOMEM) {
printk("GATT notify failed (err %d)\n", err);
return;
}
k_sleep(K_TICKS(1));
} while (err == -ENOMEM);
}
static uint8_t discover_func(struct bt_conn *conn,
const struct bt_gatt_attr *attr,
struct bt_gatt_discover_params *params)
{
SET_FLAG(flag_discover_complete);
printk("Discover complete\n");
return BT_GATT_ITER_STOP;
}
static void gatt_discover(void)
{
static struct bt_gatt_discover_params discover_params;
int err;
printk("Discovering services and characteristics\n");
discover_params.uuid = test_svc_uuid;
discover_params.func = discover_func;
discover_params.start_handle = BT_ATT_FIRST_ATTRIBUTE_HANDLE;
discover_params.end_handle = BT_ATT_LAST_ATTRIBUTE_HANDLE;
discover_params.type = BT_GATT_DISCOVER_PRIMARY;
discover_params.chan_opt = BT_ATT_CHAN_OPT_NONE;
err = bt_gatt_discover(g_conn, &discover_params);
if (err != 0) {
FAIL("Discover failed(err %d)\n", err);
}
}
BT_GATT_SERVICE_DEFINE(g_svc,
BT_GATT_PRIMARY_SERVICE(TEST_SERVICE_UUID),
BT_GATT_CHARACTERISTIC(TEST_CHRC_UUID, BT_GATT_CHRC_NOTIFY,
BT_GATT_PERM_READ, NULL, NULL, NULL),
BT_GATT_CCC(NULL,
BT_GATT_PERM_READ | BT_GATT_PERM_WRITE));
static void test_main(void)
{
int err;
device_sync_init(PERIPHERAL_ID);
err = bt_enable(NULL);
if (err != 0) {
FAIL("Bluetooth enable failed (err %d)\n", err);
}
err = bt_le_scan_start(BT_LE_SCAN_PASSIVE, device_found);
if (err != 0) {
FAIL("Scanning failed to start (err %d)\n", err);
}
printk("Scanning successfully started\n");
WAIT_FOR_FLAG(flag_is_connected);
err = bt_conn_set_security(g_conn, BT_SECURITY_L2);
if (err) {
FAIL("Failed to start encryption procedure\n");
}
WAIT_FOR_FLAG(flag_is_encrypted);
err = bt_eatt_connect(g_conn, CONFIG_BT_EATT_MAX);
if (err) {
FAIL("Sending credit based connection request failed (err %d)\n", err);
}
/* Wait for the channels to be connected */
while (bt_eatt_count(g_conn) < CONFIG_BT_EATT_MAX) {
k_sleep(K_TICKS(1));
}
printk("Waiting for sync\n");
device_sync_wait();
local_attr = &g_svc.attrs[1];
printk("############# Notification test\n");
for (int idx = 0; idx < NUM_NOTIF; idx++) {
printk("Notification %d\n", idx);
send_notification();
}
printk("############# Disconnect and reconnect\n");
for (int idx = 0; idx < CONFIG_BT_EATT_MAX; idx++) {
bt_eatt_disconnect_one(g_conn);
while (bt_eatt_count(g_conn) != (CONFIG_BT_EATT_MAX - idx)) {
k_sleep(K_TICKS(1));
}
}
printk("Connecting %d bearers\n", EATT_BEARERS_TEST);
err = bt_eatt_connect(g_conn, EATT_BEARERS_TEST);
if (err) {
FAIL("Sending credit based connection request failed (err %d)\n", err);
}
/* Wait for the channels to be connected */
while (bt_eatt_count(g_conn) < EATT_BEARERS_TEST) {
k_sleep(K_TICKS(1));
}
printk("############# Send notifications during discovery request\n");
gatt_discover();
while (!TEST_FLAG(flag_discover_complete)) {
printk("Notifying...\n");
send_notification();
}
printk("Sending final sync\n");
device_sync_send();
PASS("Client Passed\n");
}
static const struct bst_test_instance test_vcs[] = {
{
.test_id = "client",
.test_post_init_f = test_init,
.test_tick_f = test_tick,
.test_main_f = test_main
},
BSTEST_END_MARKER
};
struct bst_test_list *test_client_install(struct bst_test_list *tests)
{
return bst_add_tests(tests, test_vcs);
}

View file

@ -0,0 +1,67 @@
/*
* Copyright (c) 2022 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "common.h"
#include <zephyr/logging/log.h>
#define LOG_MODULE_NAME common
LOG_MODULE_REGISTER(LOG_MODULE_NAME, LOG_LEVEL_DBG);
void test_tick(bs_time_t HW_device_time)
{
if (bst_result != Passed) {
FAIL("test failed (not passed after %i seconds)\n", WAIT_TIME);
}
}
void test_init(void)
{
bst_ticker_set_next_tick_absolute(WAIT_TIME);
bst_result = In_progress;
}
/* Call in init functions*/
void device_sync_init(uint device_nbr)
{
uint peer;
if (device_nbr == CENTRAL_ID) {
peer = PERIPHERAL_ID;
} else {
peer = CENTRAL_ID;
}
uint dev_nbrs[BACK_CHANNELS] = { peer };
uint channel_nbrs[BACK_CHANNELS] = { 0 };
const uint *ch = bs_open_back_channel(device_nbr, dev_nbrs, channel_nbrs, BACK_CHANNELS);
if (!ch) {
LOG_ERR("bs_open_back_channel failed!");
}
}
/* Call it to make peer to proceed.*/
void device_sync_send(void)
{
uint8_t msg[1] = "S";
bs_bc_send_msg(0, msg, sizeof(msg));
}
/* Wait until peer send sync*/
void device_sync_wait(void)
{
int size_msg_received = 0;
uint8_t msg;
while (!size_msg_received) {
size_msg_received = bs_bc_is_msg_received(0);
k_sleep(K_MSEC(1));
}
bs_bc_receive_msg(0, &msg, size_msg_received);
}

View file

@ -0,0 +1,75 @@
/**
* Common functions and helpers for BSIM EATT notification tests
*
* Copyright (c) 2022 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/kernel.h>
#include "bs_types.h"
#include "bs_tracing.h"
#include "time_machine.h"
#include "bstests.h"
#include <zephyr/types.h>
#include <stddef.h>
#include <errno.h>
#include <zephyr/bluetooth/bluetooth.h>
#include <zephyr/bluetooth/hci.h>
#include <zephyr/bluetooth/conn.h>
#include <zephyr/bluetooth/uuid.h>
#include <zephyr/bluetooth/gatt.h>
#include "bs_pc_backchannel.h"
extern enum bst_result_t bst_result;
#define WAIT_TIME (30 * 1e6) /*seconds*/
#define CREATE_FLAG(flag) static atomic_t flag = (atomic_t)false
#define SET_FLAG(flag) (void)atomic_set(&flag, (atomic_t)true)
#define UNSET_FLAG(flag) (void)atomic_set(&flag, (atomic_t)false)
#define TEST_FLAG(flag) (atomic_get(&flag) == (atomic_t)true)
#define WAIT_FOR_FLAG(flag) \
while (!(bool)atomic_get(&flag)) { \
(void)k_sleep(K_MSEC(1)); \
}
#define FAIL(...) \
do { \
bst_result = Failed; \
bs_trace_error_time_line(__VA_ARGS__); \
} while (0)
#define PASS(...) \
do { \
bst_result = Passed; \
bs_trace_info_time(1, __VA_ARGS__); \
} while (0)
#define CHRC_SIZE 10
#define LONG_CHRC_SIZE 40
#define TEST_SERVICE_UUID \
BT_UUID_DECLARE_128(0x01, 0x23, 0x45, 0x67, 0x89, 0x01, 0x02, 0x03, \
0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x00, 0x00)
#define TEST_CHRC_UUID \
BT_UUID_DECLARE_128(0x01, 0x23, 0x45, 0x67, 0x89, 0x01, 0x02, 0x03, \
0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0xFF, 0x00)
#define TEST_LONG_CHRC_UUID \
BT_UUID_DECLARE_128(0x01, 0x23, 0x45, 0x67, 0x89, 0x01, 0x02, 0x03, \
0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0xFF, 0x11)
#define CENTRAL_ID 0
#define PERIPHERAL_ID 1
#define BACK_CHANNELS 1
void test_tick(bs_time_t HW_device_time);
void test_init(void);
void device_sync_init(uint device_nbr);
void device_sync_send(void);
void device_sync_wait(void);

View file

@ -0,0 +1,21 @@
/*
* Copyright (c) 2022 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "bstests.h"
extern struct bst_test_list *test_server_install(struct bst_test_list *tests);
extern struct bst_test_list *test_client_install(struct bst_test_list *tests);
bst_test_install_t test_installers[] = {
test_server_install,
test_client_install,
NULL
};
void main(void)
{
bst_main();
}

Some files were not shown because too many files have changed in this diff Show more