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/bluetooth/services/bas.h>
25 #include <zephyr/bluetooth/services/hrs.h>
26
27 #include <zephyr/sys/byteorder.h>
28
29 static struct bt_conn *default_conn;
30
31 /*
32 * Basic connection test:
33 * We expect a central to connect to us.
34 *
35 * The thread code is mostly a copy of the peripheral_hr sample device
36 */
37
38 #define WAIT_TIME 5 /*seconds*/
39 #define WAIT_TIME_REPEAT 22 /*seconds*/
40 extern enum bst_result_t bst_result;
41 static uint8_t repeat_connect;
42
43 #define FAIL(...) \
44 do { \
45 bst_result = Failed; \
46 bs_trace_error_time_line(__VA_ARGS__); \
47 } while (0)
48
49 #define PASS(...) \
50 do { \
51 bst_result = Passed; \
52 bs_trace_info_time(1, __VA_ARGS__); \
53 } while (0)
54
test_con2_init(void)55 static void test_con2_init(void)
56 {
57 bst_ticker_set_next_tick_absolute(WAIT_TIME*1e6);
58 bst_result = In_progress;
59 }
60
test_con2_repeat_init(void)61 static void test_con2_repeat_init(void)
62 {
63 bst_ticker_set_next_tick_absolute(WAIT_TIME_REPEAT*1e6);
64 bst_result = In_progress;
65 }
66
test_con2_tick(bs_time_t HW_device_time)67 static void test_con2_tick(bs_time_t HW_device_time)
68 {
69 /*
70 * If in WAIT_TIME seconds the testcase did not already pass
71 * (and finish) we consider it failed
72 */
73 if (bst_result != Passed) {
74 FAIL("test_connect2 failed (not passed after %i seconds)\n",
75 WAIT_TIME);
76 }
77 }
78
test_con2_repeat_tick(bs_time_t HW_device_time)79 static void test_con2_repeat_tick(bs_time_t HW_device_time)
80 {
81 /*
82 * If in WAIT_TIME seconds the testcase did not already pass
83 * (and finish) we consider it failed
84 */
85 if (bst_result != Passed) {
86 FAIL("test_connect2 failed (not passed after %i seconds)\n",
87 WAIT_TIME_REPEAT);
88 }
89 }
90
91 static const struct bt_data ad[] = {
92 BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)),
93 BT_DATA_BYTES(BT_DATA_UUID16_ALL,
94 BT_UUID_16_ENCODE(BT_UUID_HRS_VAL),
95 BT_UUID_16_ENCODE(BT_UUID_BAS_VAL),
96 BT_UUID_16_ENCODE(BT_UUID_CTS_VAL)),
97 };
98
connected(struct bt_conn * conn,uint8_t err)99 static void connected(struct bt_conn *conn, uint8_t err)
100 {
101 if (err) {
102 FAIL("Connection failed (err 0x%02x)\n", err);
103 } else {
104 default_conn = bt_conn_ref(conn);
105
106 printk("Peripheral Connected\n");
107 repeat_connect++;
108
109 if (repeat_connect >= 20) { /* We consider it passed */
110 PASS("Peripheral Repeat20 Testcase passed\n");
111 }
112 }
113 }
114
disconnected(struct bt_conn * conn,uint8_t reason)115 static void disconnected(struct bt_conn *conn, uint8_t reason)
116 {
117 printk("Peripheral disconnected (reason 0x%02x)\n", reason);
118
119 if (default_conn) {
120 bt_conn_unref(default_conn);
121 default_conn = NULL;
122 }
123 }
124
start_advertising(void)125 static int start_advertising(void)
126 {
127 int err;
128
129 err = bt_le_adv_start(BT_LE_ADV_CONN_FAST_1, ad, ARRAY_SIZE(ad), NULL, 0);
130 if (err) {
131 printk("Advertising failed to start (err %d)\n", err);
132 }
133
134 return err;
135 }
136
recycled(void)137 static void recycled(void)
138 {
139 start_advertising();
140 }
141
142 static struct bt_conn_cb conn_callbacks = {
143 .connected = connected,
144 .disconnected = disconnected,
145 .recycled = recycled,
146 };
147
bt_ready(void)148 static void bt_ready(void)
149 {
150 int err;
151
152 printk("Peripheral Bluetooth initialized\n");
153
154 err = bt_le_adv_start(BT_LE_ADV_CONN_FAST_1, ad, ARRAY_SIZE(ad), NULL, 0);
155 if (err) {
156 FAIL("Advertising failed to start (err %d)\n", err);
157 return;
158 }
159
160 err = start_advertising();
161
162 if (!err) {
163 printk("Advertising successfully started\n");
164 }
165 }
166
bas_notify(void)167 static void bas_notify(void)
168 {
169 uint8_t battery_level = bt_bas_get_battery_level();
170
171 battery_level--;
172
173 if (!battery_level) {
174 battery_level = 100U;
175 }
176
177 bt_bas_set_battery_level(battery_level);
178 }
179
hrs_notify(void)180 static void hrs_notify(void)
181 {
182 static uint8_t heartrate = 90U;
183
184 /* Heartrate measurements simulation */
185 heartrate++;
186 if (heartrate == 160U) {
187 heartrate = 90U;
188 }
189
190 bt_hrs_notify(heartrate);
191 }
192
test_con2_main(void)193 static void test_con2_main(void)
194 {
195 static int notify_count;
196 int err;
197
198 bt_conn_cb_register(&conn_callbacks);
199
200 err = bt_enable(NULL);
201 if (err) {
202 FAIL("Bluetooth init failed (err %d)\n", err);
203 return;
204 }
205
206 bt_ready();
207
208 /* Implement notification. At the moment there is no suitable way
209 * of starting delayed work so we do it here
210 */
211 while (1) {
212 if (IS_ENABLED(CONFIG_TEST_CONN_INTERVAL_1MS) ||
213 IS_ENABLED(CONFIG_BT_CTLR_TX_DEFER)) {
214 k_sleep(K_MSEC(1));
215 } else {
216 k_sleep(K_SECONDS(1));
217 }
218
219 /* Heartrate measurements simulation */
220 hrs_notify();
221
222 /* Battery level simulation */
223 bas_notify();
224
225 if (notify_count++ == 1) { /* We consider it passed */
226 PASS("Peripheral Testcase passed\n");
227 }
228 }
229 }
230
test_con2_repeat_main(void)231 static void test_con2_repeat_main(void)
232 {
233 int err;
234
235 bt_conn_cb_register(&conn_callbacks);
236
237 err = bt_enable(NULL);
238 if (err) {
239 FAIL("Bluetooth init failed (err %d)\n", err);
240 return;
241 }
242
243 bt_ready();
244
245 while (1) {
246 k_sleep(K_SECONDS(1));
247 }
248 }
249
250 static const struct bst_test_instance test_connect[] = {
251 {
252 .test_id = "peripheral",
253 .test_descr = "Basic connection test. It expects that a "
254 "central device can be found. The test will "
255 "pass if notifications can be sent without "
256 "crash.",
257 .test_pre_init_f = test_con2_init,
258 .test_tick_f = test_con2_tick,
259 .test_main_f = test_con2_main
260 },
261 {
262 .test_id = "peripheral_repeat20",
263 .test_descr = "Multiple connections test. It expects that a "
264 "central device connects 20 times. The test will "
265 "pass if 20 connections are succeed in less than 22 seconds",
266 .test_pre_init_f = test_con2_repeat_init,
267 .test_tick_f = test_con2_repeat_tick,
268 .test_main_f = test_con2_repeat_main
269 },
270 BSTEST_END_MARKER
271 };
272
test_connect2_install(struct bst_test_list * tests)273 struct bst_test_list *test_connect2_install(struct bst_test_list *tests)
274 {
275 tests = bst_add_tests(tests, test_connect);
276 return tests;
277 }
278