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