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