Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
101 changes: 101 additions & 0 deletions doc/connectivity/bluetooth/shell/audio/cap.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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 <addr>

# 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 <addr>

# 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
4 changes: 4 additions & 0 deletions subsys/bluetooth/audio/shell/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
86 changes: 82 additions & 4 deletions subsys/bluetooth/audio/shell/audio.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
*/

/*
* Copyright (c) 2023 Nordic Semiconductor ASA
* Copyright (c) 2023-2025 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
Expand All @@ -25,15 +25,18 @@
#include <zephyr/bluetooth/bluetooth.h>
#include <zephyr/bluetooth/hci_types.h>
#include <zephyr/bluetooth/iso.h>
#include <zephyr/kernel.h>
#include <zephyr/shell/shell.h>
#include <zephyr/sys/atomic_types.h>
#include <zephyr/sys/byteorder.h>
#include <zephyr/sys/printk.h>
#include <zephyr/sys/util.h>
#include <zephyr/sys/util_macro.h>
#include <zephyr/sys_clock.h>
#include <zephyr/toolchain.h>

#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
Expand Down Expand Up @@ -168,22 +171,24 @@ 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 {
struct bt_bap_broadcast_sink *bap_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;
};
Expand All @@ -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)
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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,
Expand Down
Loading