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