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
read_chrc(struct bt_conn * conn,const struct bt_gatt_attr * attr,void * buf,uint16_t buf_len,uint16_t offset)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
bs_sync_all_log(char * log_msg)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
bt_enable_quiet(void)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
peripheral_setup(enum bt_att_chan_opt bearer,bool timeout)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
central_setup(enum bt_att_chan_opt bearer,bool timeout)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
central_read(struct bt_conn * conn,enum bt_att_chan_opt bearer,bool timeout)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 */
test_timeout(enum bt_att_chan_opt bearer)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
connected(struct bt_conn * conn,uint8_t err)238 static void connected(struct bt_conn *conn, uint8_t err)
239 {
240 LOG_INF("Connected");
241 }
242
disconnected(struct bt_conn * conn,uint8_t reason)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
the_test(void)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 }
293