1 /**
2  * @file
3  * @brief Shell APIs for Bluetooth BAP scan delegator
4  *
5  * Copyright (c) 2020-2022 Nordic Semiconductor ASA
6  *
7  * SPDX-License-Identifier: Apache-2.0
8  */
9 
10 #include <zephyr/types.h>
11 
12 #include <zephyr/kernel.h>
13 #include <zephyr/types.h>
14 #include <zephyr/shell/shell.h>
15 #include <stdlib.h>
16 #include <zephyr/bluetooth/gatt.h>
17 #include <zephyr/bluetooth/bluetooth.h>
18 #include <zephyr/bluetooth/audio/bap.h>
19 #include "shell/bt.h"
20 
21 #define SYNC_RETRY_COUNT          6 /* similar to retries for connections */
22 #define PA_SYNC_SKIP              5
23 
24 struct sync_state {
25 	bool pa_syncing;
26 	bool past_avail;
27 	uint8_t src_id;
28 	uint16_t pa_interval;
29 	struct k_work_delayable pa_timer;
30 	struct bt_conn *conn;
31 	struct bt_le_per_adv_sync *pa_sync;
32 	const struct bt_bap_scan_delegator_recv_state *recv_state;
33 	uint8_t broadcast_code[BT_AUDIO_BROADCAST_CODE_SIZE];
34 } sync_states[CONFIG_BT_BAP_SCAN_DELEGATOR_RECV_STATE_COUNT];
35 
36 static bool past_preference = true;
37 
sync_state_get(const struct bt_bap_scan_delegator_recv_state * recv_state)38 static struct sync_state *sync_state_get(const struct bt_bap_scan_delegator_recv_state *recv_state)
39 {
40 	for (size_t i = 0U; i < ARRAY_SIZE(sync_states); i++) {
41 		if (sync_states[i].recv_state == recv_state) {
42 			return &sync_states[i];
43 		}
44 	}
45 
46 	return NULL;
47 }
48 
sync_state_get_or_new(const struct bt_bap_scan_delegator_recv_state * recv_state)49 static struct sync_state *sync_state_get_or_new(
50 	const struct bt_bap_scan_delegator_recv_state *recv_state)
51 {
52 	struct sync_state *free_state = NULL;
53 
54 	for (size_t i = 0U; i < ARRAY_SIZE(sync_states); i++) {
55 		if (sync_states[i].recv_state == NULL &&
56 		    free_state == NULL) {
57 			free_state = &sync_states[i];
58 		}
59 
60 		if (sync_states[i].recv_state == recv_state) {
61 			return &sync_states[i];
62 		}
63 	}
64 
65 	return free_state;
66 }
67 
sync_state_get_by_pa(struct bt_le_per_adv_sync * sync)68 static struct sync_state *sync_state_get_by_pa(struct bt_le_per_adv_sync *sync)
69 {
70 	for (size_t i = 0U; i < ARRAY_SIZE(sync_states); i++) {
71 		if (sync_states[i].pa_sync == sync) {
72 			return &sync_states[i];
73 		}
74 	}
75 
76 	return NULL;
77 }
78 
sync_state_new(void)79 static struct sync_state *sync_state_new(void)
80 {
81 	for (size_t i = 0U; i < ARRAY_SIZE(sync_states); i++) {
82 		if (sync_states[i].recv_state == NULL) {
83 			return &sync_states[i];
84 		}
85 	}
86 
87 	return NULL;
88 }
89 
sync_state_get_by_src_id(uint8_t src_id)90 static struct sync_state *sync_state_get_by_src_id(uint8_t src_id)
91 {
92 	for (size_t i = 0U; i < ARRAY_SIZE(sync_states); i++) {
93 		if (sync_states[i].src_id == src_id) {
94 			return &sync_states[i];
95 		}
96 	}
97 
98 	return NULL;
99 }
100 
interval_to_sync_timeout(uint16_t pa_interval)101 static uint16_t interval_to_sync_timeout(uint16_t pa_interval)
102 {
103 	uint16_t pa_timeout;
104 
105 	if (pa_interval == BT_BAP_PA_INTERVAL_UNKNOWN) {
106 		/* Use maximum value to maximize chance of success */
107 		pa_timeout = BT_GAP_PER_ADV_MAX_TIMEOUT;
108 	} else {
109 		/* Ensure that the following calculation does not overflow silently */
110 		__ASSERT(SYNC_RETRY_COUNT < 10,
111 			 "SYNC_RETRY_COUNT shall be less than 10");
112 
113 		/* Add retries and convert to unit in 10's of ms */
114 		pa_timeout = ((uint32_t)pa_interval * SYNC_RETRY_COUNT) / 10;
115 
116 		/* Enforce restraints */
117 		pa_timeout = CLAMP(pa_timeout, BT_GAP_PER_ADV_MIN_TIMEOUT,
118 				   BT_GAP_PER_ADV_MAX_TIMEOUT);
119 	}
120 
121 	return pa_timeout;
122 }
123 
pa_timer_handler(struct k_work * work)124 static void pa_timer_handler(struct k_work *work)
125 {
126 	struct k_work_delayable *dwork = k_work_delayable_from_work(work);
127 	struct sync_state *state = CONTAINER_OF(dwork, struct sync_state, pa_timer);
128 
129 	state->pa_syncing = false;
130 
131 	if (state->recv_state != NULL) {
132 		enum bt_bap_pa_state pa_state;
133 
134 		if (state->recv_state->pa_sync_state == BT_BAP_PA_STATE_INFO_REQ) {
135 			pa_state = BT_BAP_PA_STATE_NO_PAST;
136 		} else {
137 			pa_state = BT_BAP_PA_STATE_FAILED;
138 		}
139 
140 		bt_bap_scan_delegator_set_pa_state(state->recv_state->src_id,
141 						   pa_state);
142 
143 		shell_info(ctx_shell, "PA timeout for %p", state->recv_state);
144 	}
145 }
146 
pa_sync_past(struct bt_conn * conn,struct sync_state * state,uint16_t pa_interval)147 static int pa_sync_past(struct bt_conn *conn,
148 			struct sync_state *state,
149 			uint16_t pa_interval)
150 {
151 	struct bt_le_per_adv_sync_transfer_param param = { 0 };
152 	int err;
153 
154 	param.skip = PA_SYNC_SKIP;
155 	param.timeout = interval_to_sync_timeout(pa_interval);
156 
157 	err = bt_le_per_adv_sync_transfer_subscribe(conn, &param);
158 	if (err != 0) {
159 		shell_info(ctx_shell, "Could not do PAST subscribe: %d", err);
160 	} else {
161 		shell_info(ctx_shell, "Syncing with PAST: %d", err);
162 		state->pa_syncing = true;
163 		k_work_init_delayable(&state->pa_timer, pa_timer_handler);
164 		(void)k_work_reschedule(&state->pa_timer,
165 					K_MSEC(param.timeout * 10));
166 	}
167 
168 	return err;
169 }
170 
pa_sync_no_past(struct sync_state * state,uint16_t pa_interval)171 static int pa_sync_no_past(struct sync_state *state,
172 			    uint16_t pa_interval)
173 {
174 	const struct bt_bap_scan_delegator_recv_state *recv_state;
175 	struct bt_le_per_adv_sync_param param = { 0 };
176 	int err;
177 
178 	recv_state = state->recv_state;
179 
180 	bt_addr_le_copy(&param.addr, &recv_state->addr);
181 	param.options = BT_LE_PER_ADV_SYNC_OPT_FILTER_DUPLICATE;
182 	param.sid = recv_state->adv_sid;
183 	param.skip = PA_SYNC_SKIP;
184 	param.timeout = interval_to_sync_timeout(pa_interval);
185 
186 	/* TODO: Validate that the advertise is broadcasting the same
187 	 * broadcast_id that the receive state has
188 	 */
189 	err = bt_le_per_adv_sync_create(&param, &state->pa_sync);
190 	if (err != 0) {
191 		shell_info(ctx_shell, "Could not sync per adv: %d", err);
192 	} else {
193 		char addr_str[BT_ADDR_LE_STR_LEN];
194 
195 		bt_addr_le_to_str(&recv_state->addr, addr_str, sizeof(addr_str));
196 		shell_info(ctx_shell, "PA sync pending for addr %s", addr_str);
197 		state->pa_syncing = true;
198 		k_work_init_delayable(&state->pa_timer, pa_timer_handler);
199 		(void)k_work_reschedule(&state->pa_timer,
200 					K_MSEC(param.timeout * 10));
201 	}
202 
203 	return err;
204 }
205 
pa_sync_term(struct sync_state * state)206 static int pa_sync_term(struct sync_state *state)
207 {
208 	int err;
209 
210 	(void)k_work_cancel_delayable(&state->pa_timer);
211 
212 	if (state->pa_sync == NULL) {
213 		return -1;
214 	}
215 
216 	shell_info(ctx_shell, "Deleting PA sync");
217 
218 	err = bt_le_per_adv_sync_delete(state->pa_sync);
219 	if (err != 0) {
220 		shell_error(ctx_shell, "Could not delete per adv sync: %d",
221 			    err);
222 	} else {
223 		state->pa_syncing = false;
224 		state->pa_sync = NULL;
225 	}
226 
227 	return err;
228 }
229 
recv_state_updated_cb(struct bt_conn * conn,const struct bt_bap_scan_delegator_recv_state * recv_state)230 static void recv_state_updated_cb(struct bt_conn *conn,
231 				  const struct bt_bap_scan_delegator_recv_state *recv_state)
232 {
233 	shell_info(ctx_shell, "Receive state with ID %u updated", recv_state->src_id);
234 }
235 
pa_sync_req_cb(struct bt_conn * conn,const struct bt_bap_scan_delegator_recv_state * recv_state,bool past_avail,uint16_t pa_interval)236 static int pa_sync_req_cb(struct bt_conn *conn,
237 			  const struct bt_bap_scan_delegator_recv_state *recv_state,
238 			  bool past_avail, uint16_t pa_interval)
239 {
240 	struct sync_state *state;
241 
242 	shell_info(ctx_shell,
243 		   "PA Sync request: past_avail %u, broadcast_id 0x%06X, pa_interval 0x%04x: %p",
244 		   past_avail, recv_state->broadcast_id, pa_interval,
245 		   recv_state);
246 
247 	state = sync_state_get_or_new(recv_state);
248 	if (state == NULL) {
249 		shell_error(ctx_shell, "Could not get state");
250 
251 		return -1;
252 	}
253 
254 	state->recv_state = recv_state;
255 	state->src_id = recv_state->src_id;
256 
257 	if (recv_state->pa_sync_state == BT_BAP_PA_STATE_SYNCED ||
258 	    recv_state->pa_sync_state == BT_BAP_PA_STATE_INFO_REQ) {
259 		/* Already syncing */
260 		/* TODO: Terminate existing sync and then sync to new?*/
261 		return -1;
262 	}
263 
264 	if (past_avail) {
265 		state->past_avail = past_preference;
266 		state->conn = bt_conn_ref(conn);
267 	} else {
268 		state->past_avail = false;
269 	}
270 
271 	return 0;
272 }
273 
pa_sync_term_req_cb(struct bt_conn * conn,const struct bt_bap_scan_delegator_recv_state * recv_state)274 static int pa_sync_term_req_cb(struct bt_conn *conn,
275 			       const struct bt_bap_scan_delegator_recv_state *recv_state)
276 {
277 	struct sync_state *state;
278 
279 	shell_info(ctx_shell, "PA Sync term request for %p", recv_state);
280 
281 	state = sync_state_get(recv_state);
282 	if (state == NULL) {
283 		shell_error(ctx_shell, "Could not get state");
284 
285 		return -1;
286 	}
287 
288 	return pa_sync_term(state);
289 }
290 
broadcast_code_cb(struct bt_conn * conn,const struct bt_bap_scan_delegator_recv_state * recv_state,const uint8_t broadcast_code[BT_AUDIO_BROADCAST_CODE_SIZE])291 static void broadcast_code_cb(struct bt_conn *conn,
292 			      const struct bt_bap_scan_delegator_recv_state *recv_state,
293 			      const uint8_t broadcast_code[BT_AUDIO_BROADCAST_CODE_SIZE])
294 {
295 	struct sync_state *state;
296 
297 	shell_info(ctx_shell, "Broadcast code received for %p", recv_state);
298 	shell_hexdump(ctx_shell, broadcast_code, BT_AUDIO_BROADCAST_CODE_SIZE);
299 
300 	state = sync_state_get(recv_state);
301 	if (state == NULL) {
302 		shell_error(ctx_shell, "Could not get state");
303 
304 		return;
305 	}
306 
307 	(void)memcpy(state->broadcast_code, broadcast_code, BT_AUDIO_BROADCAST_CODE_SIZE);
308 }
309 
bis_sync_req_cb(struct bt_conn * conn,const struct bt_bap_scan_delegator_recv_state * recv_state,const uint32_t bis_sync_req[BT_BAP_SCAN_DELEGATOR_MAX_SUBGROUPS])310 static int bis_sync_req_cb(struct bt_conn *conn,
311 			   const struct bt_bap_scan_delegator_recv_state *recv_state,
312 			   const uint32_t bis_sync_req[BT_BAP_SCAN_DELEGATOR_MAX_SUBGROUPS])
313 {
314 	printk("BIS sync request received for %p\n", recv_state);
315 
316 	for (int i = 0; i < BT_BAP_SCAN_DELEGATOR_MAX_SUBGROUPS; i++) {
317 		printk("  [%d]: 0x%08x\n", i, bis_sync_req[i]);
318 	}
319 
320 	return 0;
321 }
322 
323 static struct bt_bap_scan_delegator_cb scan_delegator_cb = {
324 	.recv_state_updated = recv_state_updated_cb,
325 	.pa_sync_req = pa_sync_req_cb,
326 	.pa_sync_term_req = pa_sync_term_req_cb,
327 	.broadcast_code = broadcast_code_cb,
328 	.bis_sync_req = bis_sync_req_cb,
329 };
330 
pa_synced_cb(struct bt_le_per_adv_sync * sync,struct bt_le_per_adv_sync_synced_info * info)331 static void pa_synced_cb(struct bt_le_per_adv_sync *sync,
332 			 struct bt_le_per_adv_sync_synced_info *info)
333 {
334 	struct sync_state *state;
335 
336 	shell_info(ctx_shell, "PA %p synced", sync);
337 
338 	state = sync_state_get_by_pa(sync);
339 	if (state == NULL) {
340 		shell_info(ctx_shell,
341 			   "Could not get sync state from PA sync %p",
342 			   sync);
343 		return;
344 	}
345 
346 	if (state->conn != NULL) {
347 		bt_conn_unref(state->conn);
348 		state->conn = NULL;
349 	}
350 
351 	k_work_cancel_delayable(&state->pa_timer);
352 }
353 
pa_term_cb(struct bt_le_per_adv_sync * sync,const struct bt_le_per_adv_sync_term_info * info)354 static void pa_term_cb(struct bt_le_per_adv_sync *sync,
355 		       const struct bt_le_per_adv_sync_term_info *info)
356 {
357 	struct sync_state *state;
358 
359 	shell_info(ctx_shell, "PA %p sync terminated", sync);
360 
361 	state = sync_state_get_by_pa(sync);
362 	if (state == NULL) {
363 		shell_error(ctx_shell,
364 			    "Could not get sync state from PA sync %p",
365 			    sync);
366 		return;
367 	}
368 
369 	if (state->conn != NULL) {
370 		bt_conn_unref(state->conn);
371 		state->conn = NULL;
372 	}
373 
374 	k_work_cancel_delayable(&state->pa_timer);
375 }
376 
377 static struct bt_le_per_adv_sync_cb pa_sync_cb =  {
378 	.synced = pa_synced_cb,
379 	.term = pa_term_cb,
380 };
381 
cmd_bap_scan_delegator_init(const struct shell * sh,size_t argc,char ** argv)382 static int cmd_bap_scan_delegator_init(const struct shell *sh, size_t argc,
383 				       char **argv)
384 {
385 	static bool registered;
386 
387 	if (!registered) {
388 		bt_le_per_adv_sync_cb_register(&pa_sync_cb);
389 		bt_bap_scan_delegator_register_cb(&scan_delegator_cb);
390 
391 		registered = true;
392 	}
393 
394 	return 0;
395 }
cmd_bap_scan_delegator_set_past_pref(const struct shell * sh,size_t argc,char ** argv)396 static int cmd_bap_scan_delegator_set_past_pref(const struct shell *sh,
397 						size_t argc, char **argv)
398 {
399 	bool past_pref;
400 	int err;
401 
402 	err = 0;
403 
404 	past_pref = shell_strtobool(argv[1], 10, &err);
405 	if (err != 0) {
406 		shell_error(sh, "Failed to parse past_pref from %s", argv[1]);
407 		return -ENOEXEC;
408 	}
409 
410 	past_preference = past_pref;
411 
412 	return 0;
413 }
414 
cmd_bap_scan_delegator_sync_pa(const struct shell * sh,size_t argc,char ** argv)415 static int cmd_bap_scan_delegator_sync_pa(const struct shell *sh, size_t argc,
416 					  char **argv)
417 {
418 	struct sync_state *state;
419 	unsigned long src_id;
420 	int err;
421 
422 	err = 0;
423 
424 	src_id = shell_strtoul(argv[1], 16, &err);
425 	if (err != 0) {
426 		shell_error(sh, "Failed to parse src_id from %s", argv[1]);
427 
428 		return -ENOEXEC;
429 	}
430 
431 	if (src_id > UINT8_MAX) {
432 		shell_error(sh, "src_id shall be 0x00-0xff");
433 
434 		return -ENOEXEC;
435 	}
436 
437 	state = sync_state_get_by_src_id((uint8_t)src_id);
438 	if (state == NULL) {
439 		shell_error(ctx_shell, "Could not get state");
440 
441 		return -ENOEXEC;
442 	}
443 
444 	if (past_preference &&
445 	    state->past_avail &&
446 	    state->conn != NULL) {
447 		shell_info(sh, "Syncing with PAST");
448 
449 		err = pa_sync_past(state->conn, state, state->pa_interval);
450 		if (err != 0) {
451 			err = bt_bap_scan_delegator_set_pa_state(src_id,
452 								 BT_BAP_PA_STATE_INFO_REQ);
453 			if (err != 0) {
454 				shell_error(sh,
455 					    "Failed to set INFO_REQ state: %d",
456 					    err);
457 			}
458 
459 			return -ENOEXEC;
460 		}
461 
462 	} else {
463 		shell_info(sh, "Syncing without PAST");
464 		err = pa_sync_no_past(state, state->pa_interval);
465 	}
466 
467 	if (err != 0) {
468 		shell_error(sh, "Failed PA sync: %d", err);
469 
470 		return -ENOEXEC;
471 	}
472 
473 	return 0;
474 }
475 
cmd_bap_scan_delegator_term_pa(const struct shell * sh,size_t argc,char ** argv)476 static int cmd_bap_scan_delegator_term_pa(const struct shell *sh, size_t argc,
477 					  char **argv)
478 {
479 	struct sync_state *state;
480 	unsigned long src_id;
481 	int err;
482 
483 	err = 0;
484 
485 	src_id = shell_strtoul(argv[1], 16, &err);
486 	if (err != 0) {
487 		shell_error(sh, "Failed to parse src_id from %s", argv[1]);
488 
489 		return -ENOEXEC;
490 	}
491 
492 	if (src_id > UINT8_MAX) {
493 		shell_error(sh, "src_id shall be 0x00-0xff");
494 
495 		return -ENOEXEC;
496 	}
497 
498 	state = sync_state_get_by_src_id((uint8_t)src_id);
499 	if (state == NULL) {
500 		shell_error(ctx_shell, "Could not get state");
501 
502 		return -ENOEXEC;
503 	}
504 
505 	err = pa_sync_term(state);
506 	if (err != 0) {
507 		shell_error(ctx_shell, "Failed to terminate PA sync: %d", err);
508 
509 		return -ENOEXEC;
510 	}
511 
512 	return 0;
513 }
514 
cmd_bap_scan_delegator_add_src(const struct shell * sh,size_t argc,char ** argv)515 static int cmd_bap_scan_delegator_add_src(const struct shell *sh, size_t argc,
516 					  char **argv)
517 {
518 	/* TODO: Add support to select which PA sync to BIG sync to */
519 	struct bt_le_per_adv_sync *pa_sync = per_adv_syncs[0];
520 	struct bt_bap_scan_delegator_subgroup *subgroup_param;
521 	struct bt_bap_scan_delegator_add_src_param param;
522 	unsigned long broadcast_id;
523 	struct sync_state *state;
524 	unsigned long enc_state;
525 	int err;
526 
527 	err = 0;
528 
529 	broadcast_id = shell_strtoul(argv[1], 16, &err);
530 	if (err != 0) {
531 		shell_error(sh, "Failed to parse broadcast_id from %s", argv[1]);
532 
533 		return -EINVAL;
534 	}
535 
536 	if (broadcast_id > BT_AUDIO_BROADCAST_ID_MAX) {
537 		shell_error(sh, "Invalid broadcast_id %lu", broadcast_id);
538 
539 		return -EINVAL;
540 	}
541 
542 	enc_state = shell_strtoul(argv[2], 16, &err);
543 	if (err != 0) {
544 		shell_error(sh, "Failed to parse enc_state from %s", argv[2]);
545 
546 		return -EINVAL;
547 	}
548 
549 	if (enc_state > BT_BAP_BIG_ENC_STATE_BAD_CODE) {
550 		shell_error(sh, "Invalid enc_state %lu", enc_state);
551 
552 		return -EINVAL;
553 	}
554 
555 	/* TODO: Support multiple subgroups */
556 	subgroup_param = &param.subgroups[0];
557 	if (argc > 3) {
558 		unsigned long bis_sync;
559 
560 		bis_sync = shell_strtoul(argv[3], 16, &err);
561 		if (err != 0) {
562 			shell_error(sh, "Failed to parse bis_sync from %s", argv[3]);
563 
564 			return -EINVAL;
565 		}
566 
567 		if (bis_sync > BT_BAP_BIS_SYNC_NO_PREF) {
568 			shell_error(sh, "Invalid bis_sync %lu", bis_sync);
569 
570 			return -EINVAL;
571 		}
572 	} else {
573 		subgroup_param->bis_sync = 0U;
574 	}
575 
576 	if (argc > 4) {
577 		subgroup_param->metadata_len = hex2bin(argv[4], strlen(argv[4]),
578 						       subgroup_param->metadata,
579 						       sizeof(subgroup_param->metadata));
580 
581 		if (subgroup_param->metadata_len == 0U) {
582 			shell_error(sh, "Could not parse metadata");
583 
584 			return -EINVAL;
585 		}
586 	} else {
587 		subgroup_param->metadata_len = 0U;
588 	}
589 
590 	state = sync_state_new();
591 	if (state == NULL) {
592 		shell_error(ctx_shell, "Could not get new state");
593 
594 		return -ENOEXEC;
595 	}
596 
597 	param.pa_sync = pa_sync;
598 	param.encrypt_state = (enum bt_bap_big_enc_state)enc_state;
599 	param.broadcast_id = broadcast_id;
600 	param.num_subgroups = 1U;
601 
602 	err = bt_bap_scan_delegator_add_src(&param);
603 	if (err < 0) {
604 		shell_error(ctx_shell, "Failed to add source: %d", err);
605 
606 		return -ENOEXEC;
607 	}
608 
609 	state->src_id = (uint8_t)err;
610 
611 	return 0;
612 }
613 
cmd_bap_scan_delegator_mod_src(const struct shell * sh,size_t argc,char ** argv)614 static int cmd_bap_scan_delegator_mod_src(const struct shell *sh, size_t argc,
615 					  char **argv)
616 {
617 	struct bt_bap_scan_delegator_subgroup *subgroup_param;
618 	struct bt_bap_scan_delegator_mod_src_param param;
619 	unsigned long broadcast_id;
620 	unsigned long enc_state;
621 	unsigned long src_id;
622 	int err;
623 
624 	err = 0;
625 
626 	src_id = shell_strtoul(argv[1], 16, &err);
627 	if (err != 0) {
628 		shell_error(sh, "Failed to parse src_id from %s", argv[1]);
629 
630 		return -EINVAL;
631 	}
632 
633 	if (src_id > UINT8_MAX) {
634 		shell_error(sh, "Invalid src_id %lu", src_id);
635 
636 		return -EINVAL;
637 	}
638 
639 	broadcast_id = shell_strtoul(argv[2], 16, &err);
640 	if (err != 0) {
641 		shell_error(sh, "Failed to parse broadcast_id from %s", argv[2]);
642 
643 		return -EINVAL;
644 	}
645 
646 	if (broadcast_id > BT_AUDIO_BROADCAST_ID_MAX) {
647 		shell_error(sh, "Invalid broadcast_id %lu", broadcast_id);
648 
649 		return -EINVAL;
650 	}
651 
652 	enc_state = shell_strtoul(argv[3], 16, &err);
653 	if (err != 0) {
654 		shell_error(sh, "Failed to parse enc_state from %s", argv[3]);
655 
656 		return -EINVAL;
657 	}
658 
659 	if (enc_state > BT_BAP_BIG_ENC_STATE_BAD_CODE) {
660 		shell_error(sh, "Invalid enc_state %lu", enc_state);
661 
662 		return -EINVAL;
663 	}
664 
665 	/* TODO: Support multiple subgroups */
666 	subgroup_param = &param.subgroups[0];
667 	if (argc > 4) {
668 		unsigned long bis_sync;
669 
670 		bis_sync = shell_strtoul(argv[4], 16, &err);
671 		if (err != 0) {
672 			shell_error(sh, "Failed to parse bis_sync from %s", argv[4]);
673 
674 			return -EINVAL;
675 		}
676 
677 		if (bis_sync > BT_BAP_BIS_SYNC_NO_PREF) {
678 			shell_error(sh, "Invalid bis_sync %lu", bis_sync);
679 
680 			return -EINVAL;
681 		}
682 	} else {
683 		subgroup_param->bis_sync = 0U;
684 	}
685 
686 	if (argc > 5) {
687 		subgroup_param->metadata_len = hex2bin(argv[5], strlen(argv[5]),
688 						       subgroup_param->metadata,
689 						       sizeof(subgroup_param->metadata));
690 
691 		if (subgroup_param->metadata_len == 0U) {
692 			shell_error(sh, "Could not parse metadata");
693 
694 			return -EINVAL;
695 		}
696 	} else {
697 		subgroup_param->metadata_len = 0U;
698 	}
699 
700 
701 	param.src_id = (uint8_t)src_id;
702 	param.encrypt_state = (enum bt_bap_big_enc_state)enc_state;
703 	param.broadcast_id = broadcast_id;
704 	param.num_subgroups = 1U;
705 
706 	err = bt_bap_scan_delegator_mod_src(&param);
707 	if (err < 0) {
708 		shell_error(ctx_shell, "Failed to modify source: %d", err);
709 
710 		return -ENOEXEC;
711 	}
712 
713 	return 0;
714 }
715 
cmd_bap_scan_delegator_rem_src(const struct shell * sh,size_t argc,char ** argv)716 static int cmd_bap_scan_delegator_rem_src(const struct shell *sh, size_t argc,
717 					  char **argv)
718 {
719 	unsigned long src_id;
720 	int err;
721 
722 	err = 0;
723 
724 	src_id = shell_strtoul(argv[1], 16, &err);
725 	if (err != 0) {
726 		shell_error(sh, "Failed to parse src_id from %s", argv[1]);
727 
728 		return -EINVAL;
729 	}
730 
731 	if (src_id > UINT8_MAX) {
732 		shell_error(sh, "Invalid src_id %lu", src_id);
733 
734 		return -EINVAL;
735 	}
736 
737 	err = bt_bap_scan_delegator_rem_src((uint8_t)src_id);
738 	if (err < 0) {
739 		shell_error(ctx_shell, "Failed to remove source source: %d",
740 			    err);
741 
742 		return -ENOEXEC;
743 	}
744 
745 	return 0;
746 }
747 
cmd_bap_scan_delegator_bis_synced(const struct shell * sh,size_t argc,char ** argv)748 static int cmd_bap_scan_delegator_bis_synced(const struct shell *sh, size_t argc,
749 					 char **argv)
750 {
751 	uint32_t bis_syncs[CONFIG_BT_BAP_SCAN_DELEGATOR_MAX_SUBGROUPS];
752 	unsigned long pa_sync_state;
753 	unsigned long bis_synced;
754 	unsigned long src_id;
755 	int result = 0;
756 
757 	src_id = shell_strtoul(argv[1], 0, &result);
758 	if (result != 0) {
759 		shell_error(sh, "Could not parse src_id: %d", result);
760 
761 		return -ENOEXEC;
762 	}
763 
764 	if (src_id > UINT8_MAX) {
765 		shell_error(sh, "Invalid src_id: %lu", src_id);
766 
767 		return -ENOEXEC;
768 	}
769 
770 	pa_sync_state = shell_strtoul(argv[2], 0, &result);
771 	if (result != 0) {
772 		shell_error(sh, "Could not parse pa_sync_state: %d", result);
773 
774 		return -ENOEXEC;
775 	}
776 
777 	if (pa_sync_state > BT_BAP_PA_STATE_NO_PAST) {
778 		shell_error(sh, "Invalid pa_sync_state %ld", pa_sync_state);
779 
780 		return -ENOEXEC;
781 	}
782 
783 	bis_synced = shell_strtoul(argv[3], 0, &result);
784 	if (result != 0) {
785 		shell_error(sh, "Could not parse bis_synced: %d", result);
786 
787 		return -ENOEXEC;
788 	}
789 
790 	if (bis_synced > UINT32_MAX) {
791 		shell_error(sh, "Invalid bis_synced %ld", bis_synced);
792 
793 		return -ENOEXEC;
794 	}
795 
796 	for (size_t i = 0U; i < ARRAY_SIZE(bis_syncs); i++) {
797 		bis_syncs[i] = bis_synced;
798 	}
799 
800 	result = bt_bap_scan_delegator_set_bis_sync_state(src_id, bis_syncs);
801 	if (result != 0) {
802 		shell_print(sh, "Fail: %d", result);
803 	}
804 
805 	return result;
806 }
807 
cmd_bap_scan_delegator(const struct shell * sh,size_t argc,char ** argv)808 static int cmd_bap_scan_delegator(const struct shell *sh, size_t argc,
809 				  char **argv)
810 {
811 	if (argc > 1) {
812 		shell_error(sh, "%s unknown parameter: %s",
813 			    argv[0], argv[1]);
814 	} else {
815 		shell_error(sh, "%s Missing subcommand", argv[0]);
816 	}
817 
818 	return -ENOEXEC;
819 }
820 
821 SHELL_STATIC_SUBCMD_SET_CREATE(bap_scan_delegator_cmds,
822 	SHELL_CMD_ARG(init, NULL,
823 		      "Initialize the service and register callbacks",
824 		      cmd_bap_scan_delegator_init, 1, 0),
825 	SHELL_CMD_ARG(set_past_pref, NULL,
826 		      "Set PAST preference <true || false>",
827 		      cmd_bap_scan_delegator_set_past_pref, 2, 0),
828 	SHELL_CMD_ARG(sync_pa, NULL,
829 		      "Sync to PA <src_id>",
830 		      cmd_bap_scan_delegator_sync_pa, 2, 0),
831 	SHELL_CMD_ARG(term_pa, NULL,
832 		      "Terminate PA sync <src_id>",
833 		      cmd_bap_scan_delegator_term_pa, 2, 0),
834 	SHELL_CMD_ARG(add_src, NULL,
835 		      "Add a PA as source <broadcast_id> <enc_state> [bis_sync [metadata]]",
836 		      cmd_bap_scan_delegator_add_src, 3, 2),
837 	SHELL_CMD_ARG(mod_src, NULL,
838 		      "Modify source <src_id> <broadcast_id> <enc_state> [bis_sync [metadata]]",
839 		      cmd_bap_scan_delegator_mod_src, 4, 2),
840 	SHELL_CMD_ARG(rem_src, NULL,
841 		      "Remove source <src_id>",
842 		      cmd_bap_scan_delegator_rem_src, 2, 0),
843 	SHELL_CMD_ARG(synced, NULL,
844 		      "Set server scan state <src_id> <bis_syncs>",
845 		      cmd_bap_scan_delegator_bis_synced, 3, 0),
846 	SHELL_SUBCMD_SET_END
847 );
848 
849 SHELL_CMD_ARG_REGISTER(bap_scan_delegator, &bap_scan_delegator_cmds,
850 		       "Bluetooth BAP scan delegator shell commands",
851 		       cmd_bap_scan_delegator, 1, 1);
852