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