diff --git a/tests/bsim/bluetooth/host/adv/compile.sh b/tests/bsim/bluetooth/host/adv/compile.sh index 17acdbd3d0a..b0adc922df6 100755 --- a/tests/bsim/bluetooth/host/adv/compile.sh +++ b/tests/bsim/bluetooth/host/adv/compile.sh @@ -12,6 +12,9 @@ source ${ZEPHYR_BASE}/tests/bsim/compile.source app=tests/bsim/bluetooth/host/adv/resume compile app=tests/bsim/bluetooth/host/adv/resume conf_file=prj_2.conf compile +app=tests/bsim/bluetooth/host/adv/resume2/connectable compile +app=tests/bsim/bluetooth/host/adv/resume2/connecter compile +app=tests/bsim/bluetooth/host/adv/resume2/dut compile app=tests/bsim/bluetooth/host/adv/chain compile app=tests/bsim/bluetooth/host/adv/extended conf_file=prj_advertiser.conf compile app=tests/bsim/bluetooth/host/adv/extended conf_file=prj_scanner.conf compile diff --git a/tests/bsim/bluetooth/host/adv/resume2/connectable/CMakeLists.txt b/tests/bsim/bluetooth/host/adv/resume2/connectable/CMakeLists.txt new file mode 100644 index 00000000000..3770a46a75a --- /dev/null +++ b/tests/bsim/bluetooth/host/adv/resume2/connectable/CMakeLists.txt @@ -0,0 +1,21 @@ +# Copyright (c) 2024 Nordic Semiconductor ASA +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.20.0) + +find_package(Zephyr HINTS $ENV{ZEPHYR_BASE}) +project(app) + +target_sources(app PRIVATE + main.c +) + +zephyr_include_directories( + ${BSIM_COMPONENTS_PATH}/libPhyComv1/src/ + ${BSIM_COMPONENTS_PATH}/libUtilv1/src/ +) + +add_subdirectory(${ZEPHYR_BASE}/tests/bluetooth/common/testlib testlib) +target_link_libraries(app PRIVATE + testlib +) diff --git a/tests/bsim/bluetooth/host/adv/resume2/connectable/main.c b/tests/bsim/bluetooth/host/adv/resume2/connectable/main.c new file mode 100644 index 00000000000..9347ec5ada6 --- /dev/null +++ b/tests/bsim/bluetooth/host/adv/resume2/connectable/main.c @@ -0,0 +1,47 @@ +/* Copyright (c) 2024 Nordic Semiconductor ASA + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +#include +#include + +/* @file + * + * This app advertises connectable with the name "connectable". + * It only receives one connection at a time. When the remote + * disconnects, it starts advertising again. + * + * This app is added to the simulation simply to be a target for + * a connection from the DUT. + */ + +int main(void) +{ + int err; + + err = bt_enable(NULL); + __ASSERT_NO_MSG(!err); + + err = bt_set_name("connectable"); + __ASSERT_NO_MSG(!err); + + while (true) { + struct bt_conn *conn = NULL; + + bt_testlib_conn_wait_free(); + + err = bt_testlib_adv_conn( + &conn, BT_ID_DEFAULT, + (BT_LE_ADV_OPT_USE_NAME | BT_LE_ADV_OPT_FORCE_NAME_IN_AD)); + __ASSERT_NO_MSG(!err); + + bt_testlib_wait_disconnected(conn); + bt_testlib_conn_unref(&conn); + } + + return 0; +} diff --git a/tests/bsim/bluetooth/host/adv/resume2/connectable/prj.conf b/tests/bsim/bluetooth/host/adv/resume2/connectable/prj.conf new file mode 100644 index 00000000000..5b1934336a4 --- /dev/null +++ b/tests/bsim/bluetooth/host/adv/resume2/connectable/prj.conf @@ -0,0 +1,21 @@ +CONFIG_TEST=y + +CONFIG_BT=y +CONFIG_BT_PERIPHERAL=y + +CONFIG_ASSERT=y +CONFIG_BT_TESTING=y +CONFIG_LOG=y + +CONFIG_BT_DEVICE_NAME_DYNAMIC=y + +CONFIG_BT_EXT_ADV=y +CONFIG_BT_PRIVACY=n +CONFIG_BT_SCAN_WITH_IDENTITY=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=1 +CONFIG_BT_ID_MAX=1 diff --git a/tests/bsim/bluetooth/host/adv/resume2/connecter/CMakeLists.txt b/tests/bsim/bluetooth/host/adv/resume2/connecter/CMakeLists.txt new file mode 100644 index 00000000000..3770a46a75a --- /dev/null +++ b/tests/bsim/bluetooth/host/adv/resume2/connecter/CMakeLists.txt @@ -0,0 +1,21 @@ +# Copyright (c) 2024 Nordic Semiconductor ASA +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.20.0) + +find_package(Zephyr HINTS $ENV{ZEPHYR_BASE}) +project(app) + +target_sources(app PRIVATE + main.c +) + +zephyr_include_directories( + ${BSIM_COMPONENTS_PATH}/libPhyComv1/src/ + ${BSIM_COMPONENTS_PATH}/libUtilv1/src/ +) + +add_subdirectory(${ZEPHYR_BASE}/tests/bluetooth/common/testlib testlib) +target_link_libraries(app PRIVATE + testlib +) diff --git a/tests/bsim/bluetooth/host/adv/resume2/connecter/main.c b/tests/bsim/bluetooth/host/adv/resume2/connecter/main.c new file mode 100644 index 00000000000..ffdcb3183c5 --- /dev/null +++ b/tests/bsim/bluetooth/host/adv/resume2/connecter/main.c @@ -0,0 +1,66 @@ +/* Copyright (c) 2024 Nordic Semiconductor ASA + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include + +#include +#include + +LOG_MODULE_REGISTER(connecter, LOG_LEVEL_INF); + +/* @file + * + * This app scans for a device with the name "dut" and connects + * to it. It then waits for the connection to be disconnected, + * before starting over. + * + * This app is added to the simulation simply to exercise the + * the DUT's connectable advertiser. + * + * Multiple instances of this app are added to the simulation, + * to exercise BT_MAX_CONN of the DUT. + */ + +int main(void) +{ + int err; + + err = bt_enable(NULL); + __ASSERT_NO_MSG(!err); + + while (true) { + bt_addr_le_t result; + struct bt_conn *conn = NULL; + + bt_testlib_conn_wait_free(); + + err = bt_testlib_scan_find_name(&result, "dut"); + __ASSERT_NO_MSG(!err); + + /* The above scan will never timeout, but the below connect has + * a built-in timeout in the stack. + * + * The timeout causes `BT_HCI_ERR_UNKNOWN_CONN_ID`. + * + * The timeout is a good thing in this app. Maybe the DUT is + * going to change its address, so we should scan for the name + * again. + */ + + err = bt_testlib_connect(&result, &conn); + if (err) { + __ASSERT_NO_MSG(err == BT_HCI_ERR_UNKNOWN_CONN_ID); + } + + if (conn) { + bt_testlib_wait_disconnected(conn); + bt_testlib_conn_unref(&conn); + } + } + + return 0; +} diff --git a/tests/bsim/bluetooth/host/adv/resume2/connecter/prj.conf b/tests/bsim/bluetooth/host/adv/resume2/connecter/prj.conf new file mode 100644 index 00000000000..3aa128d9855 --- /dev/null +++ b/tests/bsim/bluetooth/host/adv/resume2/connecter/prj.conf @@ -0,0 +1,19 @@ +CONFIG_TEST=y + +CONFIG_BT=y +CONFIG_BT_CENTRAL=y + +CONFIG_ASSERT=y +CONFIG_BT_TESTING=y +CONFIG_LOG=y + +CONFIG_BT_EXT_ADV=n +CONFIG_BT_PRIVACY=n +CONFIG_BT_SCAN_WITH_IDENTITY=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=1 +CONFIG_BT_ID_MAX=1 diff --git a/tests/bsim/bluetooth/host/adv/resume2/dut/CMakeLists.txt b/tests/bsim/bluetooth/host/adv/resume2/dut/CMakeLists.txt new file mode 100644 index 00000000000..3770a46a75a --- /dev/null +++ b/tests/bsim/bluetooth/host/adv/resume2/dut/CMakeLists.txt @@ -0,0 +1,21 @@ +# Copyright (c) 2024 Nordic Semiconductor ASA +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.20.0) + +find_package(Zephyr HINTS $ENV{ZEPHYR_BASE}) +project(app) + +target_sources(app PRIVATE + main.c +) + +zephyr_include_directories( + ${BSIM_COMPONENTS_PATH}/libPhyComv1/src/ + ${BSIM_COMPONENTS_PATH}/libUtilv1/src/ +) + +add_subdirectory(${ZEPHYR_BASE}/tests/bluetooth/common/testlib testlib) +target_link_libraries(app PRIVATE + testlib +) diff --git a/tests/bsim/bluetooth/host/adv/resume2/dut/main.c b/tests/bsim/bluetooth/host/adv/resume2/dut/main.c new file mode 100644 index 00000000000..9c0b69f6d11 --- /dev/null +++ b/tests/bsim/bluetooth/host/adv/resume2/dut/main.c @@ -0,0 +1,200 @@ +/* Copyright (c) 2024 Nordic Semiconductor ASA + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +extern enum bst_result_t bst_result; + +LOG_MODULE_REGISTER(dut, LOG_LEVEL_INF); + +atomic_t connected_count; + +static void on_connected(struct bt_conn *conn, uint8_t conn_err) +{ + atomic_t count = atomic_inc(&connected_count) + 1; + + LOG_INF("Connected. Current count %d", count); +} + +static void on_disconnected(struct bt_conn *conn, uint8_t reason) +{ + atomic_t count = atomic_dec(&connected_count) - 1; + + LOG_INF("Disconnected. Current count %d", count); +} + +BT_CONN_CB_DEFINE(conn_callbacks) = { + .connected = on_connected, + .disconnected = on_disconnected, +}; + +static void disconnect_all(void) +{ + for (size_t i = 0; i < CONFIG_BT_MAX_CONN; i++) { + int err; + struct bt_conn *conn = bt_testlib_conn_unindex(BT_CONN_TYPE_LE, i); + + if (conn) { + err = bt_testlib_disconnect(&conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN); + __ASSERT_NO_MSG(!err); + } + } +} + +int main(void) +{ + int err; + bt_addr_le_t connectable_addr; + struct bt_conn *conn = NULL; + + bst_result = In_progress; + + err = bt_enable(NULL); + __ASSERT_NO_MSG(!err); + + err = bt_set_name("dut"); + __ASSERT_NO_MSG(!err); + + LOG_INF("---------- Test setup ----------"); + + LOG_INF("Environment test: Advertiser fills connection capacity."); + + /* `bt_le_adv_start` is invoked once, and.. */ + + err = bt_le_adv_start(BT_LE_ADV_CONN_NAME_AD, NULL, 0, NULL, 0); + __ASSERT_NO_MSG(!err); + + /* .. the advertiser shall autoresume. Since it's not + * stopped, it shall continue receivng connections until + * the stack runs out of connection objects. + */ + + LOG_INF("Waiting for connections..."); + while (atomic_get(&connected_count) < CONFIG_BT_MAX_CONN) { + k_msleep(1000); + } + + LOG_INF("Environment test done"); + + LOG_INF("Environment test: Disconnect one to see that it comes back"); + + /* Disconnect one of the connections. It does matter + * which, but object with index 0 is chosen for + * simplicity. + */ + + conn = bt_testlib_conn_unindex(BT_CONN_TYPE_LE, 0); + __ASSERT_NO_MSG(conn); + + /* Disconnect, but wait with calling unref.. */ + + err = bt_conn_disconnect(conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN); + __ASSERT_NO_MSG(!err); + bt_testlib_wait_disconnected(conn); + + /* Simulate a delayed unref. We delay to make sure the resume is not + * triggered by disconnection, but by a connection object becoming + * available. + */ + + k_sleep(K_SECONDS(10)); + + bt_testlib_conn_unref(&conn); + + /* Since there is a free connection object again, the + * advertiser shall automatically resume and receive a + * new connection. + */ + + LOG_INF("Waiting for connections..."); + while (atomic_get(&connected_count) < CONFIG_BT_MAX_CONN) { + k_msleep(1000); + } + + LOG_INF("Environment test done"); + + + LOG_INF("Clean up"); + + err = bt_le_adv_stop(); + __ASSERT_NO_MSG(!err); + + disconnect_all(); + + LOG_INF("Cleanup done"); + + + LOG_INF("Setup step: Connect one central connection"); + + err = bt_testlib_scan_find_name(&connectable_addr, "connectable"); + __ASSERT_NO_MSG(!err); + + err = bt_testlib_connect(&connectable_addr, &conn); + __ASSERT_NO_MSG(!err); + + LOG_INF("Setup step done"); + + + LOG_INF("Setup step: Start advertiser. Let it fill the connection limit."); + + /* With one connection slot taken by the central role, we fill the rest. */ + + err = bt_le_adv_start(BT_LE_ADV_CONN_NAME_AD, NULL, 0, NULL, 0); + __ASSERT_NO_MSG(!err); + + LOG_INF("Waiting for connections..."); + while (atomic_get(&connected_count) < CONFIG_BT_MAX_CONN) { + k_sleep(K_SECONDS(1)); + } + + LOG_INF("Setup step done"); + + + LOG_INF("---------- Test start ----------"); + + LOG_INF("Disconnect, wait and connect the central connection."); + + /* In this situation, disconnecting the central role should not allow + * the advertiser to resume. This behavior was introduced in 6a79c3deae. + */ + + LOG_INF("Poke: Disconnect"); + err = bt_testlib_disconnect(&conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN); + __ASSERT_NO_MSG(!err); + + LOG_INF("Poke: Wait to bait the advertiser"); + k_sleep(K_SECONDS(5)); + + LOG_INF("Observe: Connect"); + err = bt_testlib_connect(&connectable_addr, &conn); + if (err) { + /* If the test fails, it's because the advertiser 'stole' the + * central's connection slot. + */ + __ASSERT_NO_MSG(err == -ENOMEM); + + LOG_ERR("Fault: Advertiser stole the connection slot"); + bs_trace_silent_exit(1); + } + + bst_result = Passed; + LOG_INF("Test passed"); + bs_trace_silent_exit(0); + + return 0; +} diff --git a/tests/bsim/bluetooth/host/adv/resume2/dut/prj.conf b/tests/bsim/bluetooth/host/adv/resume2/dut/prj.conf new file mode 100644 index 00000000000..7553df7062b --- /dev/null +++ b/tests/bsim/bluetooth/host/adv/resume2/dut/prj.conf @@ -0,0 +1,22 @@ +CONFIG_TEST=y + +CONFIG_BT=y +CONFIG_BT_PERIPHERAL=y +CONFIG_BT_CENTRAL=y + +CONFIG_BT_DEVICE_NAME_DYNAMIC=y + +CONFIG_ASSERT=y +CONFIG_BT_TESTING=y +CONFIG_LOG=y + +CONFIG_BT_EXT_ADV=n +CONFIG_BT_PRIVACY=n +CONFIG_BT_SCAN_WITH_IDENTITY=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=3 +CONFIG_BT_ID_MAX=1 diff --git a/tests/bsim/bluetooth/host/adv/resume2/test_scripts/_build.sh b/tests/bsim/bluetooth/host/adv/resume2/test_scripts/_build.sh new file mode 100755 index 00000000000..f9b41b2e7f2 --- /dev/null +++ b/tests/bsim/bluetooth/host/adv/resume2/test_scripts/_build.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash +# Copyright (c) 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)"/connectable compile +app="$(guess_test_relpath)"/connecter compile +app="$(guess_test_relpath)"/dut compile + +wait_for_background_jobs diff --git a/tests/bsim/bluetooth/host/adv/resume2/test_scripts/run.sh b/tests/bsim/bluetooth/host/adv/resume2/test_scripts/run.sh new file mode 100755 index 00000000000..1812c0207a2 --- /dev/null +++ b/tests/bsim/bluetooth/host/adv/resume2/test_scripts/run.sh @@ -0,0 +1,36 @@ +#!/usr/bin/env bash + +# Copyright (c) 2024 Nordic Semiconductor ASA +# SPDX-License-Identifier: Apache-2.0 + +set -eu + +source ${ZEPHYR_BASE}/tests/bsim/sh_common.source + +verbosity_level=2 +simulation_id="resume2" +dut_exe="${BSIM_OUT_PATH}/bin/bs_${BOARD_TS}_$(guess_test_long_name)_dut_prj_conf" +connecter_exe="${BSIM_OUT_PATH}/bin/bs_${BOARD_TS}_$(guess_test_long_name)_connecter_prj_conf" +connectable_exe="${BSIM_OUT_PATH}/bin/bs_${BOARD_TS}_$(guess_test_long_name)_connectable_prj_conf" + +cd ${BSIM_OUT_PATH}/bin + +Execute "$dut_exe" \ + -v=${verbosity_level} -s="${simulation_id}" -d=0 -RealEncryption=1 + +Execute "$connecter_exe" \ + -v=${verbosity_level} -s="${simulation_id}" -d=1 -RealEncryption=1 + +Execute "$connecter_exe" \ + -v=${verbosity_level} -s="${simulation_id}" -d=2 -RealEncryption=1 + +Execute "$connecter_exe" \ + -v=${verbosity_level} -s="${simulation_id}" -d=3 -RealEncryption=1 + +Execute "$connectable_exe" \ + -v=${verbosity_level} -s="${simulation_id}" -d=4 -RealEncryption=1 + +Execute ./bs_2G4_phy_v1 -v=${verbosity_level} -s="${simulation_id}" \ + -D=5 -sim_length=60e6 $@ + +wait_for_background_jobs