tests: bsim: bluetooth: host: att: Add ATT timeout test
This commit tests that the host correctly disconnects from peer when ATT timeout happens. Signed-off-by: Pavel Vasilyev <pavel.vasilyev@nordicsemi.no>
This commit is contained in:
parent
f712bde122
commit
f7e8a8717b
7 changed files with 380 additions and 1 deletions
|
@ -10,7 +10,8 @@
|
||||||
|
|
||||||
#define BT_EATT_PSM 0x27
|
#define BT_EATT_PSM 0x27
|
||||||
#define BT_ATT_DEFAULT_LE_MTU 23
|
#define BT_ATT_DEFAULT_LE_MTU 23
|
||||||
#define BT_ATT_TIMEOUT K_SECONDS(30)
|
#define BT_ATT_TIMEOUT_SEC 30
|
||||||
|
#define BT_ATT_TIMEOUT K_SECONDS(BT_ATT_TIMEOUT_SEC)
|
||||||
|
|
||||||
/* Local ATT Rx MTU
|
/* Local ATT Rx MTU
|
||||||
*
|
*
|
||||||
|
|
|
@ -22,5 +22,6 @@ run_in_background ${ZEPHYR_BASE}/tests/bsim/bluetooth/host/att/sequential/compil
|
||||||
run_in_background ${ZEPHYR_BASE}/tests/bsim/bluetooth/host/att/pipeline/compile.sh
|
run_in_background ${ZEPHYR_BASE}/tests/bsim/bluetooth/host/att/pipeline/compile.sh
|
||||||
run_in_background ${ZEPHYR_BASE}/tests/bsim/bluetooth/host/att/long_read/compile.sh
|
run_in_background ${ZEPHYR_BASE}/tests/bsim/bluetooth/host/att/long_read/compile.sh
|
||||||
run_in_background ${ZEPHYR_BASE}/tests/bsim/bluetooth/host/att/open_close/compile.sh
|
run_in_background ${ZEPHYR_BASE}/tests/bsim/bluetooth/host/att/open_close/compile.sh
|
||||||
|
run_in_background ${ZEPHYR_BASE}/tests/bsim/bluetooth/host/att/timeout/compile.sh
|
||||||
|
|
||||||
wait_for_background_jobs
|
wait_for_background_jobs
|
||||||
|
|
24
tests/bsim/bluetooth/host/att/timeout/CMakeLists.txt
Normal file
24
tests/bsim/bluetooth/host/att/timeout/CMakeLists.txt
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
cmake_minimum_required(VERSION 3.20.0)
|
||||||
|
|
||||||
|
find_package(Zephyr HINTS $ENV{ZEPHYR_BASE})
|
||||||
|
project(app)
|
||||||
|
|
||||||
|
add_subdirectory(${ZEPHYR_BASE}/tests/bluetooth/common/testlib testlib)
|
||||||
|
|
||||||
|
target_sources(app PRIVATE
|
||||||
|
../long_read/bs_main.c
|
||||||
|
../long_read/bs_sync.c
|
||||||
|
main.c
|
||||||
|
)
|
||||||
|
|
||||||
|
zephyr_include_directories(
|
||||||
|
${BSIM_COMPONENTS_PATH}/libPhyComv1/src/
|
||||||
|
${BSIM_COMPONENTS_PATH}/libUtilv1/src/
|
||||||
|
../long_read/
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(app PRIVATE
|
||||||
|
testlib
|
||||||
|
)
|
14
tests/bsim/bluetooth/host/att/timeout/compile.sh
Executable file
14
tests/bsim/bluetooth/host/att/timeout/compile.sh
Executable file
|
@ -0,0 +1,14 @@
|
||||||
|
#!/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
|
292
tests/bsim/bluetooth/host/att/timeout/main.c
Normal file
292
tests/bsim/bluetooth/host/att/timeout/main.c
Normal file
|
@ -0,0 +1,292 @@
|
||||||
|
/* Copyright (c) 2024 Nordic Semiconductor ASA
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <argparse.h>
|
||||||
|
#include <zephyr/bluetooth/gatt.h>
|
||||||
|
#include <zephyr/logging/log.h>
|
||||||
|
#include <zephyr/sys/__assert.h>
|
||||||
|
#include <zephyr/settings/settings.h>
|
||||||
|
#include <zephyr/sys/byteorder.h>
|
||||||
|
#include <zephyr/bluetooth/bluetooth.h>
|
||||||
|
|
||||||
|
#include "host/att_internal.h"
|
||||||
|
|
||||||
|
#include "testlib/adv.h"
|
||||||
|
#include "testlib/att_read.h"
|
||||||
|
#include "testlib/att_write.h"
|
||||||
|
#include "bs_macro.h"
|
||||||
|
#include "bs_sync.h"
|
||||||
|
#include <testlib/conn.h>
|
||||||
|
#include "testlib/log_utils.h"
|
||||||
|
#include "testlib/scan.h"
|
||||||
|
#include "testlib/security.h"
|
||||||
|
|
||||||
|
/* This test uses system asserts to fail tests. */
|
||||||
|
BUILD_ASSERT(__ASSERT_ON);
|
||||||
|
|
||||||
|
LOG_MODULE_REGISTER(main, LOG_LEVEL_DBG);
|
||||||
|
|
||||||
|
#define CENTRAL_DEVICE_NBR 0
|
||||||
|
#define PERIPHERAL_DEVICE_NBR 1
|
||||||
|
|
||||||
|
#define UUID_1 \
|
||||||
|
BT_UUID_DECLARE_128(0xdb, 0x1f, 0xe2, 0x52, 0xf3, 0xc6, 0x43, 0x66, 0xb3, 0x92, 0x5d, \
|
||||||
|
0xc6, 0xe7, 0xc9, 0x59, 0x9d)
|
||||||
|
|
||||||
|
#define UUID_2 \
|
||||||
|
BT_UUID_DECLARE_128(0x3f, 0xa4, 0x7f, 0x44, 0x2e, 0x2a, 0x43, 0x05, 0xab, 0x38, 0x07, \
|
||||||
|
0x8d, 0x16, 0xbf, 0x99, 0xf1)
|
||||||
|
|
||||||
|
static bool trigger_att_timeout;
|
||||||
|
static K_SEM_DEFINE(disconnected_sem, 0, 1);
|
||||||
|
|
||||||
|
static ssize_t read_chrc(struct bt_conn *conn, const struct bt_gatt_attr *attr, void *buf,
|
||||||
|
uint16_t buf_len, uint16_t offset)
|
||||||
|
{
|
||||||
|
ssize_t read_len;
|
||||||
|
|
||||||
|
LOG_INF("ATT timeout will %sbe triggered", trigger_att_timeout ? "" : "not ");
|
||||||
|
|
||||||
|
if (trigger_att_timeout) {
|
||||||
|
/* Sleep longer than ATT Timeout (section 3.3.3). */
|
||||||
|
k_sleep(K_SECONDS(BT_ATT_TIMEOUT_SEC + 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
__ASSERT_NO_MSG(offset == 0);
|
||||||
|
read_len = buf_len;
|
||||||
|
|
||||||
|
__ASSERT_NO_MSG(read_len >= 2);
|
||||||
|
sys_put_le16(read_len, buf);
|
||||||
|
|
||||||
|
return read_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct bt_gatt_attr attrs[] = {
|
||||||
|
BT_GATT_PRIMARY_SERVICE(UUID_1),
|
||||||
|
BT_GATT_CHARACTERISTIC(UUID_2, BT_GATT_CHRC_READ, BT_GATT_PERM_READ, read_chrc, NULL, NULL),
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct bt_gatt_service svc = {
|
||||||
|
.attrs = attrs,
|
||||||
|
.attr_count = ARRAY_SIZE(attrs),
|
||||||
|
};
|
||||||
|
|
||||||
|
static void bs_sync_all_log(char *log_msg)
|
||||||
|
{
|
||||||
|
/* Everyone meets here. */
|
||||||
|
bt_testlib_bs_sync_all();
|
||||||
|
|
||||||
|
if (get_device_nbr() == 0) {
|
||||||
|
LOG_WRN("Sync point: %s", log_msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Everyone waits for d0 to finish logging. */
|
||||||
|
bt_testlib_bs_sync_all();
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void bt_enable_quiet(void)
|
||||||
|
{
|
||||||
|
bt_testlib_log_level_set("bt_hci_core", LOG_LEVEL_ERR);
|
||||||
|
bt_testlib_log_level_set("bt_id", LOG_LEVEL_ERR);
|
||||||
|
|
||||||
|
EXPECT_ZERO(bt_enable(NULL));
|
||||||
|
|
||||||
|
bt_testlib_log_level_set("bt_hci_core", LOG_LEVEL_INF);
|
||||||
|
bt_testlib_log_level_set("bt_id", LOG_LEVEL_INF);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct bt_conn *peripheral_setup(enum bt_att_chan_opt bearer, bool timeout)
|
||||||
|
{
|
||||||
|
struct bt_conn *conn = NULL;
|
||||||
|
|
||||||
|
EXPECT_ZERO(bt_testlib_adv_conn(&conn, BT_ID_DEFAULT, bt_get_name()));
|
||||||
|
|
||||||
|
trigger_att_timeout = timeout;
|
||||||
|
|
||||||
|
return conn;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct bt_conn *central_setup(enum bt_att_chan_opt bearer, bool timeout)
|
||||||
|
{
|
||||||
|
bt_addr_le_t adva;
|
||||||
|
struct bt_conn *conn = NULL;
|
||||||
|
|
||||||
|
EXPECT_ZERO(bt_testlib_scan_find_name(&adva, "peripheral"));
|
||||||
|
EXPECT_ZERO(bt_testlib_connect(&adva, &conn));
|
||||||
|
|
||||||
|
/* Establish EATT bearers. */
|
||||||
|
EXPECT_ZERO(bt_testlib_secure(conn, BT_SECURITY_L2));
|
||||||
|
|
||||||
|
while (bt_eatt_count(conn) == 0) {
|
||||||
|
k_msleep(100);
|
||||||
|
};
|
||||||
|
|
||||||
|
return conn;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void central_read(struct bt_conn *conn, enum bt_att_chan_opt bearer, bool timeout)
|
||||||
|
{
|
||||||
|
uint16_t actual_read_len;
|
||||||
|
uint16_t remote_read_send_len;
|
||||||
|
uint16_t handle = 0;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
NET_BUF_SIMPLE_DEFINE(attr_value, sizeof(remote_read_send_len));
|
||||||
|
|
||||||
|
err = bt_testlib_att_read_by_type_sync(&attr_value, &actual_read_len, &handle, NULL, conn,
|
||||||
|
bearer, UUID_2, BT_ATT_FIRST_ATTRIBUTE_HANDLE,
|
||||||
|
BT_ATT_LAST_ATTRIBUTE_HANDLE);
|
||||||
|
|
||||||
|
if (timeout) {
|
||||||
|
__ASSERT(err == BT_ATT_ERR_UNLIKELY, "Unexpected error %d", err);
|
||||||
|
} else {
|
||||||
|
__ASSERT(!err, "Unexpected error %d", err);
|
||||||
|
__ASSERT(attr_value.len >= sizeof(remote_read_send_len),
|
||||||
|
"Remote sent too little data.");
|
||||||
|
remote_read_send_len = net_buf_simple_pull_le16(&attr_value);
|
||||||
|
__ASSERT(remote_read_send_len == actual_read_len, "Length mismatch. %u %u",
|
||||||
|
remote_read_send_len, actual_read_len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test procedure:
|
||||||
|
*
|
||||||
|
* Central:
|
||||||
|
* 1. Connect to the peripheral.
|
||||||
|
* 2. Try to read a characteristic value.
|
||||||
|
* 3. Expect BT_ATT_ERR_UNLIKELY error.
|
||||||
|
* 4. Expect the peripheral to disconnect.
|
||||||
|
* 5. Reconnect to the peripheral.
|
||||||
|
* 6. Try to read a characteristic value.
|
||||||
|
* 7. Expect the peripheral to respond with the characteristic value.
|
||||||
|
* 8. Ensure that connection stays alive after a delay equal to ATT timeout.
|
||||||
|
* 9. Disconnect from the peripheral.
|
||||||
|
*
|
||||||
|
* Peripheral:
|
||||||
|
* 1. Start advertising.
|
||||||
|
* 2. Make the read callback sleep for more than ATT Timeout when the central tries to read.
|
||||||
|
* 3. Expect the disconnected callback to be called.
|
||||||
|
* 4. Start advertising again.
|
||||||
|
* 5. Make the read callback respond with the characteristic value when the central tries to read.
|
||||||
|
* 6. Expect the connection stay alive after a delay equal to ATT timeout.
|
||||||
|
* 7. Expect the central to disconnect.
|
||||||
|
*/
|
||||||
|
static void test_timeout(enum bt_att_chan_opt bearer)
|
||||||
|
{
|
||||||
|
bool central = (get_device_nbr() == CENTRAL_DEVICE_NBR);
|
||||||
|
bool peripheral = (get_device_nbr() == PERIPHERAL_DEVICE_NBR);
|
||||||
|
struct bt_conn *conn;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
/* Test ATT timeout. */
|
||||||
|
if (peripheral) {
|
||||||
|
conn = peripheral_setup(bearer, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (central) {
|
||||||
|
conn = central_setup(bearer, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
bs_sync_all_log("Ready to test ATT timeout");
|
||||||
|
|
||||||
|
if (central) {
|
||||||
|
central_read(conn, bearer, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
err = k_sem_take(&disconnected_sem, K_SECONDS(BT_ATT_TIMEOUT_SEC + 2));
|
||||||
|
/* Here disconnect is triggered by the Central host due to ATT timeout. */
|
||||||
|
__ASSERT(!err, "Unexpected error %d", err);
|
||||||
|
bt_testlib_conn_unref(&conn);
|
||||||
|
|
||||||
|
/* Test successful read. */
|
||||||
|
if (peripheral) {
|
||||||
|
conn = peripheral_setup(bearer, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (central) {
|
||||||
|
conn = central_setup(bearer, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
bs_sync_all_log("Ready to test successful read");
|
||||||
|
|
||||||
|
if (central) {
|
||||||
|
central_read(conn, bearer, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
err = k_sem_take(&disconnected_sem, K_SECONDS(BT_ATT_TIMEOUT_SEC + 2));
|
||||||
|
/* Check that disconnect doesn't happen during time > ATT timeout. */
|
||||||
|
__ASSERT(err == -EAGAIN, "Unexpected error %d", err);
|
||||||
|
|
||||||
|
if (central) {
|
||||||
|
/* This time disconnect from the peripheral. */
|
||||||
|
EXPECT_ZERO(bt_testlib_disconnect(&conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (peripheral) {
|
||||||
|
/* Wait for the central to disconnect. */
|
||||||
|
bt_testlib_wait_disconnected(conn);
|
||||||
|
bt_testlib_conn_unref(&conn);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Clear the semaphore. */
|
||||||
|
err = k_sem_take(&disconnected_sem, K_SECONDS(1));
|
||||||
|
__ASSERT_NO_MSG(!err);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void connected(struct bt_conn *conn, uint8_t err)
|
||||||
|
{
|
||||||
|
LOG_INF("Connected");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void disconnected(struct bt_conn *conn, uint8_t reason)
|
||||||
|
{
|
||||||
|
bool central = (get_device_nbr() == CENTRAL_DEVICE_NBR);
|
||||||
|
bool peripheral = (get_device_nbr() == PERIPHERAL_DEVICE_NBR);
|
||||||
|
uint8_t expected_reason;
|
||||||
|
|
||||||
|
LOG_INF("Disconnected: %u", reason);
|
||||||
|
|
||||||
|
if (central) {
|
||||||
|
expected_reason = BT_HCI_ERR_LOCALHOST_TERM_CONN;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (peripheral) {
|
||||||
|
expected_reason = BT_HCI_ERR_REMOTE_USER_TERM_CONN;
|
||||||
|
}
|
||||||
|
|
||||||
|
__ASSERT(expected_reason == reason, "Unexpected reason %u", reason);
|
||||||
|
|
||||||
|
k_sem_give(&disconnected_sem);
|
||||||
|
}
|
||||||
|
|
||||||
|
BT_CONN_CB_DEFINE(conn_callbacks) = {
|
||||||
|
.connected = connected,
|
||||||
|
.disconnected = disconnected,
|
||||||
|
};
|
||||||
|
|
||||||
|
void the_test(void)
|
||||||
|
{
|
||||||
|
bool peripheral = (get_device_nbr() == PERIPHERAL_DEVICE_NBR);
|
||||||
|
|
||||||
|
if (peripheral) {
|
||||||
|
EXPECT_ZERO(bt_gatt_service_register(&svc));
|
||||||
|
}
|
||||||
|
|
||||||
|
bt_enable_quiet();
|
||||||
|
|
||||||
|
if (peripheral) {
|
||||||
|
EXPECT_ZERO(bt_set_name("peripheral"));
|
||||||
|
}
|
||||||
|
|
||||||
|
bs_sync_all_log("Testing UATT");
|
||||||
|
test_timeout(BT_ATT_CHAN_OPT_UNENHANCED_ONLY);
|
||||||
|
|
||||||
|
bs_sync_all_log("Testing EATT");
|
||||||
|
test_timeout(BT_ATT_CHAN_OPT_ENHANCED_ONLY);
|
||||||
|
|
||||||
|
bs_sync_all_log("Test Complete");
|
||||||
|
|
||||||
|
PASS("Test complete\n");
|
||||||
|
}
|
25
tests/bsim/bluetooth/host/att/timeout/prj.conf
Normal file
25
tests/bsim/bluetooth/host/att/timeout/prj.conf
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
CONFIG_ASSERT=y
|
||||||
|
CONFIG_BOOT_BANNER=n
|
||||||
|
CONFIG_BT_BUF_ACL_RX_SIZE=204
|
||||||
|
CONFIG_BT_CENTRAL=y
|
||||||
|
CONFIG_BT_DEVICE_NAME_DYNAMIC=y
|
||||||
|
CONFIG_BT_EATT=y
|
||||||
|
CONFIG_BT_EXT_ADV=y
|
||||||
|
CONFIG_BT_GATT_CLIENT=y
|
||||||
|
CONFIG_BT_GATT_DYNAMIC_DB=y
|
||||||
|
CONFIG_BT_L2CAP_DYNAMIC_CHANNEL=y
|
||||||
|
CONFIG_BT_L2CAP_ECRED=y
|
||||||
|
CONFIG_BT_L2CAP_TX_MTU=200
|
||||||
|
CONFIG_BT_MAX_CONN=3
|
||||||
|
CONFIG_BT_MAX_PAIRED=2
|
||||||
|
CONFIG_BT_PERIPHERAL=y
|
||||||
|
CONFIG_BT_PRIVACY=n
|
||||||
|
CONFIG_BT_SMP=y
|
||||||
|
CONFIG_BT_TESTING=y
|
||||||
|
CONFIG_BT=y
|
||||||
|
CONFIG_FLASH_MAP=y
|
||||||
|
CONFIG_FLASH=y
|
||||||
|
CONFIG_LOG_BACKEND_FORMAT_TIMESTAMP=n
|
||||||
|
CONFIG_LOG_RUNTIME_FILTERING=y
|
||||||
|
CONFIG_LOG_TAG_MAX_LEN=20
|
||||||
|
CONFIG_LOG=y
|
22
tests/bsim/bluetooth/host/att/timeout/test_scripts/run.sh
Executable file
22
tests/bsim/bluetooth/host/att/timeout/test_scripts/run.sh
Executable file
|
@ -0,0 +1,22 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# Copyright 2024 Nordic Semiconductor ASA
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
set -eu -x
|
||||||
|
|
||||||
|
source ${ZEPHYR_BASE}/tests/bsim/sh_common.source
|
||||||
|
|
||||||
|
simulation_id="timeout"
|
||||||
|
dev_exe=bs_${BOARD_TS}_$(guess_test_long_name)_prj_conf
|
||||||
|
args_all=(-s=${simulation_id} -D=2)
|
||||||
|
args_dev=(-v=2 -RealEncryption=1 -testid=the_test)
|
||||||
|
|
||||||
|
cd "${BSIM_OUT_PATH}/bin"
|
||||||
|
|
||||||
|
Execute ./${dev_exe} "${args_all[@]}" "${args_dev[@]}" -d=0
|
||||||
|
|
||||||
|
Execute ./${dev_exe} "${args_all[@]}" "${args_dev[@]}" -d=1
|
||||||
|
|
||||||
|
Execute ./bs_2G4_phy_v1 "${args_all[@]}" -sim_length=200e6
|
||||||
|
|
||||||
|
wait_for_background_jobs
|
Loading…
Add table
Add a link
Reference in a new issue