1 /*
2 * Copyright (c) 2024 Demant A/S
3 * Copyright (c) 2024 Nordic Semiconductor ASA
4 *
5 * SPDX-License-Identifier: Apache-2.0
6 */
7
8 #include <errno.h>
9 #include <stddef.h>
10 #include <stdint.h>
11 #include <string.h>
12 #include <strings.h>
13
14 #include <zephyr/autoconf.h>
15 #include <zephyr/bluetooth/addr.h>
16 #include <zephyr/bluetooth/audio/audio.h>
17 #include <zephyr/bluetooth/audio/bap.h>
18 #include <zephyr/bluetooth/bluetooth.h>
19 #include <zephyr/bluetooth/conn.h>
20 #include <zephyr/bluetooth/gap.h>
21 #include <zephyr/bluetooth/hci.h>
22 #include <zephyr/bluetooth/hci_types.h>
23 #include <zephyr/bluetooth/iso.h>
24 #include <zephyr/bluetooth/uuid.h>
25 #include <zephyr/kernel.h>
26 #include <zephyr/net_buf.h>
27 #include <zephyr/sys/byteorder.h>
28 #include <zephyr/sys/printk.h>
29 #include <zephyr/sys/util.h>
30 #include <zephyr/types.h>
31
32 #define NAME_LEN 30
33 #define PA_SYNC_SKIP 5
34 #define PA_SYNC_INTERVAL_TO_TIMEOUT_RATIO 20 /* Set the timeout relative to interval */
35 /* Broadcast IDs are 24bit, so this is out of valid range */
36
37 static void scan_for_broadcast_sink(void);
38
39 /* Struct to collect information from scanning
40 * for Broadcast Source or Sink
41 */
42 struct scan_recv_info {
43 char bt_name[NAME_LEN];
44 char broadcast_name[NAME_LEN];
45 uint32_t broadcast_id;
46 bool has_bass;
47 bool has_pacs;
48 };
49
50 static struct bt_conn *broadcast_sink_conn;
51 static uint32_t selected_broadcast_id;
52 static uint8_t selected_sid;
53 static uint16_t selected_pa_interval;
54 static bt_addr_le_t selected_addr;
55 static struct bt_le_per_adv_sync *pa_sync;
56 static uint8_t received_base[UINT8_MAX];
57 static size_t received_base_size;
58 static struct bt_bap_bass_subgroup
59 bass_subgroups[CONFIG_BT_BAP_BASS_MAX_SUBGROUPS];
60
61 static bool scanning_for_broadcast_source;
62
63 static struct k_mutex base_store_mutex;
64 static K_SEM_DEFINE(sem_source_discovered, 0, 1);
65 static K_SEM_DEFINE(sem_sink_discovered, 0, 1);
66 static K_SEM_DEFINE(sem_sink_connected, 0, 1);
67 static K_SEM_DEFINE(sem_sink_disconnected, 0, 1);
68 static K_SEM_DEFINE(sem_security_updated, 0, 1);
69 static K_SEM_DEFINE(sem_bass_discovered, 0, 1);
70 static K_SEM_DEFINE(sem_pa_synced, 0, 1);
71 static K_SEM_DEFINE(sem_received_base_subgroups, 0, 1);
72
device_found(struct bt_data * data,void * user_data)73 static bool device_found(struct bt_data *data, void *user_data)
74 {
75 struct scan_recv_info *sr_info = (struct scan_recv_info *)user_data;
76 struct bt_uuid_16 adv_uuid;
77
78 switch (data->type) {
79 case BT_DATA_NAME_SHORTENED:
80 case BT_DATA_NAME_COMPLETE:
81 memcpy(sr_info->bt_name, data->data, MIN(data->data_len, NAME_LEN - 1));
82 return true;
83 case BT_DATA_BROADCAST_NAME:
84 memcpy(sr_info->broadcast_name, data->data, MIN(data->data_len, NAME_LEN - 1));
85 return true;
86 case BT_DATA_SVC_DATA16:
87 /* Check for Broadcast ID */
88 if (data->data_len < BT_UUID_SIZE_16 + BT_AUDIO_BROADCAST_ID_SIZE) {
89 return true;
90 }
91
92 if (!bt_uuid_create(&adv_uuid.uuid, data->data, BT_UUID_SIZE_16)) {
93 return true;
94 }
95
96 if (bt_uuid_cmp(&adv_uuid.uuid, BT_UUID_BROADCAST_AUDIO) != 0) {
97 return true;
98 }
99
100 sr_info->broadcast_id = sys_get_le24(data->data + BT_UUID_SIZE_16);
101 return true;
102 case BT_DATA_UUID16_SOME:
103 case BT_DATA_UUID16_ALL:
104 /* NOTE: According to the BAP 1.0.1 Spec,
105 * Section 3.9.2. Additional Broadcast Audio Scan Service requirements,
106 * If the Scan Delegator implements a Broadcast Sink, it should also
107 * advertise a Service Data field containing the Broadcast Audio
108 * Scan Service (BASS) UUID.
109 *
110 * However, it seems that this is not the case with the sinks available
111 * while developing this sample application. Therefore, we instead,
112 * search for the existence of BASS and PACS in the list of service UUIDs,
113 * which does seem to exist in the sinks available.
114 */
115
116 /* Check for BASS and PACS */
117 if (data->data_len % sizeof(uint16_t) != 0U) {
118 printk("UUID16 AD malformed\n");
119 return true;
120 }
121
122 for (size_t i = 0; i < data->data_len; i += sizeof(uint16_t)) {
123 const struct bt_uuid *uuid;
124 uint16_t u16;
125
126 memcpy(&u16, &data->data[i], sizeof(u16));
127 uuid = BT_UUID_DECLARE_16(sys_le16_to_cpu(u16));
128
129 if (bt_uuid_cmp(uuid, BT_UUID_BASS) == 0) {
130 sr_info->has_bass = true;
131 continue;
132 }
133
134 if (bt_uuid_cmp(uuid, BT_UUID_PACS) == 0) {
135 sr_info->has_pacs = true;
136 continue;
137 }
138 }
139 return true;
140 default:
141 return true;
142 }
143 }
144
base_store(struct bt_data * data,void * user_data)145 static bool base_store(struct bt_data *data, void *user_data)
146 {
147 const struct bt_bap_base *base = bt_bap_base_get_base_from_ad(data);
148 int base_size;
149 int base_subgroup_count;
150
151 /* Base is NULL if the data does not contain a valid BASE */
152 if (base == NULL) {
153 return true;
154 }
155
156 /* Can not fit all the received subgroups with the size CONFIG_BT_BAP_BASS_MAX_SUBGROUPS */
157 base_subgroup_count = bt_bap_base_get_subgroup_count(base);
158 if (base_subgroup_count < 0 || base_subgroup_count > CONFIG_BT_BAP_BASS_MAX_SUBGROUPS) {
159 printk("Got invalid subgroup count: %d\n", base_subgroup_count);
160 return true;
161 }
162
163 base_size = bt_bap_base_get_size(base);
164 if (base_size < 0) {
165 printk("BASE get size failed (%d)\n", base_size);
166
167 return true;
168 }
169
170 /* Compare BASE and copy if different */
171 k_mutex_lock(&base_store_mutex, K_FOREVER);
172 if ((size_t)base_size != received_base_size ||
173 memcmp(base, received_base, (size_t)base_size) != 0) {
174 (void)memcpy(received_base, base, base_size);
175 received_base_size = (size_t)base_size;
176 }
177 k_mutex_unlock(&base_store_mutex);
178
179 /* Stop parsing */
180 k_sem_give(&sem_received_base_subgroups);
181 return false;
182 }
183
pa_recv(struct bt_le_per_adv_sync * sync,const struct bt_le_per_adv_sync_recv_info * info,struct net_buf_simple * buf)184 static void pa_recv(struct bt_le_per_adv_sync *sync,
185 const struct bt_le_per_adv_sync_recv_info *info,
186 struct net_buf_simple *buf)
187 {
188 bt_data_parse(buf, base_store, NULL);
189 }
190
add_pa_sync_base_subgroup_bis_cb(const struct bt_bap_base_subgroup_bis * bis,void * user_data)191 static bool add_pa_sync_base_subgroup_bis_cb(const struct bt_bap_base_subgroup_bis *bis,
192 void *user_data)
193 {
194 struct bt_bap_bass_subgroup *subgroup_param = user_data;
195
196 subgroup_param->bis_sync |= BT_ISO_BIS_INDEX_BIT(bis->index);
197
198 return true;
199 }
200
add_pa_sync_base_subgroup_cb(const struct bt_bap_base_subgroup * subgroup,void * user_data)201 static bool add_pa_sync_base_subgroup_cb(const struct bt_bap_base_subgroup *subgroup,
202 void *user_data)
203 {
204 struct bt_bap_broadcast_assistant_add_src_param *param = user_data;
205 struct bt_bap_bass_subgroup *subgroup_param;
206 uint8_t *data;
207 int ret;
208
209 ret = bt_bap_base_get_subgroup_codec_meta(subgroup, &data);
210 if (ret < 0) {
211 return false;
212 }
213
214 subgroup_param = param->subgroups;
215
216 if (ret > ARRAY_SIZE(subgroup_param->metadata)) {
217 printk("Cannot fit %d octets into subgroup param with size %zu", ret,
218 ARRAY_SIZE(subgroup_param->metadata));
219 return false;
220 }
221
222 ret = bt_bap_base_subgroup_foreach_bis(subgroup, add_pa_sync_base_subgroup_bis_cb,
223 subgroup_param);
224 if (ret < 0) {
225 return false;
226 }
227
228 param->num_subgroups++;
229
230 return true;
231 }
232
is_substring(const char * substr,const char * str)233 static bool is_substring(const char *substr, const char *str)
234 {
235 const size_t str_len = strlen(str);
236 const size_t sub_str_len = strlen(substr);
237
238 if (sub_str_len > str_len) {
239 return false;
240 }
241
242 for (size_t pos = 0; pos < str_len; pos++) {
243 if (pos + sub_str_len > str_len) {
244 return false;
245 }
246
247 if (strncasecmp(substr, &str[pos], sub_str_len) == 0) {
248 return true;
249 }
250 }
251
252 return false;
253 }
254
interval_to_sync_timeout(uint16_t pa_interval)255 static uint16_t interval_to_sync_timeout(uint16_t pa_interval)
256 {
257 uint16_t pa_timeout;
258
259 if (pa_interval == BT_BAP_PA_INTERVAL_UNKNOWN) {
260 /* Use maximum value to maximize chance of success */
261 pa_timeout = BT_GAP_PER_ADV_MAX_TIMEOUT;
262 } else {
263 uint32_t interval_us;
264 uint32_t timeout;
265
266 /* Add retries and convert to unit in 10's of ms */
267 interval_us = BT_GAP_PER_ADV_INTERVAL_TO_US(pa_interval);
268 timeout = BT_GAP_US_TO_PER_ADV_SYNC_TIMEOUT(interval_us) *
269 PA_SYNC_INTERVAL_TO_TIMEOUT_RATIO;
270
271 /* Enforce restraints */
272 pa_timeout = CLAMP(timeout, BT_GAP_PER_ADV_MIN_TIMEOUT, BT_GAP_PER_ADV_MAX_TIMEOUT);
273 }
274
275 return pa_timeout;
276 }
277
pa_sync_create(void)278 static int pa_sync_create(void)
279 {
280 struct bt_le_per_adv_sync_param create_params = {0};
281
282 bt_addr_le_copy(&create_params.addr, &selected_addr);
283 create_params.options = BT_LE_PER_ADV_SYNC_OPT_FILTER_DUPLICATE;
284 create_params.sid = selected_sid;
285 create_params.skip = PA_SYNC_SKIP;
286 create_params.timeout = interval_to_sync_timeout(selected_pa_interval);
287
288 return bt_le_per_adv_sync_create(&create_params, &pa_sync);
289 }
290
scan_recv_cb(const struct bt_le_scan_recv_info * info,struct net_buf_simple * ad)291 static void scan_recv_cb(const struct bt_le_scan_recv_info *info,
292 struct net_buf_simple *ad)
293 {
294 int err;
295 struct scan_recv_info sr_info = {0};
296
297 if (scanning_for_broadcast_source) {
298 /* Scan for and select Broadcast Source */
299
300 sr_info.broadcast_id = BT_BAP_INVALID_BROADCAST_ID;
301
302 /* We are only interested in non-connectable periodic advertisers */
303 if ((info->adv_props & BT_GAP_ADV_PROP_CONNECTABLE) != 0 ||
304 info->interval == 0) {
305 return;
306 }
307
308 bt_data_parse(ad, device_found, (void *)&sr_info);
309
310 if (sr_info.broadcast_id != BT_BAP_INVALID_BROADCAST_ID) {
311 printk("Broadcast Source Found:\n");
312 printk(" BT Name: %s\n", sr_info.bt_name);
313 printk(" Broadcast Name: %s\n", sr_info.broadcast_name);
314 printk(" Broadcast ID: 0x%06x\n\n", sr_info.broadcast_id);
315
316 #if defined(CONFIG_SELECT_SOURCE_NAME)
317 if (strlen(CONFIG_SELECT_SOURCE_NAME) > 0U) {
318 /* Compare names with CONFIG_SELECT_SOURCE_NAME */
319 if (is_substring(CONFIG_SELECT_SOURCE_NAME, sr_info.bt_name) ||
320 is_substring(CONFIG_SELECT_SOURCE_NAME,
321 sr_info.broadcast_name)) {
322 printk("Match found for '%s'\n", CONFIG_SELECT_SOURCE_NAME);
323 } else {
324 printk("'%s' not found in names\n\n",
325 CONFIG_SELECT_SOURCE_NAME);
326 return;
327 }
328 }
329 #endif /* CONFIG_SELECT_SOURCE_NAME */
330
331 err = bt_le_scan_stop();
332 if (err != 0) {
333 printk("bt_le_scan_stop failed with %d\n", err);
334 }
335
336 /* TODO: Add support for syncing to the PA and parsing the BASE
337 * in order to obtain the right subgroup information to send to
338 * the sink when adding a broadcast source (see in main function below).
339 */
340
341 printk("Selecting Broadcast ID: 0x%06x\n", sr_info.broadcast_id);
342
343 selected_broadcast_id = sr_info.broadcast_id;
344 selected_sid = info->sid;
345 selected_pa_interval = info->interval;
346 bt_addr_le_copy(&selected_addr, info->addr);
347
348 k_sem_give(&sem_source_discovered);
349
350 printk("Attempting to PA sync to the broadcaster with id 0x%06X\n",
351 selected_broadcast_id);
352 }
353 } else {
354 /* Scan for and connect to Broadcast Sink */
355
356 /* We are only interested in connectable advertisers */
357 if ((info->adv_props & BT_GAP_ADV_PROP_CONNECTABLE) == 0) {
358 return;
359 }
360
361 bt_data_parse(ad, device_found, (void *)&sr_info);
362
363 if (sr_info.has_bass && sr_info.has_pacs) {
364 printk("Broadcast Sink Found:\n");
365 printk(" BT Name: %s\n", sr_info.bt_name);
366
367 if (strlen(CONFIG_SELECT_SINK_NAME) > 0U) {
368 /* Compare names with CONFIG_SELECT_SINK_NAME */
369 if (is_substring(CONFIG_SELECT_SINK_NAME, sr_info.bt_name)) {
370 printk("Match found for '%s'\n", CONFIG_SELECT_SINK_NAME);
371 } else {
372 printk("'%s' not found in names\n\n",
373 CONFIG_SELECT_SINK_NAME);
374 return;
375 }
376 }
377
378 err = bt_le_scan_stop();
379 if (err != 0) {
380 printk("bt_le_scan_stop failed with %d\n", err);
381 }
382
383 printk("Connecting to Broadcast Sink: %s\n", sr_info.bt_name);
384
385 err = bt_conn_le_create(info->addr, BT_CONN_LE_CREATE_CONN,
386 BT_LE_CONN_PARAM_DEFAULT,
387 &broadcast_sink_conn);
388 if (err != 0) {
389 printk("Failed creating connection (err=%u)\n", err);
390 scan_for_broadcast_sink();
391 }
392
393 k_sem_give(&sem_sink_discovered);
394 }
395 }
396 }
397
scan_timeout_cb(void)398 static void scan_timeout_cb(void)
399 {
400 printk("Scan timeout\n");
401 }
402
403 static struct bt_le_scan_cb scan_callbacks = {
404 .recv = scan_recv_cb,
405 .timeout = scan_timeout_cb,
406 };
407
scan_for_broadcast_source(void)408 static void scan_for_broadcast_source(void)
409 {
410 int err;
411
412 scanning_for_broadcast_source = true;
413
414 err = bt_le_scan_start(BT_LE_SCAN_PASSIVE, NULL);
415 if (err) {
416 printk("Scanning failed to start (err %d)\n", err);
417 return;
418 }
419
420 printk("Scanning for Broadcast Source successfully started\n");
421
422 err = k_sem_take(&sem_source_discovered, K_FOREVER);
423 if (err != 0) {
424 printk("Failed to take sem_source_discovered (err %d)\n", err);
425 }
426 }
427
scan_for_broadcast_sink(void)428 static void scan_for_broadcast_sink(void)
429 {
430 int err;
431
432 scanning_for_broadcast_source = false;
433
434 err = bt_le_scan_start(BT_LE_SCAN_PASSIVE, NULL);
435 if (err) {
436 printk("Scanning failed to start (err %d)\n", err);
437 return;
438 }
439
440 printk("Scanning for Broadcast Sink successfully started\n");
441
442 err = k_sem_take(&sem_sink_discovered, K_FOREVER);
443 if (err != 0) {
444 printk("Failed to take sem_sink_discovered (err %d)\n", err);
445 }
446 }
447
connected(struct bt_conn * conn,uint8_t err)448 static void connected(struct bt_conn *conn, uint8_t err)
449 {
450 char addr[BT_ADDR_LE_STR_LEN];
451
452 (void)bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
453
454 if (err != 0) {
455 printk("Failed to connect to %s %u %s\n", addr, err, bt_hci_err_to_str(err));
456
457 bt_conn_unref(broadcast_sink_conn);
458 broadcast_sink_conn = NULL;
459
460 scan_for_broadcast_sink();
461 return;
462 }
463
464 if (conn != broadcast_sink_conn) {
465 return;
466 }
467
468 printk("Connected: %s\n", addr);
469 k_sem_give(&sem_sink_connected);
470 }
471
disconnected(struct bt_conn * conn,uint8_t reason)472 static void disconnected(struct bt_conn *conn, uint8_t reason)
473 {
474 char addr[BT_ADDR_LE_STR_LEN];
475
476 if (conn != broadcast_sink_conn) {
477 return;
478 }
479
480 (void)bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
481
482 printk("Disconnected: %s, reason 0x%02x %s\n", addr, reason, bt_hci_err_to_str(reason));
483
484 bt_conn_unref(broadcast_sink_conn);
485 broadcast_sink_conn = NULL;
486
487 k_sem_give(&sem_sink_disconnected);
488 }
489
security_changed_cb(struct bt_conn * conn,bt_security_t level,enum bt_security_err err)490 static void security_changed_cb(struct bt_conn *conn, bt_security_t level,
491 enum bt_security_err err)
492 {
493 if (err == 0) {
494 printk("Security level changed: %u\n", level);
495 k_sem_give(&sem_security_updated);
496 } else {
497 printk("Failed to set security level: %s(%u)\n", bt_security_err_to_str(err), err);
498 }
499 }
500
bap_broadcast_assistant_discover_cb(struct bt_conn * conn,int err,uint8_t recv_state_count)501 static void bap_broadcast_assistant_discover_cb(struct bt_conn *conn, int err,
502 uint8_t recv_state_count)
503 {
504 if (err == 0) {
505 printk("BASS discover done with %u recv states\n",
506 recv_state_count);
507 k_sem_give(&sem_bass_discovered);
508 } else {
509 printk("BASS discover failed (%d)\n", err);
510 }
511 }
512
bap_broadcast_assistant_add_src_cb(struct bt_conn * conn,int err)513 static void bap_broadcast_assistant_add_src_cb(struct bt_conn *conn, int err)
514 {
515 if (err == 0) {
516 printk("BASS add source successful\n");
517 } else {
518 printk("BASS add source failed (%d)\n", err);
519 }
520 }
521
pa_sync_synced_cb(struct bt_le_per_adv_sync * sync,struct bt_le_per_adv_sync_synced_info * info)522 static void pa_sync_synced_cb(struct bt_le_per_adv_sync *sync,
523 struct bt_le_per_adv_sync_synced_info *info)
524 {
525 if (sync == pa_sync) {
526 printk("PA sync %p synced for broadcast sink with broadcast ID 0x%06X\n", sync,
527 selected_broadcast_id);
528
529 k_sem_give(&sem_pa_synced);
530 }
531 }
532
533 static struct bt_bap_broadcast_assistant_cb ba_cbs = {
534 .discover = bap_broadcast_assistant_discover_cb,
535 .add_src = bap_broadcast_assistant_add_src_cb,
536 };
537
538 static struct bt_le_per_adv_sync_cb pa_synced_cb = {
539 .synced = pa_sync_synced_cb,
540 .recv = pa_recv,
541 };
542
reset(void)543 static void reset(void)
544 {
545 printk("\n\nReset...\n\n");
546
547 broadcast_sink_conn = NULL;
548 selected_broadcast_id = BT_BAP_INVALID_BROADCAST_ID;
549 selected_sid = 0;
550 selected_pa_interval = 0;
551 (void)memset(&selected_addr, 0, sizeof(selected_addr));
552
553 k_sem_reset(&sem_source_discovered);
554 k_sem_reset(&sem_sink_discovered);
555 k_sem_reset(&sem_sink_connected);
556 k_sem_reset(&sem_sink_disconnected);
557 k_sem_reset(&sem_security_updated);
558 k_sem_reset(&sem_bass_discovered);
559 k_sem_reset(&sem_pa_synced);
560 k_sem_reset(&sem_received_base_subgroups);
561 }
562
563 BT_CONN_CB_DEFINE(conn_callbacks) = {
564 .connected = connected,
565 .disconnected = disconnected,
566 .security_changed = security_changed_cb
567 };
568
main(void)569 int main(void)
570 {
571 int err;
572 struct bt_bap_broadcast_assistant_add_src_param param = { 0 };
573
574 err = bt_enable(NULL);
575 if (err) {
576 printk("Bluetooth init failed (err %d)\n", err);
577 return 0;
578 }
579
580 printk("Bluetooth initialized\n");
581
582 bt_bap_broadcast_assistant_register_cb(&ba_cbs);
583 bt_le_per_adv_sync_cb_register(&pa_synced_cb);
584 bt_le_scan_cb_register(&scan_callbacks);
585
586 k_mutex_init(&base_store_mutex);
587
588 while (true) {
589 scan_for_broadcast_sink();
590
591 err = k_sem_take(&sem_sink_connected, K_FOREVER);
592 if (err != 0) {
593 printk("Failed to take sem_sink_connected (err %d)\n", err);
594 }
595
596 err = bt_bap_broadcast_assistant_discover(broadcast_sink_conn);
597 if (err != 0) {
598 printk("Failed to discover BASS on the sink (err %d)\n", err);
599 }
600
601 err = k_sem_take(&sem_security_updated, K_SECONDS(10));
602 if (err != 0) {
603 printk("Failed to take sem_security_updated (err %d), resetting\n", err);
604 bt_conn_disconnect(broadcast_sink_conn, BT_HCI_ERR_AUTH_FAIL);
605
606 if (k_sem_take(&sem_sink_disconnected, K_SECONDS(10)) != 0) {
607 /* This should not happen */
608 return -ETIMEDOUT;
609 }
610
611 reset();
612 continue;
613 }
614
615 err = k_sem_take(&sem_bass_discovered, K_SECONDS(10));
616 if (err != 0) {
617 if (err == -EAGAIN) {
618 printk("Failed to take sem_bass_discovered (err %d)\n", err);
619 }
620 bt_conn_disconnect(broadcast_sink_conn, BT_HCI_ERR_UNSUPP_REMOTE_FEATURE);
621
622 if (k_sem_take(&sem_sink_disconnected, K_SECONDS(10)) != 0) {
623 /* This should not happen */
624 return -ETIMEDOUT;
625 }
626
627 reset();
628 continue;
629 }
630
631 /* TODO: Discover and parse the PACS on the sink and use the information
632 * when discovering and adding a source to the sink.
633 * Also, before populating the parameters to sync to the broadcast source
634 * first, parse the source BASE and determine if the sink supports the source.
635 * If not, then look for another source.
636 */
637
638 scan_for_broadcast_source();
639
640 printk("Scan stopped, attempting to PA sync to the broadcaster with id 0x%06X\n",
641 selected_broadcast_id);
642 err = pa_sync_create();
643 if (err != 0) {
644 printk("Could not create Broadcast PA sync: %d, resetting\n", err);
645 return -ETIMEDOUT;
646 }
647
648 printk("Waiting for PA synced\n");
649 err = k_sem_take(&sem_pa_synced, K_FOREVER);
650 if (err != 0) {
651 printk("sem_pa_synced timed out, resetting\n");
652 return err;
653 }
654
655 memset(bass_subgroups, 0, sizeof(bass_subgroups));
656 bt_addr_le_copy(¶m.addr, &selected_addr);
657 param.adv_sid = selected_sid;
658 param.pa_interval = selected_pa_interval;
659 param.broadcast_id = selected_broadcast_id;
660 param.pa_sync = true;
661 param.subgroups = bass_subgroups;
662
663 /* Wait to receive subgroups */
664 err = k_sem_take(&sem_received_base_subgroups, K_FOREVER);
665 if (err != 0) {
666 printk("Failed to take sem_received_base_subgroups (err %d)\n", err);
667 return err;
668 }
669
670 k_mutex_lock(&base_store_mutex, K_FOREVER);
671 err = bt_bap_base_foreach_subgroup((const struct bt_bap_base *)received_base,
672 add_pa_sync_base_subgroup_cb, ¶m);
673 k_mutex_unlock(&base_store_mutex);
674
675 if (err < 0) {
676 printk("Could not add BASE to params %d\n", err);
677 continue;
678 }
679
680 err = bt_bap_broadcast_assistant_add_src(broadcast_sink_conn, ¶m);
681 if (err) {
682 printk("Failed to add source: %d\n", err);
683 bt_conn_disconnect(broadcast_sink_conn, BT_HCI_ERR_UNSUPP_REMOTE_FEATURE);
684
685 if (k_sem_take(&sem_sink_disconnected, K_SECONDS(10)) != 0) {
686 /* This should not happen */
687 return -ETIMEDOUT;
688 }
689
690 reset();
691 continue;
692 }
693
694 /* Reset if the sink disconnects */
695 err = k_sem_take(&sem_sink_disconnected, K_FOREVER);
696 if (err != 0) {
697 printk("Failed to take sem_sink_disconnected (err %d)\n", err);
698 }
699
700 reset();
701 }
702
703 return 0;
704 }
705