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:
Pavel Vasilyev 2024-10-08 07:48:19 +02:00 committed by Anas Nashif
commit f7e8a8717b
7 changed files with 380 additions and 1 deletions

View file

@ -10,7 +10,8 @@
#define BT_EATT_PSM 0x27
#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
*

View file

@ -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/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/timeout/compile.sh
wait_for_background_jobs

View 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
)

View 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

View 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");
}

View 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

View 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