Tests: Bluetooth: Add another ISO frag test

This test create a setup where an ISO broadcaster will send fragmented
data and get stopped after sending the first fragment and repeating that
operation multiple time to verify that buffers are not leaked.

Signed-off-by: Théo Battrel <theo.battrel@nordicsemi.no>
This commit is contained in:
Théo Battrel 2024-08-13 12:32:37 +02:00 committed by Fabio Baltieri
commit f3dcaaee35
8 changed files with 417 additions and 1 deletions

View file

@ -4310,7 +4310,14 @@ k_tid_t bt_testing_tx_tid_get(void)
/* We now TX everything from the syswq */
return &k_sys_work_q.thread;
}
#endif
#if defined(CONFIG_BT_ISO)
void bt_testing_set_iso_mtu(uint16_t mtu)
{
bt_dev.le.iso_mtu = mtu;
}
#endif /* CONFIG_BT_ISO */
#endif /* CONFIG_BT_TESTING */
int bt_enable(bt_ready_cb_t cb)
{

View file

@ -21,6 +21,7 @@ ${ZEPHYR_BASE}/tests/bsim/bluetooth/host/security/compile.sh
app=tests/bsim/bluetooth/host/iso/cis compile
app=tests/bsim/bluetooth/host/iso/bis compile
app=tests/bsim/bluetooth/host/iso/frag compile
app=tests/bsim/bluetooth/host/iso/frag_2 compile
app=tests/bsim/bluetooth/host/misc/disable compile
run_in_background ${ZEPHYR_BASE}/tests/bsim/bluetooth/host/misc/disconnect/compile.sh

View file

@ -0,0 +1,22 @@
# SPDX-License-Identifier: Apache-2.0
cmake_minimum_required(VERSION 3.20.0)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(bsim_test_iso_frag_2)
add_subdirectory(${ZEPHYR_BASE}/tests/bsim/babblekit babblekit)
target_link_libraries(app PRIVATE babblekit)
target_sources(app PRIVATE
src/broadcaster.c
src/main.c
)
zephyr_include_directories(
${BSIM_COMPONENTS_PATH}/libUtilv1/src/
${BSIM_COMPONENTS_PATH}/libPhyComv1/src/
)
target_link_options(app PUBLIC -Wl,--wrap=bt_send)

View file

@ -0,0 +1,33 @@
CONFIG_LOG=y
CONFIG_BT_ISO_LOG_LEVEL_DBG=y
# CONFIG_BT_CONN_LOG_LEVEL_DBG=y
CONFIG_ASSERT=y
CONFIG_BT_TESTING=y
CONFIG_ARCH_POSIX_TRAP_ON_FATAL=y
CONFIG_BT=y
CONFIG_BT_DEVICE_NAME="ISO frag test 2"
CONFIG_BT_EXT_ADV=y
CONFIG_BT_PER_ADV=y
CONFIG_BT_PER_ADV_SYNC=y
CONFIG_BT_ISO_BROADCASTER=y
CONFIG_BT_ISO_TX_BUF_COUNT=2
CONFIG_BT_ISO_MAX_CHAN=2
CONFIG_BT_ISO_TX_MTU=100
CONFIG_BT_ISO_RX_MTU=100
# Controller ISO configs
CONFIG_BT_CTLR_ADV_ISO=y
CONFIG_BT_CTLR_ISO_TX_BUFFER_SIZE=255
CONFIG_BT_CTLR_ISO_TX_BUFFERS=10
# Controller Extended Advertising configs
CONFIG_BT_CTLR_ADV_EXT=y
CONFIG_BT_CTLR_ADV_DATA_LEN_MAX=191
# ISO Broadcaster Controller
CONFIG_BT_CTLR_ADV_PERIODIC=y
CONFIG_BT_CTLR_ADV_ISO_PDU_LEN_MAX=247

View file

@ -0,0 +1,277 @@
/*
* Copyright (c) 2024 Nordic Semiconductor
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/sys/byteorder.h>
#include <zephyr/bluetooth/bluetooth.h>
#include <zephyr/bluetooth/iso.h>
#include <zephyr/logging/log.h>
#include <zephyr/sys/byteorder.h>
#include "babblekit/flags.h"
#include "babblekit/testcase.h"
LOG_MODULE_REGISTER(broadcaster, LOG_LEVEL_INF);
static struct bt_iso_chan iso_chans[CONFIG_BT_ISO_MAX_CHAN];
static struct bt_iso_chan *default_chan = &iso_chans[0];
NET_BUF_POOL_FIXED_DEFINE(tx_pool, CONFIG_BT_ISO_TX_BUF_COUNT,
BT_ISO_SDU_BUF_SIZE(CONFIG_BT_ISO_TX_MTU),
CONFIG_BT_CONN_TX_USER_DATA_SIZE, NULL);
static DEFINE_FLAG(iso_connected);
static DEFINE_FLAG(first_frag);
static DEFINE_FLAG(sdu_sent);
extern void bt_conn_suspend_tx(bool suspend);
extern void bt_testing_set_iso_mtu(uint16_t mtu);
static int send_data(struct bt_iso_chan *chan)
{
static uint16_t seq;
struct net_buf *buf;
int err;
if (!IS_FLAG_SET(iso_connected)) {
/* TX has been aborted */
return -ENOTCONN;
}
buf = net_buf_alloc(&tx_pool, K_NO_WAIT);
TEST_ASSERT(buf != NULL, "Failed to allocate buffer");
net_buf_reserve(buf, BT_ISO_CHAN_SEND_RESERVE);
net_buf_add(buf, 40);
LOG_INF("Sending SDU (headroom %d)", net_buf_headroom(buf));
LOG_HEXDUMP_INF(buf->data, buf->len, "SDU payload");
err = bt_iso_chan_send(default_chan, buf, seq++);
return err;
}
static void iso_connected_cb(struct bt_iso_chan *chan)
{
LOG_INF("ISO Channel %p connected", chan);
SET_FLAG(iso_connected);
}
static void iso_disconnected_cb(struct bt_iso_chan *chan, uint8_t reason)
{
LOG_INF("ISO Channel %p disconnected (reason 0x%02x)", chan, reason);
UNSET_FLAG(iso_connected);
}
static void sdu_sent_cb(struct bt_iso_chan *chan)
{
SET_FLAG(sdu_sent);
}
static void create_ext_adv(struct bt_le_ext_adv **adv)
{
int err;
LOG_INF("Creating extended advertising set with periodic advertising");
/* Create a non-connectable advertising set */
err = bt_le_ext_adv_create(BT_LE_EXT_ADV_NCONN, NULL, adv);
TEST_ASSERT(err == 0, "Unable to create extended advertising set: %d", err);
/* Set periodic advertising parameters */
err = bt_le_per_adv_set_param(*adv, BT_LE_PER_ADV_PARAM(BT_GAP_PER_ADV_FAST_INT_MIN_2,
BT_GAP_PER_ADV_FAST_INT_MAX_2,
BT_LE_PER_ADV_OPT_NONE));
TEST_ASSERT(err == 0, "Failed to set periodic advertising parameters: %d", err);
}
static void start_ext_adv(struct bt_le_ext_adv *adv)
{
int err;
LOG_INF("Starting extended and periodic advertising");
/* Start extended advertising */
err = bt_le_ext_adv_start(adv, BT_LE_EXT_ADV_START_DEFAULT);
TEST_ASSERT(err == 0, "Failed to start extended advertising: %d", err);
/* FIXME: Temporary workaround to get around an assert in the controller
* Open issue: https://github.com/zephyrproject-rtos/zephyr/issues/72852
*/
k_sleep(K_MSEC(100));
/* Enable Periodic Advertising */
err = bt_le_per_adv_start(adv);
TEST_ASSERT(err == 0, "Failed to enable periodic advertising: %d", err);
}
static void create_big(struct bt_le_ext_adv *adv, size_t cnt, struct bt_iso_big **out_big)
{
const uint16_t latency_ms = 10U;
const uint16_t sdu_interval_us = 10U * USEC_PER_MSEC;
struct bt_iso_chan *channels[ARRAY_SIZE(iso_chans)];
struct bt_iso_big_create_param param = {
.packing = BT_ISO_PACKING_SEQUENTIAL,
.framing = BT_ISO_FRAMING_UNFRAMED,
.interval = sdu_interval_us,
.bis_channels = channels,
.latency = latency_ms,
.encryption = false,
.num_bis = cnt,
};
int err;
for (size_t i = 0U; i < cnt; i++) {
channels[i] = &iso_chans[i];
}
LOG_INF("Creating BIG");
err = bt_iso_big_create(adv, &param, out_big);
TEST_ASSERT(err == 0, "Failed to create BIG: %d", err);
WAIT_FOR_FLAG(iso_connected);
}
struct bt_le_ext_adv *adv;
struct bt_iso_big *big;
static struct bt_iso_chan_ops iso_ops = {
.disconnected = iso_disconnected_cb,
.connected = iso_connected_cb,
.sent = sdu_sent_cb,
};
static struct bt_iso_chan_io_qos iso_tx = {
.sdu = CONFIG_BT_ISO_TX_MTU,
.phy = BT_GAP_LE_PHY_2M,
.rtn = 1,
.path = NULL,
};
static struct bt_iso_chan_qos iso_qos = {
.tx = &iso_tx,
.rx = NULL,
};
static void init(void)
{
int err;
err = bt_enable(NULL);
TEST_ASSERT(err == 0, "Bluetooth enable failed: %d", err);
}
static void connect_iso(void)
{
bt_testing_set_iso_mtu(10);
for (size_t i = 0U; i < ARRAY_SIZE(iso_chans); i++) {
iso_chans[i].ops = &iso_ops;
iso_chans[i].qos = &iso_qos;
}
create_ext_adv(&adv);
create_big(adv, 1U, &big);
start_ext_adv(adv);
}
static void disconnect_iso(void)
{
int err;
err = bt_iso_big_terminate(big);
TEST_ASSERT(err == 0, "bt_iso_big_terminate failed (%d)", err);
err = bt_le_per_adv_stop(adv);
TEST_ASSERT(err == 0, "bt_le_per_adv_stop failed (%d)", err);
k_msleep(100);
err = bt_le_ext_adv_stop(adv);
TEST_ASSERT(err == 0, "bt_le_ext_adv_stop failed (%d)", err);
k_msleep(100);
err = bt_le_ext_adv_delete(adv);
TEST_ASSERT(err == 0, "bt_le_ext_adv_delete failed (%d)", err);
big = NULL;
adv = NULL;
}
void entrypoint_broadcaster(void)
{
/* Test purpose:
*
* Verifies that we are not leaking buffers when getting disconnected
* while sending fragmented ISO SDU.
*
* One device:
* - `broadcaster`: sends fragmented ISO SDUs
*
* Procedure:
* - initialize Bluetooth and a BIS
* - send a fragmented SDU
* - disconnect when the first fragment is sent
* - repeat TEST_ITERATIONS time
*
* [verdict]
* - no buffer is leaked and repeating the operation success
*/
int err;
uint8_t TEST_ITERATIONS = 4;
LOG_INF("Starting ISO HCI fragmentation test 2");
init();
for (size_t i = 0; i < TEST_ITERATIONS; i++) {
connect_iso();
/* Send an SDU */
err = send_data(default_chan);
TEST_ASSERT(!err, "Failed to send data w/o TS (err %d)", err);
/* Wait until we have sent the first SDU fragment. */
WAIT_FOR_FLAG(first_frag);
disconnect_iso();
bt_conn_suspend_tx(false);
k_msleep(1000);
}
TEST_PASS_AND_EXIT("Test passed");
}
void validate_no_iso_frag(struct net_buf *buf)
{
struct bt_hci_iso_hdr *hci_hdr = (void *)buf->data;
uint16_t handle = sys_le16_to_cpu(hci_hdr->handle);
uint8_t flags = bt_iso_flags(handle);
uint8_t pb_flag = bt_iso_flags_pb(flags);
TEST_ASSERT(pb_flag == BT_ISO_SINGLE, "Packet was fragmented");
}
int __real_bt_send(struct net_buf *buf);
int __wrap_bt_send(struct net_buf *buf)
{
struct bt_hci_iso_hdr *hci_hdr = (void *)buf->data;
if (bt_buf_get_type(buf) == BT_BUF_ISO_OUT) {
uint16_t handle = sys_le16_to_cpu(hci_hdr->handle);
uint8_t flags = bt_iso_flags(handle);
uint8_t pb_flag = bt_iso_flags_pb(flags);
if (pb_flag == BT_ISO_START) {
SET_FLAG(first_frag);
bt_conn_suspend_tx(true);
}
}
return __real_bt_send(buf);
}

View file

@ -0,0 +1,45 @@
/*
* Copyright (c) 2024 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/kernel.h>
#include "bs_tracing.h"
#include "bstests.h"
#include "babblekit/testcase.h"
extern void entrypoint_broadcaster(void);
extern enum bst_result_t bst_result;
static void test_end_cb(void)
{
if (bst_result != Passed) {
TEST_PRINT("Test has not passed.");
}
}
static const struct bst_test_instance entrypoints[] = {
{
.test_id = "broadcaster",
.test_delete_f = test_end_cb,
.test_main_f = entrypoint_broadcaster,
},
BSTEST_END_MARKER,
};
static struct bst_test_list *install(struct bst_test_list *tests)
{
return bst_add_tests(tests, entrypoints);
};
bst_test_install_t test_installers[] = {install, NULL};
int main(void)
{
bst_main();
return 0;
}

View file

@ -0,0 +1,13 @@
#!/usr/bin/env bash
# Copyright 2024 Nordic Semiconductor ASA
# SPDX-License-Identifier: Apache-2.0
set -eu
: "${ZEPHYR_BASE:?ZEPHYR_BASE must be defined}"
INCR_BUILD=1
source ${ZEPHYR_BASE}/tests/bsim/compile.source
app="$(guess_test_relpath)" compile
wait_for_background_jobs

View file

@ -0,0 +1,18 @@
#!/usr/bin/env bash
# Copyright (c) 2024 Nordic Semiconductor
# SPDX-License-Identifier: Apache-2.0
source ${ZEPHYR_BASE}/tests/bsim/sh_common.source
simulation_id="iso_frag_2"
verbosity_level=2
cd ${BSIM_OUT_PATH}/bin
Execute ./bs_2G4_phy_v1 -v=${verbosity_level} -s=${simulation_id} \
-D=1 -sim_length=30e6 $@
Execute ./bs_${BOARD_TS}_tests_bsim_bluetooth_host_iso_frag_2_prj_conf \
-v=${verbosity_level} -s=${simulation_id} -d=0 -testid=broadcaster
wait_for_background_jobs