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