Bluetooth: Host: Add ISO HCI fragmentation test

Purpose is to verify we don't fragment ISO SDUs when they should fit the
controller's MTU.

Signed-off-by: Jonathan Rico <jonathan.rico@nordicsemi.no>
This commit is contained in:
Jonathan Rico 2024-08-05 10:38:54 +02:00 committed by Anas Nashif
commit 76ece09cb4
7 changed files with 364 additions and 0 deletions

View file

@ -20,6 +20,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/misc/disable compile
app=tests/bsim/bluetooth/host/misc/disconnect/dut compile

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)
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,19 @@
CONFIG_LOG=y
CONFIG_ASSERT=y
CONFIG_ARCH_POSIX_TRAP_ON_FATAL=y
CONFIG_BT=y
CONFIG_BT_DEVICE_NAME="ISO frag test"
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_MTU=5
CONFIG_BT_ISO_LOG_LEVEL_DBG=y
# Controller ISO configs
CONFIG_BT_CTLR_ADV_ISO=y
CONFIG_BT_CTLR_ISO_TX_BUFFER_SIZE=20

View file

@ -0,0 +1,246 @@
/*
* 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 "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(sdu_sent);
static int send_data(struct bt_iso_chan *chan, bool ts)
{
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_le32(buf, 0xdeadbeef);
net_buf_add_u8(buf, 0x11);
LOG_INF("Sending SDU with%s timestamp (headroom %d)", ts ? "" : "out",
net_buf_headroom(buf));
LOG_HEXDUMP_INF(buf->data, buf->len, "SDU payload");
if (ts) {
err = bt_iso_chan_send_ts(default_chan, buf, seq++, 0x00eeeee);
} else {
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);
}
static void init(void)
{
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,
};
int err;
err = bt_enable(NULL);
TEST_ASSERT(err == 0, "Bluetooth enable failed: %d", err);
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);
}
void entrypoint_broadcaster(void)
{
/* Test purpose:
*
* Verifies that we are able to send an ISO SDU that exactly fits the
* configured TX MTU, without any HCI fragmentation.
*
* One device:
* - `broadcaster`: sends two ISO SDUs
*
* Procedure:
* - initialize Bluetooth and a BIS
* - send an SDU without timestamp
* - send an SDU with timestamp
*
* [verdict]
* - no fragmentation is observed on the HCI layer
*/
int err;
LOG_INF("Starting ISO HCI fragmentation test");
init();
/* Send an SDU without timestamp */
err = send_data(default_chan, 0);
TEST_ASSERT(!err, "Failed to send data w/o TS (err %d)", err);
/* Wait until we have sent the SDU.
* Using linker wrapping, we verify that no fragmentation happens.
*/
WAIT_FOR_FLAG(sdu_sent);
/* Send an SDU with timestamp */
err = send_data(default_chan, 1);
TEST_ASSERT(!err, "Failed to send data w/ TS (err %d)", err);
/* Wait until we have sent the SDU.
* Using linker wrapping, we verify that no fragmentation happens.
*/
WAIT_FOR_FLAG(sdu_sent);
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)
{
LOG_HEXDUMP_DBG(buf->data, buf->len, "h->c");
if (bt_buf_get_type(buf) == BT_BUF_ISO_OUT) {
validate_no_iso_frag(buf);
}
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 2023 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) 2023 Nordic Semiconductor
# SPDX-License-Identifier: Apache-2.0
source ${ZEPHYR_BASE}/tests/bsim/sh_common.source
simulation_id="iso_frag"
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_prj_conf \
-v=${verbosity_level} -s=${simulation_id} -d=0 -testid=broadcaster
wait_for_background_jobs