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