1 /*
2 * Copyright (c) 2022 Nordic Semiconductor ASA
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <zephyr/bluetooth/bluetooth.h>
8 #include <zephyr/bluetooth/gatt.h>
9
10 #include "common.h"
11
12 CREATE_FLAG(flag_is_connected);
13 CREATE_FLAG(flag_discover_complete);
14 CREATE_FLAG(flag_write_complete);
15 CREATE_FLAG(flag_chan_1_read);
16 CREATE_FLAG(flag_chan_2_read);
17 CREATE_FLAG(flag_db_hash_read);
18 CREATE_FLAG(flag_encrypted);
19
20 static struct bt_conn *g_conn;
21 static uint16_t chrc_handle;
22 static uint16_t csf_handle;
23 static const struct bt_uuid *test_svc_uuid = TEST_SERVICE_UUID;
24
connected(struct bt_conn * conn,uint8_t err)25 static void connected(struct bt_conn *conn, uint8_t err)
26 {
27 char addr[BT_ADDR_LE_STR_LEN];
28
29 bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
30
31 if (err != 0) {
32 FAIL("Failed to connect to %s (%u)\n", addr, err);
33
34 return;
35 }
36
37 printk("Connected to %s\n", addr);
38
39 SET_FLAG(flag_is_connected);
40 }
41
disconnected(struct bt_conn * conn,uint8_t reason)42 static void disconnected(struct bt_conn *conn, uint8_t reason)
43 {
44 char addr[BT_ADDR_LE_STR_LEN];
45
46 if (conn != g_conn) {
47 return;
48 }
49
50 bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
51
52 printk("Disconnected: %s (reason 0x%02x)\n", addr, reason);
53
54 bt_conn_unref(g_conn);
55
56 g_conn = NULL;
57 UNSET_FLAG(flag_is_connected);
58 }
59
security_changed(struct bt_conn * conn,bt_security_t level,enum bt_security_err err)60 void security_changed(struct bt_conn *conn, bt_security_t level, enum bt_security_err err)
61 {
62 if (err != BT_SECURITY_ERR_SUCCESS) {
63 FAIL("Encryption failed\n");
64 } else if (level < BT_SECURITY_L2) {
65 FAIL("Insufficient security\n");
66 } else {
67 SET_FLAG(flag_encrypted);
68 }
69 }
70
71 BT_CONN_CB_DEFINE(conn_callbacks) = {
72 .connected = connected,
73 .disconnected = disconnected,
74 .security_changed = security_changed,
75 };
76
device_found(const bt_addr_le_t * addr,int8_t rssi,uint8_t type,struct net_buf_simple * ad)77 void device_found(const bt_addr_le_t *addr, int8_t rssi, uint8_t type, struct net_buf_simple *ad)
78 {
79 char addr_str[BT_ADDR_LE_STR_LEN];
80 int err;
81
82 if (g_conn != NULL) {
83 return;
84 }
85
86 /* We're only interested in connectable events */
87 if (type != BT_HCI_ADV_IND && type != BT_HCI_ADV_DIRECT_IND) {
88 return;
89 }
90
91 bt_addr_le_to_str(addr, addr_str, sizeof(addr_str));
92 printk("Device found: %s (RSSI %d)\n", addr_str, rssi);
93
94 printk("Stopping scan\n");
95 err = bt_le_scan_stop();
96 if (err != 0) {
97 FAIL("Could not stop scan (err %d)\n");
98
99 return;
100 }
101
102 err = bt_conn_le_create(addr, BT_CONN_LE_CREATE_CONN, BT_LE_CONN_PARAM_DEFAULT, &g_conn);
103 if (err != 0) {
104 FAIL("Could not connect to peer (err %d)", err);
105 }
106 }
107
discover_func(struct bt_conn * conn,const struct bt_gatt_attr * attr,struct bt_gatt_discover_params * params)108 static uint8_t discover_func(struct bt_conn *conn, const struct bt_gatt_attr *attr,
109 struct bt_gatt_discover_params *params)
110 {
111 int err;
112
113 if (attr == NULL) {
114 if (chrc_handle == 0) {
115 FAIL("Did not discover chrc (%x)\n", chrc_handle);
116 }
117
118 (void)memset(params, 0, sizeof(*params));
119
120 SET_FLAG(flag_discover_complete);
121
122 return BT_GATT_ITER_STOP;
123 }
124
125 printk("[ATTRIBUTE] handle %u\n", attr->handle);
126
127 if (params->type == BT_GATT_DISCOVER_PRIMARY &&
128 bt_uuid_cmp(params->uuid, TEST_SERVICE_UUID) == 0) {
129 printk("Found test service\n");
130 params->uuid = NULL;
131 params->start_handle = attr->handle + 1;
132 params->type = BT_GATT_DISCOVER_CHARACTERISTIC;
133
134 err = bt_gatt_discover(conn, params);
135 if (err != 0) {
136 FAIL("Discover failed (err %d)\n", err);
137 }
138
139 return BT_GATT_ITER_STOP;
140 } else if (params->type == BT_GATT_DISCOVER_CHARACTERISTIC) {
141 const struct bt_gatt_chrc *chrc = (struct bt_gatt_chrc *)attr->user_data;
142
143 if (bt_uuid_cmp(chrc->uuid, TEST_CHRC_UUID) == 0) {
144 printk("Found chrc\n");
145 chrc_handle = chrc->value_handle;
146
147 } else if (bt_uuid_cmp(chrc->uuid, BT_UUID_GATT_CLIENT_FEATURES) == 0) {
148 printk("Found csf\n");
149 csf_handle = chrc->value_handle;
150 }
151 }
152
153 return BT_GATT_ITER_CONTINUE;
154 }
155
gatt_discover(const struct bt_uuid * uuid,uint8_t type)156 static void gatt_discover(const struct bt_uuid *uuid, uint8_t type)
157 {
158 static struct bt_gatt_discover_params discover_params;
159 int err;
160
161 printk("Discovering services and characteristics\n");
162
163 discover_params.uuid = uuid;
164 discover_params.func = discover_func;
165 discover_params.start_handle = BT_ATT_FIRST_ATTRIBUTE_HANDLE;
166 discover_params.end_handle = BT_ATT_LAST_ATTRIBUTE_HANDLE;
167 discover_params.type = type;
168 discover_params.chan_opt = BT_ATT_CHAN_OPT_NONE;
169
170 UNSET_FLAG(flag_discover_complete);
171
172 err = bt_gatt_discover(g_conn, &discover_params);
173 if (err != 0) {
174 FAIL("Discover failed(err %d)\n", err);
175 }
176
177 WAIT_FOR_FLAG(flag_discover_complete);
178 printk("Discover complete\n");
179 }
180 static struct bt_gatt_read_params chan_1_read = {
181 .handle_count = 1,
182 .single = {
183 .handle = 0, /* Will be set later */
184 .offset = 0,
185 },
186 .chan_opt = BT_ATT_CHAN_OPT_NONE,
187 };
188 static struct bt_gatt_read_params chan_2_read = {
189 .handle_count = 1,
190 .single = {
191 .handle = 0, /* Will be set later */
192 .offset = 0,
193 },
194 .chan_opt = BT_ATT_CHAN_OPT_NONE,
195 };
196 static struct bt_gatt_read_params db_hash_read = {
197 .handle_count = 0,
198 .by_uuid = {
199 .start_handle = BT_ATT_FIRST_ATTRIBUTE_HANDLE,
200 .end_handle = BT_ATT_LAST_ATTRIBUTE_HANDLE,
201 .uuid = BT_UUID_GATT_DB_HASH,
202 },
203 .chan_opt = BT_ATT_CHAN_OPT_NONE,
204 };
205
expect_status(uint8_t err,uint8_t status)206 void expect_status(uint8_t err, uint8_t status)
207 {
208 if (err != status) {
209 FAIL("Unexpected status from read: 0x%02X, expected 0x%02X\n", err, status);
210 }
211 }
212
gatt_read_expect_success_cb(struct bt_conn * conn,uint8_t err,struct bt_gatt_read_params * params,const void * data,uint16_t length)213 static uint8_t gatt_read_expect_success_cb(struct bt_conn *conn, uint8_t err,
214 struct bt_gatt_read_params *params, const void *data,
215 uint16_t length)
216 {
217 printk("GATT read cb: err 0x%02X\n", err);
218 expect_status(err, BT_ATT_ERR_SUCCESS);
219
220 if (params == &db_hash_read) {
221 SET_FLAG(flag_db_hash_read);
222 } else if (params == &chan_1_read) {
223 SET_FLAG(flag_chan_1_read);
224 } else if (params == &chan_2_read) {
225 SET_FLAG(flag_chan_2_read);
226 } else {
227 FAIL("Unexpected params\n");
228 }
229
230 return 0;
231 }
232
gatt_read_expect_err_unlikely_cb(struct bt_conn * conn,uint8_t err,struct bt_gatt_read_params * params,const void * data,uint16_t length)233 static uint8_t gatt_read_expect_err_unlikely_cb(struct bt_conn *conn, uint8_t err,
234 struct bt_gatt_read_params *params,
235 const void *data, uint16_t length)
236 {
237 printk("GATT read cb: err 0x%02X\n", err);
238 expect_status(err, BT_ATT_ERR_UNLIKELY);
239
240 if (params == &chan_1_read) {
241 SET_FLAG(flag_chan_1_read);
242 } else if (params == &chan_2_read) {
243 SET_FLAG(flag_chan_2_read);
244 } else {
245 FAIL("Unexpected params\n");
246 }
247
248 return 0;
249 }
250
gatt_read_expect_err_out_of_sync_cb(struct bt_conn * conn,uint8_t err,struct bt_gatt_read_params * params,const void * data,uint16_t length)251 static uint8_t gatt_read_expect_err_out_of_sync_cb(struct bt_conn *conn, uint8_t err,
252 struct bt_gatt_read_params *params,
253 const void *data, uint16_t length)
254 {
255 printk("GATT read cb: err 0x%02X\n", err);
256 expect_status(err, BT_ATT_ERR_DB_OUT_OF_SYNC);
257
258 if (params == &chan_1_read) {
259 SET_FLAG(flag_chan_1_read);
260 } else if (params == &chan_2_read) {
261 SET_FLAG(flag_chan_2_read);
262 } else {
263 FAIL("Unexpected params\n");
264 }
265
266 return 0;
267 }
268
gatt_read(struct bt_gatt_read_params * read_params)269 static void gatt_read(struct bt_gatt_read_params *read_params)
270 {
271 int err;
272
273 printk("Reading\n");
274
275 err = bt_gatt_read(g_conn, read_params);
276 if (err != 0) {
277 FAIL("bt_gatt_read failed: %d\n", err);
278 }
279 }
280
write_cb(struct bt_conn * conn,uint8_t err,struct bt_gatt_write_params * params)281 static void write_cb(struct bt_conn *conn, uint8_t err, struct bt_gatt_write_params *params)
282 {
283 if (err != BT_ATT_ERR_SUCCESS) {
284 FAIL("Write failed: 0x%02X\n", err);
285 }
286
287 SET_FLAG(flag_write_complete);
288 }
289
enable_robust_caching(void)290 static void enable_robust_caching(void)
291 {
292 /* Client Supported Features Characteristic Value
293 * Bit 0: Robust Caching
294 * Bit 1: EATT
295 */
296 static const uint8_t csf[] = { BIT(0) | BIT(1) };
297 static struct bt_gatt_write_params write_params = {
298 .func = write_cb,
299 .offset = 0,
300 .data = csf,
301 .length = sizeof(csf),
302 .chan_opt = BT_ATT_CHAN_OPT_NONE,
303 };
304 int err;
305
306 printk("Writing to Client Supported Features Characteristic\n");
307
308 write_params.handle = csf_handle;
309 UNSET_FLAG(flag_write_complete);
310
311 err = bt_gatt_write(g_conn, &write_params);
312 if (err) {
313 FAIL("bt_gatt_write failed (err %d)\n", err);
314 }
315
316 WAIT_FOR_FLAG(flag_write_complete);
317 printk("Success\n");
318 }
319
test_main_common(bool connect_eatt)320 static void test_main_common(bool connect_eatt)
321 {
322 int err;
323
324 backchannel_init();
325
326 err = bt_enable(NULL);
327 if (err != 0) {
328 FAIL("Bluetooth discover failed (err %d)\n", err);
329 }
330
331 err = bt_le_scan_start(BT_LE_SCAN_PASSIVE, device_found);
332 if (err != 0) {
333 FAIL("Scanning failed to start (err %d)\n", err);
334 }
335
336 printk("Scanning successfully started\n");
337
338 WAIT_FOR_FLAG(flag_is_connected);
339
340 err = bt_conn_set_security(g_conn, BT_SECURITY_L2);
341 if (err) {
342 FAIL("Failed to start encryption procedure\n");
343 }
344
345 WAIT_FOR_FLAG(flag_encrypted);
346
347 gatt_discover(test_svc_uuid, BT_GATT_DISCOVER_PRIMARY);
348 gatt_discover(BT_UUID_GATT_CLIENT_FEATURES, BT_GATT_DISCOVER_CHARACTERISTIC);
349
350 enable_robust_caching();
351
352 if (connect_eatt) {
353 while (bt_eatt_count(g_conn) < 1) {
354 /* Wait for EATT channel to connect, in case it hasn't already */
355 k_sleep(K_MSEC(10));
356 }
357 }
358
359 /* Tell the server to register additional service */
360 backchannel_sync_send();
361
362 /* Wait for new service to be added by server */
363 backchannel_sync_wait();
364
365 chan_1_read.single.handle = chrc_handle;
366 chan_2_read.single.handle = chrc_handle;
367 }
368
test_main_db_hash_read_eatt(void)369 static void test_main_db_hash_read_eatt(void)
370 {
371 test_main_common(true);
372
373 /* Read the DB hash to become change-aware */
374 db_hash_read.func = gatt_read_expect_success_cb;
375 gatt_read(&db_hash_read);
376 WAIT_FOR_FLAG(flag_db_hash_read);
377
378 /* These shall now succeed */
379 chan_1_read.func = gatt_read_expect_success_cb;
380 chan_2_read.func = gatt_read_expect_success_cb;
381 UNSET_FLAG(flag_chan_1_read);
382 UNSET_FLAG(flag_chan_2_read);
383 gatt_read(&chan_1_read);
384 gatt_read(&chan_2_read);
385 WAIT_FOR_FLAG(flag_chan_1_read);
386 WAIT_FOR_FLAG(flag_chan_2_read);
387
388 /* Signal to server that reads are done */
389 backchannel_sync_send();
390
391 PASS("GATT client Passed\n");
392 }
393
test_main_out_of_sync_eatt(void)394 static void test_main_out_of_sync_eatt(void)
395 {
396 test_main_common(true);
397
398 chan_1_read.func = gatt_read_expect_err_out_of_sync_cb;
399 chan_2_read.func = gatt_read_expect_err_out_of_sync_cb;
400 gatt_read(&chan_1_read);
401 gatt_read(&chan_2_read);
402
403 /* Wait until received response on both reads. When robust caching is implemented
404 * on the client side, the waiting shall be done automatically by the host when
405 * reading the DB hash.
406 */
407 WAIT_FOR_FLAG(flag_chan_1_read);
408 WAIT_FOR_FLAG(flag_chan_2_read);
409
410 /* Read the DB hash to become change-aware */
411 db_hash_read.func = gatt_read_expect_success_cb;
412 gatt_read(&db_hash_read);
413 WAIT_FOR_FLAG(flag_db_hash_read);
414
415 /* These shall now succeed */
416 chan_1_read.func = gatt_read_expect_success_cb;
417 chan_2_read.func = gatt_read_expect_success_cb;
418 UNSET_FLAG(flag_chan_1_read);
419 UNSET_FLAG(flag_chan_2_read);
420 gatt_read(&chan_1_read);
421 gatt_read(&chan_2_read);
422 WAIT_FOR_FLAG(flag_chan_1_read);
423 WAIT_FOR_FLAG(flag_chan_2_read);
424
425 /* Signal to server that reads are done */
426 backchannel_sync_send();
427
428 PASS("GATT client Passed\n");
429 }
430
test_main_retry_reads_eatt(void)431 static void test_main_retry_reads_eatt(void)
432 {
433 test_main_common(true);
434
435 chan_1_read.func = gatt_read_expect_err_out_of_sync_cb;
436 chan_2_read.func = gatt_read_expect_err_out_of_sync_cb;
437 gatt_read(&chan_1_read);
438 gatt_read(&chan_2_read);
439
440 /* Wait until received response on both reads. When robust caching is implemented
441 * on the client side, the waiting shall be done automatically by the host when
442 * reading the DB hash.
443 */
444 WAIT_FOR_FLAG(flag_chan_1_read);
445 WAIT_FOR_FLAG(flag_chan_2_read);
446
447 /* Retry the reads, these shall time out */
448 chan_1_read.func = gatt_read_expect_err_unlikely_cb;
449 chan_2_read.func = gatt_read_expect_err_unlikely_cb;
450 UNSET_FLAG(flag_chan_1_read);
451 UNSET_FLAG(flag_chan_2_read);
452 gatt_read(&chan_1_read);
453 gatt_read(&chan_2_read);
454 WAIT_FOR_FLAG(flag_chan_1_read);
455 WAIT_FOR_FLAG(flag_chan_2_read);
456
457 /* Signal to server that reads are done */
458 backchannel_sync_send();
459
460 PASS("GATT client Passed\n");
461 }
462
test_main_db_hash_read_no_eatt(void)463 static void test_main_db_hash_read_no_eatt(void)
464 {
465 test_main_common(false);
466
467 /* Read the DB hash to become change-aware */
468 db_hash_read.func = gatt_read_expect_success_cb;
469 gatt_read(&db_hash_read);
470 WAIT_FOR_FLAG(flag_db_hash_read);
471
472 /* Read shall now succeed */
473 chan_1_read.func = gatt_read_expect_success_cb;
474 UNSET_FLAG(flag_chan_1_read);
475 gatt_read(&chan_1_read);
476 WAIT_FOR_FLAG(flag_chan_1_read);
477
478 /* Signal to server that reads are done */
479 backchannel_sync_send();
480
481 PASS("GATT client Passed\n");
482 }
483
test_main_out_of_sync_no_eatt(void)484 static void test_main_out_of_sync_no_eatt(void)
485 {
486 test_main_common(false);
487
488 chan_1_read.func = gatt_read_expect_err_out_of_sync_cb;
489 gatt_read(&chan_1_read);
490 WAIT_FOR_FLAG(flag_chan_1_read);
491
492 /* Read the DB hash to become change-aware */
493 db_hash_read.func = gatt_read_expect_success_cb;
494 gatt_read(&db_hash_read);
495 WAIT_FOR_FLAG(flag_db_hash_read);
496
497 /* Read shall now succeed */
498 chan_1_read.func = gatt_read_expect_success_cb;
499 UNSET_FLAG(flag_chan_1_read);
500 gatt_read(&chan_1_read);
501 WAIT_FOR_FLAG(flag_chan_1_read);
502
503 /* Signal to server that reads are done */
504 backchannel_sync_send();
505
506 PASS("GATT client Passed\n");
507 }
508
test_main_retry_reads_no_eatt(void)509 static void test_main_retry_reads_no_eatt(void)
510 {
511 test_main_common(false);
512
513 chan_1_read.func = gatt_read_expect_err_out_of_sync_cb;
514 gatt_read(&chan_1_read);
515 WAIT_FOR_FLAG(flag_chan_1_read);
516
517 /* Read again to become change-aware */
518 chan_1_read.func = gatt_read_expect_success_cb;
519 UNSET_FLAG(flag_chan_1_read);
520 gatt_read(&chan_1_read);
521 WAIT_FOR_FLAG(flag_chan_1_read);
522
523 /* Signal to server that reads are done */
524 backchannel_sync_send();
525
526 PASS("GATT client Passed\n");
527 }
528
529 static const struct bst_test_instance test_vcs[] = {
530 {
531 .test_id = "gatt_client_db_hash_read_eatt",
532 .test_pre_init_f = test_init,
533 .test_tick_f = test_tick,
534 .test_main_f = test_main_db_hash_read_eatt,
535 },
536 {
537 .test_id = "gatt_client_out_of_sync_eatt",
538 .test_pre_init_f = test_init,
539 .test_tick_f = test_tick,
540 .test_main_f = test_main_out_of_sync_eatt,
541 },
542 {
543 .test_id = "gatt_client_retry_reads_eatt",
544 .test_pre_init_f = test_init,
545 .test_tick_f = test_tick,
546 .test_main_f = test_main_retry_reads_eatt,
547 },
548 {
549 .test_id = "gatt_client_db_hash_read_no_eatt",
550 .test_pre_init_f = test_init,
551 .test_tick_f = test_tick,
552 .test_main_f = test_main_db_hash_read_no_eatt,
553 },
554 {
555 .test_id = "gatt_client_out_of_sync_no_eatt",
556 .test_pre_init_f = test_init,
557 .test_tick_f = test_tick,
558 .test_main_f = test_main_out_of_sync_no_eatt,
559 },
560 {
561 .test_id = "gatt_client_retry_reads_no_eatt",
562 .test_pre_init_f = test_init,
563 .test_tick_f = test_tick,
564 .test_main_f = test_main_retry_reads_no_eatt,
565 },
566 BSTEST_END_MARKER,
567 };
568
test_gatt_client_install(struct bst_test_list * tests)569 struct bst_test_list *test_gatt_client_install(struct bst_test_list *tests)
570 {
571 return bst_add_tests(tests, test_vcs);
572 }
573