|
18 | 18 | #include <zephyr/bluetooth/hci.h>
|
19 | 19 | #include <zephyr/bluetooth/audio/audio.h>
|
20 | 20 | #include <zephyr/bluetooth/audio/bap.h>
|
| 21 | +#include <zephyr/sys/byteorder.h> |
| 22 | + |
21 | 23 | #include "shell/bt.h"
|
22 | 24 | #include "../../host/hci_core.h"
|
23 | 25 | #include "audio.h"
|
24 | 26 |
|
| 27 | +#define INVALID_BROADCAST_ID 0xFFFFFFFFU |
| 28 | + |
25 | 29 | static struct bt_bap_base received_base;
|
26 | 30 |
|
| 31 | +static struct bt_auto_scan { |
| 32 | + uint32_t broadcast_id; |
| 33 | + bool pa_sync; |
| 34 | + struct bt_bap_scan_delegator_subgroup subgroup; |
| 35 | +} auto_scan = { |
| 36 | + .broadcast_id = INVALID_BROADCAST_ID, |
| 37 | +}; |
| 38 | + |
27 | 39 | static bool pa_decode_base(struct bt_data *data, void *user_data)
|
28 | 40 | {
|
29 | 41 | struct bt_bap_base base = { 0 };
|
@@ -480,6 +492,182 @@ static int cmd_bap_broadcast_assistant_add_src(const struct shell *sh,
|
480 | 492 | return result;
|
481 | 493 | }
|
482 | 494 |
|
| 495 | +static bool broadcast_source_found(struct bt_data *data, void *user_data) |
| 496 | +{ |
| 497 | + struct bt_bap_broadcast_assistant_add_src_param param = { 0 }; |
| 498 | + const struct bt_le_scan_recv_info *info = user_data; |
| 499 | + struct bt_uuid_16 adv_uuid; |
| 500 | + uint32_t broadcast_id; |
| 501 | + int err; |
| 502 | + |
| 503 | + /* Verify that it is a BAP broadcaster*/ |
| 504 | + |
| 505 | + if (data->type != BT_DATA_SVC_DATA16) { |
| 506 | + return true; |
| 507 | + } |
| 508 | + |
| 509 | + if (data->data_len < BT_UUID_SIZE_16 + BT_AUDIO_BROADCAST_ID_SIZE) { |
| 510 | + return true; |
| 511 | + } |
| 512 | + |
| 513 | + if (!bt_uuid_create(&adv_uuid.uuid, data->data, BT_UUID_SIZE_16)) { |
| 514 | + return true; |
| 515 | + } |
| 516 | + |
| 517 | + if (bt_uuid_cmp(&adv_uuid.uuid, BT_UUID_BROADCAST_AUDIO) != 0) { |
| 518 | + return true; |
| 519 | + } |
| 520 | + |
| 521 | + broadcast_id = sys_get_le24(data->data + BT_UUID_SIZE_16); |
| 522 | + |
| 523 | + if (broadcast_id != auto_scan.broadcast_id) { |
| 524 | + /* Not the one we want */ |
| 525 | + return false; |
| 526 | + } |
| 527 | + |
| 528 | + shell_print(ctx_shell, |
| 529 | + "Found BAP broadcast source with addressand ID 0x%06X\n", |
| 530 | + broadcast_id); |
| 531 | + |
| 532 | + bt_addr_le_copy(¶m.addr, info->addr); |
| 533 | + param.adv_sid = info->sid; |
| 534 | + param.pa_interval = info->interval; |
| 535 | + param.broadcast_id = broadcast_id; |
| 536 | + param.pa_sync = auto_scan.pa_sync; |
| 537 | + param.num_subgroups = 1; |
| 538 | + param.subgroups = &auto_scan.subgroup; |
| 539 | + |
| 540 | + err = bt_bap_broadcast_assistant_add_src(default_conn, ¶m); |
| 541 | + if (err) { |
| 542 | + shell_print(ctx_shell, "Failed to add source: %d", err); |
| 543 | + } |
| 544 | + |
| 545 | + memset(&auto_scan, 0, sizeof(auto_scan)); |
| 546 | + auto_scan.broadcast_id = INVALID_BROADCAST_ID; |
| 547 | + |
| 548 | + return false; |
| 549 | +} |
| 550 | + |
| 551 | +static void scan_recv_cb(const struct bt_le_scan_recv_info *info, |
| 552 | + struct net_buf_simple *ad) |
| 553 | +{ |
| 554 | + if (auto_scan.broadcast_id == INVALID_BROADCAST_ID) { |
| 555 | + /* no op */ |
| 556 | + return; |
| 557 | + } |
| 558 | + |
| 559 | + /* We are only interested in non-connectable periodic advertisers */ |
| 560 | + if ((info->adv_props & BT_GAP_ADV_PROP_CONNECTABLE) != 0 || |
| 561 | + info->interval == 0) { |
| 562 | + return; |
| 563 | + } |
| 564 | + |
| 565 | + bt_data_parse(ad, broadcast_source_found, (void *)info); |
| 566 | +} |
| 567 | + |
| 568 | +static void scan_timeout_cb(void) |
| 569 | +{ |
| 570 | + shell_print(ctx_shell, "Scan timeout"); |
| 571 | + |
| 572 | + if (auto_scan.broadcast_id != INVALID_BROADCAST_ID) { |
| 573 | + memset(&auto_scan, 0, sizeof(auto_scan)); |
| 574 | + auto_scan.broadcast_id = INVALID_BROADCAST_ID; |
| 575 | + } |
| 576 | +} |
| 577 | + |
| 578 | +static struct bt_le_scan_cb scan_callbacks = { |
| 579 | + .recv = scan_recv_cb, |
| 580 | + .timeout = scan_timeout_cb, |
| 581 | +}; |
| 582 | + |
| 583 | +static int cmd_bap_broadcast_assistant_add_broadcast_id(const struct shell *sh, |
| 584 | + size_t argc, |
| 585 | + char **argv) |
| 586 | +{ |
| 587 | + struct bt_bap_scan_delegator_subgroup subgroup = { 0 }; |
| 588 | + static bool scan_cbs_registered; |
| 589 | + unsigned long broadcast_id; |
| 590 | + unsigned long pa_sync; |
| 591 | + int err = 0; |
| 592 | + |
| 593 | + if (!scan_cbs_registered) { |
| 594 | + bt_le_scan_cb_register(&scan_callbacks); |
| 595 | + scan_cbs_registered = true; |
| 596 | + } |
| 597 | + |
| 598 | + if (auto_scan.broadcast_id != INVALID_BROADCAST_ID) { |
| 599 | + shell_info(sh, "Already scanning, wait for sync or timeout"); |
| 600 | + |
| 601 | + return -ENOEXEC; |
| 602 | + } |
| 603 | + |
| 604 | + broadcast_id = shell_strtoul(argv[0], 0, &err); |
| 605 | + if (err != 0) { |
| 606 | + shell_error(sh, "failed to parse broadcast_id: %d", err); |
| 607 | + |
| 608 | + return -ENOEXEC; |
| 609 | + } else if (broadcast_id > 0xFFFFFF /* 24 bits */) { |
| 610 | + shell_error(sh, "Broadcast ID maximum 24 bits (was %x)", |
| 611 | + broadcast_id); |
| 612 | + |
| 613 | + return -ENOEXEC; |
| 614 | + } |
| 615 | + |
| 616 | + pa_sync = shell_strtoul(argv[1], 0, &err); |
| 617 | + if (err != 0) { |
| 618 | + shell_error(sh, "failed to parse pa_sync: %d", err); |
| 619 | + |
| 620 | + return -ENOEXEC; |
| 621 | + } else if (pa_sync != 0U && pa_sync != 1U) { |
| 622 | + shell_error(sh, "pa_sync shall be boolean: %ul", pa_sync); |
| 623 | + |
| 624 | + return -ENOEXEC; |
| 625 | + } |
| 626 | + |
| 627 | + /* TODO: Support multiple subgroups */ |
| 628 | + if (argc > 2) { |
| 629 | + const unsigned long bis_sync = shell_strtoul(argv[2], 0, &err); |
| 630 | + |
| 631 | + if (err != 0) { |
| 632 | + shell_error(sh, "failed to parse bis_sync: %d", err); |
| 633 | + |
| 634 | + return -ENOEXEC; |
| 635 | + } else if (!IN_RANGE(bis_sync, 0, UINT32_MAX)) { |
| 636 | + shell_error(sh, "Invalid bis_sync: %lu", bis_sync); |
| 637 | + |
| 638 | + return -ENOEXEC; |
| 639 | + } |
| 640 | + |
| 641 | + subgroup.bis_sync = bis_sync; |
| 642 | + } |
| 643 | + |
| 644 | + if (argc > 3) { |
| 645 | + subgroup.metadata_len = hex2bin(argv[3], strlen(argv[3]), |
| 646 | + subgroup.metadata, |
| 647 | + sizeof(subgroup.metadata)); |
| 648 | + |
| 649 | + if (subgroup.metadata_len == 0U) { |
| 650 | + shell_error(sh, "Could not parse metadata"); |
| 651 | + |
| 652 | + return -ENOEXEC; |
| 653 | + } |
| 654 | + } |
| 655 | + |
| 656 | + err = bt_le_scan_start(BT_LE_SCAN_PASSIVE, NULL); |
| 657 | + if (err) { |
| 658 | + shell_print(sh, "Fail to start scanning: %d", err); |
| 659 | + |
| 660 | + return -ENOEXEC; |
| 661 | + } |
| 662 | + |
| 663 | + /* Store results in the `auto_scan` struct */ |
| 664 | + auto_scan.broadcast_id = broadcast_id; |
| 665 | + auto_scan.pa_sync = pa_sync; |
| 666 | + memcpy(&auto_scan.subgroup, &subgroup, sizeof(subgroup)); |
| 667 | + |
| 668 | + return 0; |
| 669 | +} |
| 670 | + |
483 | 671 | static int cmd_bap_broadcast_assistant_mod_src(const struct shell *sh,
|
484 | 672 | size_t argc, char **argv)
|
485 | 673 | {
|
@@ -819,6 +1007,10 @@ SHELL_STATIC_SUBCMD_SET_CREATE(bap_broadcast_assistant_cmds,
|
819 | 1007 | "<broadcast_id> [<pa_interval>] [<sync_bis>] "
|
820 | 1008 | "[<metadata>]",
|
821 | 1009 | cmd_bap_broadcast_assistant_add_src, 6, 3),
|
| 1010 | + SHELL_CMD_ARG(add_broadcast_id, NULL, |
| 1011 | + "Add a source by broadcast ID <broadcast_id> <sync_pa> " |
| 1012 | + "[<sync_bis>] [<metadata>]", |
| 1013 | + cmd_bap_broadcast_assistant_add_broadcast_id, 3, 2), |
822 | 1014 | SHELL_CMD_ARG(add_pa_sync, NULL,
|
823 | 1015 | "Add a PA sync as a source <sync_pa> <broadcast_id> "
|
824 | 1016 | "[bis_index [bis_index [bix_index [...]]]]>",
|
|
0 commit comments