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_is_encrypted);
14 CREATE_FLAG(flag_discover_complete);
15 CREATE_FLAG(flag_short_subscribed);
16 CREATE_FLAG(flag_long_subscribed);
17
18 static struct bt_conn *g_conn;
19 static uint16_t chrc_handle;
20 static uint16_t long_chrc_handle;
21 static const struct bt_uuid *test_svc_uuid = TEST_SERVICE_UUID;
22
connected(struct bt_conn * conn,uint8_t err)23 static void connected(struct bt_conn *conn, uint8_t err)
24 {
25 char addr[BT_ADDR_LE_STR_LEN];
26
27 bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
28
29 if (err != 0) {
30 FAIL("Failed to connect to %s (%u)\n", addr, err);
31 return;
32 }
33
34 printk("Connected to %s\n", addr);
35
36 SET_FLAG(flag_is_connected);
37 }
38
disconnected(struct bt_conn * conn,uint8_t reason)39 static void disconnected(struct bt_conn *conn, uint8_t reason)
40 {
41 char addr[BT_ADDR_LE_STR_LEN];
42
43 if (conn != g_conn) {
44 return;
45 }
46
47 bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
48
49 printk("Disconnected: %s (reason 0x%02x)\n", addr, reason);
50
51 bt_conn_unref(g_conn);
52
53 g_conn = NULL;
54 UNSET_FLAG(flag_is_connected);
55 }
56
security_changed(struct bt_conn * conn,bt_security_t level,enum bt_security_err err)57 void security_changed(struct bt_conn *conn, bt_security_t level, enum bt_security_err err)
58 {
59 if (err) {
60 FAIL("Encryption failer (%d)\n", err);
61 } else if (level < BT_SECURITY_L2) {
62 FAIL("Insufficient sec level (%d)\n", level);
63 } else {
64 SET_FLAG(flag_is_encrypted);
65 }
66 }
67
68 BT_CONN_CB_DEFINE(conn_callbacks) = {
69 .connected = connected,
70 .disconnected = disconnected,
71 .security_changed = security_changed,
72 };
73
device_found(const bt_addr_le_t * addr,int8_t rssi,uint8_t type,struct net_buf_simple * ad)74 void device_found(const bt_addr_le_t *addr, int8_t rssi, uint8_t type, struct net_buf_simple *ad)
75 {
76 char addr_str[BT_ADDR_LE_STR_LEN];
77 int err;
78
79 if (g_conn != NULL) {
80 return;
81 }
82
83 /* We're only interested in connectable events */
84 if (type != BT_HCI_ADV_IND && type != BT_HCI_ADV_DIRECT_IND) {
85 return;
86 }
87
88 bt_addr_le_to_str(addr, addr_str, sizeof(addr_str));
89 printk("Device found: %s (RSSI %d)\n", addr_str, rssi);
90
91 printk("Stopping scan\n");
92 err = bt_le_scan_stop();
93 if (err != 0) {
94 FAIL("Could not stop scan: %d");
95 return;
96 }
97
98 err = bt_conn_le_create(addr, BT_CONN_LE_CREATE_CONN, BT_LE_CONN_PARAM_DEFAULT, &g_conn);
99 if (err != 0) {
100 FAIL("Could not connect to peer: %d", err);
101 }
102 }
103
discover_func(struct bt_conn * conn,const struct bt_gatt_attr * attr,struct bt_gatt_discover_params * params)104 static uint8_t discover_func(struct bt_conn *conn, const struct bt_gatt_attr *attr,
105 struct bt_gatt_discover_params *params)
106 {
107 int err;
108
109 if (attr == NULL) {
110 if (chrc_handle == 0 || long_chrc_handle == 0) {
111 FAIL("Did not discover chrc (%x) or long_chrc (%x)", chrc_handle,
112 long_chrc_handle);
113 }
114
115 (void)memset(params, 0, sizeof(*params));
116
117 SET_FLAG(flag_discover_complete);
118
119 return BT_GATT_ITER_STOP;
120 }
121
122 printk("[ATTRIBUTE] handle %u\n", attr->handle);
123
124 if (params->type == BT_GATT_DISCOVER_PRIMARY &&
125 bt_uuid_cmp(params->uuid, TEST_SERVICE_UUID) == 0) {
126 printk("Found test service\n");
127 params->uuid = NULL;
128 params->start_handle = attr->handle + 1;
129 params->type = BT_GATT_DISCOVER_CHARACTERISTIC;
130
131 err = bt_gatt_discover(conn, params);
132 if (err != 0) {
133 FAIL("Discover failed (err %d)\n", err);
134 }
135
136 return BT_GATT_ITER_STOP;
137 } else if (params->type == BT_GATT_DISCOVER_CHARACTERISTIC) {
138 const struct bt_gatt_chrc *chrc = (struct bt_gatt_chrc *)attr->user_data;
139
140 if (bt_uuid_cmp(chrc->uuid, TEST_CHRC_UUID) == 0) {
141 printk("Found chrc\n");
142 chrc_handle = chrc->value_handle;
143 } else if (bt_uuid_cmp(chrc->uuid, TEST_LONG_CHRC_UUID) == 0) {
144 printk("Found long_chrc\n");
145 long_chrc_handle = chrc->value_handle;
146 }
147 }
148
149 return BT_GATT_ITER_CONTINUE;
150 }
151
gatt_discover(enum bt_att_chan_opt opt)152 static void gatt_discover(enum bt_att_chan_opt opt)
153 {
154 static struct bt_gatt_discover_params discover_params;
155 int err;
156
157 printk("Discovering services and characteristics\n");
158
159 discover_params.uuid = test_svc_uuid;
160 discover_params.func = discover_func;
161 discover_params.start_handle = BT_ATT_FIRST_ATTRIBUTE_HANDLE;
162 discover_params.end_handle = BT_ATT_LAST_ATTRIBUTE_HANDLE;
163 discover_params.type = BT_GATT_DISCOVER_PRIMARY;
164 discover_params.chan_opt = opt;
165
166 err = bt_gatt_discover(g_conn, &discover_params);
167 if (err != 0) {
168 FAIL("Discover failed(err %d)\n", err);
169 }
170
171 WAIT_FOR_FLAG(flag_discover_complete);
172 printk("Discover complete\n");
173 }
174
test_short_subscribed(struct bt_conn * conn,uint8_t err,struct bt_gatt_subscribe_params * params)175 static void test_short_subscribed(struct bt_conn *conn, uint8_t err,
176 struct bt_gatt_subscribe_params *params)
177 {
178 if (err) {
179 FAIL("Subscribe failed (err %d)\n", err);
180 }
181
182 SET_FLAG(flag_short_subscribed);
183
184 if (!params) {
185 printk("params NULL\n");
186 return;
187 }
188
189 if (params->value_handle == chrc_handle) {
190 printk("Subscribed to short characteristic\n");
191 } else {
192 FAIL("Unknown handle %d\n", params->value_handle);
193 }
194 }
195
test_long_subscribed(struct bt_conn * conn,uint8_t err,struct bt_gatt_subscribe_params * params)196 static void test_long_subscribed(struct bt_conn *conn, uint8_t err,
197 struct bt_gatt_subscribe_params *params)
198 {
199 if (err) {
200 FAIL("Subscribe failed (err %d)\n", err);
201 }
202
203 SET_FLAG(flag_long_subscribed);
204
205 if (!params) {
206 printk("params NULL\n");
207 return;
208 }
209
210 if (params->value_handle == long_chrc_handle) {
211 printk("Subscribed to long characteristic\n");
212 } else {
213 FAIL("Unknown handle %d\n", params->value_handle);
214 }
215 }
216
217 static volatile size_t num_notifications;
test_notify(struct bt_conn * conn,struct bt_gatt_subscribe_params * params,const void * data,uint16_t length)218 uint8_t test_notify(struct bt_conn *conn, struct bt_gatt_subscribe_params *params, const void *data,
219 uint16_t length)
220 {
221 printk("Received notification #%u with length %d\n", num_notifications++, length);
222
223 return BT_GATT_ITER_CONTINUE;
224 }
225
226 static struct bt_gatt_discover_params disc_params_short;
227 static struct bt_gatt_subscribe_params sub_params_short = {
228 .notify = test_notify,
229 .subscribe = test_short_subscribed,
230 .ccc_handle = BT_GATT_AUTO_DISCOVER_CCC_HANDLE,
231 .disc_params = &disc_params_short,
232 .end_handle = BT_ATT_LAST_ATTRIBUTE_HANDLE,
233 .value = BT_GATT_CCC_NOTIFY,
234 };
235 static struct bt_gatt_discover_params disc_params_long;
236 static struct bt_gatt_subscribe_params sub_params_long = {
237 .notify = test_notify,
238 .subscribe = test_long_subscribed,
239 .ccc_handle = BT_GATT_AUTO_DISCOVER_CCC_HANDLE,
240 .disc_params = &disc_params_long,
241 .end_handle = BT_ATT_LAST_ATTRIBUTE_HANDLE,
242 .value = BT_GATT_CCC_NOTIFY,
243 };
244
gatt_subscribe_short(enum bt_att_chan_opt opt)245 static void gatt_subscribe_short(enum bt_att_chan_opt opt)
246 {
247 int err;
248
249 sub_params_short.value_handle = chrc_handle;
250 sub_params_short.chan_opt = opt;
251 err = bt_gatt_subscribe(g_conn, &sub_params_short);
252 if (err < 0) {
253 FAIL("Failed to subscribe\n");
254 } else {
255 printk("Subscribe request sent\n");
256 }
257 }
258
gatt_unsubscribe_short(enum bt_att_chan_opt opt)259 static void gatt_unsubscribe_short(enum bt_att_chan_opt opt)
260 {
261 int err;
262
263 sub_params_short.value_handle = chrc_handle;
264 sub_params_short.chan_opt = opt;
265 err = bt_gatt_unsubscribe(g_conn, &sub_params_short);
266 if (err < 0) {
267 FAIL("Failed to unsubscribe\n");
268 } else {
269 printk("Unsubscribe request sent\n");
270 }
271 }
272
gatt_subscribe_long(enum bt_att_chan_opt opt)273 static void gatt_subscribe_long(enum bt_att_chan_opt opt)
274 {
275 int err;
276
277 UNSET_FLAG(flag_long_subscribed);
278 sub_params_long.value_handle = long_chrc_handle;
279 sub_params_long.chan_opt = opt;
280 err = bt_gatt_subscribe(g_conn, &sub_params_long);
281 if (err < 0) {
282 FAIL("Failed to subscribe\n");
283 } else {
284 printk("Subscribe request sent\n");
285 }
286 }
287
gatt_unsubscribe_long(enum bt_att_chan_opt opt)288 static void gatt_unsubscribe_long(enum bt_att_chan_opt opt)
289 {
290 int err;
291
292 UNSET_FLAG(flag_long_subscribed);
293 sub_params_long.value_handle = long_chrc_handle;
294 sub_params_long.chan_opt = opt;
295 err = bt_gatt_unsubscribe(g_conn, &sub_params_long);
296 if (err < 0) {
297 FAIL("Failed to unsubscribe\n");
298 } else {
299 printk("Unsubscribe request sent\n");
300 }
301 }
302
setup(void)303 static void setup(void)
304 {
305 int err;
306
307 err = bt_enable(NULL);
308 if (err != 0) {
309 FAIL("Bluetooth discover failed (err %d)\n", err);
310 }
311
312 err = bt_le_scan_start(BT_LE_SCAN_PASSIVE, device_found);
313 if (err != 0) {
314 FAIL("Scanning failed to start (err %d)\n", err);
315 }
316
317 printk("Scanning successfully started\n");
318
319 WAIT_FOR_FLAG(flag_is_connected);
320
321 err = bt_conn_set_security(g_conn, BT_SECURITY_L2);
322 if (err) {
323 FAIL("Starting encryption procedure failed (%d)\n", err);
324 }
325
326 WAIT_FOR_FLAG(flag_is_encrypted);
327
328 while (bt_eatt_count(g_conn) < CONFIG_BT_EATT_MAX) {
329 k_sleep(K_MSEC(10));
330 }
331
332 printk("EATT connected\n");
333 }
334
test_main_none(void)335 static void test_main_none(void)
336 {
337 setup();
338
339 gatt_discover(BT_ATT_CHAN_OPT_NONE);
340 gatt_subscribe_short(BT_ATT_CHAN_OPT_NONE);
341 gatt_subscribe_long(BT_ATT_CHAN_OPT_NONE);
342 WAIT_FOR_FLAG(flag_short_subscribed);
343 WAIT_FOR_FLAG(flag_long_subscribed);
344 printk("Subscribed\n");
345
346 while (num_notifications < NOTIFICATION_COUNT) {
347 k_sleep(K_MSEC(100));
348 }
349
350 gatt_unsubscribe_short(BT_ATT_CHAN_OPT_NONE);
351 gatt_unsubscribe_long(BT_ATT_CHAN_OPT_NONE);
352 WAIT_FOR_FLAG(flag_short_subscribed);
353 WAIT_FOR_FLAG(flag_long_subscribed);
354
355 printk("Unsubscribed\n");
356
357 PASS("GATT client Passed\n");
358 }
359
test_main_unenhanced(void)360 static void test_main_unenhanced(void)
361 {
362 setup();
363
364 gatt_discover(BT_ATT_CHAN_OPT_UNENHANCED_ONLY);
365 gatt_subscribe_short(BT_ATT_CHAN_OPT_UNENHANCED_ONLY);
366 gatt_subscribe_long(BT_ATT_CHAN_OPT_UNENHANCED_ONLY);
367 WAIT_FOR_FLAG(flag_short_subscribed);
368 WAIT_FOR_FLAG(flag_long_subscribed);
369
370 printk("Subscribed\n");
371
372 while (num_notifications < NOTIFICATION_COUNT) {
373 k_sleep(K_MSEC(100));
374 }
375
376 gatt_unsubscribe_short(BT_ATT_CHAN_OPT_UNENHANCED_ONLY);
377 gatt_unsubscribe_long(BT_ATT_CHAN_OPT_UNENHANCED_ONLY);
378 WAIT_FOR_FLAG(flag_short_subscribed);
379 WAIT_FOR_FLAG(flag_long_subscribed);
380
381 printk("Unsubscribed\n");
382
383 PASS("GATT client Passed\n");
384 }
385
test_main_enhanced(void)386 static void test_main_enhanced(void)
387 {
388 setup();
389
390 gatt_discover(BT_ATT_CHAN_OPT_ENHANCED_ONLY);
391 gatt_subscribe_short(BT_ATT_CHAN_OPT_ENHANCED_ONLY);
392 gatt_subscribe_long(BT_ATT_CHAN_OPT_ENHANCED_ONLY);
393 WAIT_FOR_FLAG(flag_short_subscribed);
394 WAIT_FOR_FLAG(flag_long_subscribed);
395
396 printk("Subscribed\n");
397
398 while (num_notifications < NOTIFICATION_COUNT) {
399 k_sleep(K_MSEC(100));
400 }
401
402 gatt_unsubscribe_short(BT_ATT_CHAN_OPT_ENHANCED_ONLY);
403 gatt_unsubscribe_long(BT_ATT_CHAN_OPT_ENHANCED_ONLY);
404 WAIT_FOR_FLAG(flag_short_subscribed);
405 WAIT_FOR_FLAG(flag_long_subscribed);
406
407 printk("Unsubscribed\n");
408
409 PASS("GATT client Passed\n");
410 }
411
test_main_mixed(void)412 static void test_main_mixed(void)
413 {
414 setup();
415
416 gatt_discover(BT_ATT_CHAN_OPT_ENHANCED_ONLY);
417 gatt_subscribe_short(BT_ATT_CHAN_OPT_ENHANCED_ONLY);
418 gatt_subscribe_long(BT_ATT_CHAN_OPT_UNENHANCED_ONLY);
419 WAIT_FOR_FLAG(flag_short_subscribed);
420 WAIT_FOR_FLAG(flag_long_subscribed);
421
422 printk("Subscribed\n");
423
424 while (num_notifications < NOTIFICATION_COUNT) {
425 k_sleep(K_MSEC(100));
426 }
427
428 gatt_unsubscribe_short(BT_ATT_CHAN_OPT_UNENHANCED_ONLY);
429 gatt_unsubscribe_long(BT_ATT_CHAN_OPT_ENHANCED_ONLY);
430 WAIT_FOR_FLAG(flag_short_subscribed);
431 WAIT_FOR_FLAG(flag_long_subscribed);
432
433 printk("Unsubscribed\n");
434
435 PASS("GATT client Passed\n");
436 }
437
438 static const struct bst_test_instance test_vcs[] = {
439 {
440 .test_id = "gatt_client_none",
441 .test_pre_init_f = test_init,
442 .test_tick_f = test_tick,
443 .test_main_f = test_main_none,
444 },
445 {
446 .test_id = "gatt_client_unenhanced",
447 .test_pre_init_f = test_init,
448 .test_tick_f = test_tick,
449 .test_main_f = test_main_unenhanced,
450 },
451 {
452 .test_id = "gatt_client_enhanced",
453 .test_pre_init_f = test_init,
454 .test_tick_f = test_tick,
455 .test_main_f = test_main_enhanced,
456 },
457 {
458 .test_id = "gatt_client_mixed",
459 .test_pre_init_f = test_init,
460 .test_tick_f = test_tick,
461 .test_main_f = test_main_mixed,
462 },
463 BSTEST_END_MARKER,
464 };
465
test_gatt_client_install(struct bst_test_list * tests)466 struct bst_test_list *test_gatt_client_install(struct bst_test_list *tests)
467 {
468 return bst_add_tests(tests, test_vcs);
469 }
470