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_ext_adv_seen);
26 CREATE_FLAG(flag_connected);
27 CREATE_FLAG(flag_conn_recycled);
28 
connected(struct bt_conn * conn,uint8_t err)29 static void connected(struct bt_conn *conn, uint8_t err)
30 {
31 	char addr[BT_ADDR_LE_STR_LEN];
32 
33 	bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
34 
35 	if (err != BT_HCI_ERR_SUCCESS) {
36 		FAIL("Failed to connect to %s: %u\n", addr, err);
37 		bt_conn_unref(g_conn);
38 		g_conn = NULL;
39 		return;
40 	}
41 
42 	printk("Connected to %s\n", addr);
43 	SET_FLAG(flag_connected);
44 }
45 
free_conn_object_work_fn(struct k_work * work)46 static void free_conn_object_work_fn(struct k_work *work)
47 {
48 	ARG_UNUSED(work);
49 
50 	bt_conn_unref(g_conn);
51 	g_conn = NULL;
52 }
53 
54 static K_WORK_DELAYABLE_DEFINE(free_conn_object_work, free_conn_object_work_fn);
55 
disconnected(struct bt_conn * conn,uint8_t reason)56 static void disconnected(struct bt_conn *conn, uint8_t reason)
57 {
58 	char addr[BT_ADDR_LE_STR_LEN];
59 
60 	bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
61 
62 	printk("Disconnected: %s (reason %u)\n", addr, reason);
63 
64 	/* Schedule to cause de-sync between disconnected and recycled events,
65 	 * in order to prove the test is relying properly on it.
66 	 */
67 	k_work_schedule(&free_conn_object_work, K_MSEC(500));
68 
69 	UNSET_FLAG(flag_connected);
70 }
71 
recycled(void)72 static void recycled(void)
73 {
74 	SET_FLAG(flag_conn_recycled);
75 }
76 
77 static struct bt_conn_cb conn_cbs = {
78 	.connected = connected,
79 	.disconnected = disconnected,
80 	.recycled = recycled,
81 };
82 
83 
scan_recv(const struct bt_le_scan_recv_info * info,struct net_buf_simple * buf)84 static void scan_recv(const struct bt_le_scan_recv_info *info,
85 		      struct net_buf_simple *buf)
86 {
87 	printk("Found advertisement. Adv-type: 0x%02x, Adv-prop: 0x%02x\n",
88 		info->adv_type, info->adv_props);
89 
90 	if (info->adv_type == BT_GAP_ADV_TYPE_EXT_ADV &&
91 	    info->adv_props & BT_GAP_ADV_PROP_EXT_ADV) {
92 		printk("Found extended advertisement!\n");
93 		SET_FLAG(flag_ext_adv_seen);
94 	}
95 
96 	if (!TEST_FLAG(flag_connected) &&
97 	    info->adv_props & BT_GAP_ADV_PROP_CONNECTABLE) {
98 		int err;
99 
100 		printk("Stopping scan\n");
101 		err = bt_le_scan_stop();
102 		if (err) {
103 			FAIL("Failed to stop scan: %d", err);
104 			return;
105 		}
106 
107 		err = bt_conn_le_create(info->addr, BT_CONN_LE_CREATE_CONN,
108 					BT_LE_CONN_PARAM_DEFAULT, &g_conn);
109 		if (err) {
110 			FAIL("Could not connect to peer: %d", err);
111 			return;
112 		}
113 	}
114 }
115 
116 static struct bt_le_scan_cb scan_callbacks = {
117 	.recv = scan_recv,
118 };
119 
common_init(void)120 static void common_init(void)
121 {
122 	int err = 0;
123 
124 	err = bt_enable(NULL);
125 
126 	if (err) {
127 		FAIL("Bluetooth init failed: %d\n", err);
128 		return;
129 	}
130 
131 	bt_conn_cb_register(&conn_cbs);
132 	bt_le_scan_cb_register(&scan_callbacks);
133 
134 	printk("Bluetooth initialized\n");
135 }
136 
start_scan(void)137 static void start_scan(void)
138 {
139 	int err;
140 
141 	printk("Start scanning...");
142 	err = bt_le_scan_start(BT_LE_SCAN_PASSIVE, NULL);
143 	if (err) {
144 		FAIL("Failed to start scan: %d\n", err);
145 		return;
146 	}
147 	printk("done.\n");
148 }
149 
main_ext_adv_scanner(void)150 static void main_ext_adv_scanner(void)
151 {
152 	common_init();
153 	start_scan();
154 
155 	printk("Waiting for extended advertisements...\n");
156 
157 	WAIT_FOR_FLAG(flag_ext_adv_seen);
158 
159 	PASS("Extended adv scanner passed\n");
160 }
161 
scan_connect_and_disconnect_cycle(void)162 static void scan_connect_and_disconnect_cycle(void)
163 {
164 	start_scan();
165 
166 	printk("Waiting for extended advertisements...\n");
167 	WAIT_FOR_FLAG(flag_ext_adv_seen);
168 
169 	printk("Waiting for connection with device...\n");
170 	WAIT_FOR_FLAG(flag_connected);
171 
172 	printk("Waiting for device disconnection...\n");
173 	WAIT_FOR_FLAG_UNSET(flag_connected);
174 
175 	printk("Waiting for Connection object to be recycled...\n");
176 	WAIT_FOR_FLAG(flag_conn_recycled);
177 
178 	/* Iteration cleanup */
179 	printk("Clearing flag for seen extended advertisements...\n");
180 	UNSET_FLAG(flag_ext_adv_seen);
181 	UNSET_FLAG(flag_conn_recycled);
182 }
183 
main_ext_adv_conn_scanner(void)184 static void main_ext_adv_conn_scanner(void)
185 {
186 	common_init();
187 
188 	scan_connect_and_disconnect_cycle();
189 
190 	start_scan();
191 	printk("Waiting to extended advertisements (again)...\n");
192 	WAIT_FOR_FLAG(flag_ext_adv_seen);
193 
194 	PASS("Extended adv scanner passed\n");
195 }
196 
main_ext_adv_conn_scanner_x5(void)197 static void main_ext_adv_conn_scanner_x5(void)
198 {
199 	common_init();
200 
201 	for (size_t i = 0 ; i < 5 ; i++) {
202 		printk("Iteration %d...\n", i);
203 		scan_connect_and_disconnect_cycle();
204 	}
205 
206 	start_scan();
207 	printk("Waiting to extended advertisements (again)...\n");
208 	WAIT_FOR_FLAG(flag_ext_adv_seen);
209 
210 	PASS("Extended adv scanner x5 passed\n");
211 }
212 
213 static const struct bst_test_instance ext_adv_scanner[] = {
214 	{
215 		.test_id = "ext_adv_scanner",
216 		.test_descr = "Basic extended advertising scanning test. "
217 			      "Will just scan an extended advertiser.",
218 		.test_pre_init_f = test_init,
219 		.test_tick_f = test_tick,
220 		.test_main_f = main_ext_adv_scanner
221 	},
222 	{
223 		.test_id = "ext_adv_conn_scanner",
224 		.test_descr = "Basic extended advertising scanning test. "
225 			      "Will scan an extended advertiser, connect "
226 			      "and verify it's detected after disconnection",
227 		.test_pre_init_f = test_init,
228 		.test_tick_f = test_tick,
229 		.test_main_f = main_ext_adv_conn_scanner
230 	},
231 	{
232 		.test_id = "ext_adv_conn_scanner_x5",
233 		.test_descr = "Basic extended advertising scanning test. "
234 			      "Will scan an extended advertiser, connect "
235 			      "and verify it's detected after disconnection,"
236 			      "repeated over 5 times",
237 		.test_pre_init_f = test_init,
238 		.test_tick_f = test_tick,
239 		.test_main_f = main_ext_adv_conn_scanner_x5
240 	},
241 	BSTEST_END_MARKER
242 };
243 
test_ext_adv_scanner(struct bst_test_list * tests)244 struct bst_test_list *test_ext_adv_scanner(struct bst_test_list *tests)
245 {
246 	return bst_add_tests(tests, ext_adv_scanner);
247 }
248 
249 bst_test_install_t test_installers[] = {
250 	test_ext_adv_scanner,
251 	NULL
252 };
253 
main(void)254 int main(void)
255 {
256 	bst_main();
257 	return 0;
258 }
259