1 /*
2  * Copyright (c) 2021-2025 Nordic Semiconductor ASA
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <ctype.h>
8 #include <zephyr/bluetooth/conn.h>
9 #include <zephyr/bluetooth/gap.h>
10 #include <zephyr/bluetooth/hci_types.h>
11 #include <zephyr/kernel.h>
12 #include <string.h>
13 #include <stdint.h>
14 #include <stdlib.h>
15 #include <zephyr/sys/util.h>
16 #include <zephyr/sys_clock.h>
17 #include <zephyr/types.h>
18 
19 
20 #include <zephyr/console/console.h>
21 #include <zephyr/bluetooth/bluetooth.h>
22 #include <zephyr/bluetooth/iso.h>
23 #include <zephyr/bluetooth/hci.h>
24 #include <zephyr/sys/byteorder.h>
25 
26 #include <zephyr/logging/log.h>
27 LOG_MODULE_REGISTER(iso_connected, LOG_LEVEL_DBG);
28 
29 #define DEVICE_NAME	CONFIG_BT_DEVICE_NAME
30 #define DEVICE_NAME_LEN (sizeof(DEVICE_NAME))
31 
32 enum benchmark_role {
33 	ROLE_CENTRAL,
34 	ROLE_PERIPHERAL,
35 	ROLE_QUIT
36 };
37 
38 enum sdu_dir {
39 	DIR_C_TO_P,
40 	DIR_P_TO_C
41 };
42 
43 #define DEFAULT_CIS_RTN         2
44 #define DEFAULT_CIS_INTERVAL_US 7500
45 #define DEFAULT_CIS_LATENCY_MS  40
46 #define DEFAULT_CIS_PHY         BT_GAP_LE_PHY_2M
47 #define DEFAULT_CIS_SDU_SIZE    CONFIG_BT_ISO_TX_MTU
48 #define DEFAULT_CIS_PACKING     0
49 #define DEFAULT_CIS_FRAMING     0
50 #define DEFAULT_CIS_COUNT       1U
51 #define DEFAULT_CIS_SEC_LEVEL   BT_SECURITY_L1
52 
53 #if defined(CONFIG_BT_ISO_TEST_PARAMS)
54 #define DEFAULT_CIS_NSE          BT_ISO_NSE_MIN
55 #define DEFAULT_CIS_BN           BT_ISO_BN_MIN
56 #define DEFAULT_CIS_PDU_SIZE     CONFIG_BT_ISO_TX_MTU
57 #define DEFAULT_CIS_FT           BT_ISO_FT_MIN
58 #define DEFAULT_CIS_ISO_INTERVAL DEFAULT_CIS_INTERVAL_US / 1250U /* N * 1.25 ms */
59 #endif /* CONFIG_BT_ISO_TEST_PARAMS */
60 
61 #define BUFFERS_ENQUEUED 2 /* Number of buffers enqueue for each channel */
62 
63 BUILD_ASSERT(BUFFERS_ENQUEUED * CONFIG_BT_ISO_MAX_CHAN <= CONFIG_BT_ISO_TX_BUF_COUNT,
64 	     "Not enough buffers to enqueue");
65 
66 struct iso_recv_stats {
67 	uint32_t iso_recv_count;
68 	uint32_t iso_lost_count;
69 };
70 
71 struct iso_chan_work {
72 	struct bt_iso_chan chan;
73 	struct k_work_delayable send_work;
74 	struct bt_iso_info info;
75 	uint16_t seq_num;
76 } iso_chans[CONFIG_BT_ISO_MAX_CHAN];
77 
78 static enum benchmark_role role;
79 static struct bt_conn *default_conn;
80 static struct bt_iso_chan *cis[CONFIG_BT_ISO_MAX_CHAN];
81 static bool advertiser_found;
82 static bt_addr_le_t adv_addr;
83 static uint32_t last_received_counter;
84 static struct iso_recv_stats stats_current_conn;
85 static struct iso_recv_stats stats_overall;
86 static int64_t iso_conn_start_time;
87 static size_t total_iso_conn_count;
88 static uint32_t iso_send_count;
89 static struct bt_iso_cig *cig;
90 
91 NET_BUF_POOL_FIXED_DEFINE(tx_pool, 1, BT_ISO_SDU_BUF_SIZE(CONFIG_BT_ISO_TX_MTU),
92 			  CONFIG_BT_CONN_TX_USER_DATA_SIZE, NULL);
93 static uint8_t iso_data[CONFIG_BT_ISO_TX_MTU];
94 
95 static K_SEM_DEFINE(sem_adv, 0, 1);
96 static K_SEM_DEFINE(sem_iso_accept, 0, 1);
97 static K_SEM_DEFINE(sem_iso_connected, 0, CONFIG_BT_ISO_MAX_CHAN);
98 static K_SEM_DEFINE(sem_iso_disconnected, 0, CONFIG_BT_ISO_MAX_CHAN);
99 static K_SEM_DEFINE(sem_connected, 0, 1);
100 static K_SEM_DEFINE(sem_disconnected, 0, 1);
101 
102 static struct bt_iso_chan_io_qos iso_tx_qos = {
103 	.sdu = DEFAULT_CIS_SDU_SIZE, /* bytes */
104 	.rtn = DEFAULT_CIS_RTN,
105 	.phy = DEFAULT_CIS_PHY,
106 #if defined(CONFIG_BT_ISO_TEST_PARAMS)
107 	.max_pdu = DEFAULT_CIS_PDU_SIZE,
108 	.burst_number = DEFAULT_CIS_BN,
109 #endif /* CONFIG_BT_ISO_TEST_PARAMS */
110 };
111 
112 static struct bt_iso_chan_io_qos iso_rx_qos = {
113 	.sdu = DEFAULT_CIS_SDU_SIZE, /* bytes */
114 	.rtn = DEFAULT_CIS_RTN,
115 	.phy = DEFAULT_CIS_PHY,
116 #if defined(CONFIG_BT_ISO_TEST_PARAMS)
117 	.max_pdu = DEFAULT_CIS_PDU_SIZE,
118 	.burst_number = DEFAULT_CIS_BN,
119 #endif /* CONFIG_BT_ISO_TEST_PARAMS */
120 };
121 
122 static struct bt_iso_chan_qos iso_qos = {
123 	.tx = &iso_tx_qos,
124 	.rx = &iso_rx_qos,
125 #if defined(CONFIG_BT_ISO_TEST_PARAMS)
126 	.num_subevents = DEFAULT_CIS_NSE,
127 #endif /* CONFIG_BT_ISO_TEST_PARAMS */
128 };
129 
130 static struct bt_iso_cig_param cig_create_param = {
131 	.c_to_p_interval = DEFAULT_CIS_INTERVAL_US, /* in microseconds */
132 	.p_to_c_interval = DEFAULT_CIS_INTERVAL_US, /* in microseconds */
133 	.c_to_p_latency = DEFAULT_CIS_LATENCY_MS, /* milliseconds */
134 	.p_to_c_latency = DEFAULT_CIS_LATENCY_MS, /* milliseconds */
135 	.sca = BT_GAP_SCA_UNKNOWN,
136 	.packing = DEFAULT_CIS_PACKING,
137 	.framing = DEFAULT_CIS_FRAMING,
138 	.cis_channels = cis,
139 	.num_cis = DEFAULT_CIS_COUNT,
140 #if defined(CONFIG_BT_ISO_TEST_PARAMS)
141 	.c_to_p_ft = DEFAULT_CIS_FT,
142 	.p_to_c_ft = DEFAULT_CIS_FT,
143 	.iso_interval = DEFAULT_CIS_ISO_INTERVAL,
144 #endif /* CONFIG_BT_ISO_TEST_PARAMS */
145 };
146 
147 static const struct bt_data sd[] = {
148 	BT_DATA(BT_DATA_NAME_COMPLETE, CONFIG_BT_DEVICE_NAME, sizeof(CONFIG_BT_DEVICE_NAME) - 1),
149 };
150 
device_role_select(void)151 static enum benchmark_role device_role_select(void)
152 {
153 	char role_char;
154 	const char central_char = 'c';
155 	const char peripheral_char = 'p';
156 	const char quit_char = 'q';
157 
158 	while (true) {
159 		printk("Choose device role - type %c (central role) or %c (peripheral role), or %c to quit: ",
160 			central_char, peripheral_char, quit_char);
161 
162 		role_char = tolower(console_getchar());
163 
164 		printk("%c\n", role_char);
165 
166 		if (role_char == central_char) {
167 			printk("Central role\n");
168 			return ROLE_CENTRAL;
169 		} else if (role_char == peripheral_char) {
170 			printk("Peripheral role\n");
171 			return ROLE_PERIPHERAL;
172 		} else if (role_char == quit_char) {
173 			printk("Quitting\n");
174 			return ROLE_QUIT;
175 		} else if (role_char == '\n' || role_char == '\r') {
176 			continue;
177 		}
178 
179 		printk("Invalid role: %c\n", role_char);
180 	}
181 }
182 
print_stats(char * name,struct iso_recv_stats * stats)183 static void print_stats(char *name, struct iso_recv_stats *stats)
184 {
185 	uint32_t total_packets;
186 
187 	total_packets = stats->iso_recv_count + stats->iso_lost_count;
188 
189 	LOG_INF("%s: Received %u/%u (%.2f%%) - Total packets lost %u",
190 		name, stats->iso_recv_count, total_packets,
191 		(double)((float)stats->iso_recv_count * 100 / total_packets),
192 		stats->iso_lost_count);
193 }
194 
iso_send(struct bt_iso_chan * chan)195 static void iso_send(struct bt_iso_chan *chan)
196 {
197 	int ret;
198 	struct net_buf *buf;
199 	struct iso_chan_work *chan_work;
200 	uint32_t interval;
201 
202 	chan_work = CONTAINER_OF(chan, struct iso_chan_work, chan);
203 
204 	if (!chan_work->info.can_send) {
205 		return;
206 	}
207 
208 	interval = (role == ROLE_CENTRAL) ?
209 		   cig_create_param.c_to_p_interval : cig_create_param.p_to_c_interval;
210 
211 	buf = net_buf_alloc(&tx_pool, K_NO_WAIT);
212 	if (buf == NULL) {
213 		LOG_ERR("Could not allocate buffer");
214 		k_work_reschedule(&chan_work->send_work, K_USEC(interval));
215 		return;
216 	}
217 
218 	net_buf_reserve(buf, BT_ISO_CHAN_SEND_RESERVE);
219 	net_buf_add_mem(buf, iso_data, iso_tx_qos.sdu);
220 
221 	ret = bt_iso_chan_send(chan, buf, chan_work->seq_num++);
222 	if (ret < 0) {
223 		LOG_ERR("Unable to send data: %d", ret);
224 		net_buf_unref(buf);
225 		k_work_reschedule(&chan_work->send_work, K_USEC(interval));
226 		return;
227 	}
228 
229 	iso_send_count++;
230 
231 	if ((iso_send_count % 100) == 0) {
232 		LOG_INF("Sending value %u", iso_send_count);
233 	}
234 }
235 
iso_timer_timeout(struct k_work * work)236 static void iso_timer_timeout(struct k_work *work)
237 {
238 	struct bt_iso_chan *chan;
239 	struct iso_chan_work *chan_work;
240 	struct k_work_delayable *delayable = k_work_delayable_from_work(work);
241 
242 	chan_work = CONTAINER_OF(delayable, struct iso_chan_work, send_work);
243 	chan = &chan_work->chan;
244 
245 	iso_send(chan);
246 }
247 
iso_sent(struct bt_iso_chan * chan)248 static void iso_sent(struct bt_iso_chan *chan)
249 {
250 	struct iso_chan_work *chan_work;
251 
252 	chan_work = CONTAINER_OF(chan, struct iso_chan_work, chan);
253 
254 	k_work_reschedule(&chan_work->send_work, K_MSEC(0));
255 }
256 
iso_recv(struct bt_iso_chan * chan,const struct bt_iso_recv_info * info,struct net_buf * buf)257 static void iso_recv(struct bt_iso_chan *chan,
258 		     const struct bt_iso_recv_info *info,
259 		     struct net_buf *buf)
260 {
261 	uint32_t total_packets;
262 	static bool stats_latest_arr[1000];
263 	static size_t stats_latest_arr_pos;
264 
265 	/* NOTE: The packets received may be on different CISes */
266 
267 	if (info->flags & BT_ISO_FLAGS_VALID) {
268 		stats_current_conn.iso_recv_count++;
269 		stats_overall.iso_recv_count++;
270 		stats_latest_arr[stats_latest_arr_pos++] = true;
271 	} else {
272 		stats_current_conn.iso_lost_count++;
273 		stats_overall.iso_lost_count++;
274 		stats_latest_arr[stats_latest_arr_pos++] = false;
275 	}
276 
277 	if (stats_latest_arr_pos == sizeof(stats_latest_arr)) {
278 		stats_latest_arr_pos = 0;
279 	}
280 
281 	total_packets = stats_overall.iso_recv_count + stats_overall.iso_lost_count;
282 
283 	if ((total_packets % 100) == 0) {
284 		struct iso_recv_stats stats_latest = { 0 };
285 
286 		for (int i = 0; i < ARRAY_SIZE(stats_latest_arr); i++) {
287 			/* If we have not yet received 1000 packets, break
288 			 * early
289 			 */
290 			if (i == total_packets) {
291 				break;
292 			}
293 
294 			if (stats_latest_arr[i]) {
295 				stats_latest.iso_recv_count++;
296 			} else {
297 				stats_latest.iso_lost_count++;
298 			}
299 		}
300 
301 		print_stats("Overall     ", &stats_overall);
302 		print_stats("Current Conn", &stats_current_conn);
303 		print_stats("Latest 1000 ", &stats_latest);
304 		LOG_INF(""); /* Empty line to separate the stats */
305 	}
306 }
307 
iso_connected(struct bt_iso_chan * chan)308 static void iso_connected(struct bt_iso_chan *chan)
309 {
310 	const struct bt_iso_chan_path hci_path = {
311 		.pid = BT_ISO_DATA_PATH_HCI,
312 		.format = BT_HCI_CODING_FORMAT_TRANSPARENT,
313 	};
314 	struct iso_chan_work *chan_work;
315 	struct bt_iso_info iso_info;
316 	int err;
317 
318 	LOG_INF("ISO Channel %p connected", chan);
319 
320 	chan_work = CONTAINER_OF(chan, struct iso_chan_work, chan);
321 	err = bt_iso_chan_get_info(chan, &chan_work->info);
322 	if (err != 0) {
323 		LOG_ERR("Could get info about chan %p: %d", chan, err);
324 	}
325 
326 	/* If multiple CIS was created, this will be the value of the last
327 	 * created in the CIG
328 	 */
329 	iso_conn_start_time = k_uptime_get();
330 
331 	chan_work = CONTAINER_OF(chan, struct iso_chan_work, chan);
332 	chan_work->seq_num = 0U;
333 
334 	if (iso_info.can_recv) {
335 		err = bt_iso_setup_data_path(chan, BT_HCI_DATAPATH_DIR_CTLR_TO_HOST, &hci_path);
336 		if (err != 0) {
337 			LOG_ERR("Failed to setup ISO RX data path: %d", err);
338 		}
339 	}
340 
341 	if (iso_info.can_send) {
342 		err = bt_iso_setup_data_path(chan, BT_HCI_DATAPATH_DIR_HOST_TO_CTLR, &hci_path);
343 		if (err != 0) {
344 			LOG_ERR("Failed to setup ISO TX data path: %d", err);
345 		}
346 	}
347 
348 	k_sem_give(&sem_iso_connected);
349 }
350 
iso_disconnected(struct bt_iso_chan * chan,uint8_t reason)351 static void iso_disconnected(struct bt_iso_chan *chan, uint8_t reason)
352 {
353 	/* Calculate cumulative moving average - Be aware that this may
354 	 * cause overflow at sufficiently large counts or durations
355 	 *
356 	 * This duration is calculated for each CIS disconnected from the time
357 	 * of the last created CIS.
358 	 */
359 	static int64_t average_duration;
360 	struct bt_iso_info iso_info;
361 	uint64_t iso_conn_duration;
362 	uint64_t total_duration;
363 	int err;
364 
365 	if (iso_conn_start_time > 0) {
366 		iso_conn_duration = k_uptime_get() - iso_conn_start_time;
367 	} else {
368 		iso_conn_duration = 0;
369 	}
370 	total_duration = iso_conn_duration + (total_iso_conn_count - 1) * average_duration;
371 
372 	average_duration = total_duration / total_iso_conn_count;
373 
374 	LOG_INF("ISO Channel %p disconnected with reason 0x%02x after "
375 		"%llu milliseconds (average duration %llu)",
376 		chan, reason, iso_conn_duration, average_duration);
377 
378 	k_sem_give(&sem_iso_disconnected);
379 
380 	err = bt_iso_chan_get_info(chan, &iso_info);
381 	if (err != 0) {
382 		LOG_ERR("Failed to get ISO info: %d\n", err);
383 	} else if (iso_info.type == BT_ISO_CHAN_TYPE_CENTRAL) {
384 		if (iso_info.can_recv) {
385 			err = bt_iso_remove_data_path(chan, BT_HCI_DATAPATH_DIR_CTLR_TO_HOST);
386 			if (err != 0) {
387 				LOG_ERR("Failed to remove ISO RX data path: %d\n", err);
388 			}
389 		}
390 
391 		if (iso_info.can_send) {
392 			err = bt_iso_remove_data_path(chan, BT_HCI_DATAPATH_DIR_HOST_TO_CTLR);
393 			if (err != 0) {
394 				LOG_ERR("Failed to remove ISO TX data path: %d\n", err);
395 			}
396 		}
397 	}
398 }
399 
400 static struct bt_iso_chan_ops iso_ops = {
401 	.recv          = iso_recv,
402 	.connected     = iso_connected,
403 	.disconnected  = iso_disconnected,
404 	.sent          = iso_sent,
405 };
406 
iso_accept(const struct bt_iso_accept_info * info,struct bt_iso_chan ** chan)407 static int iso_accept(const struct bt_iso_accept_info *info,
408 		      struct bt_iso_chan **chan)
409 {
410 	LOG_INF("Incoming ISO request from %p", (void *)info->acl);
411 
412 	for (int i = 0; i < ARRAY_SIZE(iso_chans); i++) {
413 		if (iso_chans[i].chan.state == BT_ISO_STATE_DISCONNECTED) {
414 			LOG_INF("Returning instance %d", i);
415 			*chan = &iso_chans[i].chan;
416 			cig_create_param.num_cis++;
417 
418 			k_sem_give(&sem_iso_accept);
419 			return 0;
420 		}
421 	}
422 
423 	LOG_ERR("Could not accept any more CIS");
424 
425 	*chan = NULL;
426 
427 	return -ENOMEM;
428 }
429 
430 static struct bt_iso_server iso_server = {
431 #if defined(CONFIG_BT_SMP)
432 	.sec_level = DEFAULT_CIS_SEC_LEVEL,
433 #endif /* CONFIG_BT_SMP */
434 	.accept = iso_accept,
435 };
436 
data_cb(struct bt_data * data,void * user_data)437 static bool data_cb(struct bt_data *data, void *user_data)
438 {
439 	char *name = user_data;
440 	uint8_t len;
441 
442 	switch (data->type) {
443 	case BT_DATA_NAME_SHORTENED:
444 		__fallthrough;
445 	case BT_DATA_NAME_COMPLETE:
446 		len = MIN(data->data_len, DEVICE_NAME_LEN - 1);
447 		memcpy(name, data->data, len);
448 		name[len] = '\0';
449 		return false;
450 	default:
451 		return true;
452 	}
453 }
454 
start_scan(void)455 static int start_scan(void)
456 {
457 	int err;
458 
459 	err = bt_le_scan_start(BT_LE_SCAN_ACTIVE, NULL);
460 	if (err != 0) {
461 		LOG_ERR("Scan start failed: %d", err);
462 		return err;
463 	}
464 
465 	LOG_INF("Scan started");
466 
467 	return 0;
468 }
469 
stop_scan(void)470 static int stop_scan(void)
471 {
472 	int err;
473 
474 	err = bt_le_scan_stop();
475 	if (err != 0) {
476 		LOG_ERR("Scan stop failed: %d", err);
477 		return err;
478 	}
479 
480 	LOG_INF("Scan stopped");
481 
482 	return 0;
483 }
484 
scan_recv(const struct bt_le_scan_recv_info * info,struct net_buf_simple * buf)485 static void scan_recv(const struct bt_le_scan_recv_info *info,
486 		      struct net_buf_simple *buf)
487 {
488 	char le_addr[BT_ADDR_LE_STR_LEN];
489 	char name[DEVICE_NAME_LEN];
490 
491 	if (advertiser_found) {
492 		return;
493 	}
494 
495 	(void)memset(name, 0, sizeof(name));
496 
497 	bt_data_parse(buf, data_cb, name);
498 
499 	if (strncmp(DEVICE_NAME, name, strlen(DEVICE_NAME))) {
500 		return;
501 	}
502 
503 	bt_addr_le_to_str(info->addr, le_addr, sizeof(le_addr));
504 
505 	LOG_INF("Found peripheral with address %s (RSSI %i)",
506 		le_addr, info->rssi);
507 
508 
509 	bt_addr_le_copy(&adv_addr, info->addr);
510 	advertiser_found = true;
511 	k_sem_give(&sem_adv);
512 }
513 
514 static struct bt_le_scan_cb scan_callbacks = {
515 	.recv = scan_recv,
516 };
517 
connected(struct bt_conn * conn,uint8_t err)518 static void connected(struct bt_conn *conn, uint8_t err)
519 {
520 	char addr[BT_ADDR_LE_STR_LEN];
521 
522 	bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
523 
524 	if (err != 0 && role == ROLE_CENTRAL) {
525 		LOG_INF("Failed to connect to %s: %u %s", addr, err, bt_hci_err_to_str(err));
526 
527 		bt_conn_unref(default_conn);
528 		default_conn = NULL;
529 		return;
530 	} else if (role == ROLE_PERIPHERAL) {
531 		default_conn = bt_conn_ref(conn);
532 	}
533 
534 	LOG_INF("Connected: %s", addr);
535 
536 	k_sem_give(&sem_connected);
537 }
538 
disconnected(struct bt_conn * conn,uint8_t reason)539 static void disconnected(struct bt_conn *conn, uint8_t reason)
540 {
541 	char addr[BT_ADDR_LE_STR_LEN];
542 
543 	bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
544 
545 	LOG_INF("Disconnected: %s, reason 0x%02x %s", addr, reason, bt_hci_err_to_str(reason));
546 
547 	bt_conn_unref(default_conn);
548 	default_conn = NULL;
549 	k_sem_give(&sem_disconnected);
550 }
551 
552 static struct bt_conn_cb conn_callbacks = {
553 	.connected = connected,
554 	.disconnected = disconnected,
555 };
556 
get_chars(char * buffer,size_t max_size)557 static size_t get_chars(char *buffer, size_t max_size)
558 {
559 	size_t pos = 0;
560 
561 	while (pos < max_size) {
562 		char c = tolower(console_getchar());
563 
564 		if (c == '\n' || c == '\r') {
565 			break;
566 		}
567 		printk("%c", c);
568 		buffer[pos++] = c;
569 	}
570 	printk("\n");
571 	buffer[pos] = '\0';
572 
573 	return pos;
574 }
575 
parse_rtn_arg(struct bt_iso_chan_io_qos * qos)576 static int parse_rtn_arg(struct bt_iso_chan_io_qos *qos)
577 {
578 	char buffer[4];
579 	size_t char_count;
580 	uint64_t rtn;
581 
582 	printk("Set RTN (current %u, default %u)\n",
583 	       qos->rtn, DEFAULT_CIS_RTN);
584 
585 	char_count = get_chars(buffer, sizeof(buffer) - 1);
586 	if (char_count == 0) {
587 		return DEFAULT_CIS_RTN;
588 	}
589 
590 	rtn = strtoul(buffer, NULL, 0);
591 	if (rtn > BT_ISO_CONNECTED_RTN_MAX) {
592 		printk("Invalid RTN %llu", rtn);
593 		return -EINVAL;
594 	}
595 
596 	return (int)rtn;
597 }
598 
parse_interval_arg(enum sdu_dir direction)599 static int parse_interval_arg(enum sdu_dir direction)
600 {
601 	char buffer[9];
602 	size_t char_count;
603 	uint64_t interval;
604 
605 	interval = (direction == DIR_C_TO_P) ?
606 		   cig_create_param.c_to_p_interval : cig_create_param.p_to_c_interval;
607 
608 	printk("Set %s interval (us) (current %llu, default %u)\n",
609 	       (direction == DIR_C_TO_P) ? "C to P" : "P to C",
610 	       interval, DEFAULT_CIS_INTERVAL_US);
611 
612 	char_count = get_chars(buffer, sizeof(buffer) - 1);
613 	if (char_count == 0) {
614 		return DEFAULT_CIS_INTERVAL_US;
615 	}
616 
617 	interval = strtoul(buffer, NULL, 0);
618 	if (interval < BT_ISO_SDU_INTERVAL_MIN || interval > BT_ISO_SDU_INTERVAL_MAX) {
619 		printk("Invalid interval %llu", interval);
620 		return -EINVAL;
621 	}
622 
623 	return (int)interval;
624 }
625 
parse_latency_arg(enum sdu_dir direction)626 static int parse_latency_arg(enum sdu_dir direction)
627 {
628 	char buffer[6];
629 	size_t char_count;
630 	uint64_t latency;
631 
632 	latency = (direction == DIR_C_TO_P) ?
633 		  cig_create_param.c_to_p_latency : cig_create_param.p_to_c_latency;
634 
635 	printk("Set %s latency (ms) (current %llu, default %u)\n",
636 	       (direction == DIR_C_TO_P) ? "C to P" : "P to C",
637 	       latency, DEFAULT_CIS_LATENCY_MS);
638 
639 	char_count = get_chars(buffer, sizeof(buffer) - 1);
640 	if (char_count == 0) {
641 		return DEFAULT_CIS_LATENCY_MS;
642 	}
643 
644 	latency = strtoul(buffer, NULL, 0);
645 	if (latency < BT_ISO_LATENCY_MIN || latency > BT_ISO_LATENCY_MAX) {
646 		printk("Invalid latency %llu", latency);
647 		return -EINVAL;
648 	}
649 
650 	return (int)latency;
651 }
652 
parse_phy_arg(struct bt_iso_chan_io_qos * qos)653 static int parse_phy_arg(struct bt_iso_chan_io_qos *qos)
654 {
655 	char buffer[3];
656 	size_t char_count;
657 	uint64_t phy;
658 
659 	printk("Set PHY (current %u, default %u) - %u = 1M, %u = 2M, %u = Coded\n",
660 	       qos->phy, DEFAULT_CIS_PHY, BT_GAP_LE_PHY_1M,
661 	       BT_GAP_LE_PHY_2M, BT_GAP_LE_PHY_CODED);
662 
663 	char_count = get_chars(buffer, sizeof(buffer) - 1);
664 	if (char_count == 0) {
665 		return DEFAULT_CIS_PHY;
666 	}
667 
668 	phy = strtoul(buffer, NULL, 0);
669 	if (phy != BT_GAP_LE_PHY_1M &&
670 	    phy != BT_GAP_LE_PHY_2M &&
671 	    phy != BT_GAP_LE_PHY_CODED) {
672 		printk("Invalid PHY %llu", phy);
673 		return -EINVAL;
674 	}
675 
676 	return (int)phy;
677 }
678 
parse_sdu_arg(struct bt_iso_chan_io_qos * qos)679 static int parse_sdu_arg(struct bt_iso_chan_io_qos *qos)
680 {
681 	char buffer[6];
682 	size_t char_count;
683 	uint64_t sdu;
684 
685 	printk("Set SDU (current %u, default %u)\n",
686 	       qos->sdu, DEFAULT_CIS_SDU_SIZE);
687 
688 	char_count = get_chars(buffer, sizeof(buffer) - 1);
689 	if (char_count == 0) {
690 		return DEFAULT_CIS_SDU_SIZE;
691 	}
692 
693 	sdu = strtoul(buffer, NULL, 0);
694 	if (sdu > MIN(BT_ISO_MAX_SDU, sizeof(iso_data)) ||
695 	    sdu < sizeof(uint32_t) /* room for the counter */) {
696 		printk("Invalid SDU %llu", sdu);
697 		return -EINVAL;
698 	}
699 
700 	return (int)sdu;
701 }
702 
703 #if defined(CONFIG_BT_ISO_TEST_PARAMS)
parse_c_to_p_ft_arg(void)704 static int parse_c_to_p_ft_arg(void)
705 {
706 	char buffer[4];
707 	size_t char_count;
708 	uint64_t c_to_p_ft;
709 
710 	printk("Set central to peripheral flush timeout (current %u, default %u)\n",
711 	       cig_create_param.c_to_p_ft, DEFAULT_CIS_FT);
712 
713 	char_count = get_chars(buffer, sizeof(buffer) - 1);
714 	if (char_count == 0) {
715 		return DEFAULT_CIS_FT;
716 	}
717 
718 	c_to_p_ft = strtoul(buffer, NULL, 0);
719 	if (!IN_RANGE(c_to_p_ft, BT_ISO_FT_MIN, BT_ISO_FT_MAX)) {
720 		printk("Invalid central to peripheral flush timeout %llu",
721 		       c_to_p_ft);
722 
723 		return -EINVAL;
724 	}
725 
726 	return (int)c_to_p_ft;
727 }
728 
parse_p_to_c_ft_arg(void)729 static int parse_p_to_c_ft_arg(void)
730 {
731 	char buffer[4];
732 	size_t char_count;
733 	uint64_t p_to_c_ft;
734 
735 	printk("Set peripheral to central flush timeout (current %u, default %u)\n",
736 	       cig_create_param.p_to_c_ft, DEFAULT_CIS_FT);
737 
738 	char_count = get_chars(buffer, sizeof(buffer) - 1);
739 	if (char_count == 0) {
740 		return DEFAULT_CIS_FT;
741 	}
742 
743 	p_to_c_ft = strtoul(buffer, NULL, 0);
744 	if (!IN_RANGE(p_to_c_ft, BT_ISO_FT_MIN, BT_ISO_FT_MAX)) {
745 		printk("Invalid peripheral to central flush timeout %llu",
746 		       p_to_c_ft);
747 
748 		return -EINVAL;
749 	}
750 
751 	return (int)p_to_c_ft;
752 }
753 
parse_iso_interval_arg(void)754 static int parse_iso_interval_arg(void)
755 {
756 	char buffer[8];
757 	size_t char_count;
758 	uint64_t iso_interval;
759 
760 	printk("Set ISO interval (current %u, default %u)\n",
761 	       cig_create_param.iso_interval, DEFAULT_CIS_ISO_INTERVAL);
762 
763 	char_count = get_chars(buffer, sizeof(buffer) - 1);
764 	if (char_count == 0) {
765 		return DEFAULT_CIS_ISO_INTERVAL;
766 	}
767 
768 	iso_interval = strtoul(buffer, NULL, 0);
769 	if (IN_RANGE(iso_interval, BT_ISO_ISO_INTERVAL_MIN, BT_ISO_ISO_INTERVAL_MAX)) {
770 		printk("Invalid ISO interval %llu", iso_interval);
771 
772 		return -EINVAL;
773 	}
774 
775 	return (int)iso_interval;
776 }
777 
parse_nse_arg(struct bt_iso_chan_qos * qos)778 static int parse_nse_arg(struct bt_iso_chan_qos *qos)
779 {
780 	char buffer[4];
781 	size_t char_count;
782 	uint64_t nse;
783 
784 	printk("Set number of subevents (current %u, default %u)\n",
785 	       qos->num_subevents, DEFAULT_CIS_NSE);
786 
787 	char_count = get_chars(buffer, sizeof(buffer) - 1);
788 	if (char_count == 0) {
789 		return DEFAULT_CIS_NSE;
790 	}
791 
792 	nse = strtoul(buffer, NULL, 0);
793 	if (IN_RANGE(nse, BT_ISO_NSE_MIN, BT_ISO_NSE_MAX)) {
794 		printk("Invalid number of subevents %llu", nse);
795 
796 		return -EINVAL;
797 	}
798 
799 	return (int)nse;
800 }
801 
parse_pdu_arg(const struct bt_iso_chan_io_qos * qos)802 static int parse_pdu_arg(const struct bt_iso_chan_io_qos *qos)
803 {
804 	char buffer[6];
805 	size_t char_count;
806 	uint64_t pdu;
807 
808 	printk("Set PDU (current %u, default %u)\n",
809 	       qos->max_pdu, DEFAULT_CIS_PDU_SIZE);
810 
811 	char_count = get_chars(buffer, sizeof(buffer) - 1);
812 	if (char_count == 0) {
813 		return DEFAULT_CIS_PDU_SIZE;
814 	}
815 
816 	pdu = strtoul(buffer, NULL, 0);
817 	if (pdu > BT_ISO_PDU_MAX) {
818 		printk("Invalid PDU %llu", pdu);
819 
820 		return -EINVAL;
821 	}
822 
823 	return (int)pdu;
824 }
825 
parse_bn_arg(const struct bt_iso_chan_io_qos * qos)826 static int parse_bn_arg(const struct bt_iso_chan_io_qos *qos)
827 {
828 	char buffer[4];
829 	size_t char_count;
830 	uint64_t bn;
831 
832 	printk("Set burst number (current %u, default %u)\n",
833 	       qos->burst_number, DEFAULT_CIS_BN);
834 
835 	char_count = get_chars(buffer, sizeof(buffer) - 1);
836 	if (char_count == 0) {
837 		return DEFAULT_CIS_PDU_SIZE;
838 	}
839 
840 	bn = strtoul(buffer, NULL, 0);
841 	if (!IN_RANGE(bn, BT_ISO_BN_MIN, BT_ISO_BN_MAX)) {
842 		printk("Invalid burst number %llu", bn);
843 
844 		return -EINVAL;
845 	}
846 
847 	return (int)bn;
848 }
849 #endif /* CONFIG_BT_ISO_TEST_PARAMS */
850 
parse_cis_count_arg(void)851 static int parse_cis_count_arg(void)
852 {
853 	char buffer[4];
854 	size_t char_count;
855 	uint64_t cis_count;
856 
857 	printk("Set CIS count (current %u, default %u)\n",
858 	       cig_create_param.num_cis, DEFAULT_CIS_COUNT);
859 
860 	char_count = get_chars(buffer, sizeof(buffer) - 1);
861 	if (char_count == 0) {
862 		return DEFAULT_CIS_COUNT;
863 	}
864 
865 	cis_count = strtoul(buffer, NULL, 0);
866 	if (cis_count > MAX(BT_ISO_MAX_GROUP_ISO_COUNT, CONFIG_BT_ISO_MAX_CHAN)) {
867 		printk("Invalid CIS count %llu", cis_count);
868 		return -EINVAL;
869 	}
870 
871 	return (int)cis_count;
872 }
873 
parse_cig_args(void)874 static int parse_cig_args(void)
875 {
876 	int c_to_p_interval;
877 	int p_to_c_interval;
878 	int c_to_p_latency;
879 	int p_to_c_latency;
880 	int cis_count;
881 #if defined(CONFIG_BT_ISO_TEST_PARAMS)
882 	int c_to_p_ft;
883 	int p_to_c_ft;
884 	int iso_interval;
885 	int num_subevents;
886 #endif /* CONFIG_BT_ISO_TEST_PARAMS */
887 
888 	printk("Follow the prompts. Press enter to use default values.\n");
889 
890 	cis_count = parse_cis_count_arg();
891 	if (cis_count < 0) {
892 		return -EINVAL;
893 	}
894 
895 	c_to_p_interval = parse_interval_arg(DIR_C_TO_P);
896 	if (c_to_p_interval < 0) {
897 		return -EINVAL;
898 	}
899 
900 	p_to_c_interval = parse_interval_arg(DIR_P_TO_C);
901 	if (p_to_c_interval < 0) {
902 		return -EINVAL;
903 	}
904 
905 	c_to_p_latency = parse_latency_arg(DIR_C_TO_P);
906 	if (c_to_p_latency < 0) {
907 		return -EINVAL;
908 	}
909 
910 	p_to_c_latency = parse_latency_arg(DIR_P_TO_C);
911 	if (p_to_c_latency < 0) {
912 		return -EINVAL;
913 	}
914 
915 #if defined(CONFIG_BT_ISO_TEST_PARAMS)
916 	c_to_p_ft = parse_c_to_p_ft_arg();
917 	if (c_to_p_ft < 0) {
918 		return -EINVAL;
919 	}
920 
921 	p_to_c_ft = parse_p_to_c_ft_arg();
922 	if (p_to_c_ft < 0) {
923 		return -EINVAL;
924 	}
925 
926 	iso_interval = parse_iso_interval_arg();
927 	if (iso_interval < 0) {
928 		return -EINVAL;
929 	}
930 
931 	num_subevents = parse_nse_arg(&iso_qos);
932 	if (num_subevents < 0) {
933 		return -EINVAL;
934 	}
935 #endif /* CONFIG_BT_ISO_TEST_PARAMS */
936 
937 	cig_create_param.c_to_p_interval = c_to_p_interval;
938 	cig_create_param.p_to_c_interval = p_to_c_interval;
939 	cig_create_param.c_to_p_latency = c_to_p_latency;
940 	cig_create_param.p_to_c_latency = p_to_c_latency;
941 	cig_create_param.num_cis = cis_count;
942 #if defined(CONFIG_BT_ISO_TEST_PARAMS)
943 	cig_create_param.c_to_p_ft = c_to_p_ft;
944 	cig_create_param.p_to_c_ft = p_to_c_ft;
945 	cig_create_param.iso_interval = iso_interval;
946 	iso_qos.num_subevents = num_subevents;
947 #endif /* CONFIG_BT_ISO_TEST_PARAMS */
948 
949 	return 0;
950 }
951 
parse_cis_args(struct bt_iso_chan_io_qos * qos)952 static int parse_cis_args(struct bt_iso_chan_io_qos *qos)
953 {
954 	int rtn;
955 	int phy;
956 	int sdu;
957 #if defined(CONFIG_BT_ISO_TEST_PARAMS)
958 	int max_pdu;
959 	int burst_number;
960 #endif /* CONFIG_BT_ISO_TEST_PARAMS */
961 
962 	printk("Follow the prompts. Press enter to use default values.\n");
963 
964 	rtn = parse_rtn_arg(qos);
965 	if (rtn < 0) {
966 		return -EINVAL;
967 	}
968 
969 	phy = parse_phy_arg(qos);
970 	if (phy < 0) {
971 		return -EINVAL;
972 	}
973 
974 	sdu = parse_sdu_arg(qos);
975 	if (sdu < 0) {
976 		return -EINVAL;
977 	}
978 
979 #if defined(CONFIG_BT_ISO_TEST_PARAMS)
980 	max_pdu = parse_pdu_arg(qos);
981 	if (max_pdu < 0) {
982 		return -EINVAL;
983 	}
984 
985 	burst_number = parse_bn_arg(qos);
986 	if (burst_number < 0) {
987 		return -EINVAL;
988 	}
989 #endif /* CONFIG_BT_ISO_TEST_PARAMS */
990 
991 	qos->rtn = rtn;
992 	qos->phy = phy;
993 	qos->sdu = sdu;
994 #if defined(CONFIG_BT_ISO_TEST_PARAMS)
995 	qos->max_pdu = max_pdu;
996 	qos->burst_number = burst_number;
997 #endif /* CONFIG_BT_ISO_TEST_PARAMS */
998 
999 	return 0;
1000 }
1001 
change_central_settings(void)1002 static int change_central_settings(void)
1003 {
1004 	char c;
1005 	int err;
1006 
1007 	printk("Change CIG settings (y/N)? (Current settings: cis_count=%u, "
1008 	       "C to P interval=%u, P to C interval=%u "
1009 	       "C to P latency=%u, P to C latency=%u)\n",
1010 	       cig_create_param.num_cis, cig_create_param.c_to_p_interval,
1011 	       cig_create_param.p_to_c_interval, cig_create_param.c_to_p_latency,
1012 	       cig_create_param.p_to_c_latency);
1013 
1014 	c = tolower(console_getchar());
1015 	if (c == 'y') {
1016 		err = parse_cig_args();
1017 		if (err != 0) {
1018 			return err;
1019 		}
1020 
1021 		printk("New settings: cis_count=%u, C to P interval=%u, "
1022 		       "P TO C interval=%u, C to P latency=%u "
1023 		       "P TO C latency=%u\n",
1024 		       cig_create_param.num_cis, cig_create_param.c_to_p_interval,
1025 		       cig_create_param.p_to_c_interval, cig_create_param.c_to_p_latency,
1026 		       cig_create_param.p_to_c_latency);
1027 	}
1028 
1029 	printk("Change TX settings (y/N)? (Current settings: rtn=%u, "
1030 	       "phy=%u, sdu=%u)\n",
1031 	       iso_tx_qos.rtn, iso_tx_qos.phy, iso_tx_qos.sdu);
1032 
1033 	c = tolower(console_getchar());
1034 	if (c == 'y') {
1035 		printk("Disable TX (y/N)?\n");
1036 		c = tolower(console_getchar());
1037 		if (c == 'y') {
1038 			iso_qos.tx = NULL;
1039 			printk("TX disabled\n");
1040 		} else {
1041 			iso_qos.tx = &iso_tx_qos;
1042 
1043 			err = parse_cis_args(&iso_tx_qos);
1044 			if (err != 0) {
1045 				return err;
1046 			}
1047 
1048 			printk("New settings: rtn=%u, phy=%u, sdu=%u\n",
1049 			       iso_tx_qos.rtn, iso_tx_qos.phy, iso_tx_qos.sdu);
1050 		}
1051 	}
1052 
1053 	printk("Change RX settings (y/N)? (Current settings: rtn=%u, "
1054 	       "phy=%u, sdu=%u)\n",
1055 	       iso_rx_qos.rtn, iso_rx_qos.phy, iso_rx_qos.sdu);
1056 
1057 	c = tolower(console_getchar());
1058 	if (c == 'y') {
1059 		printk("Disable RX (y/N)?\n");
1060 		c = tolower(console_getchar());
1061 		if (c == 'y') {
1062 			if (iso_qos.tx == NULL) {
1063 				LOG_ERR("Cannot disable both TX and RX\n");
1064 				return -EINVAL;
1065 			}
1066 
1067 			iso_qos.rx = NULL;
1068 			printk("RX disabled\n");
1069 		} else {
1070 
1071 			printk("Set RX settings to TX settings (Y/n)?\n");
1072 
1073 			c = tolower(console_getchar());
1074 			if (c == 'n') {
1075 				err = parse_cis_args(&iso_rx_qos);
1076 				if (err != 0) {
1077 					return err;
1078 				}
1079 
1080 				printk("New settings: rtn=%u, phy=%u, sdu=%u\n",
1081 				       iso_rx_qos.rtn, iso_rx_qos.phy,
1082 				       iso_rx_qos.sdu);
1083 			} else {
1084 				(void)memcpy(&iso_rx_qos, &iso_tx_qos,
1085 					     sizeof(iso_rx_qos));
1086 			}
1087 		}
1088 	}
1089 
1090 	return 0;
1091 }
1092 
central_create_connection(void)1093 static int central_create_connection(void)
1094 {
1095 	/* Give the controller a large range of intervals to pick from. In this benchmark sample we
1096 	 * want to prioritize ISO over ACL, but will leave the actual choice up to the controller.
1097 	 */
1098 	const struct bt_le_conn_param *conn_param =
1099 		BT_LE_CONN_PARAM(BT_GAP_INIT_CONN_INT_MIN, BT_GAP_MS_TO_CONN_INTERVAL(500U), 0,
1100 				 BT_GAP_MS_TO_CONN_TIMEOUT(4000));
1101 	int err;
1102 
1103 	advertiser_found = false;
1104 
1105 	err = start_scan();
1106 	if (err != 0) {
1107 		LOG_ERR("Could not start scan: %d", err);
1108 		return err;
1109 	}
1110 
1111 	LOG_INF("Waiting for advertiser");
1112 	err = k_sem_take(&sem_adv, K_FOREVER);
1113 	if (err != 0) {
1114 		LOG_ERR("failed to take sem_adv: %d", err);
1115 		return err;
1116 	}
1117 
1118 	LOG_INF("Stopping scan");
1119 	err = stop_scan();
1120 	if (err != 0) {
1121 		LOG_ERR("Could not stop scan: %d", err);
1122 		return err;
1123 	}
1124 
1125 	LOG_INF("Connecting");
1126 	err = bt_conn_le_create(&adv_addr, BT_CONN_LE_CREATE_CONN, conn_param, &default_conn);
1127 	if (err != 0) {
1128 		LOG_ERR("Create connection failed: %d", err);
1129 		return err;
1130 	}
1131 
1132 	err = k_sem_take(&sem_connected, K_FOREVER);
1133 	if (err != 0) {
1134 		LOG_ERR("failed to take sem_connected: %d", err);
1135 		return err;
1136 	}
1137 
1138 	return 0;
1139 }
1140 
central_create_cig(void)1141 static int central_create_cig(void)
1142 {
1143 	int err;
1144 
1145 	iso_conn_start_time = 0;
1146 
1147 	LOG_INF("Creating CIG");
1148 
1149 	err = bt_iso_cig_create(&cig_create_param, &cig);
1150 	if (err != 0) {
1151 		LOG_ERR("Failed to create CIG: %d", err);
1152 		return err;
1153 	}
1154 
1155 	return 0;
1156 }
1157 
central_connect_cis(void)1158 static int central_connect_cis(void)
1159 {
1160 	struct bt_iso_connect_param connect_param[CONFIG_BT_ISO_MAX_CHAN];
1161 	int err;
1162 
1163 	iso_conn_start_time = 0;
1164 
1165 	LOG_INF("Connecting ISO channels");
1166 
1167 	for (int i = 0; i < cig_create_param.num_cis; i++) {
1168 		connect_param[i].acl = default_conn;
1169 		connect_param[i].iso_chan = &iso_chans[i].chan;
1170 	}
1171 
1172 	err = bt_iso_chan_connect(connect_param, cig_create_param.num_cis);
1173 	if (err != 0) {
1174 		LOG_ERR("Failed to connect iso: %d", err);
1175 		return err;
1176 	}
1177 	total_iso_conn_count++;
1178 
1179 	for (int i = 0; i < cig_create_param.num_cis; i++) {
1180 		err = k_sem_take(&sem_iso_connected, K_FOREVER);
1181 		if (err != 0) {
1182 			LOG_ERR("failed to take sem_iso_connected: %d", err);
1183 			return err;
1184 		}
1185 	}
1186 
1187 	return 0;
1188 }
1189 
reset_sems(void)1190 static void reset_sems(void)
1191 {
1192 	(void)k_sem_reset(&sem_adv);
1193 	(void)k_sem_reset(&sem_iso_accept);
1194 	(void)k_sem_reset(&sem_iso_connected);
1195 	(void)k_sem_reset(&sem_iso_disconnected);
1196 	(void)k_sem_reset(&sem_connected);
1197 	(void)k_sem_reset(&sem_disconnected);
1198 }
1199 
cleanup(void)1200 static int cleanup(void)
1201 {
1202 	int err;
1203 
1204 	for (size_t i = 0; i < cig_create_param.num_cis; i++) {
1205 		(void)k_work_cancel_delayable(&iso_chans[i].send_work);
1206 	}
1207 
1208 	err = k_sem_take(&sem_disconnected, K_NO_WAIT);
1209 	if (err != 0) {
1210 		for (int i = 0; i < cig_create_param.num_cis; i++) {
1211 			err = k_sem_take(&sem_iso_disconnected, K_NO_WAIT);
1212 			if (err == 0) {
1213 				err = bt_iso_chan_disconnect(&iso_chans[i].chan);
1214 				if (err != 0) {
1215 					LOG_ERR("Could not disconnect ISO[%d]: %d",
1216 						i, err);
1217 					break;
1218 				}
1219 			} /* else ISO already disconnected */
1220 		}
1221 
1222 		err = bt_conn_disconnect(default_conn,
1223 					 BT_HCI_ERR_REMOTE_USER_TERM_CONN);
1224 		if (err != 0) {
1225 			LOG_ERR("Could not disconnect ACL: %d", err);
1226 			return err;
1227 		}
1228 
1229 		err = k_sem_take(&sem_disconnected, K_FOREVER);
1230 		if (err != 0) {
1231 			LOG_ERR("failed to take sem_disconnected: %d", err);
1232 			return err;
1233 		}
1234 	} /* else ACL already disconnected */
1235 
1236 	if (cig) {
1237 		err = bt_iso_cig_terminate(cig);
1238 		if (err != 0) {
1239 			LOG_ERR("Could not terminate CIG: %d", err);
1240 			return err;
1241 		}
1242 		cig = NULL;
1243 	}
1244 
1245 	return err;
1246 }
1247 
run_central(void)1248 static int run_central(void)
1249 {
1250 	int err;
1251 	char c;
1252 
1253 	iso_conn_start_time = 0;
1254 	last_received_counter = 0;
1255 	memset(&stats_current_conn, 0, sizeof(stats_current_conn));
1256 	reset_sems();
1257 
1258 	printk("Change ISO settings (y/N)?\n");
1259 	c = tolower(console_getchar());
1260 	if (c == 'y') {
1261 		err = change_central_settings();
1262 		if (err != 0) {
1263 			LOG_ERR("Failed to set parameters: %d", err);
1264 			return err;
1265 		}
1266 	}
1267 
1268 	/* Creating the CIG before connecting verified that it's possible before establishing a
1269 	 * connection, while also providing the controller information about our use case before
1270 	 * creating the connection, which should provide additional information to the controller
1271 	 * about which connection interval to use
1272 	 */
1273 	err = central_create_cig();
1274 	if (err != 0) {
1275 		LOG_ERR("Failed to create CIG: %d", err);
1276 		return err;
1277 	}
1278 
1279 	err = central_create_connection();
1280 	if (err != 0) {
1281 		LOG_ERR("Failed to create connection: %d", err);
1282 		return err;
1283 	}
1284 
1285 	err = central_connect_cis();
1286 	if (err != 0) {
1287 		LOG_ERR("Failed to connect CISes: %d", err);
1288 		return err;
1289 	}
1290 
1291 	for (size_t i = 0; i < cig_create_param.num_cis; i++) {
1292 		struct k_work_delayable *work = &iso_chans[i].send_work;
1293 
1294 		k_work_init_delayable(work, iso_timer_timeout);
1295 
1296 		for (int j = 0; j < BUFFERS_ENQUEUED; j++) {
1297 			iso_send(&iso_chans[i].chan);
1298 		}
1299 	}
1300 
1301 	err = k_sem_take(&sem_disconnected, K_FOREVER);
1302 	if (err != 0) {
1303 		LOG_ERR("failed to take sem_disconnected: %d", err);
1304 		return err;
1305 	}
1306 
1307 	LOG_INF("Disconnected - Cleaning up");
1308 	for (size_t i = 0; i < cig_create_param.num_cis; i++) {
1309 		(void)k_work_cancel_delayable(&iso_chans[i].send_work);
1310 	}
1311 
1312 	for (int i = 0; i < cig_create_param.num_cis; i++) {
1313 		err = k_sem_take(&sem_iso_disconnected, K_FOREVER);
1314 		if (err != 0) {
1315 			LOG_ERR("failed to take sem_iso_disconnected: %d", err);
1316 			return err;
1317 		}
1318 	}
1319 
1320 	err = bt_iso_cig_terminate(cig);
1321 	if (err != 0) {
1322 		LOG_ERR("Could not terminate CIG: %d", err);
1323 		return err;
1324 	}
1325 	cig = NULL;
1326 
1327 	return 0;
1328 }
1329 
run_peripheral(void)1330 static int run_peripheral(void)
1331 {
1332 	int err;
1333 	static bool initialized;
1334 
1335 	/* Reset */
1336 	cig_create_param.num_cis = 0;
1337 	iso_conn_start_time = 0;
1338 	last_received_counter = 0;
1339 	memset(&stats_current_conn, 0, sizeof(stats_current_conn));
1340 	reset_sems();
1341 
1342 	if (!initialized) {
1343 		LOG_INF("Registering ISO server");
1344 		err = bt_iso_server_register(&iso_server);
1345 		if (err != 0) {
1346 			LOG_ERR("ISO server register failed: %d", err);
1347 			return err;
1348 		}
1349 		initialized = true;
1350 	}
1351 
1352 	LOG_INF("Starting advertising");
1353 	err = bt_le_adv_start(BT_LE_ADV_CONN_FAST_1, NULL, 0, sd, ARRAY_SIZE(sd));
1354 	if (err != 0) {
1355 		LOG_ERR("Advertising failed to start: %d", err);
1356 		return err;
1357 	}
1358 
1359 	LOG_INF("Waiting for ACL connection");
1360 	err = k_sem_take(&sem_connected, K_FOREVER);
1361 	if (err != 0) {
1362 		LOG_ERR("failed to take sem_connected: %d", err);
1363 		return err;
1364 	}
1365 
1366 	err = bt_le_adv_stop();
1367 	if (err != 0) {
1368 		LOG_ERR("Advertising failed to stop: %d", err);
1369 		return err;
1370 	}
1371 
1372 	LOG_INF("Waiting for ISO connection");
1373 
1374 	err = k_sem_take(&sem_iso_accept, K_SECONDS(2));
1375 	if (err != 0) {
1376 		return err;
1377 	}
1378 
1379 	for (int i = 0; i < cig_create_param.num_cis; i++) {
1380 		err = k_sem_take(&sem_iso_connected, K_FOREVER);
1381 		if (err != 0) {
1382 			LOG_ERR("failed to take sem_iso_connected: %d", err);
1383 			return err;
1384 		}
1385 	}
1386 	total_iso_conn_count++;
1387 
1388 	for (size_t i = 0; i < cig_create_param.num_cis; i++) {
1389 		struct k_work_delayable *work = &iso_chans[i].send_work;
1390 
1391 		k_work_init_delayable(work, iso_timer_timeout);
1392 
1393 		for (int j = 0; j < BUFFERS_ENQUEUED; j++) {
1394 			iso_send(&iso_chans[i].chan);
1395 		}
1396 	}
1397 
1398 	/* Wait for disconnect */
1399 	err = k_sem_take(&sem_disconnected, K_FOREVER);
1400 	if (err != 0) {
1401 		LOG_ERR("failed to take sem_disconnected: %d", err);
1402 		return err;
1403 	}
1404 
1405 	for (int i = 0; i < cig_create_param.num_cis; i++) {
1406 		err = k_sem_take(&sem_iso_disconnected, K_FOREVER);
1407 		if (err != 0) {
1408 			LOG_ERR("failed to take sem_iso_disconnected: %d", err);
1409 			return err;
1410 		}
1411 	}
1412 
1413 	LOG_INF("Disconnected - Cleaning up");
1414 	for (size_t i = 0; i < cig_create_param.num_cis; i++) {
1415 		(void)k_work_cancel_delayable(&iso_chans[i].send_work);
1416 	}
1417 
1418 	return 0;
1419 }
1420 
main(void)1421 int main(void)
1422 {
1423 	int err;
1424 
1425 	LOG_INF("Starting Bluetooth Throughput example");
1426 
1427 	err = bt_enable(NULL);
1428 	if (err != 0) {
1429 		LOG_ERR("Bluetooth init failed: %d", err);
1430 		return 0;
1431 	}
1432 
1433 	bt_conn_cb_register(&conn_callbacks);
1434 	bt_le_scan_cb_register(&scan_callbacks);
1435 
1436 	err = console_init();
1437 	if (err != 0) {
1438 		LOG_ERR("Console init failed: %d", err);
1439 		return 0;
1440 	}
1441 
1442 	LOG_INF("Bluetooth initialized");
1443 
1444 	for (int i = 0; i < ARRAY_SIZE(iso_chans); i++) {
1445 		iso_chans[i].chan.ops = &iso_ops;
1446 		iso_chans[i].chan.qos = &iso_qos;
1447 		cis[i] = &iso_chans[i].chan;
1448 	}
1449 
1450 	/* Init data */
1451 	for (int i = 0; i < iso_tx_qos.sdu; i++) {
1452 		if (i < sizeof(iso_send_count)) {
1453 			continue;
1454 		}
1455 		iso_data[i] = (uint8_t)i;
1456 	}
1457 
1458 	while (true) {
1459 		int cleanup_err;
1460 
1461 		role = device_role_select();
1462 
1463 		if (role == ROLE_CENTRAL) {
1464 			err = run_central();
1465 		} else if (role == ROLE_PERIPHERAL) {
1466 			err = run_peripheral();
1467 		} else {
1468 			if (role != ROLE_QUIT) {
1469 				LOG_INF("Invalid role %u", role);
1470 				continue;
1471 			} else {
1472 				err = 0;
1473 				break;
1474 			}
1475 		}
1476 
1477 		if (err != 0) {
1478 			cleanup_err = cleanup();
1479 			if (cleanup_err != 0) {
1480 				LOG_ERR("Could not clean up: %d", err);
1481 			}
1482 		}
1483 
1484 		LOG_INF("Test complete: %d", err);
1485 	}
1486 
1487 	LOG_INF("Exiting");
1488 	return 0;
1489 }
1490