From f3dcaaee35094e9c3395ae9a59a0f08b2d0c9f03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20Battrel?= Date: Tue, 13 Aug 2024 12:32:37 +0200 Subject: [PATCH] Tests: Bluetooth: Add another ISO frag test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- subsys/bluetooth/host/hci_core.c | 9 +- tests/bsim/bluetooth/host/compile.sh | 1 + .../bluetooth/host/iso/frag_2/CMakeLists.txt | 22 ++ tests/bsim/bluetooth/host/iso/frag_2/prj.conf | 33 +++ .../host/iso/frag_2/src/broadcaster.c | 277 ++++++++++++++++++ .../bsim/bluetooth/host/iso/frag_2/src/main.c | 45 +++ .../host/iso/frag_2/tests_scripts/_compile.sh | 13 + .../host/iso/frag_2/tests_scripts/bis.sh | 18 ++ 8 files changed, 417 insertions(+), 1 deletion(-) create mode 100644 tests/bsim/bluetooth/host/iso/frag_2/CMakeLists.txt create mode 100644 tests/bsim/bluetooth/host/iso/frag_2/prj.conf create mode 100644 tests/bsim/bluetooth/host/iso/frag_2/src/broadcaster.c create mode 100644 tests/bsim/bluetooth/host/iso/frag_2/src/main.c create mode 100755 tests/bsim/bluetooth/host/iso/frag_2/tests_scripts/_compile.sh create mode 100755 tests/bsim/bluetooth/host/iso/frag_2/tests_scripts/bis.sh diff --git a/subsys/bluetooth/host/hci_core.c b/subsys/bluetooth/host/hci_core.c index 180c5f7a4e0..46cb4254313 100644 --- a/subsys/bluetooth/host/hci_core.c +++ b/subsys/bluetooth/host/hci_core.c @@ -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) { diff --git a/tests/bsim/bluetooth/host/compile.sh b/tests/bsim/bluetooth/host/compile.sh index 6c6348aa030..dfb637078c1 100755 --- a/tests/bsim/bluetooth/host/compile.sh +++ b/tests/bsim/bluetooth/host/compile.sh @@ -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 diff --git a/tests/bsim/bluetooth/host/iso/frag_2/CMakeLists.txt b/tests/bsim/bluetooth/host/iso/frag_2/CMakeLists.txt new file mode 100644 index 00000000000..9fa69bf40c7 --- /dev/null +++ b/tests/bsim/bluetooth/host/iso/frag_2/CMakeLists.txt @@ -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) diff --git a/tests/bsim/bluetooth/host/iso/frag_2/prj.conf b/tests/bsim/bluetooth/host/iso/frag_2/prj.conf new file mode 100644 index 00000000000..4ae63917a5d --- /dev/null +++ b/tests/bsim/bluetooth/host/iso/frag_2/prj.conf @@ -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 diff --git a/tests/bsim/bluetooth/host/iso/frag_2/src/broadcaster.c b/tests/bsim/bluetooth/host/iso/frag_2/src/broadcaster.c new file mode 100644 index 00000000000..6b70c3fd056 --- /dev/null +++ b/tests/bsim/bluetooth/host/iso/frag_2/src/broadcaster.c @@ -0,0 +1,277 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include + +#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, ¶m, 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); +} diff --git a/tests/bsim/bluetooth/host/iso/frag_2/src/main.c b/tests/bsim/bluetooth/host/iso/frag_2/src/main.c new file mode 100644 index 00000000000..e87fbbbdba2 --- /dev/null +++ b/tests/bsim/bluetooth/host/iso/frag_2/src/main.c @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#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; +} diff --git a/tests/bsim/bluetooth/host/iso/frag_2/tests_scripts/_compile.sh b/tests/bsim/bluetooth/host/iso/frag_2/tests_scripts/_compile.sh new file mode 100755 index 00000000000..f46c09eab66 --- /dev/null +++ b/tests/bsim/bluetooth/host/iso/frag_2/tests_scripts/_compile.sh @@ -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 diff --git a/tests/bsim/bluetooth/host/iso/frag_2/tests_scripts/bis.sh b/tests/bsim/bluetooth/host/iso/frag_2/tests_scripts/bis.sh new file mode 100755 index 00000000000..c6307d33027 --- /dev/null +++ b/tests/bsim/bluetooth/host/iso/frag_2/tests_scripts/bis.sh @@ -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