1 /*
2 * Copyright (c) 2015-2016 Intel Corporation
3 * Copyright (c) 2017-2019 Oticon A/S
4 *
5 * SPDX-License-Identifier: Apache-2.0
6 */
7 #include <zephyr/kernel.h>
8
9 #include "bs_types.h"
10 #include "bs_tracing.h"
11 #include "time_machine.h"
12 #include "bstests.h"
13
14 #include <zephyr/types.h>
15 #include <stddef.h>
16 #include <errno.h>
17 #include <zephyr/sys/printk.h>
18
19 #include <zephyr/bluetooth/bluetooth.h>
20 #include <zephyr/bluetooth/hci.h>
21 #include <zephyr/bluetooth/conn.h>
22 #include <zephyr/bluetooth/uuid.h>
23 #include <zephyr/bluetooth/gatt.h>
24 #include <zephyr/sys/byteorder.h>
25
26 static struct bt_conn *default_conn;
27
28 static struct bt_uuid_16 uuid = BT_UUID_INIT_16(0);
29 static struct bt_gatt_discover_params discover_params;
30 static struct bt_gatt_subscribe_params subscribe_params;
31
32 #if defined(CONFIG_TEST_CONN_INTERVAL_1MS)
33 #define UPDATE_PARAM_INTERVAL_MIN 1
34 #define UPDATE_PARAM_INTERVAL_MAX 1
35 #define UPDATE_PARAM_LATENCY 0
36 #define UPDATE_PARAM_TIMEOUT 10
37 #define TEST_NOTIFY_COUNT 3000
38 #else /* !CONFIG_TEST_CONN_INTERVAL_1MS */
39 #define UPDATE_PARAM_INTERVAL_MIN 25
40 #define UPDATE_PARAM_INTERVAL_MAX 45
41 #define UPDATE_PARAM_LATENCY 1
42 #define UPDATE_PARAM_TIMEOUT 250
43 #define TEST_NOTIFY_COUNT 3
44 #endif /* !CONFIG_TEST_CONN_INTERVAL_1MS */
45
46 static struct bt_le_conn_param update_params = {
47 .interval_min = UPDATE_PARAM_INTERVAL_MIN,
48 .interval_max = UPDATE_PARAM_INTERVAL_MAX,
49 .latency = UPDATE_PARAM_LATENCY,
50 .timeout = UPDATE_PARAM_TIMEOUT,
51 };
52
53 static bool encrypt_link;
54 static bool expect_ntf = true;
55 static uint8_t repeat_connect;
56 static uint8_t connected_signal;
57
58 /*
59 * Basic connection test:
60 * We expect to find a connectable peripheral to which we will
61 * connect.
62 *
63 * After connecting, we update connection parameters and channel
64 * map, and expect to receive 2 notifications.
65 * If we do, the test case passes.
66 * If we do not in 5 seconds, the testcase is considered failed
67 *
68 * The thread code is mostly a copy of the central_hr sample device
69 */
70
71 #define WAIT_TIME 6 /*seconds*/
72 #define WAIT_TIME_TX_DEFER 800 /* milliseconds */
73 #define WAIT_TIME_REPEAT 22 /*seconds*/
74 extern enum bst_result_t bst_result;
75
76 #define FAIL(...) \
77 do { \
78 bst_result = Failed; \
79 bs_trace_error_time_line(__VA_ARGS__); \
80 } while (0)
81
82 #define PASS(...) \
83 do { \
84 bst_result = Passed; \
85 bs_trace_info_time(1, __VA_ARGS__); \
86 } while (0)
87
test_con1_init(void)88 static void test_con1_init(void)
89 {
90 if (IS_ENABLED(CONFIG_BT_CTLR_TX_DEFER)) {
91 bst_ticker_set_next_tick_absolute(WAIT_TIME_TX_DEFER*1e3);
92 } else {
93 bst_ticker_set_next_tick_absolute(WAIT_TIME*1e6);
94 }
95 bst_result = In_progress;
96 }
97
test_con_encrypted_init(void)98 static void test_con_encrypted_init(void)
99 {
100 encrypt_link = true;
101 test_con1_init();
102 }
103
test_con20_init(void)104 static void test_con20_init(void)
105 {
106 repeat_connect = 20;
107 expect_ntf = false;
108 bst_ticker_set_next_tick_absolute(WAIT_TIME_REPEAT*1e6);
109 bst_result = In_progress;
110 }
111
test_con1_tick(bs_time_t HW_device_time)112 static void test_con1_tick(bs_time_t HW_device_time)
113 {
114 /*
115 * If in WAIT_TIME seconds the testcase did not already pass
116 * (and finish) we consider it failed
117 */
118 if (bst_result != Passed) {
119 FAIL("test_connect1 failed (not passed after %i seconds)\n",
120 WAIT_TIME);
121 }
122 }
123
test_con20_tick(bs_time_t HW_device_time)124 static void test_con20_tick(bs_time_t HW_device_time)
125 {
126 if (bst_result != Passed) {
127 FAIL("test_connect1 failed (not passed after %i seconds)\n",
128 WAIT_TIME_REPEAT);
129 }
130 }
131
notify_func(struct bt_conn * conn,struct bt_gatt_subscribe_params * params,const void * data,uint16_t length)132 static uint8_t notify_func(struct bt_conn *conn,
133 struct bt_gatt_subscribe_params *params,
134 const void *data, uint16_t length)
135 {
136 static uint32_t cycle_stamp;
137 static int notify_count;
138 uint32_t cycle_now;
139 uint64_t delta;
140
141 if (!data) {
142 printk("[UNSUBSCRIBED]\n");
143 params->value_handle = 0U;
144 return BT_GATT_ITER_STOP;
145 }
146
147 cycle_now = k_cycle_get_32();
148 delta = cycle_now - cycle_stamp;
149 cycle_stamp = cycle_now;
150 delta = k_cyc_to_ns_floor64(delta);
151
152 if (!IS_ENABLED(CONFIG_TEST_CONN_INTERVAL_1MS) ||
153 ((delta > (NSEC_PER_MSEC / 2U)) &&
154 (delta < (NSEC_PER_MSEC + (NSEC_PER_MSEC / 2U))))) {
155 notify_count++;
156 }
157
158 printk("[NOTIFICATION] %u. data %p length %u in %llu ns\n",
159 notify_count, data, length, delta);
160
161 if (notify_count >= TEST_NOTIFY_COUNT) { /* We consider it passed */
162 int err;
163
164 /* Disconnect before actually passing */
165 err = bt_conn_disconnect(default_conn,
166 BT_HCI_ERR_REMOTE_USER_TERM_CONN);
167 if (err) {
168 FAIL("Disconnection failed (err %d)\n", err);
169 return BT_GATT_ITER_STOP;
170 }
171
172 if (bst_result != Failed) {
173 PASS("Testcase passed\n");
174 }
175 bs_trace_silent_exit(0);
176 }
177
178 return BT_GATT_ITER_CONTINUE;
179 }
180
discover_func(struct bt_conn * conn,const struct bt_gatt_attr * attr,struct bt_gatt_discover_params * params)181 static uint8_t discover_func(struct bt_conn *conn,
182 const struct bt_gatt_attr *attr,
183 struct bt_gatt_discover_params *params)
184 {
185 int err;
186
187 if (!attr) {
188 printk("Discover complete\n");
189 memset(params, 0, sizeof(*params));
190 return BT_GATT_ITER_STOP;
191 }
192
193 printk("[ATTRIBUTE] handle %u\n", attr->handle);
194
195 if (!bt_uuid_cmp(discover_params.uuid, BT_UUID_HRS)) {
196 memcpy(&uuid, BT_UUID_HRS_MEASUREMENT, sizeof(uuid));
197 discover_params.uuid = &uuid.uuid;
198 discover_params.start_handle = attr->handle + 1;
199 discover_params.type = BT_GATT_DISCOVER_CHARACTERISTIC;
200
201 err = bt_gatt_discover(conn, &discover_params);
202 if (err) {
203 FAIL("Discover failed (err %d)\n", err);
204 }
205 } else if (!bt_uuid_cmp(discover_params.uuid,
206 BT_UUID_HRS_MEASUREMENT)) {
207 memcpy(&uuid, BT_UUID_GATT_CCC, sizeof(uuid));
208 discover_params.uuid = &uuid.uuid;
209 discover_params.start_handle = attr->handle + 2;
210 discover_params.type = BT_GATT_DISCOVER_DESCRIPTOR;
211 subscribe_params.value_handle = attr->handle + 1;
212
213 err = bt_gatt_discover(conn, &discover_params);
214 if (err) {
215 FAIL("Discover failed (err %d)\n", err);
216 }
217 } else {
218 subscribe_params.notify = notify_func;
219 subscribe_params.value = BT_GATT_CCC_NOTIFY;
220 subscribe_params.ccc_handle = attr->handle;
221
222 err = bt_gatt_subscribe(conn, &subscribe_params);
223 if (err && err != -EALREADY) {
224 FAIL("Subscribe failed (err %d)\n", err);
225 } else {
226 printk("[SUBSCRIBED]\n");
227 }
228
229 return BT_GATT_ITER_STOP;
230 }
231
232 return BT_GATT_ITER_STOP;
233 }
234
update_conn(struct bt_conn * conn,bool bonded)235 static void update_conn(struct bt_conn *conn, bool bonded)
236 {
237 int err;
238
239 if (encrypt_link != bonded) {
240 FAIL("Unexpected bonding status\n");
241 return;
242 }
243
244 printk("Updating connection (bonded: %d)\n", bonded);
245
246 err = bt_conn_le_param_update(conn, &update_params);
247 if (err) {
248 FAIL("Parameter update failed (err %d)\n", err);
249 return;
250 }
251 }
252
253 static struct bt_conn_auth_info_cb auth_cb_success = {
254 .pairing_complete = update_conn,
255 };
256
connected(struct bt_conn * conn,uint8_t conn_err)257 static void connected(struct bt_conn *conn, uint8_t conn_err)
258 {
259 char addr[BT_ADDR_LE_STR_LEN];
260 int err;
261
262 bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
263
264 if (conn_err) {
265 FAIL("Failed to connect to %s (%u)\n", addr, conn_err);
266 return;
267 }
268
269 printk("Connected: %s\n", addr);
270
271 if (conn != default_conn) {
272 return;
273 }
274
275 if (encrypt_link) {
276 k_sleep(K_MSEC(500));
277 bt_conn_auth_info_cb_register(&auth_cb_success);
278 err = bt_conn_set_security(conn, BT_SECURITY_L2);
279 if (err) {
280 FAIL("bt_conn_set_security failed (err %d)\n", err);
281 return;
282 }
283 } else {
284 update_conn(conn, false);
285 }
286 }
287
params_updated(struct bt_conn * conn,uint16_t interval,uint16_t latency,uint16_t timeout)288 static void params_updated(struct bt_conn *conn, uint16_t interval,
289 uint16_t latency, uint16_t timeout)
290 {
291 uint8_t chm[5] = { 0x11, 0x22, 0x33, 0x44, 0x00 };
292 int err;
293
294 if (interval != UPDATE_PARAM_INTERVAL_MAX ||
295 latency != UPDATE_PARAM_LATENCY ||
296 timeout != UPDATE_PARAM_TIMEOUT) {
297 FAIL("Unexpected connection parameters "
298 "(interval: %d, latency: %d, timeout: %d)\n",
299 interval, latency, timeout);
300 return;
301 }
302
303 printk("Connection parameters updated "
304 "(interval: %d, latency: %d, timeout: %d)\n",
305 interval, latency, timeout);
306
307 err = bt_le_set_chan_map(chm);
308 if (err) {
309 FAIL("Channel map update failed (err %d)\n", err);
310 return;
311 }
312
313 if (!expect_ntf) {
314 connected_signal = 1;
315 } else {
316 memcpy(&uuid, BT_UUID_HRS, sizeof(uuid));
317 discover_params.uuid = &uuid.uuid;
318 discover_params.func = discover_func;
319 discover_params.start_handle = BT_ATT_FIRST_ATTRIBUTE_HANDLE;
320 discover_params.end_handle = BT_ATT_LAST_ATTRIBUTE_HANDLE;
321 discover_params.type = BT_GATT_DISCOVER_PRIMARY;
322
323 err = bt_gatt_discover(conn, &discover_params);
324 if (err) {
325 FAIL("Discover failed(err %d)\n", err);
326 return;
327 }
328 }
329 }
330
eir_found(struct bt_data * data,void * user_data)331 static bool eir_found(struct bt_data *data, void *user_data)
332 {
333 bt_addr_le_t *addr = user_data;
334 int i;
335
336 printk("[AD]: %u data_len %u\n", data->type, data->data_len);
337
338 switch (data->type) {
339 case BT_DATA_UUID16_SOME:
340 case BT_DATA_UUID16_ALL:
341 if (data->data_len % sizeof(uint16_t) != 0U) {
342 FAIL("AD malformed\n");
343 return true;
344 }
345
346 for (i = 0; i < data->data_len; i += sizeof(uint16_t)) {
347 const struct bt_uuid *uuid;
348 struct bt_le_conn_param *param;
349 uint16_t u16;
350 int err;
351
352 memcpy(&u16, &data->data[i], sizeof(u16));
353 uuid = BT_UUID_DECLARE_16(sys_le16_to_cpu(u16));
354 if (bt_uuid_cmp(uuid, BT_UUID_HRS)) {
355 continue;
356 }
357
358 err = bt_le_scan_stop();
359 if (err) {
360 FAIL("Stop LE scan failed (err %d)\n", err);
361 continue;
362 }
363
364 param = BT_LE_CONN_PARAM_DEFAULT;
365 err = bt_conn_le_create(addr, BT_CONN_LE_CREATE_CONN,
366 param, &default_conn);
367 if (err) {
368 printk("Create conn failed (err %d)\n", err);
369 }
370
371 return false;
372 }
373 }
374
375 return true;
376 }
377
device_found(const bt_addr_le_t * addr,int8_t rssi,uint8_t type,struct net_buf_simple * ad)378 static void device_found(const bt_addr_le_t *addr, int8_t rssi, uint8_t type,
379 struct net_buf_simple *ad)
380 {
381 char dev[BT_ADDR_LE_STR_LEN];
382
383 bt_addr_le_to_str(addr, dev, sizeof(dev));
384 printk("[DEVICE]: %s, AD evt type %u, AD data len %u, RSSI %i\n",
385 dev, type, ad->len, rssi);
386
387 /* We're only interested in connectable events */
388 if (type == BT_GAP_ADV_TYPE_ADV_IND ||
389 type == BT_GAP_ADV_TYPE_ADV_DIRECT_IND) {
390 bt_data_parse(ad, eir_found, (void *)addr);
391 }
392 }
393
disconnected(struct bt_conn * conn,uint8_t reason)394 static void disconnected(struct bt_conn *conn, uint8_t reason)
395 {
396 char addr[BT_ADDR_LE_STR_LEN];
397 int err;
398
399 bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
400
401 printk("Disconnected: %s (reason 0x%02x)\n", addr, reason);
402
403 if (default_conn != conn) {
404 return;
405 }
406
407 bt_conn_unref(default_conn);
408 default_conn = NULL;
409
410 /* This demo doesn't require active scan */
411 err = bt_le_scan_start(BT_LE_SCAN_PASSIVE, device_found);
412 if (err) {
413 FAIL("Scanning failed to start (err %d)\n", err);
414 }
415
416 printk("Scanning successfully re-started\n");
417 }
418
419 static struct bt_conn_cb conn_callbacks = {
420 .connected = connected,
421 .disconnected = disconnected,
422 .le_param_updated = params_updated,
423 };
424
test_con1_main(void)425 static void test_con1_main(void)
426 {
427 int err;
428
429 bt_conn_cb_register(&conn_callbacks);
430
431 err = bt_enable(NULL);
432
433 if (err) {
434 FAIL("Bluetooth init failed (err %d)\n", err);
435 return;
436 }
437
438 printk("Bluetooth initialized\n");
439
440 err = bt_le_scan_start(BT_LE_SCAN_ACTIVE, device_found);
441
442 if (err) {
443 FAIL("Scanning failed to start (err %d)\n", err);
444 return;
445 }
446
447 printk("Scanning successfully started\n");
448 }
449
test_con20_main(void)450 static void test_con20_main(void)
451 {
452 int err;
453
454 bt_conn_cb_register(&conn_callbacks);
455
456 err = bt_enable(NULL);
457
458 if (err) {
459 FAIL("Bluetooth init failed (err %d)\n", err);
460 return;
461 }
462
463 printk("Bluetooth initialized\n");
464
465 err = bt_le_scan_start(BT_LE_SCAN_ACTIVE, device_found);
466
467 if (err) {
468 FAIL("Scanning failed to start (err %d)\n", err);
469 return;
470 }
471
472 printk("Scanning successfully started\n");
473 /* Implement notification. At the moment there is no suitable way
474 * of starting delayed work so we do it here
475 */
476 while (1) {
477 k_sleep(K_MSEC(500));
478
479 if (connected_signal) {
480 /* Disconnect and continue */
481 printk("Central Disconnect\n");
482 connected_signal = 0;
483 err = bt_conn_disconnect(default_conn,
484 BT_HCI_ERR_REMOTE_USER_TERM_CONN);
485 if (err) {
486 FAIL("Disconnection failed (err %d)\n", err);
487 return;
488 }
489
490 if (bst_result != Failed) {
491 if (repeat_connect) {
492 printk("Disconnection OK\n");
493 } else {
494 PASS("Testcase passed\n");
495 }
496 }
497 if (!repeat_connect || bst_result == Failed) {
498 bs_trace_silent_exit(0);
499 }
500 repeat_connect--;
501 }
502 }
503
504 }
505
506 static const struct bst_test_instance test_connect[] = {
507 {
508 .test_id = "central",
509 .test_descr = "Basic connection test. It expects that a "
510 "peripheral device can be found. The test will "
511 "pass if it can connect to it, and receive a "
512 "notification in less than 5 seconds.",
513 .test_pre_init_f = test_con1_init,
514 .test_tick_f = test_con1_tick,
515 .test_main_f = test_con1_main
516 },
517 {
518 .test_id = "central_encrypted",
519 .test_descr = "Same as central but with an encrypted link",
520 .test_pre_init_f = test_con_encrypted_init,
521 .test_tick_f = test_con1_tick,
522 .test_main_f = test_con1_main
523 },
524 {
525 .test_id = "central_repeat20",
526 .test_descr = "Multiple connections test. It expects that a "
527 "peripheral device can be found. The test will "
528 "pass if it can connect to it 20 times, in less than 22 seconds."
529 "Disconnect and re-connect 20 times",
530 .test_pre_init_f = test_con20_init,
531 .test_tick_f = test_con20_tick,
532 .test_main_f = test_con20_main
533 },
534 BSTEST_END_MARKER
535 };
536
test_connect1_install(struct bst_test_list * tests)537 struct bst_test_list *test_connect1_install(struct bst_test_list *tests)
538 {
539 tests = bst_add_tests(tests, test_connect);
540 return tests;
541 }
542