diff --git a/doc/connectivity/bluetooth/shell/audio/cap.rst b/doc/connectivity/bluetooth/shell/audio/cap.rst index 47ff4781e0cac..c22d5bb0d99c1 100644 --- a/doc/connectivity/bluetooth/shell/audio/cap.rst +++ b/doc/connectivity/bluetooth/shell/audio/cap.rst @@ -485,3 +485,104 @@ Distributing the broadcast code Broadcast reception start completed uart:~$ cap_commander distribute_broadcast_code 0 "BroadcastCode" Distribute broadcast code completed + +CAP Handover +************ + +The handover procedures allow the user to switch between unicast and broadcast streams. Since +broadcast streams are always unidirectional, the procedures will only work for streams with audio +direction from the Initiator to the Acceptor (sink streams). + +Using the CAP Hanover procedures +================================ + +When the Bluetooth stack has been initialized (:code:`bt init`), +,one or more remote CAP acceptor devices have been connected, +and audio streams have been set up, +the handover procedures can be used to switch between unicast and broadcast. +Before any of the handover procedures can be used, +the :code:`bap discover`, :code:`cap_initiator discover` +and :code:`bap_broadcast_assistant discover` commands must have been issued and completed. + +.. code-block:: console + + cap_handover --help + cap_handover - Bluetooth CAP handover shell commands + Subcommands: + unicast_to_broadcast : Handover current unicast group to broadcast (unicast + group will be deleted) + broadcast_to_unicast : Handover current broadcast source to unicast + (broadcast source will be deleted) + + + +Handover unicast to broadcast +----------------------------- + +This command hands over one or more unicast streams from unicast to broadcast. + +.. code-block:: console + + uart:~$ bt init + uart:~$ bap init + uart:~$ bt connect + + # Discover necessary services + uart:~$ bap discover + uart:~$ cap_initiator discover + uart:~$ bap_broadcast_assistant discover + + # Setup unicast audio e.g. using the ac_1 + uart:~$ cap_initiator ac_1 + + # Create a non-connectable and non-scannable extended advertising set for broadcast + uart:~$ bt adv-create nconn-nscan ext-adv + uart:~$ bt per-adv-param + + # Perform the handover and update the advertising data to contain the broadcast ID + uart:~$ cap_handover unicast_to_broadcast + uart:~$ bt adv-data dev-name discov + uart:~$ bt per-adv-data + + # Enable periodic advertising (extended advertising is enabled as part of handover) + uart:~$ bt per-adv on + +Handover broadcast to unicast +----------------------------- + +This command hands over one or more unicast streams from broadcast to unicast. + +.. code-block:: console + + uart:~$ bt init + uart:~$ bap init + uart:~$ bt connect + + # Discover necessary services + uart:~$ bap discover + uart:~$ cap_initiator discover + uart:~$ bap_broadcast_assistant discover + + # Create a non-connectable and non-scannable extended advertising set for broadcast + uart:~$ bt adv-create nconn-nscan ext-adv + uart:~$ bt per-adv-param + + # Setup broadcast audio e.g. using the ac_12 + uart:~$ cap_initiator ac_12 + uart:~$ cap_initiator broadcast_start + + # Set advertising data and enable advertising + uart:~$ bt adv-data dev-name discov + uart:~$ bt per-adv-data + uart:~$ bt per-adv on + uart:~$ bt adv-start + + # Wait for broadcast sink to self-scan, or use broadcast reception to instruct sink to sync + + # Perform the handover + uart:~$ cap_handover broadcast_to_unicast + + # Terminate the advertiser (optional) + bt adv-stop + bt per-adv off + bt adv-delete diff --git a/subsys/bluetooth/audio/shell/CMakeLists.txt b/subsys/bluetooth/audio/shell/CMakeLists.txt index db1641a86f893..0486d22e3eb8d 100644 --- a/subsys/bluetooth/audio/shell/CMakeLists.txt +++ b/subsys/bluetooth/audio/shell/CMakeLists.txt @@ -71,6 +71,10 @@ zephyr_library_sources_ifdef( CONFIG_BT_CAP_COMMANDER cap_commander.c ) +zephyr_library_sources_ifdef( + CONFIG_BT_CAP_HANDOVER + cap_handover.c + ) zephyr_library_sources_ifdef( CONFIG_BT_HAS_CLIENT has_client.c diff --git a/subsys/bluetooth/audio/shell/audio.h b/subsys/bluetooth/audio/shell/audio.h index 9695e0c869580..239d2cb3a4640 100644 --- a/subsys/bluetooth/audio/shell/audio.h +++ b/subsys/bluetooth/audio/shell/audio.h @@ -5,7 +5,7 @@ */ /* - * Copyright (c) 2023 Nordic Semiconductor ASA + * Copyright (c) 2023-2025 Nordic Semiconductor ASA * * SPDX-License-Identifier: Apache-2.0 */ @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -32,8 +33,10 @@ #include #include #include +#include #include "common/bt_shell_private.h" +#include "host/shell/bt.h" #define SHELL_PRINT_INDENT_LEVEL_SIZE 2 #define MAX_CODEC_FRAMES_PER_SDU 4U @@ -168,14 +171,17 @@ void bap_usb_get_frame(struct shell_stream *sh_stream, enum bt_audio_location ch size_t bap_usb_get_frame_size(const struct shell_stream *sh_stream); struct broadcast_source { - bool is_cap; + bool is_cap: 1; + bool handover_in_progress: 1; union { struct bt_bap_broadcast_source *bap_source; struct bt_cap_broadcast_source *cap_source; }; struct bt_audio_codec_cfg codec_cfg; struct bt_bap_qos_cfg qos; - uint32_t broadcast_id; + uint32_t broadcast_id; /* BT_BAP_INVALID_BROADCAST_ID when not in use */ + uint8_t addr_type; + uint8_t adv_sid; /* BT_GAP_SID_INVALID when not in use */ }; struct broadcast_sink { @@ -183,7 +189,6 @@ struct broadcast_sink { struct bt_le_per_adv_sync *pa_sync; uint8_t received_base[UINT8_MAX]; uint8_t base_size; - uint32_t broadcast_id; size_t stream_cnt; bool syncable; }; @@ -196,6 +201,32 @@ struct unicast_group { }; }; +struct broadcast_assistant_recv_state { + /* Number of receive state on the remote device */ + uint8_t recv_state_count; + +#if defined(CONFIG_BT_BAP_BROADCAST_SOURCE) + /* Contains the src_id representing our local default_broadcast */ + uint8_t default_source_src_id; + uint8_t default_source_subgroup_count; + bool default_source_big_synced; +#endif /* CONFIG_BT_BAP_BROADCAST_SOURCE */ +}; + +struct scan_delegator_sync_state { + uint8_t broadcast_code[BT_ISO_BROADCAST_CODE_SIZE]; + const struct bt_bap_scan_delegator_recv_state *recv_state; + struct bt_le_per_adv_sync *pa_sync; + struct bt_conn *conn; + struct k_work_delayable pa_timer; + uint32_t broadcast_id; + uint16_t pa_interval; + bool active; + bool pa_syncing; + bool past_avail; + uint8_t src_id; +}; + #define BAP_UNICAST_AC_MAX_CONN 2U #define BAP_UNICAST_AC_MAX_SNK (2U * BAP_UNICAST_AC_MAX_CONN) #define BAP_UNICAST_AC_MAX_SRC (2U * BAP_UNICAST_AC_MAX_CONN) @@ -235,9 +266,40 @@ extern struct named_lc3_preset default_sink_preset; extern struct named_lc3_preset default_source_preset; int cap_ac_unicast(const struct shell *sh, const struct cap_unicast_ac_param *param); + +#if defined(CONFIG_BT_CAP_INITIATOR) +extern struct bt_cap_unicast_audio_start_stream_param + cap_initiator_audio_start_stream_params[UNICAST_CLIENT_STREAM_COUNT]; +extern struct bt_cap_unicast_group_stream_param + cap_initiator_unicast_group_stream_params[UNICAST_CLIENT_STREAM_COUNT]; +extern struct bt_cap_unicast_group_stream_pair_param + cap_initiator_unicast_group_pair_params[UNICAST_CLIENT_STREAM_COUNT]; +extern struct bt_cap_unicast_audio_start_param cap_initiator_unicast_audio_start_param; +extern struct bt_cap_unicast_group_param cap_initiator_unicast_group_param; +#endif /* CONFIG_BT_CAP_INITIATOR */ #endif /* CONFIG_BT_BAP_UNICAST_CLIENT */ #endif /* CONFIG_BT_BAP_UNICAST */ +#if defined(CONFIG_BT_BAP_BROADCAST_ASSISTANT) +extern struct broadcast_assistant_recv_state broadcast_assistant_recv_states[CONFIG_BT_MAX_CONN]; + +#if defined(CONFIG_BT_CAP_COMMANDER) +extern struct bt_cap_commander_broadcast_reception_stop_param cap_commander_reception_stop_param; +extern struct bt_cap_commander_broadcast_reception_stop_member_param + cap_commander_reception_stop_member_params[CONFIG_BT_MAX_CONN]; +#endif /* CONFIG_BT_CAP_COMMANDER */ +#endif /* CONFIG_BT_BAP_BROADCAST_ASSISTANT */ +#if defined(CONFIG_BT_BAP_SCAN_DELEGATOR) +extern struct scan_delegator_sync_state + scan_delegator_sync_states[CONFIG_BT_BAP_SCAN_DELEGATOR_RECV_STATE_COUNT]; + +struct scan_delegator_sync_state *scan_delegator_sync_state_new(void); +struct scan_delegator_sync_state * +scan_delegator_sync_state_get_by_pa(struct bt_le_per_adv_sync *sync); +struct scan_delegator_sync_state * +scan_delegator_sync_state_get_by_values(uint32_t broadcast_id, uint8_t addr_type, uint8_t sid); +#endif /* CONFIG_BT_BAP_SCAN_DELEGATOR */ + static inline void print_qos(const struct bt_bap_qos_cfg *qos) { #if defined(CONFIG_BT_BAP_BROADCAST_SOURCE) || defined(CONFIG_BT_BAP_UNICAST) @@ -796,6 +858,22 @@ int cap_ac_broadcast(const struct shell *sh, size_t argc, char **argv, extern struct shell_stream broadcast_source_streams[CONFIG_BT_BAP_BROADCAST_SRC_STREAM_COUNT]; extern struct broadcast_source default_source; extern struct named_lc3_preset default_broadcast_source_preset; + +#if defined(CONFIG_BT_CAP_INITIATOR) +#define MAX_CAP_BROADCAST_STREAMS \ + MAX(BAP_UNICAST_AC_MAX_SRC, \ + COND_CODE_1(CONFIG_BT_CAP_HANDOVER, \ + (CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SNK_COUNT), (0))) +BUILD_ASSERT(MAX_CAP_BROADCAST_STREAMS > CONFIG_BT_BAP_BROADCAST_SRC_STREAM_COUNT, + "CONFIG_BT_BAP_BROADCAST_SRC_STREAM_COUNT needs to be equal or greater to " + "MAX_CAP_BROADCAST_STREAMS"); + +extern struct bt_cap_initiator_broadcast_stream_param + cap_initiator_broadcast_stream_params[MAX_CAP_BROADCAST_STREAMS]; + +extern struct bt_cap_initiator_broadcast_subgroup_param cap_initiator_broadcast_subgroup_param; +extern struct bt_cap_initiator_broadcast_create_param cap_initiator_broadcast_create_param; +#endif /* CONFIG_BT_CAP_INITIATOR */ #endif /* CONFIG_BT_BAP_BROADCAST_SOURCE */ static inline bool print_base_subgroup_bis_cb(const struct bt_bap_base_subgroup_bis *bis, diff --git a/subsys/bluetooth/audio/shell/bap.c b/subsys/bluetooth/audio/shell/bap.c index d2f562e7fdac4..cd3c940f45a2b 100644 --- a/subsys/bluetooth/audio/shell/bap.c +++ b/subsys/bluetooth/audio/shell/bap.c @@ -5,7 +5,7 @@ /* * Copyright (c) 2020 Intel Corporation - * Copyright (c) 2022-2023 Nordic Semiconductor ASA + * Copyright (c) 2022-2025 Nordic Semiconductor ASA * * SPDX-License-Identifier: Apache-2.0 */ @@ -61,24 +61,24 @@ #if defined(CONFIG_BT_BAP_UNICAST) -struct shell_stream unicast_streams[CONFIG_BT_MAX_CONN * - MAX(UNICAST_SERVER_STREAM_COUNT, UNICAST_CLIENT_STREAM_COUNT)]; +struct shell_stream unicast_streams[CONFIG_BT_MAX_CONN * MAX(UNICAST_SERVER_STREAM_COUNT, + UNICAST_CLIENT_STREAM_COUNT)] = {0}; #if defined(CONFIG_BT_BAP_UNICAST_CLIENT) -struct unicast_group default_unicast_group; +struct unicast_group default_unicast_group = {0}; static struct bt_bap_unicast_client_cb unicast_client_cbs; #if CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SNK_COUNT > 0 -struct bt_bap_ep *snks[CONFIG_BT_MAX_CONN][CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SNK_COUNT]; +struct bt_bap_ep *snks[CONFIG_BT_MAX_CONN][CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SNK_COUNT] = {0}; #endif /* CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SNK_COUNT > 0 */ #if CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SRC_COUNT > 0 -struct bt_bap_ep *srcs[CONFIG_BT_MAX_CONN][CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SRC_COUNT]; +struct bt_bap_ep *srcs[CONFIG_BT_MAX_CONN][CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SRC_COUNT] = {0}; #endif /* CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SRC_COUNT > 0 */ #endif /* CONFIG_BT_BAP_UNICAST_CLIENT */ #endif /* CONFIG_BT_BAP_UNICAST */ #if defined(CONFIG_BT_BAP_BROADCAST_SOURCE) -struct shell_stream broadcast_source_streams[CONFIG_BT_BAP_BROADCAST_SRC_STREAM_COUNT]; -struct broadcast_source default_source; +struct shell_stream broadcast_source_streams[CONFIG_BT_BAP_BROADCAST_SRC_STREAM_COUNT] = {0}; +struct broadcast_source default_source = {0}; #endif /* CONFIG_BT_BAP_BROADCAST_SOURCE */ #if defined(CONFIG_BT_BAP_BROADCAST_SINK) static struct shell_stream broadcast_sink_streams[CONFIG_BT_BAP_BROADCAST_SNK_STREAM_COUNT]; @@ -390,7 +390,6 @@ static bool encode_frame(struct shell_stream *sh_stream, uint8_t index, size_t f false; } - /* TODO: Move the following to a function in bap_usb.c*/ bap_usb_get_frame(sh_stream, chan_alloc, lc3_tx_buf); } else { /* Generate sine wave */ @@ -817,6 +816,20 @@ static int set_metadata(struct bt_audio_codec_cfg *codec_cfg, const char *meta_s } #if defined(CONFIG_BT_BAP_UNICAST_CLIENT) +static void disconnected_cb(struct bt_conn *conn, uint8_t reason) +{ +#if CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SNK_COUNT > 0 + (void)memset(snks[bt_conn_index(conn)], 0, sizeof(snks[0])); +#endif /* CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SNK_COUNT > 0 */ +#if CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SRC_COUNT > 0 + (void)memset(srcs[bt_conn_index(conn)], 0, sizeof(srcs[0])); +#endif /* CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SRC_COUNT > 0 */ +} + +BT_CONN_CB_DEFINE(conn_callbacks) = { + .disconnected = disconnected_cb, +}; + static uint8_t stream_dir(const struct bt_bap_stream *stream) { if (stream->conn) { @@ -2299,7 +2312,6 @@ struct bt_broadcast_info { static struct broadcast_sink_auto_scan { struct broadcast_sink *broadcast_sink; struct bt_broadcast_info broadcast_info; - struct bt_le_per_adv_sync **out_sync; } auto_scan = { .broadcast_info = { .broadcast_id = BT_BAP_INVALID_BROADCAST_ID, @@ -2363,6 +2375,49 @@ static bool scan_check_and_get_broadcast_values(struct bt_data *data, void *user } } +static void pa_sync_broadcast_sink(const struct bt_le_scan_recv_info *info) +{ + struct bt_le_per_adv_sync_param create_params = {0}; + int err; + + err = bt_le_scan_stop(); + if (err != 0) { + bt_shell_error("Could not stop scan: %d", err); + } + + bt_addr_le_copy(&create_params.addr, info->addr); + create_params.options = BT_LE_PER_ADV_SYNC_OPT_FILTER_DUPLICATE; + create_params.sid = info->sid; + create_params.skip = PA_SYNC_SKIP; + create_params.timeout = interval_to_sync_timeout(info->interval); + + bt_shell_print("Attempting to PA sync to the broadcaster"); + err = bt_le_per_adv_sync_create(&create_params, &per_adv_syncs[selected_per_adv_sync]); + if (err != 0) { + bt_shell_error("Could not create Broadcast PA sync: %d", err); + } else { + struct bt_le_per_adv_sync *pa_sync = per_adv_syncs[selected_per_adv_sync]; + struct scan_delegator_sync_state *sync_state = NULL; + + default_broadcast_sink.pa_sync = pa_sync; + + sync_state = scan_delegator_sync_state_get_by_values( + auto_scan.broadcast_info.broadcast_id, info->addr->type, info->sid); + if (sync_state == NULL) { + sync_state = scan_delegator_sync_state_new(); + + if (sync_state == NULL) { + bt_shell_error("Could not get new sync state"); + + return; + } + } + + sync_state->pa_sync = pa_sync; + sync_state->broadcast_id = auto_scan.broadcast_info.broadcast_id; + } +} + static void broadcast_scan_recv(const struct bt_le_scan_recv_info *info, struct net_buf_simple *ad) { struct bt_broadcast_info sr_info = {0}; @@ -2384,8 +2439,11 @@ static void broadcast_scan_recv(const struct bt_le_scan_recv_info *info, struct bt_addr_le_to_str(info->addr, addr_str, sizeof(addr_str)); - bt_shell_print("Found broadcaster with ID 0x%06X (%s) and addr %s and sid 0x%02X ", - sr_info.broadcast_id, sr_info.broadcast_name, addr_str, info->sid); + bt_shell_print("Found broadcaster with ID 0x%06X (%s) and addr %s and sid 0x%02X (scanning " + "for 0x%06X (%s))", + sr_info.broadcast_id, sr_info.broadcast_name, addr_str, info->sid, + auto_scan.broadcast_info.broadcast_id, + auto_scan.broadcast_info.broadcast_name); if ((auto_scan.broadcast_info.broadcast_id == BT_BAP_INVALID_BROADCAST_ID) && (strlen(auto_scan.broadcast_info.broadcast_name) == 0U)) { @@ -2399,34 +2457,16 @@ static void broadcast_scan_recv(const struct bt_le_scan_recv_info *info, struct is_substring(auto_scan.broadcast_info.broadcast_name, sr_info.broadcast_name)) { auto_scan.broadcast_info.broadcast_id = sr_info.broadcast_id; identified_broadcast = true; - - bt_shell_print("Found matched broadcast name '%s' with address %s", - sr_info.broadcast_name, addr_str); + } else { + /* no op */ + return; } - if (identified_broadcast && (auto_scan.broadcast_sink != NULL) && - (auto_scan.broadcast_sink->pa_sync == NULL)) { - struct bt_le_per_adv_sync_param create_params = {0}; - int err; - - err = bt_le_scan_stop(); - if (err != 0) { - bt_shell_error("Could not stop scan: %d", err); - } - - bt_addr_le_copy(&create_params.addr, info->addr); - create_params.options = BT_LE_PER_ADV_SYNC_OPT_FILTER_DUPLICATE; - create_params.sid = info->sid; - create_params.skip = PA_SYNC_SKIP; - create_params.timeout = interval_to_sync_timeout(info->interval); + bt_shell_print("Found matched broadcast with address %s%s", addr_str, + info->interval > 0U ? "" : " but is not syncable"); - bt_shell_print("Attempting to PA sync to the broadcaster"); - err = bt_le_per_adv_sync_create(&create_params, auto_scan.out_sync); - if (err != 0) { - bt_shell_error("Could not create Broadcast PA sync: %d", err); - } else { - auto_scan.broadcast_sink->pa_sync = *auto_scan.out_sync; - } + if (info->interval > 0U && identified_broadcast && auto_scan.broadcast_sink != NULL) { + pa_sync_broadcast_sink(info); } } @@ -2460,8 +2500,7 @@ static void syncable(struct bt_bap_broadcast_sink *sink, const struct bt_iso_big static void bap_pa_sync_synced_cb(struct bt_le_per_adv_sync *sync, struct bt_le_per_adv_sync_synced_info *info) { - if (auto_scan.broadcast_sink != NULL && auto_scan.out_sync != NULL && - sync == *auto_scan.out_sync) { + if (auto_scan.broadcast_sink != NULL && auto_scan.broadcast_sink->pa_sync == sync) { bt_shell_print("PA synced to broadcast with broadcast ID 0x%06x", auto_scan.broadcast_info.broadcast_id); @@ -3070,7 +3109,6 @@ static void clear_stream_data(struct shell_stream *sh_stream) /* All streams in the broadcast sink has been terminated */ memset(&default_broadcast_sink.received_base, 0, sizeof(default_broadcast_sink.received_base)); - default_broadcast_sink.broadcast_id = 0; default_broadcast_sink.syncable = false; } } @@ -3218,6 +3256,12 @@ static int cmd_create_broadcast(const struct shell *sh, size_t argc, return -ENOEXEC; } + if (IS_ENABLED(CONFIG_BT_CAP_HANDOVER) && default_source.handover_in_progress) { + shell_info(sh, "CAP Handover in progress"); + + return -ENOEXEC; + } + named_preset = &default_broadcast_source_preset; for (size_t i = 1U; i < argc; i++) { @@ -3315,6 +3359,7 @@ static int cmd_start_broadcast(const struct shell *sh, size_t argc, char *argv[]) { struct bt_le_ext_adv *adv = adv_sets[selected_adv]; + struct bt_le_ext_adv_info adv_info; int err; if (adv == NULL) { @@ -3322,6 +3367,12 @@ static int cmd_start_broadcast(const struct shell *sh, size_t argc, return -ENOEXEC; } + err = bt_le_ext_adv_get_info(adv, &adv_info); + if (err != 0) { + shell_error(sh, "Failed to get adv info: %d\n", err); + return -ENOEXEC; + } + if (default_source.bap_source == NULL || default_source.is_cap) { shell_info(sh, "Broadcast source not created"); return -ENOEXEC; @@ -3333,6 +3384,9 @@ static int cmd_start_broadcast(const struct shell *sh, size_t argc, return err; } + default_source.addr_type = adv_info.addr->type; + default_source.adv_sid = adv_info.sid; + return 0; } @@ -3351,6 +3405,8 @@ static int cmd_stop_broadcast(const struct shell *sh, size_t argc, char *argv[]) return err; } + default_source.adv_sid = BT_GAP_SID_INVALID; + return 0; } @@ -3415,7 +3471,6 @@ static int cmd_create_broadcast_sink(const struct shell *sh, size_t argc, char * auto_scan.broadcast_sink = &default_broadcast_sink; auto_scan.broadcast_info.broadcast_id = broadcast_id; - auto_scan.out_sync = &per_adv_syncs[selected_per_adv_sync]; } else { shell_print(sh, "Creating broadcast sink with broadcast ID 0x%06X", (uint32_t)broadcast_id); @@ -3427,6 +3482,52 @@ static int cmd_create_broadcast_sink(const struct shell *sh, size_t argc, char * shell_error(sh, "Failed to create broadcast sink: %d", err); return -ENOEXEC; + } else { + struct scan_delegator_sync_state *sync_state = NULL; + + default_broadcast_sink.pa_sync = per_adv_sync; + + /* Lookup sync_state by PA sync or by values */ + sync_state = scan_delegator_sync_state_get_by_pa(per_adv_sync); + if (sync_state == NULL) { + struct bt_le_per_adv_sync_info sync_info; + + err = bt_le_per_adv_sync_get_info(per_adv_sync, &sync_info); + if (err != 0) { + bt_shell_error("Failed to get sync info: %d", err); + err = bt_bap_broadcast_sink_delete( + default_broadcast_sink.bap_sink); + if (err != 0) { + bt_shell_error( + "Failed to delete broadcast sink: %d", err); + } + + return -ENOEXEC; + } + + sync_state = scan_delegator_sync_state_get_by_values( + auto_scan.broadcast_info.broadcast_id, sync_info.addr.type, + sync_info.sid); + } + + if (sync_state == NULL) { + sync_state = scan_delegator_sync_state_new(); + + if (sync_state == NULL) { + bt_shell_error("Could not get new sync state"); + err = bt_bap_broadcast_sink_delete( + default_broadcast_sink.bap_sink); + if (err != 0) { + bt_shell_error( + "Failed to delete broadcast sink: %d", err); + } + + return -ENOEXEC; + } + } + + sync_state->pa_sync = per_adv_sync; + sync_state->broadcast_id = (uint32_t)broadcast_id; } } @@ -3468,7 +3569,6 @@ static int cmd_create_sink_by_name(const struct shell *sh, size_t argc, char *ar auto_scan.broadcast_info.broadcast_id = BT_BAP_INVALID_BROADCAST_ID; auto_scan.broadcast_sink = &default_broadcast_sink; - auto_scan.out_sync = &per_adv_syncs[selected_per_adv_sync]; return 0; } @@ -3849,6 +3949,7 @@ static int cmd_init(const struct shell *sh, size_t argc, char *argv[]) } default_source.broadcast_id = BT_BAP_INVALID_BROADCAST_ID; + default_source.adv_sid = BT_GAP_SID_INVALID; #endif /* CONFIG_BT_BAP_BROADCAST_SOURCE */ #if defined(CONFIG_LIBLC3) diff --git a/subsys/bluetooth/audio/shell/bap_broadcast_assistant.c b/subsys/bluetooth/audio/shell/bap_broadcast_assistant.c index 8734805f0bcf0..3f3a849e5ea0d 100644 --- a/subsys/bluetooth/audio/shell/bap_broadcast_assistant.c +++ b/subsys/bluetooth/audio/shell/bap_broadcast_assistant.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -55,6 +56,18 @@ struct bt_scan_recv_info { char broadcast_name[BT_AUDIO_BROADCAST_NAME_LEN_MAX + 1]; }; +struct broadcast_assistant_recv_state broadcast_assistant_recv_states[CONFIG_BT_MAX_CONN]; + +static void disconnected_cb(struct bt_conn *conn, uint8_t reason) +{ + (void)memset(&broadcast_assistant_recv_states[bt_conn_index(conn)], 0, + sizeof(broadcast_assistant_recv_states[0])); +} + +BT_CONN_CB_DEFINE(conn_callbacks) = { + .disconnected = disconnected_cb, +}; + static bool pa_decode_base(struct bt_data *data, void *user_data) { const struct bt_bap_base *base = bt_bap_base_get_base_from_ad(data); @@ -98,6 +111,8 @@ static void bap_broadcast_assistant_discover_cb(struct bt_conn *conn, int err, bt_shell_error("BASS discover failed (%d)", err); } else { bt_shell_print("BASS discover done with %u recv states", recv_state_count); + broadcast_assistant_recv_states[bt_conn_index(conn)].recv_state_count = + recv_state_count; } } @@ -166,6 +181,7 @@ static void bap_broadcast_assistant_recv_state_cb( struct bt_le_ext_adv *ext_adv = NULL; /* Lookup matching PA sync */ + /* TODO: Need to consider SID and Broadcast ID as well */ for (size_t i = 0U; i < ARRAY_SIZE(per_adv_syncs); i++) { if (per_adv_syncs[i] != NULL && bt_addr_le_eq(&per_adv_syncs[i]->addr, &state->addr)) { @@ -176,12 +192,33 @@ static void bap_broadcast_assistant_recv_state_cb( } if (per_adv_sync && IS_ENABLED(CONFIG_BT_PER_ADV_SYNC_TRANSFER_SENDER)) { - bt_shell_print("Sending PAST"); - err = bt_le_per_adv_sync_transfer(per_adv_sync, - conn, - BT_UUID_BASS_VAL); + struct bt_le_per_adv_sync_info sync_info; + const bool adva_matches_ea = false; /* don't know */ + bool adva_matches_src_addr; + uint16_t service_data = 0U; + + err = bt_le_per_adv_sync_get_info(per_adv_sync, &sync_info); + if (err != 0) { + bt_shell_error("Failed to get sync info: %d", err); + + return; + } + + adva_matches_src_addr = bt_addr_le_eq(&sync_info.addr, &state->addr); + if (!adva_matches_ea) { + service_data |= BIT(0); + } + if (!adva_matches_src_addr) { + service_data |= BIT(1); + } + + service_data |= ((uint16_t)state->src_id << 8); + + bt_shell_print("Sending PAST"); + + err = bt_le_per_adv_sync_transfer(per_adv_sync, conn, service_data); if (err != 0) { bt_shell_error("Could not transfer periodic adv sync: %d", err); } @@ -225,6 +262,29 @@ static void bap_broadcast_assistant_recv_state_cb( bt_shell_error("Could not send PA to Scan Delegator"); } } + +#if defined(CONFIG_BT_BAP_BROADCAST_SOURCE) + /* The combination of broadcast ID, address type and SID is what makes a receive state + * unique - Use that to compare when storing the src_id related to our broadcast + */ + if (err == 0 && state->broadcast_id == default_source.broadcast_id && + state->addr.type == default_source.addr_type && + state->adv_sid == default_source.adv_sid) { + struct broadcast_assistant_recv_state *recv_state = + &broadcast_assistant_recv_states[bt_conn_index(conn)]; + + recv_state->default_source_src_id = state->src_id; + recv_state->default_source_subgroup_count = state->num_subgroups; + + recv_state->default_source_big_synced = false; + for (uint8_t i = 0U; i < state->num_subgroups; i++) { + if (state->subgroups[i].bis_sync != 0) { + recv_state->default_source_big_synced = true; + break; + } + } + } +#endif /* CONFIG_BT_BAP_BROADCAST_SOURCE */ } static void bap_broadcast_assistant_recv_state_removed_cb(struct bt_conn *conn, uint8_t src_id) diff --git a/subsys/bluetooth/audio/shell/bap_scan_delegator.c b/subsys/bluetooth/audio/shell/bap_scan_delegator.c index e88b153c332fb..e63be874a169c 100644 --- a/subsys/bluetooth/audio/shell/bap_scan_delegator.c +++ b/subsys/bluetooth/audio/shell/bap_scan_delegator.c @@ -32,23 +32,15 @@ #include #include