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