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