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