1 /*
2 * Copyright (c) 2021-2024 Nordic Semiconductor ASA
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <zephyr/device.h>
8 #include <zephyr/devicetree.h>
9 #include <zephyr/drivers/gpio.h>
10 #include <zephyr/bluetooth/bluetooth.h>
11 #include <zephyr/bluetooth/conn.h>
12 #include <zephyr/bluetooth/hci_types.h>
13 #include <zephyr/bluetooth/iso.h>
14 #include <zephyr/sys/byteorder.h>
15
16 #define TIMEOUT_SYNC_CREATE K_SECONDS(10)
17 #define NAME_LEN 30
18
19 #define BT_LE_SCAN_CUSTOM BT_LE_SCAN_PARAM(BT_LE_SCAN_TYPE_ACTIVE, \
20 BT_LE_SCAN_OPT_NONE, \
21 BT_GAP_SCAN_FAST_INTERVAL, \
22 BT_GAP_SCAN_FAST_WINDOW)
23
24 #define PA_RETRY_COUNT 6
25
26 #define BIS_ISO_CHAN_COUNT MIN(2U, CONFIG_BT_ISO_MAX_CHAN)
27
28 static bool per_adv_found;
29 static bool per_adv_lost;
30 static bt_addr_le_t per_addr;
31 static uint8_t per_sid;
32 static uint32_t per_interval_us;
33
34 static uint32_t iso_recv_count;
35
36 static K_SEM_DEFINE(sem_per_adv, 0, 1);
37 static K_SEM_DEFINE(sem_per_sync, 0, 1);
38 static K_SEM_DEFINE(sem_per_sync_lost, 0, 1);
39 static K_SEM_DEFINE(sem_per_big_info, 0, 1);
40 static K_SEM_DEFINE(sem_big_sync, 0, BIS_ISO_CHAN_COUNT);
41 static K_SEM_DEFINE(sem_big_sync_lost, 0, BIS_ISO_CHAN_COUNT);
42
43 /* The devicetree node identifier for the "led0" alias. */
44 #define LED0_NODE DT_ALIAS(led0)
45
46 #ifdef CONFIG_ISO_BLINK_LED0
47 static const struct gpio_dt_spec led_gpio = GPIO_DT_SPEC_GET(LED0_NODE, gpios);
48 #define BLINK_ONOFF K_MSEC(500)
49
50 static struct k_work_delayable blink_work;
51 static bool led_is_on;
52 static bool blink;
53
blink_timeout(struct k_work * work)54 static void blink_timeout(struct k_work *work)
55 {
56 if (!blink) {
57 return;
58 }
59
60 led_is_on = !led_is_on;
61 gpio_pin_set_dt(&led_gpio, (int)led_is_on);
62
63 k_work_schedule(&blink_work, BLINK_ONOFF);
64 }
65 #endif
66
data_cb(struct bt_data * data,void * user_data)67 static bool data_cb(struct bt_data *data, void *user_data)
68 {
69 char *name = user_data;
70 uint8_t len;
71
72 switch (data->type) {
73 case BT_DATA_NAME_SHORTENED:
74 case BT_DATA_NAME_COMPLETE:
75 len = MIN(data->data_len, NAME_LEN - 1);
76 memcpy(name, data->data, len);
77 name[len] = '\0';
78 return false;
79 default:
80 return true;
81 }
82 }
83
phy2str(uint8_t phy)84 static const char *phy2str(uint8_t phy)
85 {
86 switch (phy) {
87 case 0: return "No packets";
88 case BT_GAP_LE_PHY_1M: return "LE 1M";
89 case BT_GAP_LE_PHY_2M: return "LE 2M";
90 case BT_GAP_LE_PHY_CODED: return "LE Coded";
91 default: return "Unknown";
92 }
93 }
94
scan_recv(const struct bt_le_scan_recv_info * info,struct net_buf_simple * buf)95 static void scan_recv(const struct bt_le_scan_recv_info *info,
96 struct net_buf_simple *buf)
97 {
98 char le_addr[BT_ADDR_LE_STR_LEN];
99 char name[NAME_LEN];
100
101 (void)memset(name, 0, sizeof(name));
102
103 bt_data_parse(buf, data_cb, name);
104
105 bt_addr_le_to_str(info->addr, le_addr, sizeof(le_addr));
106 printk("[DEVICE]: %s, AD evt type %u, Tx Pwr: %i, RSSI %i %s "
107 "C:%u S:%u D:%u SR:%u E:%u Prim: %s, Secn: %s, "
108 "Interval: 0x%04x (%u us), SID: %u\n",
109 le_addr, info->adv_type, info->tx_power, info->rssi, name,
110 (info->adv_props & BT_GAP_ADV_PROP_CONNECTABLE) != 0,
111 (info->adv_props & BT_GAP_ADV_PROP_SCANNABLE) != 0,
112 (info->adv_props & BT_GAP_ADV_PROP_DIRECTED) != 0,
113 (info->adv_props & BT_GAP_ADV_PROP_SCAN_RESPONSE) != 0,
114 (info->adv_props & BT_GAP_ADV_PROP_EXT_ADV) != 0,
115 phy2str(info->primary_phy), phy2str(info->secondary_phy),
116 info->interval, BT_CONN_INTERVAL_TO_US(info->interval), info->sid);
117
118 if (!per_adv_found && info->interval) {
119 per_adv_found = true;
120
121 per_sid = info->sid;
122 per_interval_us = BT_CONN_INTERVAL_TO_US(info->interval);
123 bt_addr_le_copy(&per_addr, info->addr);
124
125 k_sem_give(&sem_per_adv);
126 }
127 }
128
129 static struct bt_le_scan_cb scan_callbacks = {
130 .recv = scan_recv,
131 };
132
sync_cb(struct bt_le_per_adv_sync * sync,struct bt_le_per_adv_sync_synced_info * info)133 static void sync_cb(struct bt_le_per_adv_sync *sync,
134 struct bt_le_per_adv_sync_synced_info *info)
135 {
136 char le_addr[BT_ADDR_LE_STR_LEN];
137
138 bt_addr_le_to_str(info->addr, le_addr, sizeof(le_addr));
139
140 printk("PER_ADV_SYNC[%u]: [DEVICE]: %s synced, "
141 "Interval 0x%04x (%u ms), PHY %s\n",
142 bt_le_per_adv_sync_get_index(sync), le_addr,
143 info->interval, info->interval * 5 / 4, phy2str(info->phy));
144
145 k_sem_give(&sem_per_sync);
146 }
147
term_cb(struct bt_le_per_adv_sync * sync,const struct bt_le_per_adv_sync_term_info * info)148 static void term_cb(struct bt_le_per_adv_sync *sync,
149 const struct bt_le_per_adv_sync_term_info *info)
150 {
151 char le_addr[BT_ADDR_LE_STR_LEN];
152
153 bt_addr_le_to_str(info->addr, le_addr, sizeof(le_addr));
154
155 printk("PER_ADV_SYNC[%u]: [DEVICE]: %s sync terminated\n",
156 bt_le_per_adv_sync_get_index(sync), le_addr);
157
158 per_adv_lost = true;
159 k_sem_give(&sem_per_sync_lost);
160 }
161
recv_cb(struct bt_le_per_adv_sync * sync,const struct bt_le_per_adv_sync_recv_info * info,struct net_buf_simple * buf)162 static void recv_cb(struct bt_le_per_adv_sync *sync,
163 const struct bt_le_per_adv_sync_recv_info *info,
164 struct net_buf_simple *buf)
165 {
166 char le_addr[BT_ADDR_LE_STR_LEN];
167 char data_str[129];
168
169 bt_addr_le_to_str(info->addr, le_addr, sizeof(le_addr));
170 bin2hex(buf->data, buf->len, data_str, sizeof(data_str));
171
172 printk("PER_ADV_SYNC[%u]: [DEVICE]: %s, tx_power %i, "
173 "RSSI %i, CTE %u, data length %u, data: %s\n",
174 bt_le_per_adv_sync_get_index(sync), le_addr, info->tx_power,
175 info->rssi, info->cte_type, buf->len, data_str);
176 }
177
biginfo_cb(struct bt_le_per_adv_sync * sync,const struct bt_iso_biginfo * biginfo)178 static void biginfo_cb(struct bt_le_per_adv_sync *sync,
179 const struct bt_iso_biginfo *biginfo)
180 {
181 char le_addr[BT_ADDR_LE_STR_LEN];
182
183 bt_addr_le_to_str(biginfo->addr, le_addr, sizeof(le_addr));
184
185 printk("BIG INFO[%u]: [DEVICE]: %s, sid 0x%02x, "
186 "num_bis %u, nse %u, interval 0x%04x (%u ms), "
187 "bn %u, pto %u, irc %u, max_pdu %u, "
188 "sdu_interval %u us, max_sdu %u, phy %s, "
189 "%s framing, %sencrypted\n",
190 bt_le_per_adv_sync_get_index(sync), le_addr, biginfo->sid,
191 biginfo->num_bis, biginfo->sub_evt_count,
192 biginfo->iso_interval,
193 (biginfo->iso_interval * 5 / 4),
194 biginfo->burst_number, biginfo->offset,
195 biginfo->rep_count, biginfo->max_pdu, biginfo->sdu_interval,
196 biginfo->max_sdu, phy2str(biginfo->phy),
197 biginfo->framing ? "with" : "without",
198 biginfo->encryption ? "" : "not ");
199
200
201 k_sem_give(&sem_per_big_info);
202 }
203
204 static struct bt_le_per_adv_sync_cb sync_callbacks = {
205 .synced = sync_cb,
206 .term = term_cb,
207 .recv = recv_cb,
208 .biginfo = biginfo_cb,
209 };
210
iso_recv(struct bt_iso_chan * chan,const struct bt_iso_recv_info * info,struct net_buf * buf)211 static void iso_recv(struct bt_iso_chan *chan, const struct bt_iso_recv_info *info,
212 struct net_buf *buf)
213 {
214 char data_str[128];
215 size_t str_len;
216 uint32_t count = 0; /* only valid if the data is a counter */
217
218 if (buf->len == sizeof(count)) {
219 count = sys_get_le32(buf->data);
220 if (IS_ENABLED(CONFIG_ISO_ALIGN_PRINT_INTERVALS)) {
221 iso_recv_count = count;
222 }
223 }
224
225 if ((iso_recv_count % CONFIG_ISO_PRINT_INTERVAL) == 0) {
226 str_len = bin2hex(buf->data, buf->len, data_str, sizeof(data_str));
227 printk("Incoming data channel %p flags 0x%x seq_num %u ts %u len %u: "
228 "%s (counter value %u)\n", chan, info->flags, info->seq_num,
229 info->ts, buf->len, data_str, count);
230 }
231
232 iso_recv_count++;
233 }
234
iso_connected(struct bt_iso_chan * chan)235 static void iso_connected(struct bt_iso_chan *chan)
236 {
237 const struct bt_iso_chan_path hci_path = {
238 .pid = BT_ISO_DATA_PATH_HCI,
239 .format = BT_HCI_CODING_FORMAT_TRANSPARENT,
240 };
241 int err;
242
243 printk("ISO Channel %p connected\n", chan);
244
245 err = bt_iso_setup_data_path(chan, BT_HCI_DATAPATH_DIR_CTLR_TO_HOST, &hci_path);
246 if (err != 0) {
247 printk("Failed to setup ISO RX data path: %d\n", err);
248 }
249
250 k_sem_give(&sem_big_sync);
251 }
252
iso_disconnected(struct bt_iso_chan * chan,uint8_t reason)253 static void iso_disconnected(struct bt_iso_chan *chan, uint8_t reason)
254 {
255 printk("ISO Channel %p disconnected with reason 0x%02x\n",
256 chan, reason);
257
258 if (reason != BT_HCI_ERR_OP_CANCELLED_BY_HOST) {
259 k_sem_give(&sem_big_sync_lost);
260 }
261 }
262
263 static struct bt_iso_chan_ops iso_ops = {
264 .recv = iso_recv,
265 .connected = iso_connected,
266 .disconnected = iso_disconnected,
267 };
268
269 static struct bt_iso_chan_io_qos iso_rx_qos[BIS_ISO_CHAN_COUNT];
270
271 static struct bt_iso_chan_qos bis_iso_qos[] = {
272 { .rx = &iso_rx_qos[0], },
273 { .rx = &iso_rx_qos[1], },
274 };
275
276 static struct bt_iso_chan bis_iso_chan[] = {
277 { .ops = &iso_ops,
278 .qos = &bis_iso_qos[0], },
279 { .ops = &iso_ops,
280 .qos = &bis_iso_qos[1], },
281 };
282
283 static struct bt_iso_chan *bis[] = {
284 &bis_iso_chan[0],
285 &bis_iso_chan[1],
286 };
287
288 static struct bt_iso_big_sync_param big_sync_param = {
289 .bis_channels = bis,
290 .num_bis = BIS_ISO_CHAN_COUNT,
291 .bis_bitfield = (BIT_MASK(BIS_ISO_CHAN_COUNT)),
292 .mse = BT_ISO_SYNC_MSE_ANY, /* any number of subevents */
293 .sync_timeout = 100, /* in 10 ms units */
294 };
295
reset_semaphores(void)296 static void reset_semaphores(void)
297 {
298 k_sem_reset(&sem_per_adv);
299 k_sem_reset(&sem_per_sync);
300 k_sem_reset(&sem_per_sync_lost);
301 k_sem_reset(&sem_per_big_info);
302 k_sem_reset(&sem_big_sync);
303 k_sem_reset(&sem_big_sync_lost);
304 }
305
main(void)306 int main(void)
307 {
308 struct bt_le_per_adv_sync_param sync_create_param;
309 struct bt_le_per_adv_sync *sync;
310 struct bt_iso_big *big;
311 uint32_t sem_timeout_us;
312 int err;
313
314 iso_recv_count = 0;
315
316 printk("Starting Synchronized Receiver Demo\n");
317
318 #ifdef CONFIG_ISO_BLINK_LED0
319 printk("Get reference to LED device...");
320
321 if (!gpio_is_ready_dt(&led_gpio)) {
322 printk("LED gpio device not ready.\n");
323 return 0;
324 }
325 printk("done.\n");
326
327 printk("Configure GPIO pin...");
328 err = gpio_pin_configure_dt(&led_gpio, GPIO_OUTPUT_ACTIVE);
329 if (err) {
330 return 0;
331 }
332 printk("done.\n");
333
334 k_work_init_delayable(&blink_work, blink_timeout);
335 #endif /* CONFIG_ISO_BLINK_LED0 */
336
337 /* Initialize the Bluetooth Subsystem */
338 err = bt_enable(NULL);
339 if (err) {
340 printk("Bluetooth init failed (err %d)\n", err);
341 return 0;
342 }
343
344 printk("Scan callbacks register...");
345 bt_le_scan_cb_register(&scan_callbacks);
346 printk("success.\n");
347
348 printk("Periodic Advertising callbacks register...");
349 bt_le_per_adv_sync_cb_register(&sync_callbacks);
350 printk("Success.\n");
351
352 do {
353 reset_semaphores();
354 per_adv_lost = false;
355
356 printk("Start scanning...");
357 err = bt_le_scan_start(BT_LE_SCAN_CUSTOM, NULL);
358 if (err) {
359 printk("failed (err %d)\n", err);
360 return 0;
361 }
362 printk("success.\n");
363
364 #ifdef CONFIG_ISO_BLINK_LED0
365 printk("Start blinking LED...\n");
366 led_is_on = false;
367 blink = true;
368 gpio_pin_set_dt(&led_gpio, (int)led_is_on);
369 k_work_reschedule(&blink_work, BLINK_ONOFF);
370 #endif /* CONFIG_ISO_BLINK_LED0 */
371
372 printk("Waiting for periodic advertising...\n");
373 per_adv_found = false;
374 err = k_sem_take(&sem_per_adv, K_FOREVER);
375 if (err) {
376 printk("failed (err %d)\n", err);
377 return 0;
378 }
379 printk("Found periodic advertising.\n");
380
381 printk("Stop scanning...");
382 err = bt_le_scan_stop();
383 if (err) {
384 printk("failed (err %d)\n", err);
385 return 0;
386 }
387 printk("success.\n");
388
389 printk("Creating Periodic Advertising Sync...");
390 bt_addr_le_copy(&sync_create_param.addr, &per_addr);
391 sync_create_param.options = 0;
392 sync_create_param.sid = per_sid;
393 sync_create_param.skip = 0;
394 /* Multiple PA interval with retry count and convert to unit of 10 ms */
395 sync_create_param.timeout = (per_interval_us * PA_RETRY_COUNT) /
396 (10 * USEC_PER_MSEC);
397 sem_timeout_us = per_interval_us * PA_RETRY_COUNT;
398 err = bt_le_per_adv_sync_create(&sync_create_param, &sync);
399 if (err) {
400 printk("failed (err %d)\n", err);
401 return 0;
402 }
403 printk("success.\n");
404
405 printk("Waiting for periodic sync...\n");
406 err = k_sem_take(&sem_per_sync, K_USEC(sem_timeout_us));
407 if (err) {
408 printk("failed (err %d)\n", err);
409
410 printk("Deleting Periodic Advertising Sync...");
411 err = bt_le_per_adv_sync_delete(sync);
412 if (err) {
413 printk("failed (err %d)\n", err);
414 return 0;
415 }
416 continue;
417 }
418 printk("Periodic sync established.\n");
419
420 printk("Waiting for BIG info...\n");
421 err = k_sem_take(&sem_per_big_info, K_USEC(sem_timeout_us));
422 if (err) {
423 printk("failed (err %d)\n", err);
424
425 if (per_adv_lost) {
426 continue;
427 }
428
429 printk("Deleting Periodic Advertising Sync...");
430 err = bt_le_per_adv_sync_delete(sync);
431 if (err) {
432 printk("failed (err %d)\n", err);
433 return 0;
434 }
435 continue;
436 }
437 printk("Periodic sync established.\n");
438
439 big_sync_create:
440 printk("Create BIG Sync...\n");
441 err = bt_iso_big_sync(sync, &big_sync_param, &big);
442 if (err) {
443 printk("failed (err %d)\n", err);
444 return 0;
445 }
446 printk("success.\n");
447
448 for (uint8_t chan = 0U; chan < BIS_ISO_CHAN_COUNT; chan++) {
449 printk("Waiting for BIG sync chan %u...\n", chan);
450 err = k_sem_take(&sem_big_sync, TIMEOUT_SYNC_CREATE);
451 if (err) {
452 break;
453 }
454 printk("BIG sync chan %u successful.\n", chan);
455 }
456 if (err) {
457 printk("failed (err %d)\n", err);
458
459 printk("BIG Sync Terminate...");
460 err = bt_iso_big_terminate(big);
461 if (err) {
462 printk("failed (err %d)\n", err);
463 return 0;
464 }
465 printk("done.\n");
466
467 goto per_sync_lost_check;
468 }
469 printk("BIG sync established.\n");
470
471 #ifdef CONFIG_ISO_BLINK_LED0
472 printk("Stop blinking LED.\n");
473 blink = false;
474 /* If this fails, we'll exit early in the handler because blink
475 * is false.
476 */
477 k_work_cancel_delayable(&blink_work);
478
479 /* Keep LED on */
480 led_is_on = true;
481 gpio_pin_set_dt(&led_gpio, (int)led_is_on);
482 #endif /* CONFIG_ISO_BLINK_LED0 */
483
484 for (uint8_t chan = 0U; chan < BIS_ISO_CHAN_COUNT; chan++) {
485 printk("Waiting for BIG sync lost chan %u...\n", chan);
486 err = k_sem_take(&sem_big_sync_lost, K_FOREVER);
487 if (err) {
488 printk("failed (err %d)\n", err);
489 return 0;
490 }
491 printk("BIG sync lost chan %u.\n", chan);
492 }
493 printk("BIG sync lost.\n");
494
495 per_sync_lost_check:
496 printk("Check for periodic sync lost...\n");
497 err = k_sem_take(&sem_per_sync_lost, K_NO_WAIT);
498 if (err) {
499 /* Periodic Sync active, go back to creating BIG Sync */
500 goto big_sync_create;
501 }
502 printk("Periodic sync lost.\n");
503 } while (true);
504 }
505