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