1 /**
2  * @file
3  * @brief Shell APIs for Bluetooth BASS client
4  *
5  * Copyright (c) 2020-2023 Nordic Semiconductor ASA
6  *
7  * SPDX-License-Identifier: Apache-2.0
8  */
9 
10 #include <errno.h>
11 #include <stdbool.h>
12 #include <stddef.h>
13 #include <stdint.h>
14 #include <string.h>
15 
16 #include <zephyr/autoconf.h>
17 #include <zephyr/bluetooth/audio/audio.h>
18 #include <zephyr/bluetooth/audio/bap.h>
19 #include <zephyr/bluetooth/addr.h>
20 #include <zephyr/bluetooth/bluetooth.h>
21 #include <zephyr/bluetooth/gap.h>
22 #include <zephyr/bluetooth/gatt.h>
23 #include <zephyr/bluetooth/hci.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/shell/shell.h>
29 #include <zephyr/shell/shell_string_conv.h>
30 #include <zephyr/sys/byteorder.h>
31 #include <zephyr/sys/util.h>
32 #include <zephyr/sys/util_macro.h>
33 #include <zephyr/types.h>
34 
35 #include "common/bt_shell_private.h"
36 #include "host/shell/bt.h"
37 #include "host/hci_core.h"
38 #include "audio.h"
39 
40 static uint8_t received_base[UINT8_MAX];
41 static size_t received_base_size;
42 
43 static struct bt_auto_scan {
44 	uint32_t broadcast_id;
45 	char broadcast_name[BT_AUDIO_BROADCAST_NAME_LEN_MAX + 1];
46 	bool pa_sync;
47 	struct bt_bap_bass_subgroup subgroup;
48 } auto_scan = {
49 	.broadcast_id = BT_BAP_INVALID_BROADCAST_ID,
50 };
51 
52 struct bt_scan_recv_info {
53 	uint32_t broadcast_id;
54 	char broadcast_name[BT_AUDIO_BROADCAST_NAME_LEN_MAX + 1];
55 };
56 
pa_decode_base(struct bt_data * data,void * user_data)57 static bool pa_decode_base(struct bt_data *data, void *user_data)
58 {
59 	const struct bt_bap_base *base = bt_bap_base_get_base_from_ad(data);
60 	int base_size;
61 
62 	/* Base is NULL if the data does not contain a valid BASE */
63 	if (base == NULL) {
64 		return true;
65 	}
66 
67 	base_size = bt_bap_base_get_size(base);
68 	if (base_size < 0) {
69 		bt_shell_error("BASE get size failed (%d)", base_size);
70 
71 		return true;
72 	}
73 
74 	/* Compare BASE and print if different */
75 	if ((size_t)base_size != received_base_size ||
76 	    memcmp(base, received_base, (size_t)base_size) != 0) {
77 		(void)memcpy(received_base, base, base_size);
78 		received_base_size = (size_t)base_size;
79 
80 		print_base((const struct bt_bap_base *)received_base);
81 	}
82 
83 	return false;
84 }
85 
pa_recv(struct bt_le_per_adv_sync * sync,const struct bt_le_per_adv_sync_recv_info * info,struct net_buf_simple * buf)86 static void pa_recv(struct bt_le_per_adv_sync *sync,
87 		    const struct bt_le_per_adv_sync_recv_info *info,
88 		    struct net_buf_simple *buf)
89 {
90 	bt_data_parse(buf, pa_decode_base, NULL);
91 }
92 
bap_broadcast_assistant_discover_cb(struct bt_conn * conn,int err,uint8_t recv_state_count)93 static void bap_broadcast_assistant_discover_cb(struct bt_conn *conn, int err,
94 						uint8_t recv_state_count)
95 {
96 	if (err != 0) {
97 		bt_shell_error("BASS discover failed (%d)", err);
98 	} else {
99 		bt_shell_print("BASS discover done with %u recv states", recv_state_count);
100 	}
101 }
102 
bap_broadcast_assistant_scan_cb(const struct bt_le_scan_recv_info * info,uint32_t broadcast_id)103 static void bap_broadcast_assistant_scan_cb(const struct bt_le_scan_recv_info *info,
104 					    uint32_t broadcast_id)
105 {
106 	char le_addr[BT_ADDR_LE_STR_LEN];
107 
108 	bt_addr_le_to_str(info->addr, le_addr, sizeof(le_addr));
109 	bt_shell_print(
110 		"[DEVICE]: %s, broadcast_id 0x%06X, interval (ms) %u (0x%04x)), SID 0x%x, RSSI %i",
111 		le_addr, broadcast_id, BT_GAP_PER_ADV_INTERVAL_TO_MS(info->interval),
112 		info->interval, info->sid, info->rssi);
113 }
114 
metadata_entry(struct bt_data * data,void * user_data)115 static bool metadata_entry(struct bt_data *data, void *user_data)
116 {
117 	char metadata[512];
118 
119 	bin2hex(data->data, data->data_len, metadata, sizeof(metadata));
120 
121 	bt_shell_print("\t\tMetadata length %u, type %u, data: %s",
122 		       data->data_len, data->type, metadata);
123 
124 	return true;
125 }
126 
bap_broadcast_assistant_recv_state_cb(struct bt_conn * conn,int err,const struct bt_bap_scan_delegator_recv_state * state)127 static void bap_broadcast_assistant_recv_state_cb(
128 	struct bt_conn *conn, int err,
129 	const struct bt_bap_scan_delegator_recv_state *state)
130 {
131 	char le_addr[BT_ADDR_LE_STR_LEN];
132 	char bad_code[33];
133 	bool is_bad_code;
134 
135 	if (err != 0) {
136 		bt_shell_error("BASS recv state read failed (%d)", err);
137 		return;
138 	}
139 
140 	bt_addr_le_to_str(&state->addr, le_addr, sizeof(le_addr));
141 	bin2hex(state->bad_code, BT_ISO_BROADCAST_CODE_SIZE, bad_code, sizeof(bad_code));
142 
143 	is_bad_code = state->encrypt_state == BT_BAP_BIG_ENC_STATE_BAD_CODE;
144 	bt_shell_print(
145 		"BASS recv state: src_id %u, addr %s, sid %u, broadcast_id 0x%06X, sync_state "
146 		"%u, encrypt_state %u%s%s",
147 		state->src_id, le_addr, state->adv_sid, state->broadcast_id,
148 		state->pa_sync_state, state->encrypt_state, is_bad_code ? ", bad code" : "",
149 		is_bad_code ? bad_code : "");
150 
151 	for (int i = 0; i < state->num_subgroups; i++) {
152 		const struct bt_bap_bass_subgroup *subgroup = &state->subgroups[i];
153 		struct net_buf_simple buf;
154 
155 		bt_shell_print("\t[%d]: BIS sync 0x%04X, metadata_len %zu", i,
156 			       subgroup->bis_sync, subgroup->metadata_len);
157 
158 		net_buf_simple_init_with_data(&buf, (void *)subgroup->metadata,
159 					      subgroup->metadata_len);
160 		bt_data_parse(&buf, metadata_entry, NULL);
161 	}
162 
163 	if (state->pa_sync_state == BT_BAP_PA_STATE_INFO_REQ) {
164 		struct bt_le_per_adv_sync *per_adv_sync = NULL;
165 		struct bt_le_ext_adv *ext_adv = NULL;
166 
167 		/* Lookup matching PA sync */
168 		for (size_t i = 0U; i < ARRAY_SIZE(per_adv_syncs); i++) {
169 			if (per_adv_syncs[i] != NULL &&
170 			    bt_addr_le_eq(&per_adv_syncs[i]->addr, &state->addr)) {
171 				per_adv_sync = per_adv_syncs[i];
172 				bt_shell_print("Found matching PA sync [%zu]", i);
173 				break;
174 			}
175 		}
176 
177 		if (per_adv_sync && IS_ENABLED(CONFIG_BT_PER_ADV_SYNC_TRANSFER_SENDER)) {
178 			bt_shell_print("Sending PAST");
179 
180 			err = bt_le_per_adv_sync_transfer(per_adv_sync,
181 							  conn,
182 							  BT_UUID_BASS_VAL);
183 
184 			if (err != 0) {
185 				bt_shell_error("Could not transfer periodic adv sync: %d", err);
186 			}
187 
188 			return;
189 		}
190 
191 		/* If no PA sync was found, check for local advertisers */
192 		for (int i = 0; i < ARRAY_SIZE(adv_sets); i++) {
193 			struct bt_le_oob oob_local;
194 
195 			if (adv_sets[i] == NULL) {
196 				continue;
197 			}
198 
199 			err = bt_le_ext_adv_oob_get_local(adv_sets[i],
200 							  &oob_local);
201 
202 			if (err != 0) {
203 				bt_shell_error("Could not get local OOB %d", err);
204 				return;
205 			}
206 
207 			if (bt_addr_le_eq(&oob_local.addr, &state->addr)) {
208 				ext_adv = adv_sets[i];
209 				break;
210 			}
211 		}
212 
213 		if (ext_adv != NULL && IS_ENABLED(CONFIG_BT_PER_ADV) &&
214 		    IS_ENABLED(CONFIG_BT_PER_ADV_SYNC_TRANSFER_SENDER)) {
215 			bt_shell_print("Sending local PAST");
216 
217 			err = bt_le_per_adv_set_info_transfer(ext_adv, conn,
218 							      BT_UUID_BASS_VAL);
219 
220 			if (err != 0) {
221 				bt_shell_error("Could not transfer per adv set info: %d", err);
222 			}
223 		} else {
224 			bt_shell_error("Could not send PA to Scan Delegator");
225 		}
226 	}
227 }
228 
bap_broadcast_assistant_recv_state_removed_cb(struct bt_conn * conn,uint8_t src_id)229 static void bap_broadcast_assistant_recv_state_removed_cb(struct bt_conn *conn, uint8_t src_id)
230 {
231 	bt_shell_print("BASS recv state %u removed", src_id);
232 }
233 
bap_broadcast_assistant_scan_start_cb(struct bt_conn * conn,int err)234 static void bap_broadcast_assistant_scan_start_cb(struct bt_conn *conn, int err)
235 {
236 	if (err != 0) {
237 		bt_shell_error("BASS scan start failed (%d)", err);
238 	} else {
239 		bt_shell_print("BASS scan start successful");
240 	}
241 }
242 
bap_broadcast_assistant_scan_stop_cb(struct bt_conn * conn,int err)243 static void bap_broadcast_assistant_scan_stop_cb(struct bt_conn *conn, int err)
244 {
245 	if (err != 0) {
246 		bt_shell_error("BASS scan stop failed (%d)", err);
247 	} else {
248 		bt_shell_print("BASS scan stop successful");
249 	}
250 }
251 
bap_broadcast_assistant_add_src_cb(struct bt_conn * conn,int err)252 static void bap_broadcast_assistant_add_src_cb(struct bt_conn *conn, int err)
253 {
254 	if (err != 0) {
255 		bt_shell_error("BASS add source failed (%d)", err);
256 	} else {
257 		bt_shell_print("BASS add source successful");
258 	}
259 }
260 
bap_broadcast_assistant_mod_src_cb(struct bt_conn * conn,int err)261 static void bap_broadcast_assistant_mod_src_cb(struct bt_conn *conn, int err)
262 {
263 	if (err != 0) {
264 		bt_shell_error("BASS modify source failed (%d)", err);
265 	} else {
266 		bt_shell_print("BASS modify source successful");
267 	}
268 }
269 
bap_broadcast_assistant_broadcast_code_cb(struct bt_conn * conn,int err)270 static void bap_broadcast_assistant_broadcast_code_cb(struct bt_conn *conn,
271 						      int err)
272 {
273 	if (err != 0) {
274 		bt_shell_error("BASS broadcast code failed (%d)", err);
275 	} else {
276 		bt_shell_print("BASS broadcast code successful");
277 	}
278 }
279 
bap_broadcast_assistant_rem_src_cb(struct bt_conn * conn,int err)280 static void bap_broadcast_assistant_rem_src_cb(struct bt_conn *conn, int err)
281 {
282 	if (err != 0) {
283 		bt_shell_error("BASS remove source failed (%d)", err);
284 	} else {
285 		bt_shell_print("BASS remove source successful");
286 	}
287 }
288 
289 static struct bt_bap_broadcast_assistant_cb cbs = {
290 	.discover = bap_broadcast_assistant_discover_cb,
291 	.scan = bap_broadcast_assistant_scan_cb,
292 	.recv_state = bap_broadcast_assistant_recv_state_cb,
293 	.recv_state_removed = bap_broadcast_assistant_recv_state_removed_cb,
294 	.scan_start = bap_broadcast_assistant_scan_start_cb,
295 	.scan_stop = bap_broadcast_assistant_scan_stop_cb,
296 	.add_src = bap_broadcast_assistant_add_src_cb,
297 	.mod_src = bap_broadcast_assistant_mod_src_cb,
298 	.broadcast_code = bap_broadcast_assistant_broadcast_code_cb,
299 	.rem_src = bap_broadcast_assistant_rem_src_cb,
300 };
301 
cmd_bap_broadcast_assistant_scan_start(const struct shell * sh,size_t argc,char ** argv)302 static int cmd_bap_broadcast_assistant_scan_start(const struct shell *sh,
303 						  size_t argc, char **argv)
304 {
305 	int result;
306 	int start_scan = false;
307 
308 	if (argc > 1) {
309 		result = 0;
310 
311 		start_scan = shell_strtobool(argv[1], 0, &result);
312 		if (result != 0) {
313 			shell_error(sh, "Could not parse start_scan: %d",
314 				    result);
315 
316 			return -ENOEXEC;
317 		}
318 	}
319 
320 	result = bt_bap_broadcast_assistant_scan_start(default_conn,
321 						       (bool)start_scan);
322 	if (result) {
323 		shell_print(sh, "Fail: %d", result);
324 	}
325 
326 	return result;
327 }
328 
cmd_bap_broadcast_assistant_scan_stop(const struct shell * sh,size_t argc,char ** argv)329 static int cmd_bap_broadcast_assistant_scan_stop(const struct shell *sh,
330 						 size_t argc, char **argv)
331 {
332 	int result;
333 
334 	result = bt_bap_broadcast_assistant_scan_stop(default_conn);
335 	if (result) {
336 		shell_print(sh, "Fail: %d", result);
337 	}
338 
339 	return result;
340 }
341 
cmd_bap_broadcast_assistant_add_src(const struct shell * sh,size_t argc,char ** argv)342 static int cmd_bap_broadcast_assistant_add_src(const struct shell *sh,
343 					       size_t argc, char **argv)
344 {
345 	struct bt_bap_broadcast_assistant_add_src_param param = { 0 };
346 	struct bt_bap_bass_subgroup subgroup = { 0 };
347 	unsigned long broadcast_id;
348 	unsigned long adv_sid;
349 	int result;
350 
351 	result = bt_addr_le_from_str(argv[1], argv[2], &param.addr);
352 	if (result) {
353 		shell_error(sh, "Invalid peer address (err %d)", result);
354 
355 		return -ENOEXEC;
356 	}
357 
358 	adv_sid = shell_strtoul(argv[3], 0, &result);
359 	if (result != 0) {
360 		shell_error(sh, "Could not parse adv_sid: %d", result);
361 
362 		return -ENOEXEC;
363 	}
364 
365 	if (adv_sid > BT_GAP_SID_MAX) {
366 		shell_error(sh, "Invalid adv_sid: %lu", adv_sid);
367 
368 		return -ENOEXEC;
369 	}
370 
371 	param.adv_sid = adv_sid;
372 
373 	param.pa_sync = shell_strtobool(argv[4], 0, &result);
374 	if (result != 0) {
375 		shell_error(sh, "Could not parse adv_sid: %d", result);
376 
377 		return -ENOEXEC;
378 	}
379 
380 	broadcast_id = shell_strtoul(argv[5], 0, &result);
381 	if (result != 0) {
382 		shell_error(sh, "Could not parse broadcast_id: %d", result);
383 
384 		return -ENOEXEC;
385 	}
386 
387 	if (broadcast_id > BT_AUDIO_BROADCAST_ID_MAX) {
388 		shell_error(sh, "Invalid broadcast_id: %lu", broadcast_id);
389 
390 		return -ENOEXEC;
391 	}
392 
393 	param.broadcast_id = broadcast_id;
394 
395 	if (argc > 6) {
396 		unsigned long pa_interval;
397 
398 		pa_interval = shell_strtoul(argv[6], 0, &result);
399 		if (result) {
400 			shell_error(sh, "Could not parse pa_interval: %d",
401 				    result);
402 
403 			return -ENOEXEC;
404 		}
405 
406 		if (!IN_RANGE(pa_interval,
407 			      BT_GAP_PER_ADV_MIN_INTERVAL,
408 			      BT_GAP_PER_ADV_MAX_INTERVAL)) {
409 			shell_error(sh, "Invalid pa_interval: %lu",
410 				    pa_interval);
411 
412 			return -ENOEXEC;
413 		}
414 
415 		param.pa_interval = pa_interval;
416 	} else {
417 		param.pa_interval = BT_BAP_PA_INTERVAL_UNKNOWN;
418 	}
419 
420 	/* TODO: Support multiple subgroups */
421 	if (argc > 7) {
422 		unsigned long bis_sync;
423 
424 		bis_sync = shell_strtoul(argv[7], 0, &result);
425 		if (result) {
426 			shell_error(sh, "Could not parse bis_sync: %d", result);
427 
428 			return -ENOEXEC;
429 		}
430 
431 		if (!BT_BAP_BASS_VALID_BIT_BITFIELD(bis_sync)) {
432 			shell_error(sh, "Invalid bis_sync: %lu", bis_sync);
433 
434 			return -ENOEXEC;
435 		}
436 
437 		subgroup.bis_sync = bis_sync;
438 	}
439 
440 	if (argc > 8) {
441 		size_t metadata_len;
442 
443 		metadata_len = hex2bin(argv[8], strlen(argv[8]),
444 				       subgroup.metadata,
445 				       sizeof(subgroup.metadata));
446 
447 		if (metadata_len == 0U) {
448 			shell_error(sh, "Could not parse metadata");
449 
450 			return -ENOEXEC;
451 		}
452 
453 		/* sizeof(subgroup.metadata) can always fit in uint8_t */
454 
455 		subgroup.metadata_len = metadata_len;
456 	}
457 
458 	param.num_subgroups = 1;
459 	param.subgroups = &subgroup;
460 
461 	result = bt_bap_broadcast_assistant_add_src(default_conn, &param);
462 	if (result) {
463 		shell_print(sh, "Fail: %d", result);
464 	}
465 
466 	return result;
467 }
468 
broadcast_source_found(struct bt_data * data,void * user_data)469 static bool broadcast_source_found(struct bt_data *data, void *user_data)
470 {
471 	struct bt_scan_recv_info *sr_info = (struct bt_scan_recv_info *)user_data;
472 	struct bt_uuid_16 adv_uuid;
473 
474 	switch (data->type) {
475 	case BT_DATA_SVC_DATA16:
476 		if (data->data_len < BT_UUID_SIZE_16 + BT_AUDIO_BROADCAST_ID_SIZE) {
477 			return true;
478 		}
479 
480 		if (!bt_uuid_create(&adv_uuid.uuid, data->data, BT_UUID_SIZE_16)) {
481 			return true;
482 		}
483 
484 		if (bt_uuid_cmp(&adv_uuid.uuid, BT_UUID_BROADCAST_AUDIO) != 0) {
485 			return true;
486 		}
487 
488 		sr_info->broadcast_id = sys_get_le24(data->data + BT_UUID_SIZE_16);
489 		return true;
490 	case BT_DATA_BROADCAST_NAME:
491 		if (!IN_RANGE(data->data_len, BT_AUDIO_BROADCAST_NAME_LEN_MIN,
492 		    BT_AUDIO_BROADCAST_NAME_LEN_MAX)) {
493 			return true;
494 		}
495 
496 		utf8_lcpy(sr_info->broadcast_name, data->data, (data->data_len) + 1);
497 		return true;
498 	default:
499 		return true;
500 	}
501 }
502 
scan_recv_cb(const struct bt_le_scan_recv_info * info,struct net_buf_simple * ad)503 static void scan_recv_cb(const struct bt_le_scan_recv_info *info,
504 			 struct net_buf_simple *ad)
505 {
506 	struct bt_scan_recv_info sr_info = { 0 };
507 	struct bt_bap_broadcast_assistant_add_src_param param = { 0 };
508 	int err;
509 
510 	sr_info.broadcast_id = BT_BAP_INVALID_BROADCAST_ID;
511 
512 	if ((auto_scan.broadcast_id == BT_BAP_INVALID_BROADCAST_ID) &&
513 	    (strlen(auto_scan.broadcast_name) == 0U)) {
514 		/* no op */
515 		return;
516 	}
517 
518 	/* We are only interested in non-connectable periodic advertisers */
519 	if ((info->adv_props & BT_GAP_ADV_PROP_CONNECTABLE) != 0 ||
520 	    info->interval == 0) {
521 		return;
522 	}
523 
524 	if (!passes_scan_filter(info, ad)) {
525 		return;
526 	}
527 
528 	bt_data_parse(ad, broadcast_source_found, (void *)&sr_info);
529 
530 	/* Verify that it is a BAP broadcaster*/
531 	if (sr_info.broadcast_id != BT_BAP_INVALID_BROADCAST_ID) {
532 		char addr_str[BT_ADDR_LE_STR_LEN];
533 		bool identified_broadcast = false;
534 
535 		bt_addr_le_to_str(info->addr, addr_str, sizeof(addr_str));
536 
537 		if (sr_info.broadcast_id == auto_scan.broadcast_id) {
538 			identified_broadcast = true;
539 		}
540 
541 		if ((strlen(auto_scan.broadcast_name) != 0U) &&
542 		    is_substring(auto_scan.broadcast_name, sr_info.broadcast_name)) {
543 			identified_broadcast = true;
544 
545 			bt_shell_print("Found matched broadcast name '%s' with address %s",
546 				       sr_info.broadcast_name, addr_str);
547 		}
548 
549 		if (identified_broadcast) {
550 			bt_shell_print(
551 				"Found BAP broadcast source with address %s and ID 0x%06X\n",
552 				addr_str, sr_info.broadcast_id);
553 
554 			err = bt_le_scan_stop();
555 			if (err) {
556 				bt_shell_error("Failed to stop scan: %d", err);
557 			}
558 
559 			bt_addr_le_copy(&param.addr, info->addr);
560 			param.adv_sid = info->sid;
561 			param.pa_interval = info->interval;
562 			param.broadcast_id = sr_info.broadcast_id;
563 			param.pa_sync = auto_scan.pa_sync;
564 			param.num_subgroups = 1;
565 			param.subgroups = &auto_scan.subgroup;
566 
567 			err = bt_bap_broadcast_assistant_add_src(default_conn, &param);
568 			if (err) {
569 				bt_shell_print("Failed to add source: %d", err);
570 			}
571 
572 			memset(&auto_scan, 0, sizeof(auto_scan));
573 			auto_scan.broadcast_id = BT_BAP_INVALID_BROADCAST_ID;
574 		}
575 	}
576 }
577 
scan_timeout_cb(void)578 static void scan_timeout_cb(void)
579 {
580 	bt_shell_print("Scan timeout");
581 
582 	memset(&auto_scan, 0, sizeof(auto_scan));
583 	auto_scan.broadcast_id = BT_BAP_INVALID_BROADCAST_ID;
584 }
585 
586 static struct bt_le_scan_cb scan_callbacks = {
587 	.recv = scan_recv_cb,
588 	.timeout = scan_timeout_cb,
589 };
590 
cmd_bap_broadcast_assistant_discover(const struct shell * sh,size_t argc,char ** argv)591 static int cmd_bap_broadcast_assistant_discover(const struct shell *sh,
592 						size_t argc, char **argv)
593 {
594 	static bool registered;
595 	int result;
596 
597 	if (!registered) {
598 		static struct bt_le_per_adv_sync_cb cb = {
599 			.recv = pa_recv,
600 		};
601 
602 		bt_le_per_adv_sync_cb_register(&cb);
603 
604 		bt_bap_broadcast_assistant_register_cb(&cbs);
605 
606 		bt_le_scan_cb_register(&scan_callbacks);
607 
608 		registered = true;
609 	}
610 
611 	result = bt_bap_broadcast_assistant_discover(default_conn);
612 	if (result) {
613 		shell_print(sh, "Fail: %d", result);
614 	}
615 
616 	return result;
617 }
618 
cmd_bap_broadcast_assistant_add_broadcast_id(const struct shell * sh,size_t argc,char ** argv)619 static int cmd_bap_broadcast_assistant_add_broadcast_id(const struct shell *sh,
620 							size_t argc,
621 							char **argv)
622 {
623 	struct bt_bap_bass_subgroup subgroup = { 0 };
624 	unsigned long broadcast_id;
625 	int err = 0;
626 
627 	if (auto_scan.broadcast_id != BT_BAP_INVALID_BROADCAST_ID) {
628 		shell_info(sh, "Already scanning, wait for sync or timeout");
629 
630 		return -ENOEXEC;
631 	}
632 
633 	broadcast_id = shell_strtoul(argv[1], 0, &err);
634 	if (err != 0) {
635 		shell_error(sh, "failed to parse broadcast_id: %d", err);
636 
637 		return -ENOEXEC;
638 	} else if (broadcast_id > 0xFFFFFF /* 24 bits */) {
639 		shell_error(sh, "Broadcast ID maximum 24 bits (was %lu)", broadcast_id);
640 
641 		return -ENOEXEC;
642 	}
643 
644 	auto_scan.pa_sync = shell_strtobool(argv[2], 0, &err);
645 	if (err != 0) {
646 		shell_error(sh, "Could not parse pa_sync: %d", err);
647 
648 		return -ENOEXEC;
649 	}
650 
651 	/* TODO: Support multiple subgroups */
652 	if (argc > 3) {
653 		const unsigned long bis_sync = shell_strtoul(argv[3], 0, &err);
654 
655 		if (err != 0) {
656 			shell_error(sh, "failed to parse bis_sync: %d", err);
657 
658 			return -ENOEXEC;
659 		} else if (!BT_BAP_BASS_VALID_BIT_BITFIELD(bis_sync)) {
660 			shell_error(sh, "Invalid bis_sync: %lu", bis_sync);
661 
662 			return -ENOEXEC;
663 		}
664 
665 		subgroup.bis_sync = bis_sync;
666 	}
667 
668 	if (argc > 4) {
669 		subgroup.metadata_len = hex2bin(argv[4], strlen(argv[4]), subgroup.metadata,
670 						sizeof(subgroup.metadata));
671 
672 		if (subgroup.metadata_len == 0U) {
673 			shell_error(sh, "Could not parse metadata");
674 
675 			return -ENOEXEC;
676 		}
677 	}
678 
679 	err = bt_le_scan_start(BT_LE_SCAN_PASSIVE, NULL);
680 	if (err) {
681 		shell_print(sh, "Fail to start scanning: %d", err);
682 
683 		return -ENOEXEC;
684 	}
685 
686 	/* Store results in the `auto_scan` struct */
687 	auto_scan.broadcast_id = broadcast_id;
688 	memcpy(&auto_scan.subgroup, &subgroup, sizeof(subgroup));
689 	memset(auto_scan.broadcast_name, 0, sizeof(auto_scan.broadcast_name));
690 
691 	return 0;
692 }
693 
cmd_bap_broadcast_assistant_add_broadcast_name(const struct shell * sh,size_t argc,char ** argv)694 static int cmd_bap_broadcast_assistant_add_broadcast_name(const struct shell *sh,
695 							  size_t argc, char **argv)
696 {
697 	struct bt_bap_bass_subgroup subgroup = { 0 };
698 	char *broadcast_name;
699 	int err = 0;
700 
701 	broadcast_name = argv[1];
702 	if (!IN_RANGE(strlen(broadcast_name), BT_AUDIO_BROADCAST_NAME_LEN_MIN,
703 	    BT_AUDIO_BROADCAST_NAME_LEN_MAX)) {
704 
705 		shell_error(sh, "Broadcast name should be minimum %d "
706 			    "and maximum %d characters", BT_AUDIO_BROADCAST_NAME_LEN_MIN,
707 			    BT_AUDIO_BROADCAST_NAME_LEN_MAX);
708 
709 		return -ENOEXEC;
710 	}
711 
712 	auto_scan.pa_sync = shell_strtobool(argv[2], 0, &err);
713 	if (err != 0) {
714 		shell_error(sh, "Could not parse pa_sync: %d", err);
715 
716 		return -ENOEXEC;
717 	}
718 
719 	/* TODO: Support multiple subgroups */
720 	if (argc > 3) {
721 		const unsigned long bis_sync = shell_strtoul(argv[3], 0, &err);
722 
723 		if (err != 0) {
724 			shell_error(sh, "failed to parse bis_sync: %d", err);
725 
726 			return -ENOEXEC;
727 		} else if (!BT_BAP_BASS_VALID_BIT_BITFIELD(bis_sync)) {
728 			shell_error(sh, "Invalid bis_sync: %lu", bis_sync);
729 
730 			return -ENOEXEC;
731 		}
732 
733 		subgroup.bis_sync = bis_sync;
734 	}
735 
736 	if (argc > 4) {
737 		subgroup.metadata_len = hex2bin(argv[4], strlen(argv[4]), subgroup.metadata,
738 						sizeof(subgroup.metadata));
739 
740 		if (subgroup.metadata_len == 0U) {
741 			shell_error(sh, "Could not parse metadata");
742 
743 			return -ENOEXEC;
744 		}
745 	}
746 
747 	err = bt_le_scan_start(BT_LE_SCAN_PASSIVE, NULL);
748 	if (err) {
749 		shell_print(sh, "Fail to start scanning: %d", err);
750 
751 		return -ENOEXEC;
752 	}
753 
754 	/* Store results in the `auto_scan` struct */
755 	utf8_lcpy(auto_scan.broadcast_name, broadcast_name, strlen(broadcast_name) + 1);
756 	auto_scan.broadcast_id = BT_BAP_INVALID_BROADCAST_ID;
757 	memcpy(&auto_scan.subgroup, &subgroup, sizeof(subgroup));
758 
759 	return 0;
760 }
761 
cmd_bap_broadcast_assistant_mod_src(const struct shell * sh,size_t argc,char ** argv)762 static int cmd_bap_broadcast_assistant_mod_src(const struct shell *sh,
763 					       size_t argc, char **argv)
764 {
765 	struct bt_bap_broadcast_assistant_mod_src_param param = { 0 };
766 	struct bt_bap_bass_subgroup subgroup = { 0 };
767 	unsigned long src_id;
768 	int result = 0;
769 
770 	src_id = shell_strtoul(argv[1], 0, &result);
771 	if (result != 0) {
772 		shell_error(sh, "Could not parse src_id: %d", result);
773 
774 		return -ENOEXEC;
775 	}
776 
777 	if (src_id > UINT8_MAX) {
778 		shell_error(sh, "Invalid src_id: %lu", src_id);
779 
780 		return -ENOEXEC;
781 	}
782 	param.src_id = src_id;
783 
784 	param.pa_sync = shell_strtobool(argv[2], 0, &result);
785 	if (result != 0) {
786 		shell_error(sh, "Could not parse adv_sid: %d", result);
787 
788 		return -ENOEXEC;
789 	}
790 
791 	if (argc > 3) {
792 		if (strcmp(argv[3], "unknown") == 0) {
793 			param.pa_interval = BT_BAP_PA_INTERVAL_UNKNOWN;
794 		} else {
795 			unsigned long pa_interval;
796 
797 			pa_interval = shell_strtoul(argv[3], 0, &result);
798 			if (result) {
799 				shell_error(sh, "Could not parse pa_interval: %d", result);
800 
801 				return -ENOEXEC;
802 			}
803 
804 			if (!IN_RANGE(pa_interval, BT_GAP_PER_ADV_MIN_INTERVAL,
805 				      BT_GAP_PER_ADV_MAX_INTERVAL)) {
806 				shell_error(sh, "Invalid pa_interval: %lu", pa_interval);
807 
808 				return -ENOEXEC;
809 			}
810 
811 			param.pa_interval = pa_interval;
812 		}
813 	} else {
814 		param.pa_interval = BT_BAP_PA_INTERVAL_UNKNOWN;
815 	}
816 
817 	/* TODO: Support multiple subgroups */
818 	if (argc > 4) {
819 		unsigned long bis_sync;
820 
821 		bis_sync = shell_strtoul(argv[4], 0, &result);
822 		if (result) {
823 			shell_error(sh, "Could not parse bis_sync: %d", result);
824 
825 			return -ENOEXEC;
826 		}
827 
828 		if (!BT_BAP_BASS_VALID_BIT_BITFIELD(bis_sync)) {
829 			shell_error(sh, "Invalid bis_sync: %lu", bis_sync);
830 
831 			return -ENOEXEC;
832 		}
833 
834 		subgroup.bis_sync = bis_sync;
835 	}
836 
837 	if (argc > 5) {
838 		size_t metadata_len;
839 
840 		metadata_len = hex2bin(argv[5], strlen(argv[5]),
841 				       subgroup.metadata,
842 				       sizeof(subgroup.metadata));
843 
844 		if (metadata_len == 0U) {
845 			shell_error(sh, "Could not parse metadata");
846 
847 			return -ENOEXEC;
848 		}
849 
850 		/* sizeof(subgroup.metadata) can always fit in uint8_t */
851 
852 		subgroup.metadata_len = metadata_len;
853 	}
854 
855 	param.num_subgroups = 1;
856 	param.subgroups = &subgroup;
857 
858 	result = bt_bap_broadcast_assistant_mod_src(default_conn, &param);
859 	if (result) {
860 		shell_print(sh, "Fail: %d", result);
861 	}
862 
863 	return result;
864 }
865 
add_pa_sync_base_subgroup_bis_cb(const struct bt_bap_base_subgroup_bis * bis,void * user_data)866 static inline bool add_pa_sync_base_subgroup_bis_cb(const struct bt_bap_base_subgroup_bis *bis,
867 						    void *user_data)
868 {
869 	struct bt_bap_bass_subgroup *subgroup_param = user_data;
870 
871 	subgroup_param->bis_sync |= BT_ISO_BIS_INDEX_BIT(bis->index);
872 
873 	return true;
874 }
875 
add_pa_sync_base_subgroup_cb(const struct bt_bap_base_subgroup * subgroup,void * user_data)876 static inline bool add_pa_sync_base_subgroup_cb(const struct bt_bap_base_subgroup *subgroup,
877 						void *user_data)
878 {
879 	struct bt_bap_broadcast_assistant_add_src_param *param = user_data;
880 	struct bt_bap_bass_subgroup *subgroup_param;
881 	uint8_t *data;
882 	int ret;
883 
884 	if (param->num_subgroups == CONFIG_BT_BAP_BASS_MAX_SUBGROUPS) {
885 		bt_shell_warn("Cannot fit all subgroups param with size %d",
886 			      CONFIG_BT_BAP_BASS_MAX_SUBGROUPS);
887 
888 		return true; /* return true to avoid returning -ECANCELED as this is OK */
889 	}
890 
891 	ret = bt_bap_base_get_subgroup_codec_meta(subgroup, &data);
892 	if (ret < 0) {
893 		return false;
894 	}
895 
896 	subgroup_param = &param->subgroups[param->num_subgroups];
897 
898 	if (ret > ARRAY_SIZE(subgroup_param->metadata)) {
899 		bt_shell_info("Cannot fit %d octets into subgroup param with size %zu", ret,
900 			      ARRAY_SIZE(subgroup_param->metadata));
901 		return false;
902 	}
903 
904 	ret = bt_bap_base_subgroup_foreach_bis(subgroup, add_pa_sync_base_subgroup_bis_cb,
905 					       subgroup_param);
906 
907 	if (ret < 0) {
908 		return false;
909 	}
910 
911 	param->num_subgroups++;
912 
913 	return true;
914 }
915 
cmd_bap_broadcast_assistant_add_pa_sync(const struct shell * sh,size_t argc,char ** argv)916 static int cmd_bap_broadcast_assistant_add_pa_sync(const struct shell *sh,
917 						   size_t argc, char **argv)
918 {
919 	struct bt_bap_bass_subgroup subgroup_params[CONFIG_BT_BAP_BASS_MAX_SUBGROUPS];
920 	struct bt_bap_broadcast_assistant_add_src_param param = { 0 };
921 	/* TODO: Add support to select which PA sync to BIG sync to */
922 	struct bt_le_per_adv_sync *pa_sync = per_adv_syncs[0];
923 	struct bt_le_per_adv_sync_info pa_info;
924 	unsigned long broadcast_id;
925 	uint32_t bis_bitfield_req;
926 	uint32_t subgroups_bis_sync;
927 	int err;
928 
929 	if (pa_sync == NULL) {
930 		shell_error(sh, "PA not synced");
931 
932 		return -ENOEXEC;
933 	}
934 
935 	err = bt_le_per_adv_sync_get_info(pa_sync, &pa_info);
936 	if (err != 0) {
937 		shell_error(sh, "Could not get PA sync info: %d", err);
938 
939 		return -ENOEXEC;
940 	}
941 
942 	bt_addr_le_copy(&param.addr, &pa_info.addr);
943 	param.adv_sid = pa_info.sid;
944 	param.pa_interval = pa_info.interval;
945 
946 	memset(&subgroup_params, 0, sizeof(subgroup_params));
947 
948 	param.pa_sync = shell_strtobool(argv[1], 0, &err);
949 	if (err != 0) {
950 		shell_error(sh, "Could not parse pa_sync: %d", err);
951 
952 		return -ENOEXEC;
953 	}
954 
955 	broadcast_id = shell_strtoul(argv[2], 0, &err);
956 	if (err != 0) {
957 		shell_error(sh, "failed to parse broadcast_id: %d", err);
958 
959 		return -ENOEXEC;
960 	} else if (broadcast_id > BT_AUDIO_BROADCAST_ID_MAX /* 24 bits */) {
961 		shell_error(sh, "Invalid Broadcast ID: %x",
962 			    param.broadcast_id);
963 
964 		return -ENOEXEC;
965 	}
966 
967 	param.broadcast_id = broadcast_id;
968 
969 	bis_bitfield_req = 0U;
970 	subgroups_bis_sync = 0U;
971 	for (size_t i = 3U; i < argc; i++) {
972 		const unsigned long index = shell_strtoul(argv[i], 16, &err);
973 
974 		if (err != 0) {
975 			shell_error(sh, "failed to parse index: %d", err);
976 
977 			return -ENOEXEC;
978 		}
979 
980 		if (index < BT_ISO_BIS_INDEX_MIN ||
981 		    index > BT_ISO_BIS_INDEX_MAX) {
982 			shell_error(sh, "Invalid index: %ld", index);
983 
984 			return -ENOEXEC;
985 		}
986 
987 		bis_bitfield_req |= BT_ISO_BIS_INDEX_BIT(index);
988 	}
989 
990 	param.subgroups = subgroup_params;
991 	if (received_base_size > 0) {
992 		err = bt_bap_base_foreach_subgroup((const struct bt_bap_base *)received_base,
993 						   add_pa_sync_base_subgroup_cb, &param);
994 		if (err < 0) {
995 			shell_error(sh, "Could not add BASE to params %d", err);
996 
997 			return -ENOEXEC;
998 		}
999 	}
1000 
1001 	/* use the BASE to verify the BIS indexes set by command */
1002 	for (size_t j = 0U; j < param.num_subgroups; j++) {
1003 		if (bis_bitfield_req == 0) {
1004 			/* Request a PA sync without BIS sync */
1005 			subgroup_params[j].bis_sync = 0;
1006 		} else {
1007 			subgroups_bis_sync |= subgroup_params[j].bis_sync;
1008 			/* only set the BIS index field as optional parameters */
1009 			/* not to whatever is in the BASE */
1010 			subgroup_params[j].bis_sync &= bis_bitfield_req;
1011 		}
1012 	}
1013 
1014 	if ((subgroups_bis_sync & bis_bitfield_req) != bis_bitfield_req) {
1015 		/* bis_sync of all subgroups should contain at least all the bits in request */
1016 		/* Otherwise Command will be rejected */
1017 		shell_error(sh, "Cannot set BIS index 0x%06X when BASE subgroups only "
1018 			    "supports %d", bis_bitfield_req, subgroups_bis_sync);
1019 		return -ENOEXEC;
1020 	}
1021 
1022 	err = bt_bap_broadcast_assistant_add_src(default_conn, &param);
1023 	if (err != 0) {
1024 		shell_print(sh, "Fail: %d", err);
1025 
1026 		return -ENOEXEC;
1027 	}
1028 
1029 	return 0;
1030 }
1031 
cmd_bap_broadcast_assistant_broadcast_code(const struct shell * sh,size_t argc,char ** argv)1032 static int cmd_bap_broadcast_assistant_broadcast_code(const struct shell *sh,
1033 						      size_t argc, char **argv)
1034 {
1035 	uint8_t broadcast_code[BT_ISO_BROADCAST_CODE_SIZE] = {0};
1036 	size_t broadcast_code_len;
1037 	unsigned long src_id;
1038 	int result = 0;
1039 
1040 	src_id = shell_strtoul(argv[1], 0, &result);
1041 	if (result != 0) {
1042 		shell_error(sh, "Could not parse src_id: %d", result);
1043 
1044 		return -ENOEXEC;
1045 	}
1046 
1047 	if (src_id > UINT8_MAX) {
1048 		shell_error(sh, "Invalid src_id: %lu", src_id);
1049 
1050 		return -ENOEXEC;
1051 	}
1052 
1053 	broadcast_code_len = strlen(argv[2]);
1054 	if (!IN_RANGE(broadcast_code_len, 1, BT_ISO_BROADCAST_CODE_SIZE)) {
1055 		shell_error(sh, "Invalid broadcast code length: %zu", broadcast_code_len);
1056 
1057 		return -ENOEXEC;
1058 	}
1059 
1060 	memcpy(broadcast_code, argv[2], broadcast_code_len);
1061 
1062 	shell_info(sh, "Sending broadcast code:");
1063 	shell_hexdump(sh, broadcast_code, sizeof(broadcast_code));
1064 
1065 	result = bt_bap_broadcast_assistant_set_broadcast_code(default_conn,
1066 							       src_id,
1067 							       broadcast_code);
1068 	if (result) {
1069 		shell_print(sh, "Fail: %d", result);
1070 	}
1071 
1072 	return result;
1073 }
1074 
cmd_bap_broadcast_assistant_rem_src(const struct shell * sh,size_t argc,char ** argv)1075 static int cmd_bap_broadcast_assistant_rem_src(const struct shell *sh,
1076 					       size_t argc, char **argv)
1077 {
1078 	unsigned long src_id;
1079 	int result = 0;
1080 
1081 	src_id = shell_strtoul(argv[1], 0, &result);
1082 	if (result != 0) {
1083 		shell_error(sh, "Could not parse src_id: %d", result);
1084 
1085 		return -ENOEXEC;
1086 	}
1087 
1088 	if (src_id > UINT8_MAX) {
1089 		shell_error(sh, "Invalid src_id: %lu", src_id);
1090 
1091 		return -ENOEXEC;
1092 	}
1093 
1094 	result = bt_bap_broadcast_assistant_rem_src(default_conn, src_id);
1095 	if (result) {
1096 		shell_print(sh, "Fail: %d", result);
1097 	}
1098 
1099 	return result;
1100 }
1101 
cmd_bap_broadcast_assistant_read_recv_state(const struct shell * sh,size_t argc,char ** argv)1102 static int cmd_bap_broadcast_assistant_read_recv_state(const struct shell *sh,
1103 						       size_t argc, char **argv)
1104 {
1105 	unsigned long idx;
1106 	int result = 0;
1107 
1108 	idx = shell_strtoul(argv[1], 0, &result);
1109 	if (result != 0) {
1110 		shell_error(sh, "Could not parse idx: %d", result);
1111 
1112 		return -ENOEXEC;
1113 	}
1114 
1115 	if (idx > UINT8_MAX) {
1116 		shell_error(sh, "Invalid idx: %lu", idx);
1117 
1118 		return -ENOEXEC;
1119 	}
1120 
1121 	result = bt_bap_broadcast_assistant_read_recv_state(default_conn, idx);
1122 	if (result) {
1123 		shell_print(sh, "Fail: %d", result);
1124 	}
1125 
1126 	return result;
1127 }
1128 
cmd_bap_broadcast_assistant(const struct shell * sh,size_t argc,char ** argv)1129 static int cmd_bap_broadcast_assistant(const struct shell *sh, size_t argc,
1130 				       char **argv)
1131 {
1132 	if (argc > 1) {
1133 		shell_error(sh, "%s unknown parameter: %s",
1134 			    argv[0], argv[1]);
1135 	} else {
1136 		shell_error(sh, "%s Missing subcommand", argv[0]);
1137 	}
1138 
1139 	return -ENOEXEC;
1140 }
1141 
1142 SHELL_STATIC_SUBCMD_SET_CREATE(
1143 	bap_broadcast_assistant_cmds,
1144 	SHELL_CMD_ARG(discover, NULL, "Discover BASS on the server",
1145 		      cmd_bap_broadcast_assistant_discover, 1, 0),
1146 	SHELL_CMD_ARG(scan_start, NULL, "Start scanning for broadcasters",
1147 		      cmd_bap_broadcast_assistant_scan_start, 1, 1),
1148 	SHELL_CMD_ARG(scan_stop, NULL, "Stop scanning for BISs",
1149 		      cmd_bap_broadcast_assistant_scan_stop, 1, 0),
1150 	SHELL_CMD_ARG(add_src, NULL,
1151 		      "Add a source <address: XX:XX:XX:XX:XX:XX> "
1152 		      "<type: public/random> <adv_sid> <sync_pa> "
1153 		      "<broadcast_id> [<pa_interval>] [<sync_bis>] "
1154 		      "[<metadata>]",
1155 		      cmd_bap_broadcast_assistant_add_src, 6, 3),
1156 	SHELL_CMD_ARG(add_broadcast_id, NULL,
1157 		      "Add a source by broadcast ID <broadcast_id> <sync_pa> "
1158 		      "[<sync_bis>] [<metadata>]",
1159 		      cmd_bap_broadcast_assistant_add_broadcast_id, 3, 2),
1160 	SHELL_CMD_ARG(add_broadcast_name, NULL,
1161 		      "Add a source by broadcast name <broadcast_name> <sync_pa> "
1162 		      "[<sync_bis>] [<metadata>]",
1163 		      cmd_bap_broadcast_assistant_add_broadcast_name, 3, 2),
1164 	SHELL_CMD_ARG(add_pa_sync, NULL,
1165 		      "Add a PA sync as a source <sync_pa> <broadcast_id> "
1166 		      "[bis_index [bis_index [bix_index [...]]]]>",
1167 		      cmd_bap_broadcast_assistant_add_pa_sync, 3, BT_ISO_MAX_GROUP_ISO_COUNT),
1168 	SHELL_CMD_ARG(mod_src, NULL,
1169 		      "Set sync <src_id> <sync_pa> [<pa_interval> | \"unknown\"] "
1170 		      "[<sync_bis>] [<metadata>]",
1171 		      cmd_bap_broadcast_assistant_mod_src, 3, 2),
1172 	SHELL_CMD_ARG(broadcast_code, NULL,
1173 		      "Send a string-based broadcast code of up to 16 bytes "
1174 		      "<src_id> <broadcast code>",
1175 		      cmd_bap_broadcast_assistant_broadcast_code, 3, 0),
1176 	SHELL_CMD_ARG(rem_src, NULL, "Remove a source <src_id>",
1177 		      cmd_bap_broadcast_assistant_rem_src, 2, 0),
1178 	SHELL_CMD_ARG(read_state, NULL, "Remove a source <index>",
1179 		      cmd_bap_broadcast_assistant_read_recv_state, 2, 0),
1180 	SHELL_SUBCMD_SET_END);
1181 
1182 SHELL_CMD_ARG_REGISTER(bap_broadcast_assistant, &bap_broadcast_assistant_cmds,
1183 		       "Bluetooth BAP broadcast assistant client shell commands",
1184 		       cmd_bap_broadcast_assistant, 1, 1);
1185