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, ¶m);
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(¶m.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(¶m, &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 = ¶m.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(¶m);
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 = ¶m.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(¶m);
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