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 <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/conn.h>
22 #include <zephyr/bluetooth/gap.h>
23 #include <zephyr/bluetooth/gatt.h>
24 #include <zephyr/bluetooth/iso.h>
25 #include <zephyr/bluetooth/uuid.h>
26 #include <zephyr/kernel.h>
27 #include <zephyr/shell/shell.h>
28 #include <zephyr/shell/shell_string_conv.h>
29 #include <zephyr/sys/__assert.h>
30 #include <zephyr/sys/printk.h>
31 #include <zephyr/sys/util.h>
32 #include <zephyr/types.h>
33
34 #include <audio/bap_internal.h>
35 #include "host/shell/bt.h"
36 #include "common/bt_shell_private.h"
37
38 #define PA_SYNC_INTERVAL_TO_TIMEOUT_RATIO 20 /* Set the timeout relative to interval */
39 #define PA_SYNC_SKIP 5
40
41 static struct sync_state {
42 bool pa_syncing;
43 bool past_avail;
44 uint8_t src_id;
45 uint16_t pa_interval;
46 struct k_work_delayable pa_timer;
47 struct bt_conn *conn;
48 struct bt_le_per_adv_sync *pa_sync;
49 const struct bt_bap_scan_delegator_recv_state *recv_state;
50 uint8_t broadcast_code[BT_ISO_BROADCAST_CODE_SIZE];
51 } sync_states[CONFIG_BT_BAP_SCAN_DELEGATOR_RECV_STATE_COUNT];
52
53 static bool past_preference = true;
54
bap_scan_delegator_ad_data_add(struct bt_data data[],size_t data_size)55 size_t bap_scan_delegator_ad_data_add(struct bt_data data[], size_t data_size)
56 {
57 static uint8_t ad_bap_scan_delegator[2] = {
58 BT_UUID_16_ENCODE(BT_UUID_BASS_VAL),
59 };
60
61 __ASSERT(data_size > 0, "No space for ad_bap_scan_delegator");
62 data[0].type = BT_DATA_SVC_DATA16;
63 data[0].data_len = ARRAY_SIZE(ad_bap_scan_delegator);
64 data[0].data = &ad_bap_scan_delegator[0];
65
66 return 1U;
67 }
68
sync_state_get(const struct bt_bap_scan_delegator_recv_state * recv_state)69 static struct sync_state *sync_state_get(const struct bt_bap_scan_delegator_recv_state *recv_state)
70 {
71 for (size_t i = 0U; i < ARRAY_SIZE(sync_states); i++) {
72 if (sync_states[i].recv_state == recv_state) {
73 return &sync_states[i];
74 }
75 }
76
77 return NULL;
78 }
79
sync_state_get_or_new(const struct bt_bap_scan_delegator_recv_state * recv_state)80 static struct sync_state *sync_state_get_or_new(
81 const struct bt_bap_scan_delegator_recv_state *recv_state)
82 {
83 struct sync_state *free_state = NULL;
84
85 for (size_t i = 0U; i < ARRAY_SIZE(sync_states); i++) {
86 if (sync_states[i].recv_state == NULL &&
87 free_state == NULL) {
88 free_state = &sync_states[i];
89 }
90
91 if (sync_states[i].recv_state == recv_state) {
92 return &sync_states[i];
93 }
94 }
95
96 return free_state;
97 }
98
sync_state_get_by_pa(struct bt_le_per_adv_sync * sync)99 static struct sync_state *sync_state_get_by_pa(struct bt_le_per_adv_sync *sync)
100 {
101 for (size_t i = 0U; i < ARRAY_SIZE(sync_states); i++) {
102 if (sync_states[i].pa_sync == sync) {
103 return &sync_states[i];
104 }
105 }
106
107 return NULL;
108 }
109
110 static struct sync_state *
sync_state_get_by_sync_info(const struct bt_le_per_adv_sync_synced_info * info)111 sync_state_get_by_sync_info(const struct bt_le_per_adv_sync_synced_info *info)
112 {
113 for (size_t i = 0U; i < ARRAY_SIZE(sync_states); i++) {
114 if (sync_states[i].recv_state != NULL &&
115 bt_addr_le_eq(info->addr, &sync_states[i].recv_state->addr) &&
116 info->sid == sync_states[i].recv_state->adv_sid) {
117
118 return &sync_states[i];
119 }
120 }
121
122 return NULL;
123 }
124
sync_state_new(void)125 static struct sync_state *sync_state_new(void)
126 {
127 for (size_t i = 0U; i < ARRAY_SIZE(sync_states); i++) {
128 if (sync_states[i].recv_state == NULL) {
129 return &sync_states[i];
130 }
131 }
132
133 return NULL;
134 }
135
sync_state_get_by_src_id(uint8_t src_id)136 static struct sync_state *sync_state_get_by_src_id(uint8_t src_id)
137 {
138 for (size_t i = 0U; i < ARRAY_SIZE(sync_states); i++) {
139 if (sync_states[i].src_id == src_id) {
140 return &sync_states[i];
141 }
142 }
143
144 return NULL;
145 }
146
interval_to_sync_timeout(uint16_t pa_interval)147 static uint16_t interval_to_sync_timeout(uint16_t pa_interval)
148 {
149 uint16_t pa_timeout;
150
151 if (pa_interval == BT_BAP_PA_INTERVAL_UNKNOWN) {
152 /* Use maximum value to maximize chance of success */
153 pa_timeout = BT_GAP_PER_ADV_MAX_TIMEOUT;
154 } else {
155 uint32_t interval_us;
156 uint32_t timeout;
157
158 /* Add retries and convert to unit in 10's of ms */
159 interval_us = BT_GAP_PER_ADV_INTERVAL_TO_US(pa_interval);
160 timeout = BT_GAP_US_TO_PER_ADV_SYNC_TIMEOUT(interval_us) *
161 PA_SYNC_INTERVAL_TO_TIMEOUT_RATIO;
162
163 /* Enforce restraints */
164 pa_timeout = CLAMP(timeout, BT_GAP_PER_ADV_MIN_TIMEOUT, BT_GAP_PER_ADV_MAX_TIMEOUT);
165 }
166
167 return pa_timeout;
168 }
169
pa_timer_handler(struct k_work * work)170 static void pa_timer_handler(struct k_work *work)
171 {
172 struct k_work_delayable *dwork = k_work_delayable_from_work(work);
173 struct sync_state *state = CONTAINER_OF(dwork, struct sync_state, pa_timer);
174
175 state->pa_syncing = false;
176
177 if (state->recv_state != NULL) {
178 enum bt_bap_pa_state pa_state;
179
180 if (state->recv_state->pa_sync_state == BT_BAP_PA_STATE_INFO_REQ) {
181 pa_state = BT_BAP_PA_STATE_NO_PAST;
182 } else {
183 pa_state = BT_BAP_PA_STATE_FAILED;
184 }
185
186 bt_bap_scan_delegator_set_pa_state(state->recv_state->src_id,
187 pa_state);
188
189 bt_shell_info("PA timeout for %p", state->recv_state);
190 }
191 }
192
193 #if defined(CONFIG_BT_PER_ADV_SYNC_TRANSFER_SENDER)
pa_sync_past(struct bt_conn * conn,struct sync_state * state,uint16_t pa_interval)194 static int pa_sync_past(struct bt_conn *conn,
195 struct sync_state *state,
196 uint16_t pa_interval)
197 {
198 struct bt_le_per_adv_sync_transfer_param param = { 0 };
199 int err;
200
201 param.skip = PA_SYNC_SKIP;
202 param.timeout = interval_to_sync_timeout(pa_interval);
203
204 err = bt_le_per_adv_sync_transfer_subscribe(conn, ¶m);
205 if (err != 0) {
206 bt_shell_info("Could not do PAST subscribe: %d", err);
207 } else {
208 bt_shell_info("Syncing with PAST: %d", err);
209 state->pa_syncing = true;
210 k_work_init_delayable(&state->pa_timer, pa_timer_handler);
211 (void)k_work_reschedule(&state->pa_timer,
212 K_MSEC(param.timeout * 10));
213 }
214
215 return err;
216 }
217 #endif /* CONFIG_BT_PER_ADV_SYNC_TRANSFER_SENDER */
218
pa_sync_no_past(struct sync_state * state,uint16_t pa_interval)219 static int pa_sync_no_past(struct sync_state *state,
220 uint16_t pa_interval)
221 {
222 const struct bt_bap_scan_delegator_recv_state *recv_state;
223 struct bt_le_per_adv_sync_param param = { 0 };
224 int err;
225
226 recv_state = state->recv_state;
227
228 bt_addr_le_copy(¶m.addr, &recv_state->addr);
229 param.options = BT_LE_PER_ADV_SYNC_OPT_FILTER_DUPLICATE;
230 param.sid = recv_state->adv_sid;
231 param.skip = PA_SYNC_SKIP;
232 param.timeout = interval_to_sync_timeout(pa_interval);
233
234 /* TODO: Validate that the advertise is broadcasting the same
235 * broadcast_id that the receive state has
236 */
237 err = bt_le_per_adv_sync_create(¶m, &state->pa_sync);
238 if (err != 0) {
239 bt_shell_info("Could not sync per adv: %d", err);
240 } else {
241 char addr_str[BT_ADDR_LE_STR_LEN];
242
243 bt_addr_le_to_str(&recv_state->addr, addr_str, sizeof(addr_str));
244 bt_shell_info("PA sync pending for addr %s", addr_str);
245 state->pa_syncing = true;
246 k_work_init_delayable(&state->pa_timer, pa_timer_handler);
247 (void)k_work_reschedule(&state->pa_timer,
248 K_MSEC(param.timeout * 10));
249 }
250
251 return err;
252 }
253
pa_sync_term(struct sync_state * state)254 static int pa_sync_term(struct sync_state *state)
255 {
256 int err;
257
258 (void)k_work_cancel_delayable(&state->pa_timer);
259
260 if (state->pa_sync == NULL) {
261 return -1;
262 }
263
264 bt_shell_info("Deleting PA sync");
265
266 err = bt_le_per_adv_sync_delete(state->pa_sync);
267 if (err != 0) {
268 bt_shell_error("Could not delete per adv sync: %d", err);
269 } else {
270 state->pa_syncing = false;
271 state->pa_sync = NULL;
272 }
273
274 return err;
275 }
276
recv_state_updated_cb(struct bt_conn * conn,const struct bt_bap_scan_delegator_recv_state * recv_state)277 static void recv_state_updated_cb(struct bt_conn *conn,
278 const struct bt_bap_scan_delegator_recv_state *recv_state)
279 {
280 bt_shell_info("Receive state with ID %u updated", recv_state->src_id);
281 }
282
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)283 static int pa_sync_req_cb(struct bt_conn *conn,
284 const struct bt_bap_scan_delegator_recv_state *recv_state,
285 bool past_avail, uint16_t pa_interval)
286 {
287 struct sync_state *state;
288
289 bt_shell_info(
290 "PA Sync request: past_avail %u, broadcast_id 0x%06X, pa_interval 0x%04x: %p",
291 past_avail, recv_state->broadcast_id, pa_interval, recv_state);
292
293 state = sync_state_get_or_new(recv_state);
294 if (state == NULL) {
295 bt_shell_error("Could not get state");
296
297 return -1;
298 }
299
300 state->recv_state = recv_state;
301 state->src_id = recv_state->src_id;
302
303 if (recv_state->pa_sync_state == BT_BAP_PA_STATE_SYNCED ||
304 recv_state->pa_sync_state == BT_BAP_PA_STATE_INFO_REQ) {
305 /* Already syncing */
306 /* TODO: Terminate existing sync and then sync to new?*/
307 return -1;
308 }
309
310 if (past_avail) {
311 state->past_avail = past_preference;
312 state->conn = bt_conn_ref(conn);
313 } else {
314 state->past_avail = false;
315 }
316
317 return 0;
318 }
319
pa_sync_term_req_cb(struct bt_conn * conn,const struct bt_bap_scan_delegator_recv_state * recv_state)320 static int pa_sync_term_req_cb(struct bt_conn *conn,
321 const struct bt_bap_scan_delegator_recv_state *recv_state)
322 {
323 struct sync_state *state;
324
325 bt_shell_info("PA Sync term request for %p", recv_state);
326
327 state = sync_state_get(recv_state);
328 if (state == NULL) {
329 bt_shell_error("Could not get state");
330
331 return -1;
332 }
333
334 return pa_sync_term(state);
335 }
336
broadcast_code_cb(struct bt_conn * conn,const struct bt_bap_scan_delegator_recv_state * recv_state,const uint8_t broadcast_code[BT_ISO_BROADCAST_CODE_SIZE])337 static void broadcast_code_cb(struct bt_conn *conn,
338 const struct bt_bap_scan_delegator_recv_state *recv_state,
339 const uint8_t broadcast_code[BT_ISO_BROADCAST_CODE_SIZE])
340 {
341 struct sync_state *state;
342
343 bt_shell_info("Broadcast code received for %p", recv_state);
344 bt_shell_hexdump(broadcast_code, BT_ISO_BROADCAST_CODE_SIZE);
345
346 state = sync_state_get(recv_state);
347 if (state == NULL) {
348 bt_shell_error("Could not get state");
349
350 return;
351 }
352
353 (void)memcpy(state->broadcast_code, broadcast_code, BT_ISO_BROADCAST_CODE_SIZE);
354 }
355
bis_sync_req_cb(struct bt_conn * conn,const struct bt_bap_scan_delegator_recv_state * recv_state,const uint32_t bis_sync_req[CONFIG_BT_BAP_BASS_MAX_SUBGROUPS])356 static int bis_sync_req_cb(struct bt_conn *conn,
357 const struct bt_bap_scan_delegator_recv_state *recv_state,
358 const uint32_t bis_sync_req[CONFIG_BT_BAP_BASS_MAX_SUBGROUPS])
359 {
360 printk("BIS sync request received for %p\n", recv_state);
361
362 for (int i = 0; i < CONFIG_BT_BAP_BASS_MAX_SUBGROUPS; i++) {
363 printk(" [%d]: 0x%08x\n", i, bis_sync_req[i]);
364 }
365
366 return 0;
367 }
368
369 static struct bt_bap_scan_delegator_cb scan_delegator_cb = {
370 .recv_state_updated = recv_state_updated_cb,
371 .pa_sync_req = pa_sync_req_cb,
372 .pa_sync_term_req = pa_sync_term_req_cb,
373 .broadcast_code = broadcast_code_cb,
374 .bis_sync_req = bis_sync_req_cb,
375 };
376
pa_synced_cb(struct bt_le_per_adv_sync * sync,struct bt_le_per_adv_sync_synced_info * info)377 static void pa_synced_cb(struct bt_le_per_adv_sync *sync,
378 struct bt_le_per_adv_sync_synced_info *info)
379 {
380 struct sync_state *state;
381
382 bt_shell_info("PA %p synced", sync);
383
384 if (info->conn == NULL) {
385 state = sync_state_get_by_pa(sync);
386 } else {
387 /* In case of PAST we need to use the addr instead */
388 state = sync_state_get_by_sync_info(info);
389 }
390
391 if (state == NULL) {
392 bt_shell_info("Could not get sync state from PA sync %p", sync);
393 return;
394 }
395
396 if (state->conn != NULL) {
397 bt_conn_unref(state->conn);
398 state->conn = NULL;
399 }
400
401 k_work_cancel_delayable(&state->pa_timer);
402 }
403
pa_term_cb(struct bt_le_per_adv_sync * sync,const struct bt_le_per_adv_sync_term_info * info)404 static void pa_term_cb(struct bt_le_per_adv_sync *sync,
405 const struct bt_le_per_adv_sync_term_info *info)
406 {
407 struct sync_state *state;
408
409 bt_shell_info("PA %p sync terminated", sync);
410
411 state = sync_state_get_by_pa(sync);
412 if (state == NULL) {
413 bt_shell_error("Could not get sync state from PA sync %p", sync);
414 return;
415 }
416
417 if (state->conn != NULL) {
418 bt_conn_unref(state->conn);
419 state->conn = NULL;
420 }
421
422 k_work_cancel_delayable(&state->pa_timer);
423 }
424
425 static struct bt_le_per_adv_sync_cb pa_sync_cb = {
426 .synced = pa_synced_cb,
427 .term = pa_term_cb,
428 };
429
cmd_bap_scan_delegator_init(const struct shell * sh,size_t argc,char ** argv)430 static int cmd_bap_scan_delegator_init(const struct shell *sh, size_t argc,
431 char **argv)
432 {
433 static bool registered;
434
435 if (!registered) {
436 int err;
437
438 err = bt_bap_scan_delegator_register(&scan_delegator_cb);
439 if (err) {
440 shell_error(sh, "Failed to register scan delegator (err: %d)", err);
441 return -ENOEXEC;
442 }
443
444 err = bt_le_per_adv_sync_cb_register(&pa_sync_cb);
445 if (err) {
446 shell_error(sh, "Failed to register PA sync callbacks (err: %d)", err);
447 return -ENOEXEC;
448 }
449
450 registered = true;
451 }
452
453 return 0;
454 }
cmd_bap_scan_delegator_set_past_pref(const struct shell * sh,size_t argc,char ** argv)455 static int cmd_bap_scan_delegator_set_past_pref(const struct shell *sh,
456 size_t argc, char **argv)
457 {
458 bool past_pref;
459 int err;
460
461 err = 0;
462
463 past_pref = shell_strtobool(argv[1], 10, &err);
464 if (err != 0) {
465 shell_error(sh, "Failed to parse past_pref from %s", argv[1]);
466 return -ENOEXEC;
467 }
468
469 past_preference = past_pref;
470
471 return 0;
472 }
473
cmd_bap_scan_delegator_sync_pa(const struct shell * sh,size_t argc,char ** argv)474 static int cmd_bap_scan_delegator_sync_pa(const struct shell *sh, size_t argc,
475 char **argv)
476 {
477 struct sync_state *state;
478 unsigned long src_id;
479 int err;
480
481 err = 0;
482
483 src_id = shell_strtoul(argv[1], 16, &err);
484 if (err != 0) {
485 shell_error(sh, "Failed to parse src_id from %s", argv[1]);
486
487 return -ENOEXEC;
488 }
489
490 if (src_id > UINT8_MAX) {
491 shell_error(sh, "src_id shall be 0x00-0xff");
492
493 return -ENOEXEC;
494 }
495
496 state = sync_state_get_by_src_id((uint8_t)src_id);
497 if (state == NULL) {
498 shell_error(sh, "Could not get state");
499
500 return -ENOEXEC;
501 }
502
503 if (0) {
504 #if defined(CONFIG_BT_PER_ADV_SYNC_TRANSFER_SENDER)
505 } else if (past_preference &&
506 state->past_avail &&
507 state->conn != NULL) {
508 shell_info(sh, "Syncing with PAST");
509
510 err = pa_sync_past(state->conn, state, state->pa_interval);
511 if (err == 0) {
512 err = bt_bap_scan_delegator_set_pa_state(src_id,
513 BT_BAP_PA_STATE_INFO_REQ);
514 if (err != 0) {
515 shell_error(sh,
516 "Failed to set INFO_REQ state: %d",
517 err);
518 }
519
520 return -ENOEXEC;
521 }
522 #endif /* CONFIG_BT_PER_ADV_SYNC_TRANSFER_SENDER */
523 } else {
524 shell_info(sh, "Syncing without PAST");
525 err = pa_sync_no_past(state, state->pa_interval);
526 }
527
528 if (err != 0) {
529 shell_error(sh, "Failed PA sync: %d", err);
530
531 return -ENOEXEC;
532 }
533
534 return 0;
535 }
536
cmd_bap_scan_delegator_term_pa(const struct shell * sh,size_t argc,char ** argv)537 static int cmd_bap_scan_delegator_term_pa(const struct shell *sh, size_t argc,
538 char **argv)
539 {
540 struct sync_state *state;
541 unsigned long src_id;
542 int err;
543
544 err = 0;
545
546 src_id = shell_strtoul(argv[1], 16, &err);
547 if (err != 0) {
548 shell_error(sh, "Failed to parse src_id from %s", argv[1]);
549
550 return -ENOEXEC;
551 }
552
553 if (src_id > UINT8_MAX) {
554 shell_error(sh, "src_id shall be 0x00-0xff");
555
556 return -ENOEXEC;
557 }
558
559 state = sync_state_get_by_src_id((uint8_t)src_id);
560 if (state == NULL) {
561 shell_error(sh, "Could not get state");
562
563 return -ENOEXEC;
564 }
565
566 err = pa_sync_term(state);
567 if (err != 0) {
568 shell_error(sh, "Failed to terminate PA sync: %d", err);
569
570 return -ENOEXEC;
571 }
572
573 return 0;
574 }
575
cmd_bap_scan_delegator_add_src(const struct shell * sh,size_t argc,char ** argv)576 static int cmd_bap_scan_delegator_add_src(const struct shell *sh, size_t argc, char **argv)
577 {
578 struct bt_bap_scan_delegator_add_src_param param = {0};
579 struct bt_bap_bass_subgroup *subgroup_param;
580 unsigned long broadcast_id;
581 struct sync_state *state;
582 unsigned long enc_state;
583 unsigned long adv_sid;
584 int err;
585
586 err = bt_addr_le_from_str(argv[1], argv[2], ¶m.addr);
587 if (err != 0) {
588 shell_error(sh, "Invalid peer address (err %d)", err);
589
590 return -ENOEXEC;
591 }
592
593 adv_sid = shell_strtoul(argv[3], 0, &err);
594 if (err != 0) {
595 shell_error(sh, "Could not parse adv_sid: %d", err);
596
597 return -ENOEXEC;
598 }
599
600 if (adv_sid > BT_GAP_SID_MAX) {
601 shell_error(sh, "Invalid adv_sid: %lu", adv_sid);
602
603 return -ENOEXEC;
604 }
605
606 param.sid = adv_sid;
607
608 broadcast_id = shell_strtoul(argv[4], 16, &err);
609 if (err != 0) {
610 shell_error(sh, "Failed to parse broadcast_id from %s", argv[1]);
611
612 return -EINVAL;
613 }
614
615 if (broadcast_id > BT_AUDIO_BROADCAST_ID_MAX) {
616 shell_error(sh, "Invalid broadcast_id %lu", broadcast_id);
617
618 return -EINVAL;
619 }
620
621 enc_state = shell_strtoul(argv[5], 16, &err);
622 if (err != 0) {
623 shell_error(sh, "Failed to parse enc_state from %s", argv[2]);
624
625 return -EINVAL;
626 }
627
628 if (enc_state > BT_BAP_BIG_ENC_STATE_BAD_CODE) {
629 shell_error(sh, "Invalid enc_state %s", bt_bap_big_enc_state_str(enc_state));
630
631 return -EINVAL;
632 }
633
634 /* TODO: Support multiple subgroups */
635 subgroup_param = ¶m.subgroups[0];
636 if (argc > 6) {
637 unsigned long bis_sync;
638
639 bis_sync = shell_strtoul(argv[6], 16, &err);
640 if (err != 0) {
641 shell_error(sh, "Failed to parse bis_sync from %s", argv[3]);
642
643 return -EINVAL;
644 }
645
646 if (bis_sync > BT_BAP_BIS_SYNC_NO_PREF) {
647 shell_error(sh, "Invalid bis_sync %lu", bis_sync);
648
649 return -EINVAL;
650 }
651 } else {
652 subgroup_param->bis_sync = 0U;
653 }
654
655 if (argc > 7) {
656 subgroup_param->metadata_len =
657 hex2bin(argv[4], strlen(argv[7]), subgroup_param->metadata,
658 sizeof(subgroup_param->metadata));
659
660 if (subgroup_param->metadata_len == 0U) {
661 shell_error(sh, "Could not parse metadata");
662
663 return -EINVAL;
664 }
665 } else {
666 subgroup_param->metadata_len = 0U;
667 }
668
669 state = sync_state_new();
670 if (state == NULL) {
671 shell_error(sh, "Could not get new state");
672
673 return -ENOEXEC;
674 }
675
676 param.encrypt_state = (enum bt_bap_big_enc_state)enc_state;
677 param.broadcast_id = broadcast_id;
678 param.num_subgroups = 1U;
679
680 err = bt_bap_scan_delegator_add_src(¶m);
681 if (err < 0) {
682 shell_error(sh, "Failed to add source: %d", err);
683
684 return -ENOEXEC;
685 }
686
687 state->src_id = (uint8_t)err;
688
689 return 0;
690 }
691
cmd_bap_scan_delegator_add_src_by_pa_sync(const struct shell * sh,size_t argc,char ** argv)692 static int cmd_bap_scan_delegator_add_src_by_pa_sync(const struct shell *sh, size_t argc,
693 char **argv)
694 {
695 struct bt_le_per_adv_sync *pa_sync = per_adv_syncs[selected_per_adv_sync];
696 struct bt_bap_scan_delegator_add_src_param param = {0};
697 struct bt_bap_bass_subgroup *subgroup_param;
698 struct bt_le_per_adv_sync_info sync_info;
699 unsigned long broadcast_id;
700 struct sync_state *state;
701 unsigned long enc_state;
702 int err;
703
704 err = bt_le_per_adv_sync_get_info(pa_sync, &sync_info);
705 if (err != 0) {
706 shell_error(sh, "Failed to get sync info: %d", err);
707
708 return -ENOEXEC;
709 }
710 bt_addr_le_copy(¶m.addr, &sync_info.addr);
711 param.sid = sync_info.sid;
712
713 broadcast_id = shell_strtoul(argv[1], 16, &err);
714 if (err != 0) {
715 shell_error(sh, "Failed to parse broadcast_id from %s", argv[1]);
716
717 return -EINVAL;
718 }
719
720 if (broadcast_id > BT_AUDIO_BROADCAST_ID_MAX) {
721 shell_error(sh, "Invalid broadcast_id %lu", broadcast_id);
722
723 return -EINVAL;
724 }
725
726 enc_state = shell_strtoul(argv[2], 16, &err);
727 if (err != 0) {
728 shell_error(sh, "Failed to parse enc_state from %s", argv[2]);
729
730 return -EINVAL;
731 }
732
733 if (enc_state > BT_BAP_BIG_ENC_STATE_BAD_CODE) {
734 shell_error(sh, "Invalid enc_state %s", bt_bap_big_enc_state_str(enc_state));
735
736 return -EINVAL;
737 }
738
739 /* TODO: Support multiple subgroups */
740 subgroup_param = ¶m.subgroups[0];
741 if (argc > 3) {
742 unsigned long bis_sync;
743
744 bis_sync = shell_strtoul(argv[3], 16, &err);
745 if (err != 0) {
746 shell_error(sh, "Failed to parse bis_sync from %s", argv[3]);
747
748 return -EINVAL;
749 }
750
751 if (bis_sync > BT_BAP_BIS_SYNC_NO_PREF) {
752 shell_error(sh, "Invalid bis_sync %lu", bis_sync);
753
754 return -EINVAL;
755 }
756 } else {
757 subgroup_param->bis_sync = 0U;
758 }
759
760 if (argc > 4) {
761 subgroup_param->metadata_len =
762 hex2bin(argv[4], strlen(argv[4]), subgroup_param->metadata,
763 sizeof(subgroup_param->metadata));
764
765 if (subgroup_param->metadata_len == 0U) {
766 shell_error(sh, "Could not parse metadata");
767
768 return -EINVAL;
769 }
770 } else {
771 subgroup_param->metadata_len = 0U;
772 }
773
774 state = sync_state_new();
775 if (state == NULL) {
776 shell_error(sh, "Could not get new state");
777
778 return -ENOEXEC;
779 }
780
781 param.encrypt_state = (enum bt_bap_big_enc_state)enc_state;
782 param.broadcast_id = broadcast_id;
783 param.num_subgroups = 1U;
784
785 err = bt_bap_scan_delegator_add_src(¶m);
786 if (err < 0) {
787 shell_error(sh, "Failed to add source: %d", err);
788
789 return -ENOEXEC;
790 }
791
792 state->src_id = (uint8_t)err;
793
794 return 0;
795 }
796
cmd_bap_scan_delegator_mod_src(const struct shell * sh,size_t argc,char ** argv)797 static int cmd_bap_scan_delegator_mod_src(const struct shell *sh, size_t argc,
798 char **argv)
799 {
800 struct bt_bap_bass_subgroup *subgroup_param;
801 struct bt_bap_scan_delegator_mod_src_param param;
802 unsigned long broadcast_id;
803 unsigned long enc_state;
804 unsigned long src_id;
805 int err;
806
807 err = 0;
808
809 src_id = shell_strtoul(argv[1], 16, &err);
810 if (err != 0) {
811 shell_error(sh, "Failed to parse src_id from %s", argv[1]);
812
813 return -EINVAL;
814 }
815
816 if (src_id > UINT8_MAX) {
817 shell_error(sh, "Invalid src_id %lu", src_id);
818
819 return -EINVAL;
820 }
821
822 broadcast_id = shell_strtoul(argv[2], 16, &err);
823 if (err != 0) {
824 shell_error(sh, "Failed to parse broadcast_id from %s", argv[2]);
825
826 return -EINVAL;
827 }
828
829 if (broadcast_id > BT_AUDIO_BROADCAST_ID_MAX) {
830 shell_error(sh, "Invalid broadcast_id %lu", broadcast_id);
831
832 return -EINVAL;
833 }
834
835 enc_state = shell_strtoul(argv[3], 16, &err);
836 if (err != 0) {
837 shell_error(sh, "Failed to parse enc_state from %s", argv[3]);
838
839 return -EINVAL;
840 }
841
842 if (enc_state > BT_BAP_BIG_ENC_STATE_BAD_CODE) {
843 shell_error(sh, "Invalid enc_state %s", bt_bap_big_enc_state_str(enc_state));
844
845 return -EINVAL;
846 }
847
848 /* TODO: Support multiple subgroups */
849 subgroup_param = ¶m.subgroups[0];
850 if (argc > 4) {
851 unsigned long bis_sync;
852
853 bis_sync = shell_strtoul(argv[4], 16, &err);
854 if (err != 0) {
855 shell_error(sh, "Failed to parse bis_sync from %s", argv[4]);
856
857 return -EINVAL;
858 }
859
860 if (bis_sync > BT_BAP_BIS_SYNC_NO_PREF) {
861 shell_error(sh, "Invalid bis_sync %lu", bis_sync);
862
863 return -EINVAL;
864 }
865 } else {
866 subgroup_param->bis_sync = 0U;
867 }
868
869 if (argc > 5) {
870 subgroup_param->metadata_len = hex2bin(argv[5], strlen(argv[5]),
871 subgroup_param->metadata,
872 sizeof(subgroup_param->metadata));
873
874 if (subgroup_param->metadata_len == 0U) {
875 shell_error(sh, "Could not parse metadata");
876
877 return -EINVAL;
878 }
879 } else {
880 subgroup_param->metadata_len = 0U;
881 }
882
883 param.src_id = (uint8_t)src_id;
884 param.encrypt_state = (enum bt_bap_big_enc_state)enc_state;
885 param.broadcast_id = broadcast_id;
886 param.num_subgroups = 1U;
887
888 err = bt_bap_scan_delegator_mod_src(¶m);
889 if (err < 0) {
890 shell_error(sh, "Failed to modify source: %d", err);
891
892 return -ENOEXEC;
893 }
894
895 return 0;
896 }
897
cmd_bap_scan_delegator_rem_src(const struct shell * sh,size_t argc,char ** argv)898 static int cmd_bap_scan_delegator_rem_src(const struct shell *sh, size_t argc,
899 char **argv)
900 {
901 unsigned long src_id;
902 int err;
903
904 err = 0;
905
906 src_id = shell_strtoul(argv[1], 16, &err);
907 if (err != 0) {
908 shell_error(sh, "Failed to parse src_id from %s", argv[1]);
909
910 return -EINVAL;
911 }
912
913 if (src_id > UINT8_MAX) {
914 shell_error(sh, "Invalid src_id %lu", src_id);
915
916 return -EINVAL;
917 }
918
919 err = bt_bap_scan_delegator_rem_src((uint8_t)src_id);
920 if (err < 0) {
921 shell_error(sh, "Failed to remove source source: %d", err);
922
923 return -ENOEXEC;
924 }
925
926 return 0;
927 }
928
cmd_bap_scan_delegator_bis_synced(const struct shell * sh,size_t argc,char ** argv)929 static int cmd_bap_scan_delegator_bis_synced(const struct shell *sh, size_t argc,
930 char **argv)
931 {
932 uint32_t bis_syncs[CONFIG_BT_BAP_BASS_MAX_SUBGROUPS];
933 unsigned long pa_sync_state;
934 unsigned long bis_synced;
935 unsigned long src_id;
936 int result = 0;
937
938 src_id = shell_strtoul(argv[1], 0, &result);
939 if (result != 0) {
940 shell_error(sh, "Could not parse src_id: %d", result);
941
942 return -ENOEXEC;
943 }
944
945 if (src_id > UINT8_MAX) {
946 shell_error(sh, "Invalid src_id: %lu", src_id);
947
948 return -ENOEXEC;
949 }
950
951 pa_sync_state = shell_strtoul(argv[2], 0, &result);
952 if (result != 0) {
953 shell_error(sh, "Could not parse pa_sync_state: %d", result);
954
955 return -ENOEXEC;
956 }
957
958 if (pa_sync_state > BT_BAP_PA_STATE_NO_PAST) {
959 shell_error(sh, "Invalid pa_sync_state %s", bt_bap_pa_state_str(pa_sync_state));
960
961 return -ENOEXEC;
962 }
963
964 bis_synced = shell_strtoul(argv[3], 0, &result);
965 if (result != 0) {
966 shell_error(sh, "Could not parse bis_synced: %d", result);
967
968 return -ENOEXEC;
969 }
970
971 if (bis_synced > UINT32_MAX) {
972 shell_error(sh, "Invalid bis_synced %ld", bis_synced);
973
974 return -ENOEXEC;
975 }
976
977 for (size_t i = 0U; i < ARRAY_SIZE(bis_syncs); i++) {
978 bis_syncs[i] = bis_synced;
979 }
980
981 result = bt_bap_scan_delegator_set_bis_sync_state(src_id, bis_syncs);
982 if (result != 0) {
983 shell_print(sh, "Fail: %d", result);
984 }
985
986 return result;
987 }
988
cmd_bap_scan_delegator(const struct shell * sh,size_t argc,char ** argv)989 static int cmd_bap_scan_delegator(const struct shell *sh, size_t argc,
990 char **argv)
991 {
992 if (argc > 1) {
993 shell_error(sh, "%s unknown parameter: %s",
994 argv[0], argv[1]);
995 } else {
996 shell_error(sh, "%s Missing subcommand", argv[0]);
997 }
998
999 return -ENOEXEC;
1000 }
1001
1002 SHELL_STATIC_SUBCMD_SET_CREATE(bap_scan_delegator_cmds,
1003 SHELL_CMD_ARG(init, NULL,
1004 "Initialize the service and register callbacks",
1005 cmd_bap_scan_delegator_init, 1, 0),
1006 SHELL_CMD_ARG(set_past_pref, NULL,
1007 "Set PAST preference <true || false>",
1008 cmd_bap_scan_delegator_set_past_pref, 2, 0),
1009 SHELL_CMD_ARG(sync_pa, NULL,
1010 "Sync to PA <src_id>",
1011 cmd_bap_scan_delegator_sync_pa, 2, 0),
1012 SHELL_CMD_ARG(term_pa, NULL,
1013 "Terminate PA sync <src_id>",
1014 cmd_bap_scan_delegator_term_pa, 2, 0),
1015 SHELL_CMD_ARG(add_src, NULL,
1016 "Add a PA as source <addr> <sid> <broadcast_id> <enc_state> "
1017 "[bis_sync [metadata]]",
1018 cmd_bap_scan_delegator_add_src, 5, 2),
1019 SHELL_CMD_ARG(add_src_by_pa_sync, NULL,
1020 "Add a PA as source <broadcast_id> <enc_state> [bis_sync [metadata]]",
1021 cmd_bap_scan_delegator_add_src_by_pa_sync, 3, 2),
1022 SHELL_CMD_ARG(mod_src, NULL,
1023 "Modify source <src_id> <broadcast_id> <enc_state> [bis_sync [metadata]]",
1024 cmd_bap_scan_delegator_mod_src, 4, 2),
1025 SHELL_CMD_ARG(rem_src, NULL,
1026 "Remove source <src_id>",
1027 cmd_bap_scan_delegator_rem_src, 2, 0),
1028 SHELL_CMD_ARG(synced, NULL,
1029 "Set server scan state <src_id> <bis_syncs>",
1030 cmd_bap_scan_delegator_bis_synced, 3, 0),
1031 SHELL_SUBCMD_SET_END,
1032 );
1033
1034 SHELL_CMD_ARG_REGISTER(bap_scan_delegator, &bap_scan_delegator_cmds,
1035 "Bluetooth BAP scan delegator shell commands",
1036 cmd_bap_scan_delegator, 1, 1);
1037