1 /*
2 * Copyright (c) 2021-2024 Nordic Semiconductor ASA
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <ctype.h>
8 #include <stdlib.h>
9 #include <stdint.h>
10
11 #include <zephyr/bluetooth/gap.h>
12 #include <zephyr/console/console.h>
13 #include <zephyr/bluetooth/bluetooth.h>
14 #include <zephyr/bluetooth/iso.h>
15 #include <zephyr/kernel.h>
16 #include <zephyr/sys/byteorder.h>
17
18 #include <zephyr/logging/log.h>
19 #include <zephyr/sys/util.h>
20 #include <zephyr/sys_clock.h>
21 LOG_MODULE_REGISTER(iso_broadcast_broadcaster, LOG_LEVEL_DBG);
22
23 #define DEFAULT_BIS_RTN 2
24 #define DEFAULT_BIS_INTERVAL_US 7500
25 #define DEFAULT_BIS_LATENCY_MS 10
26 #define DEFAULT_BIS_PHY BT_GAP_LE_PHY_2M
27 #define DEFAULT_BIS_SDU CONFIG_BT_ISO_TX_MTU
28 #define DEFAULT_BIS_PACKING 0
29 #define DEFAULT_BIS_FRAMING 0
30 #define DEFAULT_BIS_COUNT CONFIG_BT_ISO_MAX_CHAN
31 #if defined(CONFIG_BT_ISO_TEST_PARAMS)
32 #define DEFAULT_BIS_NSE BT_ISO_NSE_MIN
33 #define DEFAULT_BIS_BN BT_ISO_BN_MIN
34 #define DEFAULT_BIS_PDU_SIZE CONFIG_BT_ISO_TX_MTU
35 #define DEFAULT_BIS_IRC BT_ISO_IRC_MIN
36 #define DEFAULT_BIS_PTO BT_ISO_PTO_MIN
37 #define DEFAULT_BIS_ISO_INTERVAL DEFAULT_BIS_INTERVAL_US / 1250U /* N * 10 ms */
38 #endif /* CONFIG_BT_ISO_TEST_PARAMS */
39
40 NET_BUF_POOL_FIXED_DEFINE(bis_tx_pool, CONFIG_BT_ISO_TX_BUF_COUNT,
41 BT_ISO_SDU_BUF_SIZE(CONFIG_BT_ISO_TX_MTU),
42 CONFIG_BT_CONN_TX_USER_DATA_SIZE, NULL);
43
44 static K_SEM_DEFINE(sem_big_complete, 0, 1);
45 static K_SEM_DEFINE(sem_big_term, 0, 1);
46 static struct k_work_delayable iso_send_work;
47 static uint32_t iso_send_count;
48 static uint8_t iso_data[CONFIG_BT_ISO_TX_MTU];
49 static uint8_t connected_bis;
50
51 static struct bt_iso_chan bis_iso_chans[CONFIG_BT_ISO_MAX_CHAN];
52 static struct bt_iso_chan *bis[CONFIG_BT_ISO_MAX_CHAN];
53 /* We use a single seq_num for all the BIS as they share the same SDU interval */
54 static uint16_t seq_num;
55 static struct bt_iso_big_create_param big_create_param = {
56 .num_bis = DEFAULT_BIS_COUNT,
57 .bis_channels = bis,
58 .packing = DEFAULT_BIS_PACKING, /* 0 - sequential, 1 - interleaved */
59 .framing = DEFAULT_BIS_FRAMING, /* 0 - unframed, 1 - framed */
60 .interval = DEFAULT_BIS_INTERVAL_US, /* in microseconds */
61 .latency = DEFAULT_BIS_LATENCY_MS, /* milliseconds */
62 #if defined(CONFIG_BT_ISO_TEST_PARAMS)
63 .irc = DEFAULT_BIS_IRC,
64 .pto = DEFAULT_BIS_PTO,
65 .iso_interval = DEFAULT_BIS_ISO_INTERVAL,
66 #endif /* CONFIG_BT_ISO_TEST_PARAMS */
67 };
68
69 static const struct bt_data ad[] = {
70 BT_DATA(BT_DATA_NAME_COMPLETE, CONFIG_BT_DEVICE_NAME, sizeof(CONFIG_BT_DEVICE_NAME) - 1),
71 };
72
iso_connected(struct bt_iso_chan * chan)73 static void iso_connected(struct bt_iso_chan *chan)
74 {
75 LOG_INF("ISO Channel %p connected", chan);
76
77 connected_bis++;
78 if (connected_bis == big_create_param.num_bis) {
79 seq_num = 0U;
80 k_sem_give(&sem_big_complete);
81 }
82 }
83
iso_disconnected(struct bt_iso_chan * chan,uint8_t reason)84 static void iso_disconnected(struct bt_iso_chan *chan, uint8_t reason)
85 {
86 LOG_INF("ISO Channel %p disconnected with reason 0x%02x",
87 chan, reason);
88
89 connected_bis--;
90 if (connected_bis == 0) {
91 k_sem_give(&sem_big_term);
92 }
93 }
94
95 static struct bt_iso_chan_ops iso_ops = {
96 .connected = iso_connected,
97 .disconnected = iso_disconnected,
98 };
99
100 static struct bt_iso_chan_io_qos iso_tx_qos = {
101 .sdu = DEFAULT_BIS_SDU, /* bytes */
102 .rtn = DEFAULT_BIS_RTN,
103 .phy = DEFAULT_BIS_PHY,
104 #if defined(CONFIG_BT_ISO_TEST_PARAMS)
105 .max_pdu = DEFAULT_BIS_PDU_SIZE,
106 .burst_number = DEFAULT_BIS_BN,
107 #endif /* CONFIG_BT_ISO_TEST_PARAMS */
108 };
109
110 static struct bt_iso_chan_qos bis_iso_qos = {
111 .tx = &iso_tx_qos,
112 #if defined(CONFIG_BT_ISO_TEST_PARAMS)
113 .num_subevents = DEFAULT_BIS_NSE,
114 #endif /* CONFIG_BT_ISO_TEST_PARAMS */
115 };
116
get_chars(char * buffer,size_t max_size)117 static size_t get_chars(char *buffer, size_t max_size)
118 {
119 size_t pos = 0;
120
121 while (pos < max_size) {
122 char c = tolower(console_getchar());
123
124 if (c == '\n' || c == '\r') {
125 break;
126 }
127 printk("%c", c);
128 buffer[pos++] = c;
129 }
130 printk("\n");
131 buffer[pos] = '\0';
132
133 return pos;
134 }
135
parse_rtn_arg(void)136 static int parse_rtn_arg(void)
137 {
138 char buffer[4];
139 size_t char_count;
140 uint64_t rtn;
141
142 printk("Set RTN (current %u, default %u)\n",
143 iso_tx_qos.rtn, DEFAULT_BIS_RTN);
144
145 char_count = get_chars(buffer, sizeof(buffer) - 1);
146 if (char_count == 0) {
147 return DEFAULT_BIS_RTN;
148 }
149
150 rtn = strtoul(buffer, NULL, 0);
151 if (rtn > BT_ISO_BROADCAST_RTN_MAX) {
152 printk("Invalid RTN %llu", rtn);
153 return -EINVAL;
154 }
155
156 return (int)rtn;
157 }
158
parse_interval_arg(void)159 static int parse_interval_arg(void)
160 {
161 char buffer[9];
162 size_t char_count;
163 uint64_t interval;
164
165 printk("Set interval (us) (current %u, default %u)\n",
166 big_create_param.interval, DEFAULT_BIS_INTERVAL_US);
167
168 char_count = get_chars(buffer, sizeof(buffer) - 1);
169 if (char_count == 0) {
170 return DEFAULT_BIS_INTERVAL_US;
171 }
172
173 interval = strtoul(buffer, NULL, 0);
174 if (interval < BT_ISO_SDU_INTERVAL_MIN || interval > BT_ISO_SDU_INTERVAL_MAX) {
175 printk("Invalid interval %llu", interval);
176 return -EINVAL;
177 }
178
179 return (int)interval;
180 }
181
parse_latency_arg(void)182 static int parse_latency_arg(void)
183 {
184 char buffer[6];
185 size_t char_count;
186 uint64_t latency;
187
188 printk("Set latency (ms) (current %u, default %u)\n",
189 big_create_param.latency, DEFAULT_BIS_LATENCY_MS);
190
191 char_count = get_chars(buffer, sizeof(buffer) - 1);
192 if (char_count == 0) {
193 return DEFAULT_BIS_LATENCY_MS;
194 }
195
196 latency = strtoul(buffer, NULL, 0);
197 if (latency < BT_ISO_LATENCY_MIN || latency > BT_ISO_LATENCY_MAX) {
198 printk("Invalid latency %llu", latency);
199 return -EINVAL;
200 }
201
202 return (int)latency;
203 }
204
parse_phy_arg(void)205 static int parse_phy_arg(void)
206 {
207 char buffer[3];
208 size_t char_count;
209 uint64_t phy;
210
211 printk("Set PHY (current %u, default %u) - %u = 1M, %u = 2M, %u = Coded\n",
212 iso_tx_qos.phy, DEFAULT_BIS_PHY, BT_GAP_LE_PHY_1M,
213 BT_GAP_LE_PHY_2M, BT_GAP_LE_PHY_CODED);
214
215 char_count = get_chars(buffer, sizeof(buffer) - 1);
216 if (char_count == 0) {
217 return DEFAULT_BIS_PHY;
218 }
219
220 phy = strtoul(buffer, NULL, 0);
221 if (phy != BT_GAP_LE_PHY_1M &&
222 phy != BT_GAP_LE_PHY_2M &&
223 phy != BT_GAP_LE_PHY_CODED) {
224 printk("Invalid PHY %llu", phy);
225 return -EINVAL;
226 }
227
228 return (int)phy;
229 }
230
parse_sdu_arg(void)231 static int parse_sdu_arg(void)
232 {
233 char buffer[6];
234 size_t char_count;
235 uint64_t sdu;
236
237 printk("Set SDU (current %u, default %u)\n",
238 iso_tx_qos.sdu, DEFAULT_BIS_SDU);
239
240 char_count = get_chars(buffer, sizeof(buffer) - 1);
241 if (char_count == 0) {
242 return DEFAULT_BIS_SDU;
243 }
244
245 sdu = strtoul(buffer, NULL, 0);
246 if (sdu > MIN(BT_ISO_MAX_SDU, sizeof(iso_data))) {
247 printk("Invalid SDU %llu", sdu);
248 return -EINVAL;
249 }
250
251 return (int)sdu;
252 }
253
254 #if defined(CONFIG_BT_ISO_TEST_PARAMS)
parse_irc_arg(void)255 static int parse_irc_arg(void)
256 {
257 size_t char_count;
258 char buffer[4];
259 uint64_t irc;
260
261 printk("Set IRC (current %u, default %u)\n",
262 big_create_param.irc, DEFAULT_BIS_IRC);
263
264 char_count = get_chars(buffer, sizeof(buffer) - 1);
265 if (char_count == 0) {
266 return DEFAULT_BIS_IRC;
267 }
268
269 irc = strtoul(buffer, NULL, 0);
270 if (!IN_RANGE(irc, BT_ISO_IRC_MIN, BT_ISO_IRC_MAX)) {
271 printk("Invalid IRC %llu", irc);
272
273 return -EINVAL;
274 }
275
276 return (int)irc;
277 }
278
parse_pto_arg(void)279 static int parse_pto_arg(void)
280 {
281 size_t char_count;
282 char buffer[4];
283 uint64_t pto;
284
285 printk("Set PTO (current %u, default %u)\n",
286 big_create_param.pto, DEFAULT_BIS_PTO);
287
288 char_count = get_chars(buffer, sizeof(buffer) - 1);
289 if (char_count == 0) {
290 return DEFAULT_BIS_PTO;
291 }
292
293 pto = strtoul(buffer, NULL, 0);
294 if (!IN_RANGE(pto, BT_ISO_PTO_MIN, BT_ISO_PTO_MAX)) {
295 printk("Invalid PTO %llu", pto);
296
297 return -EINVAL;
298 }
299
300 return (int)pto;
301 }
302
parse_iso_interval_arg(void)303 static int parse_iso_interval_arg(void)
304 {
305 uint64_t iso_interval;
306 size_t char_count;
307 char buffer[8];
308
309 printk("Set ISO interval (current %u, default %u)\n",
310 big_create_param.iso_interval, DEFAULT_BIS_ISO_INTERVAL);
311
312 char_count = get_chars(buffer, sizeof(buffer) - 1);
313 if (char_count == 0) {
314 return DEFAULT_BIS_ISO_INTERVAL;
315 }
316
317 iso_interval = strtoul(buffer, NULL, 0);
318 if (!IN_RANGE(iso_interval, BT_ISO_ISO_INTERVAL_MIN, BT_ISO_ISO_INTERVAL_MAX)) {
319 printk("Invalid ISO interval %llu", iso_interval);
320
321 return -EINVAL;
322 }
323
324 return (int)iso_interval;
325 }
326
parse_nse_arg(void)327 static int parse_nse_arg(void)
328 {
329 uint64_t num_subevents;
330 size_t char_count;
331 char buffer[4];
332
333 printk("Set number of subevents (current %u, default %u)\n",
334 bis_iso_qos.num_subevents, DEFAULT_BIS_NSE);
335
336 char_count = get_chars(buffer, sizeof(buffer) - 1);
337 if (char_count == 0) {
338 return DEFAULT_BIS_NSE;
339 }
340
341 num_subevents = strtoul(buffer, NULL, 0);
342 if (!IN_RANGE(num_subevents, BT_ISO_NSE_MIN, BT_ISO_NSE_MAX)) {
343 printk("Invalid number of subevents %llu", num_subevents);
344
345 return -EINVAL;
346 }
347
348 return (int)num_subevents;
349 }
350
parse_max_pdu_arg(void)351 static int parse_max_pdu_arg(void)
352 {
353 size_t char_count;
354 uint64_t max_pdu;
355 char buffer[6];
356
357 printk("Set max PDU (current %u, default %u)\n",
358 iso_tx_qos.max_pdu, DEFAULT_BIS_PDU_SIZE);
359
360 char_count = get_chars(buffer, sizeof(buffer) - 1);
361 if (char_count == 0) {
362 return DEFAULT_BIS_PDU_SIZE;
363 }
364
365 max_pdu = strtoul(buffer, NULL, 0);
366 if (max_pdu > BT_ISO_PDU_MAX) {
367 printk("Invalid max PDU %llu", max_pdu);
368
369 return -EINVAL;
370 }
371
372 return (int)max_pdu;
373 }
374
parse_bn_arg(void)375 static int parse_bn_arg(void)
376 {
377 uint64_t burst_number;
378 size_t char_count;
379 char buffer[4];
380
381 printk("Set burst number (current %u, default %u)\n",
382 iso_tx_qos.burst_number, DEFAULT_BIS_BN);
383
384 char_count = get_chars(buffer, sizeof(buffer) - 1);
385 if (char_count == 0) {
386 return DEFAULT_BIS_PDU_SIZE;
387 }
388
389 burst_number = strtoul(buffer, NULL, 0);
390 if (!IN_RANGE(burst_number, BT_ISO_BN_MIN, BT_ISO_BN_MAX)) {
391 printk("Invalid burst number %llu", burst_number);
392
393 return -EINVAL;
394 }
395
396 return (int)burst_number;
397 }
398 #endif /* CONFIG_BT_ISO_TEST_PARAMS */
399
parse_packing_arg(void)400 static int parse_packing_arg(void)
401 {
402 char buffer[3];
403 size_t char_count;
404 uint64_t packing;
405
406 printk("Set packing (current %u, default %u)\n",
407 big_create_param.packing, DEFAULT_BIS_PACKING);
408
409 char_count = get_chars(buffer, sizeof(buffer) - 1);
410 if (char_count == 0) {
411 return DEFAULT_BIS_PACKING;
412 }
413
414 packing = strtoul(buffer, NULL, 0);
415 if (packing != BT_ISO_PACKING_SEQUENTIAL &&
416 packing != BT_ISO_PACKING_INTERLEAVED) {
417 printk("Invalid packing %llu", packing);
418 return -EINVAL;
419 }
420
421 return (int)packing;
422 }
423
parse_framing_arg(void)424 static int parse_framing_arg(void)
425 {
426 char buffer[3];
427 size_t char_count;
428 uint64_t framing;
429
430 printk("Set framing (current %u, default %u)\n",
431 big_create_param.framing, DEFAULT_BIS_FRAMING);
432
433 char_count = get_chars(buffer, sizeof(buffer) - 1);
434 if (char_count == 0) {
435 return DEFAULT_BIS_FRAMING;
436 }
437
438 framing = strtoul(buffer, NULL, 0);
439 if (framing != BT_ISO_FRAMING_UNFRAMED &&
440 framing != BT_ISO_FRAMING_FRAMED) {
441 printk("Invalid framing %llu", framing);
442 return -EINVAL;
443 }
444
445 return (int)framing;
446 }
447
parse_bis_count_arg(void)448 static int parse_bis_count_arg(void)
449 {
450 char buffer[4];
451 size_t char_count;
452 uint64_t bis_count;
453
454 printk("Set BIS count (current %u, default %u)\n",
455 big_create_param.num_bis, DEFAULT_BIS_COUNT);
456
457 char_count = get_chars(buffer, sizeof(buffer) - 1);
458 if (char_count == 0) {
459 return DEFAULT_BIS_COUNT;
460 }
461
462 bis_count = strtoul(buffer, NULL, 0);
463 if (bis_count > MAX(BT_ISO_MAX_GROUP_ISO_COUNT, CONFIG_BT_ISO_MAX_CHAN)) {
464 printk("Invalid BIS count %llu", bis_count);
465 return -EINVAL;
466 }
467
468 return (int)bis_count;
469 }
470
parse_args(void)471 static int parse_args(void)
472 {
473 int rtn;
474 int interval;
475 int latency;
476 int phy;
477 int sdu;
478 int packing;
479 int framing;
480 int bis_count;
481 #if defined(CONFIG_BT_ISO_TEST_PARAMS)
482 int num_subevents;
483 int iso_interval;
484 int burst_number;
485 int max_pdu;
486 int irc;
487 int pto;
488 #endif /* CONFIG_BT_ISO_TEST_PARAMS */
489
490 printk("Follow the prompts. Press enter to use default values.\n");
491
492 rtn = parse_rtn_arg();
493 if (rtn < 0) {
494 return -EINVAL;
495 }
496
497 interval = parse_interval_arg();
498 if (interval < 0) {
499 return -EINVAL;
500 }
501
502 latency = parse_latency_arg();
503 if (latency < 0) {
504 return -EINVAL;
505 }
506
507 phy = parse_phy_arg();
508 if (phy < 0) {
509 return -EINVAL;
510 }
511
512 sdu = parse_sdu_arg();
513 if (sdu < 0) {
514 return -EINVAL;
515 }
516
517 packing = parse_packing_arg();
518 if (packing < 0) {
519 return -EINVAL;
520 }
521
522 framing = parse_framing_arg();
523 if (framing < 0) {
524 return -EINVAL;
525 }
526
527 bis_count = parse_bis_count_arg();
528 if (bis_count < 0) {
529 return -EINVAL;
530 }
531
532 #if defined(CONFIG_BT_ISO_TEST_PARAMS)
533 irc = parse_irc_arg();
534 if (irc < 0) {
535 return -EINVAL;
536 }
537
538 pto = parse_pto_arg();
539 if (pto < 0) {
540 return -EINVAL;
541 }
542
543 iso_interval = parse_iso_interval_arg();
544 if (iso_interval < 0) {
545 return -EINVAL;
546 }
547
548 num_subevents = parse_nse_arg();
549 if (num_subevents < 0) {
550 return -EINVAL;
551 }
552
553 max_pdu = parse_max_pdu_arg();
554 if (max_pdu < 0) {
555 return -EINVAL;
556 }
557
558 burst_number = parse_bn_arg();
559 if (burst_number < 0) {
560 return -EINVAL;
561 }
562 #endif /* CONFIG_BT_ISO_TEST_PARAMS */
563
564 iso_tx_qos.rtn = rtn;
565 iso_tx_qos.phy = phy;
566 iso_tx_qos.sdu = sdu;
567 big_create_param.interval = interval;
568 big_create_param.latency = latency;
569 big_create_param.packing = packing;
570 big_create_param.framing = framing;
571 big_create_param.num_bis = bis_count;
572 #if defined(CONFIG_BT_ISO_TEST_PARAMS)
573 bis_iso_qos.num_subevents = num_subevents;
574 iso_tx_qos.max_pdu = max_pdu;
575 iso_tx_qos.burst_number = burst_number;
576 big_create_param.irc = irc;
577 big_create_param.pto = pto;
578 big_create_param.iso_interval = iso_interval;
579 #endif /* CONFIG_BT_ISO_TEST_PARAMS */
580
581 return 0;
582 }
583
iso_timer_timeout(struct k_work * work)584 static void iso_timer_timeout(struct k_work *work)
585 {
586 int ret;
587 struct net_buf *buf;
588
589 /* Reschedule as early as possible to reduce time skewing
590 * Use the ISO interval minus a few microseconds to keep the buffer
591 * full. This might occasionally skip a transmit, i.e. where the host
592 * calls `bt_iso_chan_send` but the controller only sending a single
593 * ISO packet.
594 */
595 k_work_reschedule(&iso_send_work, K_USEC(big_create_param.interval - 100));
596
597 for (int i = 0; i < big_create_param.num_bis; i++) {
598 buf = net_buf_alloc(&bis_tx_pool, K_NO_WAIT);
599 if (buf == NULL) {
600 LOG_ERR("Could not allocate buffer");
601 return;
602 }
603
604 net_buf_reserve(buf, BT_ISO_CHAN_SEND_RESERVE);
605 net_buf_add_mem(buf, iso_data, iso_tx_qos.sdu);
606 ret = bt_iso_chan_send(&bis_iso_chans[i], buf, seq_num);
607 if (ret < 0) {
608 LOG_ERR("Unable to broadcast data: %d", ret);
609 net_buf_unref(buf);
610 return;
611 }
612 iso_send_count++;
613
614 if ((iso_send_count % 100) == 0) {
615 LOG_INF("Sent %u packets", iso_send_count);
616 }
617 }
618
619 seq_num++;
620 }
621
calc_adv_interval(uint32_t sdu_interval_us)622 static uint16_t calc_adv_interval(uint32_t sdu_interval_us)
623 {
624 /* Default to 6 times the SDU interval and then reduce until we reach a reasonable maximum
625 * advertising interval (BT_GAP_PER_ADV_SLOW_INT_MAX)
626 * sdu_interval_us can never be larger than 1048ms (BT_ISO_SDU_INTERVAL_MAX)
627 * so a multiple of it will always be possible to keep under BT_GAP_PER_ADV_SLOW_INT_MAX
628 * (1200ms)
629 */
630
631 /* Convert from microseconds to advertising interval units (0.625ms)*/
632 const uint32_t interval = BT_GAP_US_TO_ADV_INTERVAL(sdu_interval_us);
633 uint32_t adv_interval = interval * 6U;
634
635 /* Ensure that the adv interval is between BT_GAP_PER_ADV_FAST_INT_MIN_1 and
636 * BT_GAP_PER_ADV_SLOW_INT_MAX for the sake of interopability
637 */
638 while (adv_interval < BT_GAP_PER_ADV_FAST_INT_MIN_1) {
639 adv_interval += interval;
640 }
641
642 while (adv_interval > BT_GAP_PER_ADV_SLOW_INT_MAX) {
643 adv_interval -= interval;
644 }
645
646 /* If we cannot convert back then it's not a lossless conversion */
647 if (big_create_param.framing == BT_ISO_FRAMING_UNFRAMED &&
648 BT_GAP_ADV_INTERVAL_TO_US(interval) != sdu_interval_us) {
649 LOG_INF("Advertising interval of 0x04%x is not a multiple of the advertising "
650 "interval unit (0.625 ms) and may be subpar. Suggest to adjust SDU "
651 "interval %u to be a multiple of 0.625 ms",
652 adv_interval, sdu_interval_us);
653 }
654
655 return adv_interval;
656 }
657
create_big(struct bt_le_ext_adv ** adv,struct bt_iso_big ** big)658 static int create_big(struct bt_le_ext_adv **adv, struct bt_iso_big **big)
659 {
660 /* Some controllers work best when Extended Advertising interval is a multiple
661 * of the ISO Interval minus 10 ms (max. advertising random delay). This is
662 * required to place the AUX_ADV_IND PDUs in a non-overlapping interval with the
663 * Broadcast ISO radio events.
664 */
665 const uint16_t adv_interval = calc_adv_interval(big_create_param.interval);
666 const uint16_t ext_adv_interval = adv_interval - BT_GAP_MS_TO_ADV_INTERVAL(10U);
667 const struct bt_le_adv_param *ext_adv_param =
668 BT_LE_ADV_PARAM(BT_LE_ADV_OPT_EXT_ADV, ext_adv_interval, ext_adv_interval, NULL);
669 const struct bt_le_per_adv_param *per_adv_param =
670 BT_LE_PER_ADV_PARAM(adv_interval, adv_interval, BT_LE_PER_ADV_OPT_NONE);
671 int err;
672
673 /* Create a non-connectable advertising set */
674 LOG_INF("Creating Extended Advertising set");
675 err = bt_le_ext_adv_create(ext_adv_param, NULL, adv);
676 if (err != 0) {
677 LOG_ERR("Failed to create advertising set (err %d)", err);
678 return err;
679 }
680
681 /* Set advertising data to have complete local name set */
682 err = bt_le_ext_adv_set_data(*adv, ad, ARRAY_SIZE(ad), NULL, 0);
683 if (err) {
684 LOG_ERR("Failed to set advertising data (err %d)", err);
685 return err;
686 }
687
688 LOG_INF("Setting Periodic Advertising parameters");
689 /* Set periodic advertising parameters */
690 err = bt_le_per_adv_set_param(*adv, per_adv_param);
691 if (err != 0) {
692 LOG_ERR("Failed to set periodic advertising parameters (err %d)",
693 err);
694 return err;
695 }
696
697 /* Enable Periodic Advertising */
698 LOG_INF("Starting Periodic Advertising");
699 err = bt_le_per_adv_start(*adv);
700 if (err != 0) {
701 LOG_ERR("Failed to enable periodic advertising (err %d)", err);
702 return err;
703 }
704
705 /* Start extended advertising */
706 LOG_INF("Starting Extended Advertising set");
707 err = bt_le_ext_adv_start(*adv, BT_LE_EXT_ADV_START_DEFAULT);
708 if (err != 0) {
709 LOG_ERR("Failed to start extended advertising (err %d)", err);
710 return err;
711 }
712
713 /* Prepare BIG */
714 for (int i = 0; i < ARRAY_SIZE(bis_iso_chans); i++) {
715 bis_iso_chans[i].ops = &iso_ops;
716 bis_iso_chans[i].qos = &bis_iso_qos;
717 bis[i] = &bis_iso_chans[i];
718 }
719
720 /* Create BIG */
721 LOG_INF("Creating BIG");
722 err = bt_iso_big_create(*adv, &big_create_param, big);
723 if (err != 0) {
724 LOG_ERR("Failed to create BIG (err %d)", err);
725 return err;
726 }
727
728 LOG_INF("Waiting for BIG complete");
729 err = k_sem_take(&sem_big_complete, K_FOREVER);
730 if (err != 0) {
731 LOG_ERR("failed to take sem_big_complete (err %d)", err);
732 return err;
733 }
734 LOG_INF("BIG created");
735
736 return 0;
737 }
738
delete_big(struct bt_le_ext_adv ** adv,struct bt_iso_big ** big)739 static int delete_big(struct bt_le_ext_adv **adv, struct bt_iso_big **big)
740 {
741 int err;
742
743 if (*big != NULL) {
744 err = bt_iso_big_terminate(*big);
745 if (err != 0) {
746 LOG_ERR("Failed to terminate BIG (err %d)", err);
747 return err;
748 }
749 err = k_sem_take(&sem_big_term, K_FOREVER);
750 if (err != 0) {
751 LOG_ERR("failed to take sem_big_term (err %d)", err);
752 return err;
753 }
754 *big = NULL;
755 }
756
757 if (*adv != NULL) {
758 err = bt_le_per_adv_stop(*adv);
759 if (err != 0) {
760 LOG_ERR("Failed to stop periodic advertising (err %d)", err);
761 return err;
762 }
763
764 err = bt_le_ext_adv_stop(*adv);
765 if (err != 0) {
766 LOG_ERR("Failed to stop advertising (err %d)", err);
767 return err;
768 }
769
770 err = bt_le_ext_adv_delete(*adv);
771 if (err != 0) {
772 LOG_ERR("Failed to delete advertiser (err %d)", err);
773 return err;
774 }
775
776 *adv = NULL;
777 }
778
779 return 0;
780 }
781
reset_sems(void)782 static void reset_sems(void)
783 {
784 (void)k_sem_reset(&sem_big_complete);
785 (void)k_sem_reset(&sem_big_term);
786 }
787
test_run_broadcaster(void)788 int test_run_broadcaster(void)
789 {
790 struct bt_le_ext_adv *adv;
791 struct bt_iso_big *big;
792 int err;
793 char c;
794 static bool data_initialized;
795
796 reset_sems();
797
798 printk("Change settings (y/N)? (Current settings: rtn=%u, interval=%u, "
799 "latency=%u, phy=%u, sdu=%u, packing=%u, framing=%u, "
800 "bis_count=%u)\n", iso_tx_qos.rtn, big_create_param.interval,
801 big_create_param.latency, iso_tx_qos.phy, iso_tx_qos.sdu,
802 big_create_param.packing, big_create_param.framing,
803 big_create_param.num_bis);
804
805 c = tolower(console_getchar());
806 if (c == 'y') {
807 err = parse_args();
808 if (err != 0) {
809 LOG_ERR("Could not parse args: %d", err);
810 return err;
811 }
812
813 printk("New settings: rtn=%u, interval=%u, latency=%u, "
814 "phy=%u, sdu=%u, packing=%u, framing=%u, bis_count=%u\n",
815 iso_tx_qos.rtn, big_create_param.interval,
816 big_create_param.latency, iso_tx_qos.phy, iso_tx_qos.sdu,
817 big_create_param.packing, big_create_param.framing,
818 big_create_param.num_bis);
819 }
820
821 err = create_big(&adv, &big);
822 if (err) {
823 int del_err;
824
825 LOG_ERR("Could not create BIG: %d", err);
826
827 del_err = delete_big(&adv, &big);
828 if (del_err) {
829 LOG_ERR("Could not delete BIG: %d", del_err);
830 }
831
832 return err;
833 }
834
835 iso_send_count = 0;
836
837 if (!data_initialized) {
838 /* Init data */
839 for (int i = 0; i < iso_tx_qos.sdu; i++) {
840 iso_data[i] = (uint8_t)i;
841 }
842
843 data_initialized = true;
844 }
845
846 k_work_init_delayable(&iso_send_work, iso_timer_timeout);
847 k_work_schedule(&iso_send_work, K_MSEC(0));
848
849 while (true) {
850 printk("Press 'q' to end the broadcast\n");
851 c = tolower(console_getchar());
852 if (c == 'q') {
853 break;
854 }
855 }
856
857 LOG_INF("Ending broadcast");
858 (void)k_work_cancel_delayable(&iso_send_work);
859
860 err = delete_big(&adv, &big);
861 if (err) {
862 LOG_ERR("Could not delete BIG: %d", err);
863 return err;
864 }
865
866 return 0;
867 }
868