1 /*
2  * Copyright (c) 2023 Nordic Semiconductor
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include "babblekit/testcase.h"
8 #include "babblekit/flags.h"
9 #include "common.h"
10 
11 #include <zephyr/bluetooth/bluetooth.h>
12 #include <zephyr/bluetooth/iso.h>
13 #include <zephyr/sys/printk.h>
14 
15 #define ENQUEUE_COUNT 2
16 
17 extern enum bst_result_t bst_result;
18 static struct bt_iso_chan iso_chans[CONFIG_BT_ISO_MAX_CHAN];
19 static struct bt_iso_chan *default_chan = &iso_chans[0];
20 static struct bt_iso_cig *cig;
21 static uint16_t seq_num;
22 static volatile size_t enqueue_cnt;
23 static uint32_t latency_ms = 10U; /* 10ms */
24 static uint32_t interval_us = 10U * USEC_PER_MSEC; /* 10 ms */
25 NET_BUF_POOL_FIXED_DEFINE(tx_pool, ENQUEUE_COUNT, BT_ISO_SDU_BUF_SIZE(CONFIG_BT_ISO_TX_MTU),
26 			  CONFIG_BT_CONN_TX_USER_DATA_SIZE, NULL);
27 
28 BUILD_ASSERT(CONFIG_BT_ISO_MAX_CHAN > 1, "CONFIG_BT_ISO_MAX_CHAN shall be at least 2");
29 
30 DEFINE_FLAG_STATIC(flag_iso_connected);
31 
send_data_cb(struct k_work * work)32 static void send_data_cb(struct k_work *work)
33 {
34 	static uint8_t buf_data[CONFIG_BT_ISO_TX_MTU];
35 	static size_t len_to_send = 1;
36 	static bool data_initialized;
37 	struct net_buf *buf;
38 	int ret;
39 
40 	if (!IS_FLAG_SET(flag_iso_connected)) {
41 		/* TX has been aborted */
42 		return;
43 	}
44 
45 	if (!data_initialized) {
46 		for (int i = 0; i < ARRAY_SIZE(buf_data); i++) {
47 			buf_data[i] = (uint8_t)i;
48 		}
49 
50 		data_initialized = true;
51 	}
52 
53 	buf = net_buf_alloc(&tx_pool, K_FOREVER);
54 	net_buf_reserve(buf, BT_ISO_CHAN_SEND_RESERVE);
55 
56 	net_buf_add_mem(buf, buf_data, len_to_send);
57 
58 	ret = bt_iso_chan_send(default_chan, buf, seq_num++);
59 	if (ret < 0) {
60 		printk("Failed to send ISO data (%d)\n", ret);
61 		net_buf_unref(buf);
62 
63 		/* Reschedule for next interval */
64 		k_work_reschedule(k_work_delayable_from_work(work), K_USEC(interval_us));
65 
66 		return;
67 	}
68 
69 	len_to_send++;
70 	if (len_to_send > ARRAY_SIZE(buf_data)) {
71 		len_to_send = 1;
72 	}
73 
74 	enqueue_cnt--;
75 	if (enqueue_cnt > 0U) {
76 		/* If we have more buffers available, we reschedule the workqueue item immediately
77 		 * to trigger another encode + TX, but without blocking this call for too long
78 		 */
79 		k_work_reschedule(k_work_delayable_from_work(work), K_NO_WAIT);
80 	}
81 }
82 K_WORK_DELAYABLE_DEFINE(iso_send_work, send_data_cb);
83 
device_found(const bt_addr_le_t * addr,int8_t rssi,uint8_t type,struct net_buf_simple * ad)84 static void device_found(const bt_addr_le_t *addr, int8_t rssi, uint8_t type,
85 			 struct net_buf_simple *ad)
86 {
87 	int err;
88 
89 	err = bt_le_scan_stop();
90 	if (err) {
91 		TEST_FAIL("Failed to stop scanning (err %d)", err);
92 
93 		return;
94 	}
95 
96 	err = bt_conn_le_create(addr, BT_CONN_LE_CREATE_CONN, BT_LE_CONN_PARAM_DEFAULT,
97 				&default_conn);
98 	if (err) {
99 		TEST_FAIL("Failed to create connection (err %d)", err);
100 
101 		return;
102 	}
103 }
104 
iso_connected(struct bt_iso_chan * chan)105 static void iso_connected(struct bt_iso_chan *chan)
106 {
107 	printk("ISO Channel %p connected\n", chan);
108 
109 	seq_num = 0U;
110 	enqueue_cnt = ENQUEUE_COUNT;
111 
112 	if (chan == default_chan) {
113 		/* Start send timer */
114 		k_work_schedule(&iso_send_work, K_MSEC(0));
115 
116 		SET_FLAG(flag_iso_connected);
117 	}
118 }
119 
iso_disconnected(struct bt_iso_chan * chan,uint8_t reason)120 static void iso_disconnected(struct bt_iso_chan *chan, uint8_t reason)
121 {
122 	printk("ISO Channel %p disconnected (reason 0x%02x)\n", chan, reason);
123 
124 	if (chan == default_chan) {
125 		k_work_cancel_delayable(&iso_send_work);
126 
127 		UNSET_FLAG(flag_iso_connected);
128 	}
129 }
130 
sdu_sent_cb(struct bt_iso_chan * chan)131 static void sdu_sent_cb(struct bt_iso_chan *chan)
132 {
133 	int err;
134 
135 	enqueue_cnt++;
136 
137 	if (!IS_FLAG_SET(flag_iso_connected)) {
138 		/* TX has been aborted */
139 		return;
140 	}
141 
142 	err = k_work_schedule(&iso_send_work, K_NO_WAIT);
143 	if (err < 0) {
144 		TEST_FAIL("Failed to schedule TX for chan %p: %d", chan, err);
145 	}
146 }
147 
init(void)148 static void init(void)
149 {
150 	static struct bt_iso_chan_ops iso_ops = {
151 		.connected = iso_connected,
152 		.disconnected = iso_disconnected,
153 		.sent = sdu_sent_cb,
154 	};
155 	static struct bt_iso_chan_io_qos iso_tx = {
156 		.sdu = CONFIG_BT_ISO_TX_MTU,
157 		.phy = BT_GAP_LE_PHY_2M,
158 		.rtn = 1,
159 		.path = NULL,
160 	};
161 	static struct bt_iso_chan_qos iso_qos = {
162 		.tx = &iso_tx,
163 		.rx = NULL,
164 	};
165 	int err;
166 
167 	err = bt_enable(NULL);
168 	if (err != 0) {
169 		TEST_FAIL("Bluetooth enable failed (err %d)", err);
170 
171 		return;
172 	}
173 
174 	for (size_t i = 0U; i < ARRAY_SIZE(iso_chans); i++) {
175 		iso_chans[i].ops = &iso_ops;
176 		iso_chans[i].qos = &iso_qos;
177 #if defined(CONFIG_BT_SMP)
178 		iso_chans[i].required_sec_level = BT_SECURITY_L2;
179 #endif /* CONFIG_BT_SMP */
180 	}
181 }
182 
set_cig_defaults(struct bt_iso_cig_param * param)183 static void set_cig_defaults(struct bt_iso_cig_param *param)
184 {
185 	param->cis_channels = &default_chan;
186 	param->num_cis = 1U;
187 	param->sca = BT_GAP_SCA_UNKNOWN;
188 	param->packing = BT_ISO_PACKING_SEQUENTIAL;
189 	param->framing = BT_ISO_FRAMING_UNFRAMED;
190 	param->c_to_p_latency = latency_ms;   /* ms */
191 	param->p_to_c_latency = latency_ms;   /* ms */
192 	param->c_to_p_interval = interval_us; /* us */
193 	param->p_to_c_interval = interval_us; /* us */
194 
195 }
196 
create_cig(size_t iso_channels)197 static void create_cig(size_t iso_channels)
198 {
199 	struct bt_iso_chan *channels[ARRAY_SIZE(iso_chans)];
200 	struct bt_iso_cig_param param;
201 	int err;
202 
203 	for (size_t i = 0U; i < iso_channels; i++) {
204 		channels[i] = &iso_chans[i];
205 	}
206 
207 	set_cig_defaults(&param);
208 	param.num_cis = iso_channels;
209 	param.cis_channels = channels;
210 
211 	err = bt_iso_cig_create(&param, &cig);
212 	if (err != 0) {
213 		TEST_FAIL("Failed to create CIG (%d)", err);
214 
215 		return;
216 	}
217 }
218 
reconfigure_cig_interval(struct bt_iso_cig_param * param)219 static int reconfigure_cig_interval(struct bt_iso_cig_param *param)
220 {
221 	int err;
222 
223 	/* Test modifying CIG parameter without any CIS */
224 	param->num_cis = 0U;
225 	param->c_to_p_interval = 7500; /* us */
226 	param->p_to_c_interval = param->c_to_p_interval;
227 	err = bt_iso_cig_reconfigure(cig, param);
228 	if (err != 0) {
229 		TEST_FAIL("Failed to reconfigure CIG to new interval (%d)", err);
230 
231 		return err;
232 	}
233 
234 	err = bt_iso_cig_reconfigure(cig, param);
235 	if (err != 0) {
236 		TEST_FAIL("Failed to reconfigure CIG to same interval (%d)", err);
237 
238 		return err;
239 	}
240 
241 	/* Test modifying to different values for both intervals */
242 	param->c_to_p_interval = 5000; /* us */
243 	param->p_to_c_interval = 2500; /* us */
244 	err = bt_iso_cig_reconfigure(cig, param);
245 	if (err != 0) {
246 		TEST_FAIL("Failed to reconfigure CIG to new interval (%d)", err);
247 
248 		return err;
249 	}
250 
251 	return 0;
252 }
253 
reconfigure_cig_latency(struct bt_iso_cig_param * param)254 static int reconfigure_cig_latency(struct bt_iso_cig_param *param)
255 {
256 	int err;
257 
258 	/* Test modifying CIG latency without any CIS */
259 	param->num_cis = 0U;
260 	param->c_to_p_latency = 20; /* ms */
261 	param->p_to_c_latency = param->c_to_p_latency;
262 	err = bt_iso_cig_reconfigure(cig, param);
263 	if (err != 0) {
264 		TEST_FAIL("Failed to reconfigure CIG latency (%d)", err);
265 
266 		return err;
267 	}
268 
269 	param->c_to_p_latency = 30; /* ms */
270 	param->p_to_c_latency = 40; /* ms */
271 	err = bt_iso_cig_reconfigure(cig, param);
272 	if (err != 0) {
273 		TEST_FAIL("Failed to reconfigure CIG for different latencies (%d)", err);
274 
275 		return err;
276 	}
277 
278 	return 0;
279 }
280 
reconfigure_cig(void)281 static void reconfigure_cig(void)
282 {
283 	struct bt_iso_chan *channels[2];
284 	struct bt_iso_cig_param param;
285 	int err;
286 
287 	for (size_t i = 0U; i < ARRAY_SIZE(channels); i++) {
288 		channels[i] = &iso_chans[i];
289 	}
290 
291 	set_cig_defaults(&param);
292 
293 	/* Test modifying existing CIS */
294 	default_chan->qos->tx->rtn++;
295 
296 	err = bt_iso_cig_reconfigure(cig, &param);
297 	if (err != 0) {
298 		TEST_FAIL("Failed to reconfigure CIS to new RTN (%d)", err);
299 
300 		return;
301 	}
302 
303 	/* Test modifying interval parameter */
304 	err = reconfigure_cig_interval(&param);
305 	if (err != 0) {
306 		return;
307 	}
308 
309 	/* Test modifying latency parameter */
310 	err = reconfigure_cig_latency(&param);
311 	if (err != 0) {
312 		return;
313 	}
314 
315 	/* Add CIS to the CIG and restore all other parameters */
316 	set_cig_defaults(&param);
317 	param.cis_channels = &channels[1];
318 
319 	err = bt_iso_cig_reconfigure(cig, &param);
320 	if (err != 0) {
321 		TEST_FAIL("Failed to reconfigure CIG with new CIS and original parameters (%d)",
322 			  err);
323 
324 		return;
325 	}
326 }
327 
connect_acl(void)328 static void connect_acl(void)
329 {
330 	int err;
331 
332 	err = bt_le_scan_start(BT_LE_SCAN_PASSIVE, device_found);
333 	if (err != 0) {
334 		TEST_FAIL("Scanning failed to start (err %d)", err);
335 
336 		return;
337 	}
338 
339 	WAIT_FOR_FLAG(flag_connected);
340 }
341 
connect_cis(void)342 static void connect_cis(void)
343 {
344 	const struct bt_iso_connect_param connect_param = {
345 		.acl = default_conn,
346 		.iso_chan = default_chan,
347 	};
348 	int err;
349 
350 	err = bt_iso_chan_connect(&connect_param, 1);
351 	if (err) {
352 		TEST_FAIL("Failed to connect ISO (%d)", err);
353 
354 		return;
355 	}
356 
357 	WAIT_FOR_FLAG(flag_iso_connected);
358 }
359 
disconnect_cis(void)360 static void disconnect_cis(void)
361 {
362 	int err;
363 
364 	err = bt_iso_chan_disconnect(default_chan);
365 	if (err) {
366 		TEST_FAIL("Failed to disconnect ISO (err %d)", err);
367 
368 		return;
369 	}
370 
371 	WAIT_FOR_FLAG_UNSET(flag_iso_connected);
372 }
373 
disconnect_acl(void)374 static void disconnect_acl(void)
375 {
376 	int err;
377 
378 	err = bt_conn_disconnect(default_conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN);
379 	if (err) {
380 		TEST_FAIL("Failed to disconnect ACL (err %d)", err);
381 
382 		return;
383 	}
384 
385 	WAIT_FOR_FLAG_UNSET(flag_connected);
386 }
387 
terminate_cig(void)388 static void terminate_cig(void)
389 {
390 	int err;
391 
392 	err = bt_iso_cig_terminate(cig);
393 	if (err != 0) {
394 		TEST_FAIL("Failed to terminate CIG (%d)", err);
395 
396 		return;
397 	}
398 
399 	cig = NULL;
400 }
401 
reset_bluetooth(void)402 static void reset_bluetooth(void)
403 {
404 	int err;
405 
406 	printk("Resetting Bluetooth\n");
407 
408 	err = bt_disable();
409 	if (err != 0) {
410 		TEST_FAIL("Failed to disable (%d)", err);
411 
412 		return;
413 	}
414 
415 	/* After a disable, all CIGs and BIGs are removed */
416 	cig = NULL;
417 
418 	err = bt_enable(NULL);
419 	if (err != 0) {
420 		TEST_FAIL("Failed to re-enable (%d)", err);
421 
422 		return;
423 	}
424 }
425 
test_main(void)426 static void test_main(void)
427 {
428 	init();
429 	create_cig(1);
430 	reconfigure_cig();
431 	connect_acl();
432 	connect_cis();
433 
434 	while (seq_num < 100U) {
435 		k_sleep(K_USEC(interval_us));
436 	}
437 
438 	disconnect_cis();
439 	disconnect_acl();
440 	terminate_cig();
441 
442 	TEST_PASS("Test passed");
443 }
444 
test_main_disable(void)445 static void test_main_disable(void)
446 {
447 	init();
448 
449 	/* Setup and connect before disabling */
450 	create_cig(ARRAY_SIZE(iso_chans));
451 	connect_acl();
452 	connect_cis();
453 
454 	/* Reset BT to see if we can set it up again */
455 	reset_bluetooth();
456 
457 	/* Set everything up again to see if everything still works as expected */
458 	create_cig(ARRAY_SIZE(iso_chans));
459 	connect_acl();
460 	connect_cis();
461 
462 	while (seq_num < 100U) {
463 		k_sleep(K_USEC(interval_us));
464 	}
465 
466 	disconnect_cis();
467 	disconnect_acl();
468 	terminate_cig();
469 
470 	TEST_PASS("Disable test passed");
471 }
472 
473 static const struct bst_test_instance test_def[] = {
474 	{
475 		.test_id = "central",
476 		.test_descr = "Central",
477 		.test_main_f = test_main,
478 	},
479 	{
480 		.test_id = "central_disable",
481 		.test_descr = "CIS central that tests bt_disable for ISO",
482 		.test_main_f = test_main_disable,
483 	},
484 	BSTEST_END_MARKER,
485 };
486 
test_main_cis_central_install(struct bst_test_list * tests)487 struct bst_test_list *test_main_cis_central_install(struct bst_test_list *tests)
488 {
489 	return bst_add_tests(tests, test_def);
490 }
491