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