1 /*
2 * Copyright (c) 2021 Nordic Semiconductor ASA
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <ctype.h>
8 #include <stdlib.h>
9 #include <zephyr/console/console.h>
10 #include <zephyr/bluetooth/bluetooth.h>
11 #include <zephyr/bluetooth/iso.h>
12 #include <zephyr/sys/byteorder.h>
13
14 #include <zephyr/logging/log.h>
15 LOG_MODULE_REGISTER(iso_broadcast_broadcaster, LOG_LEVEL_DBG);
16
17 #define DEFAULT_BIS_RTN 2
18 #define DEFAULT_BIS_INTERVAL_US 7500
19 #define DEFAULT_BIS_LATENCY_MS 10
20 #define DEFAULT_BIS_PHY BT_GAP_LE_PHY_2M
21 #define DEFAULT_BIS_SDU CONFIG_BT_ISO_TX_MTU
22 #define DEFAULT_BIS_PACKING 0
23 #define DEFAULT_BIS_FRAMING 0
24 #define DEFAULT_BIS_COUNT CONFIG_BT_ISO_MAX_CHAN
25
26 NET_BUF_POOL_FIXED_DEFINE(bis_tx_pool, CONFIG_BT_ISO_TX_BUF_COUNT,
27 BT_ISO_SDU_BUF_SIZE(CONFIG_BT_ISO_TX_MTU),
28 CONFIG_BT_CONN_TX_USER_DATA_SIZE, NULL);
29
30 static K_SEM_DEFINE(sem_big_complete, 0, 1);
31 static K_SEM_DEFINE(sem_big_term, 0, 1);
32 static struct k_work_delayable iso_send_work;
33 static uint32_t iso_send_count;
34 static uint8_t iso_data[CONFIG_BT_ISO_TX_MTU];
35 static uint8_t connected_bis;
36
37 static struct bt_iso_chan bis_iso_chans[CONFIG_BT_ISO_MAX_CHAN];
38 static struct bt_iso_chan *bis[CONFIG_BT_ISO_MAX_CHAN];
39 /* We use a single seq_num for all the BIS as they share the same SDU interval */
40 static uint16_t seq_num;
41 static struct bt_iso_big_create_param big_create_param = {
42 .num_bis = DEFAULT_BIS_COUNT,
43 .bis_channels = bis,
44 .packing = DEFAULT_BIS_PACKING, /* 0 - sequential, 1 - interleaved */
45 .framing = DEFAULT_BIS_FRAMING, /* 0 - unframed, 1 - framed */
46 .interval = DEFAULT_BIS_INTERVAL_US, /* in microseconds */
47 .latency = DEFAULT_BIS_LATENCY_MS, /* milliseconds */
48 };
49
iso_connected(struct bt_iso_chan * chan)50 static void iso_connected(struct bt_iso_chan *chan)
51 {
52 LOG_INF("ISO Channel %p connected", chan);
53
54 connected_bis++;
55 if (connected_bis == big_create_param.num_bis) {
56 seq_num = 0U;
57 k_sem_give(&sem_big_complete);
58 }
59 }
60
iso_disconnected(struct bt_iso_chan * chan,uint8_t reason)61 static void iso_disconnected(struct bt_iso_chan *chan, uint8_t reason)
62 {
63 LOG_INF("ISO Channel %p disconnected with reason 0x%02x",
64 chan, reason);
65
66 connected_bis--;
67 if (connected_bis == big_create_param.num_bis) {
68 k_sem_give(&sem_big_term);
69 }
70 }
71
72 static struct bt_iso_chan_ops iso_ops = {
73 .connected = iso_connected,
74 .disconnected = iso_disconnected,
75 };
76
77 static struct bt_iso_chan_io_qos iso_tx_qos = {
78 .sdu = DEFAULT_BIS_SDU, /* bytes */
79 .rtn = DEFAULT_BIS_RTN,
80 .phy = DEFAULT_BIS_PHY,
81 };
82
83 static struct bt_iso_chan_qos bis_iso_qos = {
84 .tx = &iso_tx_qos,
85 };
86
get_chars(char * buffer,size_t max_size)87 static size_t get_chars(char *buffer, size_t max_size)
88 {
89 size_t pos = 0;
90
91 while (pos < max_size) {
92 char c = tolower(console_getchar());
93
94 if (c == '\n' || c == '\r') {
95 break;
96 }
97 printk("%c", c);
98 buffer[pos++] = c;
99 }
100 printk("\n");
101 buffer[pos] = '\0';
102
103 return pos;
104 }
105
parse_rtn_arg(void)106 static int parse_rtn_arg(void)
107 {
108 char buffer[4];
109 size_t char_count;
110 uint64_t rtn;
111
112 printk("Set RTN (current %u, default %u)\n",
113 iso_tx_qos.rtn, DEFAULT_BIS_RTN);
114
115 char_count = get_chars(buffer, sizeof(buffer) - 1);
116 if (char_count == 0) {
117 return DEFAULT_BIS_RTN;
118 }
119
120 rtn = strtoul(buffer, NULL, 0);
121 if (rtn > BT_ISO_BROADCAST_RTN_MAX) {
122 printk("Invalid RTN %llu", rtn);
123 return -EINVAL;
124 }
125
126 return (int)rtn;
127 }
128
parse_interval_arg(void)129 static int parse_interval_arg(void)
130 {
131 char buffer[9];
132 size_t char_count;
133 uint64_t interval;
134
135 printk("Set interval (us) (current %u, default %u)\n",
136 big_create_param.interval, DEFAULT_BIS_INTERVAL_US);
137
138 char_count = get_chars(buffer, sizeof(buffer) - 1);
139 if (char_count == 0) {
140 return DEFAULT_BIS_INTERVAL_US;
141 }
142
143 interval = strtoul(buffer, NULL, 0);
144 if (interval < BT_ISO_SDU_INTERVAL_MIN || interval > BT_ISO_SDU_INTERVAL_MAX) {
145 printk("Invalid interval %llu", interval);
146 return -EINVAL;
147 }
148
149 return (int)interval;
150 }
151
parse_latency_arg(void)152 static int parse_latency_arg(void)
153 {
154 char buffer[6];
155 size_t char_count;
156 uint64_t latency;
157
158 printk("Set latency (ms) (current %u, default %u)\n",
159 big_create_param.latency, DEFAULT_BIS_LATENCY_MS);
160
161 char_count = get_chars(buffer, sizeof(buffer) - 1);
162 if (char_count == 0) {
163 return DEFAULT_BIS_LATENCY_MS;
164 }
165
166 latency = strtoul(buffer, NULL, 0);
167 if (latency < BT_ISO_LATENCY_MIN || latency > BT_ISO_LATENCY_MAX) {
168 printk("Invalid latency %llu", latency);
169 return -EINVAL;
170 }
171
172 return (int)latency;
173 }
174
parse_phy_arg(void)175 static int parse_phy_arg(void)
176 {
177 char buffer[3];
178 size_t char_count;
179 uint64_t phy;
180
181 printk("Set PHY (current %u, default %u) - %u = 1M, %u = 2M, %u = Coded\n",
182 iso_tx_qos.phy, DEFAULT_BIS_PHY, BT_GAP_LE_PHY_1M,
183 BT_GAP_LE_PHY_2M, BT_GAP_LE_PHY_CODED);
184
185 char_count = get_chars(buffer, sizeof(buffer) - 1);
186 if (char_count == 0) {
187 return DEFAULT_BIS_PHY;
188 }
189
190 phy = strtoul(buffer, NULL, 0);
191 if (phy != BT_GAP_LE_PHY_1M &&
192 phy != BT_GAP_LE_PHY_2M &&
193 phy != BT_GAP_LE_PHY_CODED) {
194 printk("Invalid PHY %llu", phy);
195 return -EINVAL;
196 }
197
198 return (int)phy;
199 }
200
parse_sdu_arg(void)201 static int parse_sdu_arg(void)
202 {
203 char buffer[6];
204 size_t char_count;
205 uint64_t sdu;
206
207 printk("Set SDU (current %u, default %u)\n",
208 iso_tx_qos.sdu, DEFAULT_BIS_SDU);
209
210 char_count = get_chars(buffer, sizeof(buffer) - 1);
211 if (char_count == 0) {
212 return DEFAULT_BIS_SDU;
213 }
214
215 sdu = strtoul(buffer, NULL, 0);
216 if (sdu > MIN(BT_ISO_MAX_SDU, sizeof(iso_data))) {
217 printk("Invalid SDU %llu", sdu);
218 return -EINVAL;
219 }
220
221 return (int)sdu;
222 }
223
parse_packing_arg(void)224 static int parse_packing_arg(void)
225 {
226 char buffer[3];
227 size_t char_count;
228 uint64_t packing;
229
230 printk("Set packing (current %u, default %u)\n",
231 big_create_param.packing, DEFAULT_BIS_PACKING);
232
233 char_count = get_chars(buffer, sizeof(buffer) - 1);
234 if (char_count == 0) {
235 return DEFAULT_BIS_PACKING;
236 }
237
238 packing = strtoul(buffer, NULL, 0);
239 if (packing != BT_ISO_PACKING_SEQUENTIAL &&
240 packing != BT_ISO_PACKING_INTERLEAVED) {
241 printk("Invalid packing %llu", packing);
242 return -EINVAL;
243 }
244
245 return (int)packing;
246 }
247
parse_framing_arg(void)248 static int parse_framing_arg(void)
249 {
250 char buffer[3];
251 size_t char_count;
252 uint64_t framing;
253
254 printk("Set framing (current %u, default %u)\n",
255 big_create_param.framing, DEFAULT_BIS_FRAMING);
256
257 char_count = get_chars(buffer, sizeof(buffer) - 1);
258 if (char_count == 0) {
259 return DEFAULT_BIS_FRAMING;
260 }
261
262 framing = strtoul(buffer, NULL, 0);
263 if (framing != BT_ISO_FRAMING_UNFRAMED &&
264 framing != BT_ISO_FRAMING_FRAMED) {
265 printk("Invalid framing %llu", framing);
266 return -EINVAL;
267 }
268
269 return (int)framing;
270 }
271
parse_bis_count_arg(void)272 static int parse_bis_count_arg(void)
273 {
274 char buffer[4];
275 size_t char_count;
276 uint64_t bis_count;
277
278 printk("Set BIS count (current %u, default %u)\n",
279 big_create_param.num_bis, DEFAULT_BIS_COUNT);
280
281 char_count = get_chars(buffer, sizeof(buffer) - 1);
282 if (char_count == 0) {
283 return DEFAULT_BIS_COUNT;
284 }
285
286 bis_count = strtoul(buffer, NULL, 0);
287 if (bis_count > MAX(BT_ISO_MAX_GROUP_ISO_COUNT, CONFIG_BT_ISO_MAX_CHAN)) {
288 printk("Invalid BIS count %llu", bis_count);
289 return -EINVAL;
290 }
291
292 return (int)bis_count;
293 }
294
parse_args(void)295 static int parse_args(void)
296 {
297 int rtn;
298 int interval;
299 int latency;
300 int phy;
301 int sdu;
302 int packing;
303 int framing;
304 int bis_count;
305
306 printk("Follow the prompts. Press enter to use default values.\n");
307
308 rtn = parse_rtn_arg();
309 if (rtn < 0) {
310 return -EINVAL;
311 }
312
313 interval = parse_interval_arg();
314 if (interval < 0) {
315 return -EINVAL;
316 }
317
318 latency = parse_latency_arg();
319 if (latency < 0) {
320 return -EINVAL;
321 }
322
323 phy = parse_phy_arg();
324 if (phy < 0) {
325 return -EINVAL;
326 }
327
328 sdu = parse_sdu_arg();
329 if (sdu < 0) {
330 return -EINVAL;
331 }
332
333 packing = parse_packing_arg();
334 if (packing < 0) {
335 return -EINVAL;
336 }
337
338 framing = parse_framing_arg();
339 if (framing < 0) {
340 return -EINVAL;
341 }
342
343 bis_count = parse_bis_count_arg();
344 if (bis_count < 0) {
345 return -EINVAL;
346 }
347
348 iso_tx_qos.rtn = rtn;
349 iso_tx_qos.phy = phy;
350 iso_tx_qos.sdu = sdu;
351 big_create_param.interval = interval;
352 big_create_param.latency = latency;
353 big_create_param.packing = packing;
354 big_create_param.framing = framing;
355 big_create_param.num_bis = bis_count;
356
357 return 0;
358 }
359
iso_timer_timeout(struct k_work * work)360 static void iso_timer_timeout(struct k_work *work)
361 {
362 int ret;
363 struct net_buf *buf;
364
365 /* Reschedule as early as possible to reduce time skewing
366 * Use the ISO interval minus a few microseconds to keep the buffer
367 * full. This might occasionally skip a transmit, i.e. where the host
368 * calls `bt_iso_chan_send` but the controller only sending a single
369 * ISO packet.
370 */
371 k_work_reschedule(&iso_send_work, K_USEC(big_create_param.interval - 100));
372
373 for (int i = 0; i < big_create_param.num_bis; i++) {
374 buf = net_buf_alloc(&bis_tx_pool, K_FOREVER);
375 if (buf == NULL) {
376 LOG_ERR("Could not allocate buffer");
377 return;
378 }
379
380 net_buf_reserve(buf, BT_ISO_CHAN_SEND_RESERVE);
381 net_buf_add_mem(buf, iso_data, iso_tx_qos.sdu);
382 ret = bt_iso_chan_send(&bis_iso_chans[i], buf, seq_num,
383 BT_ISO_TIMESTAMP_NONE);
384 if (ret < 0) {
385 LOG_ERR("Unable to broadcast data: %d", ret);
386 net_buf_unref(buf);
387 return;
388 }
389 iso_send_count++;
390
391 if ((iso_send_count % 100) == 0) {
392 LOG_INF("Sent %u packets", iso_send_count);
393 }
394 }
395
396 seq_num++;
397 }
398
create_big(struct bt_le_ext_adv ** adv,struct bt_iso_big ** big)399 static int create_big(struct bt_le_ext_adv **adv, struct bt_iso_big **big)
400 {
401 int err;
402
403 /* Create a non-connectable non-scannable advertising set */
404 LOG_INF("Creating Extended Advertising set");
405 err = bt_le_ext_adv_create(BT_LE_EXT_ADV_NCONN_NAME, NULL, adv);
406 if (err != 0) {
407 LOG_ERR("Failed to create advertising set (err %d)", err);
408 return err;
409 }
410
411 LOG_INF("Setting Periodic Advertising parameters");
412 /* Set periodic advertising parameters */
413 err = bt_le_per_adv_set_param(*adv, BT_LE_PER_ADV_DEFAULT);
414 if (err != 0) {
415 LOG_ERR("Failed to set periodic advertising parameters (err %d)",
416 err);
417 return err;
418 }
419
420 /* Enable Periodic Advertising */
421 LOG_INF("Starting Periodic Advertising");
422 err = bt_le_per_adv_start(*adv);
423 if (err != 0) {
424 LOG_ERR("Failed to enable periodic advertising (err %d)", err);
425 return err;
426 }
427
428 /* Start extended advertising */
429 LOG_INF("Starting Extended Advertising set");
430 err = bt_le_ext_adv_start(*adv, BT_LE_EXT_ADV_START_DEFAULT);
431 if (err != 0) {
432 LOG_ERR("Failed to start extended advertising (err %d)", err);
433 return err;
434 }
435
436 /* Prepare BIG */
437 for (int i = 0; i < ARRAY_SIZE(bis_iso_chans); i++) {
438 bis_iso_chans[i].ops = &iso_ops;
439 bis_iso_chans[i].qos = &bis_iso_qos;
440 bis[i] = &bis_iso_chans[i];
441 }
442
443 /* Create BIG */
444 LOG_INF("Creating BIG");
445 err = bt_iso_big_create(*adv, &big_create_param, big);
446 if (err != 0) {
447 LOG_ERR("Failed to create BIG (err %d)", err);
448 return err;
449 }
450
451 LOG_INF("Waiting for BIG complete");
452 err = k_sem_take(&sem_big_complete, K_FOREVER);
453 if (err != 0) {
454 LOG_ERR("failed to take sem_big_complete (err %d)", err);
455 return err;
456 }
457 LOG_INF("BIG created");
458
459 return 0;
460 }
461
delete_big(struct bt_le_ext_adv ** adv,struct bt_iso_big ** big)462 static int delete_big(struct bt_le_ext_adv **adv, struct bt_iso_big **big)
463 {
464 int err;
465
466 err = bt_iso_big_terminate(*big);
467 if (err != 0) {
468 LOG_ERR("Failed to terminate BIG (err %d)", err);
469 return err;
470 }
471 *big = NULL;
472
473 err = bt_le_per_adv_stop(*adv);
474 if (err != 0) {
475 LOG_ERR("Failed to stop periodic advertising (err %d)", err);
476 return err;
477 }
478
479 err = bt_le_ext_adv_stop(*adv);
480 if (err != 0) {
481 LOG_ERR("Failed to stop advertising (err %d)", err);
482 return err;
483 }
484
485 err = bt_le_ext_adv_delete(*adv);
486 if (err != 0) {
487 LOG_ERR("Failed to delete advertiser (err %d)", err);
488 return err;
489 }
490
491 *adv = NULL;
492
493 return 0;
494 }
495
reset_sems(void)496 static void reset_sems(void)
497 {
498 (void)k_sem_reset(&sem_big_complete);
499 (void)k_sem_reset(&sem_big_term);
500 }
501
test_run_broadcaster(void)502 int test_run_broadcaster(void)
503 {
504 struct bt_le_ext_adv *adv;
505 struct bt_iso_big *big;
506 int err;
507 char c;
508 static bool data_initialized;
509
510 reset_sems();
511
512 printk("Change settings (y/N)? (Current settings: rtn=%u, interval=%u, "
513 "latency=%u, phy=%u, sdu=%u, packing=%u, framing=%u, "
514 "bis_count=%u)\n", iso_tx_qos.rtn, big_create_param.interval,
515 big_create_param.latency, iso_tx_qos.phy, iso_tx_qos.sdu,
516 big_create_param.packing, big_create_param.framing,
517 big_create_param.num_bis);
518
519 c = tolower(console_getchar());
520 if (c == 'y') {
521 err = parse_args();
522 if (err != 0) {
523 LOG_ERR("Could not parse args: %d", err);
524 return err;
525 }
526
527 printk("New settings: rtn=%u, interval=%u, latency=%u, "
528 "phy=%u, sdu=%u, packing=%u, framing=%u, bis_count=%u\n",
529 iso_tx_qos.rtn, big_create_param.interval,
530 big_create_param.latency, iso_tx_qos.phy, iso_tx_qos.sdu,
531 big_create_param.packing, big_create_param.framing,
532 big_create_param.num_bis);
533 }
534
535 err = create_big(&adv, &big);
536 if (err) {
537 LOG_ERR("Could not create BIG: %d", err);
538 return err;
539 }
540
541 iso_send_count = 0;
542
543 if (!data_initialized) {
544 /* Init data */
545 for (int i = 0; i < iso_tx_qos.sdu; i++) {
546 iso_data[i] = (uint8_t)i;
547 }
548
549 data_initialized = true;
550 }
551
552 k_work_init_delayable(&iso_send_work, iso_timer_timeout);
553 k_work_schedule(&iso_send_work, K_MSEC(0));
554
555 while (true) {
556 printk("Press 'q' to end the broadcast\n");
557 c = tolower(console_getchar());
558 if (c == 'q') {
559 break;
560 }
561 }
562
563 LOG_INF("Ending broadcast");
564 (void)k_work_cancel_delayable(&iso_send_work);
565
566 err = delete_big(&adv, &big);
567 if (err) {
568 LOG_ERR("Could not delete BIG: %d", err);
569 return err;
570 }
571
572 return 0;
573 }
574