Skip to content

Bluetooth: CAP: Implement unicast to broadcast handover #85642

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
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
77 changes: 47 additions & 30 deletions include/zephyr/bluetooth/audio/cap.h
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,22 @@ struct bt_cap_initiator_cb {
*/
void (*broadcast_stopped)(struct bt_cap_broadcast_source *source, uint8_t reason);
#endif /* CONFIG_BT_BAP_BROADCAST_SOURCE */
#if defined(CONFIG_BT_CAP_HANDOVER)
/**
* @brief The unicast to broadcast handover procedure has finished
*
* @param err 0 if success else a negative errno value.
* @param conn Pointer to the connection where the error
* occurred or NULL local failure.
* @param unicast_group NULL if the unicast group was deleted during the procedure, else
* pointer to the unicast group provided in the parameters.
* @param broadcast_source Pointer to newly created broadcast source, or NULL in case of an
* error happening before it was created.
*/
void (*unicast_to_broadcast_complete)(int err, struct bt_conn *conn,
struct bt_cap_unicast_group *unicast_group,
struct bt_cap_broadcast_source *broadcast_source);
#endif /* CONFIG_BT_CAP_HANDOVER */
};

/**
Expand Down Expand Up @@ -814,50 +830,54 @@ int bt_cap_initiator_broadcast_get_base(struct bt_cap_broadcast_source *broadcas

/** Parameters for bt_cap_initiator_unicast_to_broadcast() */
struct bt_cap_unicast_to_broadcast_param {
/** The type of the set. */
enum bt_cap_set_type type;

/** The source unicast group with the streams. */
struct bt_bap_unicast_group *unicast_group;
struct bt_cap_unicast_group *unicast_group;

/**
* @brief Whether or not to encrypt the streams.
* @brief The advertising set to use for the broadcast source
*
* If set to true, then the broadcast code in @p broadcast_code
* will be used to encrypt the streams.
* This shall remain valid until the procedure has completed.
* If the advertising set is not started at the time of calling
* bt_cap_initiator_unicast_to_broadcast(),
* the procedure will start the advertising set with @ref BT_LE_EXT_ADV_START_DEFAULT.
*/
bool encrypt;
struct bt_le_ext_adv *ext_adv;

/** The SID of the advertising set. */
uint8_t sid;

/** The periodic advertising interval configured for the advertising set. */
uint16_t pa_interval;

/** The broadcast ID the advertising set is, or will be, using. */
uint32_t broadcast_id;

/**
* @brief 16-octet broadcast code.
* @brief Broadcast source parameters.
*
* Only valid if @p encrypt is true.
*
* If the value is a string or a the value is less than 16 octets,
* the remaining octets shall be 0.
*
* Example:
* The string "Broadcast Code" shall be
* [42 72 6F 61 64 63 61 73 74 20 43 6F 64 65 00 00]
* These parameters shall remain valid until the procedure has completed.
*/
uint8_t broadcast_code[BT_ISO_BROADCAST_CODE_SIZE];
struct bt_cap_initiator_broadcast_create_param *broadcast_create_param;
};

/**
* @brief Hands over the data streams in a unicast group to a broadcast source.
* @brief Hands over the sink streams in a unicast group to a broadcast source.
*
* The streams in the unicast group will be stopped and the unicast group
* will be deleted. This can only be done for source streams.
* All streams in the provided unicast group will be stopped and released. The sink streams will be
* tranferred to a broadcast source, and the broadcast source information will be shared with
* all accepters that are currently receiving audio. Any stream that is not in the streaming state
* will only be released.
*
* @note @kconfig{CONFIG_BT_CAP_INITIATOR},
* @kconfig{CONFIG_BT_BAP_UNICAST_CLIENT} and
* @kconfig{CONFIG_BT_BAP_BROADCAST_SOURCE} must be enabled for this function
* to be enabled.
* @kconfig_dep{CONFIG_BT_CAP_HANDOVER}
*
* @param param The parameters for the handover.
* @param source The resulting broadcast source.
* @param param The parameters for the handover.
*
* @return 0 on success or negative error value on failure.
*/
int bt_cap_initiator_unicast_to_broadcast(const struct bt_cap_unicast_to_broadcast_param *param,
struct bt_cap_broadcast_source **source);
int bt_cap_initiator_unicast_to_broadcast(const struct bt_cap_unicast_to_broadcast_param *param);

/** Parameters for bt_cap_initiator_broadcast_to_unicast() */
struct bt_cap_broadcast_to_unicast_param {
Expand Down Expand Up @@ -890,10 +910,7 @@ struct bt_cap_broadcast_to_unicast_param {
* The streams in the broadcast source will be stopped and the broadcast source
* will be deleted.
*
* @note @kconfig{CONFIG_BT_CAP_INITIATOR},
* @kconfig{CONFIG_BT_BAP_UNICAST_CLIENT} and
* @kconfig{CONFIG_BT_BAP_BROADCAST_SOURCE} must be enabled for this function
* to be enabled.
* @kconfig_dep{CONFIG_BT_CAP_HANDOVER}
*
* @param[in] param The parameters for the handover.
* @param[out] unicast_group The resulting broadcast source.
Expand Down
8 changes: 8 additions & 0 deletions subsys/bluetooth/audio/Kconfig.cap
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,11 @@ config BT_CAP_COMMANDER
BT_MCC
help
Enabling this will enable the CAP Initiator role.

config BT_CAP_HANDOVER
bool "Common Audio Profile Handover Procedures"
depends on BT_CAP_COMMANDER && BT_CAP_INITIATOR && BT_BAP_BROADCAST_ASSISTANT && \
BT_BAP_BROADCAST_SOURCE && BT_BAP_UNICAST_CLIENT_ASE_SNK_COUNT > 0
help
Enable support for the CAP Handover procedures, allowing a device to switch between
broadcast and unicast for a usecase.
43 changes: 31 additions & 12 deletions subsys/bluetooth/audio/cap_commander.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2022-2023 Nordic Semiconductor ASA
* Copyright (c) 2022-2025 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
Expand Down Expand Up @@ -28,6 +28,7 @@
#include <zephyr/sys/__assert.h>
#include <zephyr/sys/check.h>
#include <zephyr/sys/util.h>
#include <zephyr/sys/util_macro.h>

#include "audio_internal.h"
#include "bap_endpoint.h"
Expand Down Expand Up @@ -325,7 +326,7 @@ static bool valid_broadcast_reception_start_param(
return true;
}

int bt_cap_commander_broadcast_reception_start(
int cap_commander_broadcast_reception_start(
const struct bt_cap_commander_broadcast_reception_start_param *param)
{
struct bt_bap_broadcast_assistant_add_src_param add_src_param = {0};
Expand All @@ -334,16 +335,6 @@ int bt_cap_commander_broadcast_reception_start(
struct bt_conn *conn;
int err;

if (!valid_broadcast_reception_start_param(param)) {
return -EINVAL;
}

if (bt_cap_common_test_and_set_proc_active()) {
LOG_DBG("A CAP procedure is already in progress");

return -EBUSY;
}

broadcast_assistant_cb.add_src = cap_commander_broadcast_assistant_add_src_cb;
if (!broadcast_assistant_cb_registered) {
err = cap_commander_register_broadcast_assistant_cb();
Expand Down Expand Up @@ -406,6 +397,22 @@ int bt_cap_commander_broadcast_reception_start(
return 0;
}

int bt_cap_commander_broadcast_reception_start(
const struct bt_cap_commander_broadcast_reception_start_param *param)
{
if (!valid_broadcast_reception_start_param(param)) {
return -EINVAL;
}

if (bt_cap_common_test_and_set_proc_active()) {
LOG_DBG("A CAP procedure is already in progress");

return -EBUSY;
}

return cap_commander_broadcast_reception_start(param);
}

static void
copy_broadcast_reception_stop_param(struct bt_bap_broadcast_assistant_mod_src_param *mod_src_param,
struct cap_broadcast_reception_stop *stop_param)
Expand Down Expand Up @@ -875,6 +882,18 @@ static void cap_commander_proc_complete(void)
failed_conn = active_proc->failed_conn;
err = active_proc->err;
proc_type = active_proc->proc_type;

if (IS_ENABLED(CONFIG_BT_CAP_HANDOVER) && bt_cap_common_handover_is_active()) {
/* Complete handover procedure. At this point we do not know if the remote
* device will attempt to use PAST or scan for itself, so it's best to leave
* this up to the application layer
*/
__ASSERT_NO_MSG(proc_type == BT_CAP_COMMON_PROC_TYPE_BROADCAST_RECEPTION_START);

bt_cap_handover_proc_complete();
return;
}

bt_cap_common_clear_active_proc();

if (cap_cb == NULL) {
Expand Down
15 changes: 14 additions & 1 deletion subsys/bluetooth/audio/cap_common.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2023-2024 Nordic Semiconductor ASA
* Copyright (c) 2023-2025 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
Expand All @@ -21,6 +21,7 @@
#include <zephyr/sys/atomic.h>
#include <zephyr/sys/check.h>
#include <zephyr/sys/util.h>
#include <zephyr/sys/util_macro.h>

#include "cap_internal.h"
#include "csip_internal.h"
Expand Down Expand Up @@ -74,6 +75,18 @@ bool bt_cap_common_subproc_is_type(enum bt_cap_common_subproc_type subproc_type)
}
#endif /* CONFIG_BT_CAP_INITIATOR_UNICAST */

#if defined(CONFIG_BT_CAP_HANDOVER)
void bt_cap_common_set_handover_active(void)
{
atomic_set_bit(active_proc.proc_state_flags, BT_CAP_COMMON_PROC_STATE_HANDOVER);
}

bool bt_cap_common_handover_is_active(void)
{
return atomic_test_bit(active_proc.proc_state_flags, BT_CAP_COMMON_PROC_STATE_HANDOVER);
}
#endif /* CONFIG_BT_CAP_HANDOVER */

struct bt_conn *bt_cap_common_get_member_conn(enum bt_cap_set_type type,
const union bt_cap_set_member *member)
{
Expand Down
Loading