Skip to content

Commit be772e1

Browse files
PavelVPVrlubos
authored andcommitted
[nrf fromtree] 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 <[email protected]> (cherry picked from commit f7e8a87)
1 parent 723ca15 commit be772e1

File tree

7 files changed

+380
-1
lines changed

7 files changed

+380
-1
lines changed

subsys/bluetooth/host/att_internal.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@
1010

1111
#define BT_EATT_PSM 0x27
1212
#define BT_ATT_DEFAULT_LE_MTU 23
13-
#define BT_ATT_TIMEOUT K_SECONDS(30)
13+
#define BT_ATT_TIMEOUT_SEC 30
14+
#define BT_ATT_TIMEOUT K_SECONDS(BT_ATT_TIMEOUT_SEC)
1415

1516
/* Local ATT Rx MTU
1617
*

tests/bsim/bluetooth/host/att/compile.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,5 +22,6 @@ run_in_background ${ZEPHYR_BASE}/tests/bsim/bluetooth/host/att/sequential/compil
2222
run_in_background ${ZEPHYR_BASE}/tests/bsim/bluetooth/host/att/pipeline/compile.sh
2323
run_in_background ${ZEPHYR_BASE}/tests/bsim/bluetooth/host/att/long_read/compile.sh
2424
run_in_background ${ZEPHYR_BASE}/tests/bsim/bluetooth/host/att/open_close/compile.sh
25+
run_in_background ${ZEPHYR_BASE}/tests/bsim/bluetooth/host/att/timeout/compile.sh
2526

2627
wait_for_background_jobs
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# SPDX-License-Identifier: Apache-2.0
2+
3+
cmake_minimum_required(VERSION 3.20.0)
4+
5+
find_package(Zephyr HINTS $ENV{ZEPHYR_BASE})
6+
project(app)
7+
8+
add_subdirectory(${ZEPHYR_BASE}/tests/bluetooth/common/testlib testlib)
9+
10+
target_sources(app PRIVATE
11+
../long_read/bs_main.c
12+
../long_read/bs_sync.c
13+
main.c
14+
)
15+
16+
zephyr_include_directories(
17+
${BSIM_COMPONENTS_PATH}/libPhyComv1/src/
18+
${BSIM_COMPONENTS_PATH}/libUtilv1/src/
19+
../long_read/
20+
)
21+
22+
target_link_libraries(app PRIVATE
23+
testlib
24+
)
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
#!/usr/bin/env bash
2+
# Copyright 2024 Nordic Semiconductor ASA
3+
# SPDX-License-Identifier: Apache-2.0
4+
5+
set -eu
6+
: "${ZEPHYR_BASE:?ZEPHYR_BASE must be defined}"
7+
8+
INCR_BUILD=1
9+
10+
source ${ZEPHYR_BASE}/tests/bsim/compile.source
11+
12+
app="$(guess_test_relpath)" compile
13+
14+
wait_for_background_jobs
Lines changed: 292 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,292 @@
1+
/* Copyright (c) 2024 Nordic Semiconductor ASA
2+
* SPDX-License-Identifier: Apache-2.0
3+
*/
4+
5+
#include <argparse.h>
6+
#include <zephyr/bluetooth/gatt.h>
7+
#include <zephyr/logging/log.h>
8+
#include <zephyr/sys/__assert.h>
9+
#include <zephyr/settings/settings.h>
10+
#include <zephyr/sys/byteorder.h>
11+
#include <zephyr/bluetooth/bluetooth.h>
12+
13+
#include "host/att_internal.h"
14+
15+
#include "testlib/adv.h"
16+
#include "testlib/att_read.h"
17+
#include "testlib/att_write.h"
18+
#include "bs_macro.h"
19+
#include "bs_sync.h"
20+
#include <testlib/conn.h>
21+
#include "testlib/log_utils.h"
22+
#include "testlib/scan.h"
23+
#include "testlib/security.h"
24+
25+
/* This test uses system asserts to fail tests. */
26+
BUILD_ASSERT(__ASSERT_ON);
27+
28+
LOG_MODULE_REGISTER(main, LOG_LEVEL_DBG);
29+
30+
#define CENTRAL_DEVICE_NBR 0
31+
#define PERIPHERAL_DEVICE_NBR 1
32+
33+
#define UUID_1 \
34+
BT_UUID_DECLARE_128(0xdb, 0x1f, 0xe2, 0x52, 0xf3, 0xc6, 0x43, 0x66, 0xb3, 0x92, 0x5d, \
35+
0xc6, 0xe7, 0xc9, 0x59, 0x9d)
36+
37+
#define UUID_2 \
38+
BT_UUID_DECLARE_128(0x3f, 0xa4, 0x7f, 0x44, 0x2e, 0x2a, 0x43, 0x05, 0xab, 0x38, 0x07, \
39+
0x8d, 0x16, 0xbf, 0x99, 0xf1)
40+
41+
static bool trigger_att_timeout;
42+
static K_SEM_DEFINE(disconnected_sem, 0, 1);
43+
44+
static ssize_t read_chrc(struct bt_conn *conn, const struct bt_gatt_attr *attr, void *buf,
45+
uint16_t buf_len, uint16_t offset)
46+
{
47+
ssize_t read_len;
48+
49+
LOG_INF("ATT timeout will %sbe triggered", trigger_att_timeout ? "" : "not ");
50+
51+
if (trigger_att_timeout) {
52+
/* Sleep longer than ATT Timeout (section 3.3.3). */
53+
k_sleep(K_SECONDS(BT_ATT_TIMEOUT_SEC + 1));
54+
}
55+
56+
__ASSERT_NO_MSG(offset == 0);
57+
read_len = buf_len;
58+
59+
__ASSERT_NO_MSG(read_len >= 2);
60+
sys_put_le16(read_len, buf);
61+
62+
return read_len;
63+
}
64+
65+
static struct bt_gatt_attr attrs[] = {
66+
BT_GATT_PRIMARY_SERVICE(UUID_1),
67+
BT_GATT_CHARACTERISTIC(UUID_2, BT_GATT_CHRC_READ, BT_GATT_PERM_READ, read_chrc, NULL, NULL),
68+
};
69+
70+
static struct bt_gatt_service svc = {
71+
.attrs = attrs,
72+
.attr_count = ARRAY_SIZE(attrs),
73+
};
74+
75+
static void bs_sync_all_log(char *log_msg)
76+
{
77+
/* Everyone meets here. */
78+
bt_testlib_bs_sync_all();
79+
80+
if (get_device_nbr() == 0) {
81+
LOG_WRN("Sync point: %s", log_msg);
82+
}
83+
84+
/* Everyone waits for d0 to finish logging. */
85+
bt_testlib_bs_sync_all();
86+
}
87+
88+
static inline void bt_enable_quiet(void)
89+
{
90+
bt_testlib_log_level_set("bt_hci_core", LOG_LEVEL_ERR);
91+
bt_testlib_log_level_set("bt_id", LOG_LEVEL_ERR);
92+
93+
EXPECT_ZERO(bt_enable(NULL));
94+
95+
bt_testlib_log_level_set("bt_hci_core", LOG_LEVEL_INF);
96+
bt_testlib_log_level_set("bt_id", LOG_LEVEL_INF);
97+
}
98+
99+
static struct bt_conn *peripheral_setup(enum bt_att_chan_opt bearer, bool timeout)
100+
{
101+
struct bt_conn *conn = NULL;
102+
103+
EXPECT_ZERO(bt_testlib_adv_conn(&conn, BT_ID_DEFAULT, bt_get_name()));
104+
105+
trigger_att_timeout = timeout;
106+
107+
return conn;
108+
}
109+
110+
static struct bt_conn *central_setup(enum bt_att_chan_opt bearer, bool timeout)
111+
{
112+
bt_addr_le_t adva;
113+
struct bt_conn *conn = NULL;
114+
115+
EXPECT_ZERO(bt_testlib_scan_find_name(&adva, "peripheral"));
116+
EXPECT_ZERO(bt_testlib_connect(&adva, &conn));
117+
118+
/* Establish EATT bearers. */
119+
EXPECT_ZERO(bt_testlib_secure(conn, BT_SECURITY_L2));
120+
121+
while (bt_eatt_count(conn) == 0) {
122+
k_msleep(100);
123+
};
124+
125+
return conn;
126+
}
127+
128+
static void central_read(struct bt_conn *conn, enum bt_att_chan_opt bearer, bool timeout)
129+
{
130+
uint16_t actual_read_len;
131+
uint16_t remote_read_send_len;
132+
uint16_t handle = 0;
133+
int err;
134+
135+
NET_BUF_SIMPLE_DEFINE(attr_value, sizeof(remote_read_send_len));
136+
137+
err = bt_testlib_att_read_by_type_sync(&attr_value, &actual_read_len, &handle, NULL, conn,
138+
bearer, UUID_2, BT_ATT_FIRST_ATTRIBUTE_HANDLE,
139+
BT_ATT_LAST_ATTRIBUTE_HANDLE);
140+
141+
if (timeout) {
142+
__ASSERT(err == BT_ATT_ERR_UNLIKELY, "Unexpected error %d", err);
143+
} else {
144+
__ASSERT(!err, "Unexpected error %d", err);
145+
__ASSERT(attr_value.len >= sizeof(remote_read_send_len),
146+
"Remote sent too little data.");
147+
remote_read_send_len = net_buf_simple_pull_le16(&attr_value);
148+
__ASSERT(remote_read_send_len == actual_read_len, "Length mismatch. %u %u",
149+
remote_read_send_len, actual_read_len);
150+
}
151+
}
152+
153+
/**
154+
* Test procedure:
155+
*
156+
* Central:
157+
* 1. Connect to the peripheral.
158+
* 2. Try to read a characteristic value.
159+
* 3. Expect BT_ATT_ERR_UNLIKELY error.
160+
* 4. Expect the peripheral to disconnect.
161+
* 5. Reconnect to the peripheral.
162+
* 6. Try to read a characteristic value.
163+
* 7. Expect the peripheral to respond with the characteristic value.
164+
* 8. Ensure that connection stays alive after a delay equal to ATT timeout.
165+
* 9. Disconnect from the peripheral.
166+
*
167+
* Peripheral:
168+
* 1. Start advertising.
169+
* 2. Make the read callback sleep for more than ATT Timeout when the central tries to read.
170+
* 3. Expect the disconnected callback to be called.
171+
* 4. Start advertising again.
172+
* 5. Make the read callback respond with the characteristic value when the central tries to read.
173+
* 6. Expect the connection stay alive after a delay equal to ATT timeout.
174+
* 7. Expect the central to disconnect.
175+
*/
176+
static void test_timeout(enum bt_att_chan_opt bearer)
177+
{
178+
bool central = (get_device_nbr() == CENTRAL_DEVICE_NBR);
179+
bool peripheral = (get_device_nbr() == PERIPHERAL_DEVICE_NBR);
180+
struct bt_conn *conn;
181+
int err;
182+
183+
/* Test ATT timeout. */
184+
if (peripheral) {
185+
conn = peripheral_setup(bearer, true);
186+
}
187+
188+
if (central) {
189+
conn = central_setup(bearer, true);
190+
}
191+
192+
bs_sync_all_log("Ready to test ATT timeout");
193+
194+
if (central) {
195+
central_read(conn, bearer, true);
196+
}
197+
198+
err = k_sem_take(&disconnected_sem, K_SECONDS(BT_ATT_TIMEOUT_SEC + 2));
199+
/* Here disconnect is triggered by the Central host due to ATT timeout. */
200+
__ASSERT(!err, "Unexpected error %d", err);
201+
bt_testlib_conn_unref(&conn);
202+
203+
/* Test successful read. */
204+
if (peripheral) {
205+
conn = peripheral_setup(bearer, false);
206+
}
207+
208+
if (central) {
209+
conn = central_setup(bearer, false);
210+
}
211+
212+
bs_sync_all_log("Ready to test successful read");
213+
214+
if (central) {
215+
central_read(conn, bearer, false);
216+
}
217+
218+
err = k_sem_take(&disconnected_sem, K_SECONDS(BT_ATT_TIMEOUT_SEC + 2));
219+
/* Check that disconnect doesn't happen during time > ATT timeout. */
220+
__ASSERT(err == -EAGAIN, "Unexpected error %d", err);
221+
222+
if (central) {
223+
/* This time disconnect from the peripheral. */
224+
EXPECT_ZERO(bt_testlib_disconnect(&conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN));
225+
}
226+
227+
if (peripheral) {
228+
/* Wait for the central to disconnect. */
229+
bt_testlib_wait_disconnected(conn);
230+
bt_testlib_conn_unref(&conn);
231+
}
232+
233+
/* Clear the semaphore. */
234+
err = k_sem_take(&disconnected_sem, K_SECONDS(1));
235+
__ASSERT_NO_MSG(!err);
236+
}
237+
238+
static void connected(struct bt_conn *conn, uint8_t err)
239+
{
240+
LOG_INF("Connected");
241+
}
242+
243+
static void disconnected(struct bt_conn *conn, uint8_t reason)
244+
{
245+
bool central = (get_device_nbr() == CENTRAL_DEVICE_NBR);
246+
bool peripheral = (get_device_nbr() == PERIPHERAL_DEVICE_NBR);
247+
uint8_t expected_reason;
248+
249+
LOG_INF("Disconnected: %u", reason);
250+
251+
if (central) {
252+
expected_reason = BT_HCI_ERR_LOCALHOST_TERM_CONN;
253+
}
254+
255+
if (peripheral) {
256+
expected_reason = BT_HCI_ERR_REMOTE_USER_TERM_CONN;
257+
}
258+
259+
__ASSERT(expected_reason == reason, "Unexpected reason %u", reason);
260+
261+
k_sem_give(&disconnected_sem);
262+
}
263+
264+
BT_CONN_CB_DEFINE(conn_callbacks) = {
265+
.connected = connected,
266+
.disconnected = disconnected,
267+
};
268+
269+
void the_test(void)
270+
{
271+
bool peripheral = (get_device_nbr() == PERIPHERAL_DEVICE_NBR);
272+
273+
if (peripheral) {
274+
EXPECT_ZERO(bt_gatt_service_register(&svc));
275+
}
276+
277+
bt_enable_quiet();
278+
279+
if (peripheral) {
280+
EXPECT_ZERO(bt_set_name("peripheral"));
281+
}
282+
283+
bs_sync_all_log("Testing UATT");
284+
test_timeout(BT_ATT_CHAN_OPT_UNENHANCED_ONLY);
285+
286+
bs_sync_all_log("Testing EATT");
287+
test_timeout(BT_ATT_CHAN_OPT_ENHANCED_ONLY);
288+
289+
bs_sync_all_log("Test Complete");
290+
291+
PASS("Test complete\n");
292+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
CONFIG_ASSERT=y
2+
CONFIG_BOOT_BANNER=n
3+
CONFIG_BT_BUF_ACL_RX_SIZE=204
4+
CONFIG_BT_CENTRAL=y
5+
CONFIG_BT_DEVICE_NAME_DYNAMIC=y
6+
CONFIG_BT_EATT=y
7+
CONFIG_BT_EXT_ADV=y
8+
CONFIG_BT_GATT_CLIENT=y
9+
CONFIG_BT_GATT_DYNAMIC_DB=y
10+
CONFIG_BT_L2CAP_DYNAMIC_CHANNEL=y
11+
CONFIG_BT_L2CAP_ECRED=y
12+
CONFIG_BT_L2CAP_TX_MTU=200
13+
CONFIG_BT_MAX_CONN=3
14+
CONFIG_BT_MAX_PAIRED=2
15+
CONFIG_BT_PERIPHERAL=y
16+
CONFIG_BT_PRIVACY=n
17+
CONFIG_BT_SMP=y
18+
CONFIG_BT_TESTING=y
19+
CONFIG_BT=y
20+
CONFIG_FLASH_MAP=y
21+
CONFIG_FLASH=y
22+
CONFIG_LOG_BACKEND_FORMAT_TIMESTAMP=n
23+
CONFIG_LOG_RUNTIME_FILTERING=y
24+
CONFIG_LOG_TAG_MAX_LEN=20
25+
CONFIG_LOG=y
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
#!/usr/bin/env bash
2+
# Copyright 2024 Nordic Semiconductor ASA
3+
# SPDX-License-Identifier: Apache-2.0
4+
5+
set -eu -x
6+
7+
source ${ZEPHYR_BASE}/tests/bsim/sh_common.source
8+
9+
simulation_id="timeout"
10+
dev_exe=bs_${BOARD_TS}_$(guess_test_long_name)_prj_conf
11+
args_all=(-s=${simulation_id} -D=2)
12+
args_dev=(-v=2 -RealEncryption=1 -testid=the_test)
13+
14+
cd "${BSIM_OUT_PATH}/bin"
15+
16+
Execute ./${dev_exe} "${args_all[@]}" "${args_dev[@]}" -d=0
17+
18+
Execute ./${dev_exe} "${args_all[@]}" "${args_dev[@]}" -d=1
19+
20+
Execute ./bs_2G4_phy_v1 "${args_all[@]}" -sim_length=200e6
21+
22+
wait_for_background_jobs

0 commit comments

Comments
 (0)