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