1 /**
2  * Copyright (c) 2024 Croxel, Inc.
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 #include <zephyr/kernel.h>
7 
8 #include "common.h"
9 
10 #include "bs_types.h"
11 #include "bs_tracing.h"
12 #include "time_machine.h"
13 #include "bstests.h"
14 
15 #include <zephyr/types.h>
16 #include <zephyr/sys/printk.h>
17 
18 #include <zephyr/bluetooth/bluetooth.h>
19 #include <zephyr/bluetooth/conn.h>
20 
21 extern enum bst_result_t bst_result;
22 
23 static struct bt_conn *g_conn;
24 
25 CREATE_FLAG(flag_connected);
26 CREATE_FLAG(flag_conn_recycled);
27 
common_init(void)28 static void common_init(void)
29 {
30 	int err;
31 
32 	err = bt_enable(NULL);
33 
34 	if (err) {
35 		FAIL("Bluetooth init failed: %d\n", err);
36 		return;
37 	}
38 	printk("Bluetooth initialized\n");
39 }
40 
create_ext_adv_set(struct bt_le_ext_adv ** adv,bool connectable)41 static void create_ext_adv_set(struct bt_le_ext_adv **adv, bool connectable)
42 {
43 	int err;
44 
45 	printk("Creating extended advertising set...");
46 
47 	const struct bt_le_adv_param *adv_param = connectable ?
48 		BT_LE_EXT_ADV_CONN : BT_LE_EXT_ADV_NCONN;
49 
50 	err = bt_le_ext_adv_create(adv_param, NULL, adv);
51 	if (err) {
52 		printk("Failed to create advertising set: %d\n", err);
53 		return;
54 	}
55 	printk("done.\n");
56 }
57 
start_ext_adv_set(struct bt_le_ext_adv * adv)58 static void start_ext_adv_set(struct bt_le_ext_adv *adv)
59 {
60 	int err;
61 
62 	printk("Starting Extended Advertising...");
63 	err = bt_le_ext_adv_start(adv, BT_LE_EXT_ADV_START_DEFAULT);
64 	if (err) {
65 		printk("Failed to start extended advertising: %d\n", err);
66 		return;
67 	}
68 	printk("done.\n");
69 }
70 
stop_ext_adv_set(struct bt_le_ext_adv * adv)71 static void stop_ext_adv_set(struct bt_le_ext_adv *adv)
72 {
73 	int err;
74 
75 	printk("Stopping Extended Advertising...");
76 	err = bt_le_ext_adv_stop(adv);
77 	if (err) {
78 		printk("Failed to stop extended advertising: %d\n",
79 		       err);
80 		return;
81 	}
82 	printk("done.\n");
83 }
84 
delete_adv_set(struct bt_le_ext_adv * adv)85 static void delete_adv_set(struct bt_le_ext_adv *adv)
86 {
87 	int err;
88 
89 	printk("Delete extended advertising set...");
90 	err = bt_le_ext_adv_delete(adv);
91 	if (err) {
92 		printk("Failed Delete extended advertising set: %d\n", err);
93 		return;
94 	}
95 	printk("done.\n");
96 }
97 
disconnect_from_target(void)98 static void disconnect_from_target(void)
99 {
100 	int err;
101 
102 	printk("Disconnecting...\n");
103 
104 	err = bt_conn_disconnect(g_conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN);
105 	if (err) {
106 		FAIL("BT Disconnect failed: %d\n", err);
107 		return;
108 	}
109 }
110 
connected(struct bt_conn * conn,uint8_t err)111 static void connected(struct bt_conn *conn, uint8_t err)
112 {
113 	char addr[BT_ADDR_LE_STR_LEN];
114 
115 	bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
116 
117 	if (err != BT_HCI_ERR_SUCCESS) {
118 		FAIL("Failed to connect to %s: %u\n", addr, err);
119 		return;
120 	}
121 
122 	printk("Connected to %s\n", addr);
123 	if (g_conn != NULL) {
124 		FAIL("Attempt to override connection object without clean-up\n");
125 		return;
126 	}
127 	g_conn = bt_conn_ref(conn);
128 	SET_FLAG(flag_connected);
129 }
130 
free_conn_object_work_fn(struct k_work * work)131 static void free_conn_object_work_fn(struct k_work *work)
132 {
133 	ARG_UNUSED(work);
134 
135 	bt_conn_unref(g_conn);
136 	g_conn = NULL;
137 }
138 
139 static K_WORK_DELAYABLE_DEFINE(free_conn_object_work, free_conn_object_work_fn);
140 
disconnected(struct bt_conn * conn,uint8_t reason)141 static void disconnected(struct bt_conn *conn, uint8_t reason)
142 {
143 	char addr[BT_ADDR_LE_STR_LEN];
144 
145 	bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
146 
147 	printk("Disconnected: %s (reason %u)\n", addr, reason);
148 
149 	/* Schedule to cause de-sync between disconnected and recycled events,
150 	 * in order to prove the test is relying properly on it.
151 	 */
152 	k_work_schedule(&free_conn_object_work, K_MSEC(100));
153 
154 	UNSET_FLAG(flag_connected);
155 }
156 
recycled(void)157 static void recycled(void)
158 {
159 	SET_FLAG(flag_conn_recycled);
160 }
161 
162 static struct bt_conn_cb conn_cbs = {
163 	.connected = connected,
164 	.disconnected = disconnected,
165 	.recycled = recycled,
166 };
167 
main_ext_adv_advertiser(void)168 static void main_ext_adv_advertiser(void)
169 {
170 	struct bt_le_ext_adv *ext_adv;
171 
172 	common_init();
173 
174 	create_ext_adv_set(&ext_adv, false);
175 	start_ext_adv_set(ext_adv);
176 
177 	/* Advertise for a bit */
178 	k_sleep(K_SECONDS(5));
179 
180 	stop_ext_adv_set(ext_adv);
181 	delete_adv_set(ext_adv);
182 
183 	ext_adv = NULL;
184 
185 	PASS("Extended advertiser passed\n");
186 }
187 
adv_connect_and_disconnect_cycle(void)188 static void adv_connect_and_disconnect_cycle(void)
189 {
190 	struct bt_le_ext_adv *ext_adv;
191 
192 	create_ext_adv_set(&ext_adv, true);
193 	start_ext_adv_set(ext_adv);
194 
195 	printk("Waiting for connection...\n");
196 	WAIT_FOR_FLAG(flag_connected);
197 
198 	disconnect_from_target();
199 	WAIT_FOR_FLAG_UNSET(flag_connected);
200 
201 	printk("Waiting for Connection object to be recycled...\n");
202 	WAIT_FOR_FLAG(flag_conn_recycled);
203 
204 	/* Iteration Cleanup */
205 	UNSET_FLAG(flag_conn_recycled);
206 	stop_ext_adv_set(ext_adv);
207 	delete_adv_set(ext_adv);
208 }
209 
main_ext_conn_adv_advertiser(void)210 static void main_ext_conn_adv_advertiser(void)
211 {
212 	struct bt_le_ext_adv *ext_adv;
213 
214 	common_init();
215 
216 	bt_conn_cb_register(&conn_cbs);
217 
218 	adv_connect_and_disconnect_cycle();
219 
220 	create_ext_adv_set(&ext_adv, false);
221 	start_ext_adv_set(ext_adv);
222 
223 	/* Advertise for a bit */
224 	k_sleep(K_SECONDS(5));
225 
226 	stop_ext_adv_set(ext_adv);
227 	delete_adv_set(ext_adv);
228 
229 	ext_adv = NULL;
230 
231 	PASS("Extended advertiser passed\n");
232 }
233 
main_ext_conn_adv_advertiser_x5(void)234 static void main_ext_conn_adv_advertiser_x5(void)
235 {
236 	struct bt_le_ext_adv *ext_adv;
237 
238 	common_init();
239 
240 	bt_conn_cb_register(&conn_cbs);
241 
242 	for (size_t i = 0 ; i < 5 ; i++) {
243 		printk("Iteration %d...\n", i);
244 		adv_connect_and_disconnect_cycle();
245 	}
246 
247 	/* Advertise for a bit */
248 	create_ext_adv_set(&ext_adv, false);
249 	start_ext_adv_set(ext_adv);
250 	k_sleep(K_SECONDS(5));
251 	stop_ext_adv_set(ext_adv);
252 	delete_adv_set(ext_adv);
253 	ext_adv = NULL;
254 
255 	PASS("Extended advertiser passed\n");
256 }
257 
258 static const struct bst_test_instance ext_adv_advertiser[] = {
259 	{
260 		.test_id = "ext_adv_advertiser",
261 		.test_descr = "Basic extended advertising test. "
262 			      "Will just start extended advertising.",
263 		.test_pre_init_f = test_init,
264 		.test_tick_f = test_tick,
265 		.test_main_f = main_ext_adv_advertiser
266 	},
267 	{
268 		.test_id = "ext_adv_conn_advertiser",
269 		.test_descr = "Basic connectable extended advertising test. "
270 			      "Starts extended advertising, and restarts it after disconnecting",
271 		.test_pre_init_f = test_init,
272 		.test_tick_f = test_tick,
273 		.test_main_f = main_ext_conn_adv_advertiser
274 	},
275 	{
276 		.test_id = "ext_adv_conn_advertiser_x5",
277 		.test_descr = "Basic connectable extended advertising test. "
278 			      "Starts extended advertising, and restarts it after disconnecting, "
279 			      "repeated over 5 times",
280 		.test_pre_init_f = test_init,
281 		.test_tick_f = test_tick,
282 		.test_main_f = main_ext_conn_adv_advertiser_x5
283 	},
284 	BSTEST_END_MARKER
285 };
286 
test_ext_adv_advertiser(struct bst_test_list * tests)287 struct bst_test_list *test_ext_adv_advertiser(struct bst_test_list *tests)
288 {
289 	return bst_add_tests(tests, ext_adv_advertiser);
290 }
291 
292 bst_test_install_t test_installers[] = {
293 	test_ext_adv_advertiser,
294 	NULL
295 };
296 
main(void)297 int main(void)
298 {
299 	bst_main();
300 	return 0;
301 }
302