Skip to content

Commit d331e58

Browse files
committed
Bluetooth: CAP: Implement unicast to broadcast handover
Implement the unicast to broadcast handover procedure, as per the Bluetooth CAP specificiation. Signed-off-by: Emil Gydesen <emil.gydesen@nordicsemi.no>
1 parent fcbe3ad commit d331e58

20 files changed

+2612
-176
lines changed

include/zephyr/bluetooth/audio/bap.h

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1720,6 +1720,32 @@ int bt_bap_unicast_group_add_streams(struct bt_bap_unicast_group *unicast_group,
17201720
*/
17211721
int bt_bap_unicast_group_delete(struct bt_bap_unicast_group *unicast_group);
17221722

1723+
/** Callback function for bt_bap_unicast_group_foreach_stream()
1724+
*
1725+
* @param stream The audio stream
1726+
* @param user_data User data
1727+
*
1728+
* @retval true Stop iterating.
1729+
* @retval false Continue iterating.
1730+
*/
1731+
typedef bool (*bt_bap_unicast_group_foreach_stream_func_t)(struct bt_bap_stream *stream,
1732+
void *user_data);
1733+
1734+
/**
1735+
* @brief Iterate through all streams in a unicast group
1736+
*
1737+
* @param unicast_group The unicast group
1738+
* @param func The callback function
1739+
* @param user_data User specified data that sent to the callback function
1740+
*
1741+
* @retval 0 Success (even if no streams exists in the group).
1742+
* @retval -ECANCELED Iteration was stopped by the callback function before complete.
1743+
* @retval -EINVAL @p unicast_group or @p func were NULL.
1744+
*/
1745+
int bt_bap_unicast_group_foreach_stream(struct bt_bap_unicast_group *unicast_group,
1746+
bt_bap_unicast_group_foreach_stream_func_t func,
1747+
void *user_data);
1748+
17231749
/** Unicast Client callback structure */
17241750
struct bt_bap_unicast_client_cb {
17251751
/**

include/zephyr/bluetooth/audio/cap.h

Lines changed: 63 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,22 @@ struct bt_cap_initiator_cb {
147147
*/
148148
void (*broadcast_stopped)(struct bt_cap_broadcast_source *source, uint8_t reason);
149149
#endif /* CONFIG_BT_BAP_BROADCAST_SOURCE */
150+
#if defined(CONFIG_BT_CAP_HANDOVER_SUPPORTED)
151+
/**
152+
* @brief The unicast to broadcast handover procedure has finished
153+
*
154+
* @param err 0 if success else a negative errno value.
155+
* @param conn Pointer to the connection where the error
156+
* occurred or NULL local failure.
157+
* @param unicast_group NULL if the unicast group was deleted during the procedure, else
158+
* pointer to the unicast group provided in the parameters.
159+
* @param broadcast_source Pointer to newly created broadcast source, or NULL in case of an
160+
* error happening before it was created.
161+
*/
162+
void (*unicast_to_broadcast_complete)(int err, struct bt_conn *conn,
163+
struct bt_cap_unicast_group *unicast_group,
164+
struct bt_cap_broadcast_source *broadcast_source);
165+
#endif /* CONFIG_BT_CAP_HANDOVER_SUPPORTED */
150166
};
151167

152168
/**
@@ -398,6 +414,32 @@ int bt_cap_unicast_group_add_streams(struct bt_cap_unicast_group *unicast_group,
398414
*/
399415
int bt_cap_unicast_group_delete(struct bt_cap_unicast_group *unicast_group);
400416

417+
/** Callback function for bt_bap_unicast_group_foreach_stream()
418+
*
419+
* @param stream The audio stream
420+
* @param user_data User data
421+
*
422+
* @retval true Stop iterating.
423+
* @retval false Continue iterating.
424+
*/
425+
typedef bool (*bt_cap_unicast_group_foreach_stream_func_t)(struct bt_cap_stream *stream,
426+
void *user_data);
427+
428+
/**
429+
* @brief Iterate through all streams in a unicast group
430+
*
431+
* @param unicast_group The unicast group
432+
* @param func The callback function
433+
* @param user_data User specified data that sent to the callback function
434+
*
435+
* @retval 0 Success (even if no streams exists in the group).
436+
* @retval -ECANCELED Iteration was stopped by the callback function before complete.
437+
* @retval -EINVAL @p unicast_group or @p func were NULL.
438+
*/
439+
int bt_cap_unicast_group_foreach_stream(struct bt_cap_unicast_group *unicast_group,
440+
bt_cap_unicast_group_foreach_stream_func_t func,
441+
void *user_data);
442+
401443
/** Stream specific parameters for the bt_cap_initiator_unicast_audio_start() function */
402444
struct bt_cap_unicast_audio_start_stream_param {
403445
/** Coordinated or ad-hoc set member. */
@@ -788,50 +830,47 @@ int bt_cap_initiator_broadcast_get_base(struct bt_cap_broadcast_source *broadcas
788830

789831
/** Parameters for bt_cap_initiator_unicast_to_broadcast() */
790832
struct bt_cap_unicast_to_broadcast_param {
833+
/** The type of the set. */
834+
enum bt_cap_set_type type;
835+
791836
/** The source unicast group with the streams. */
792-
struct bt_bap_unicast_group *unicast_group;
837+
struct bt_cap_unicast_group *unicast_group;
793838

794839
/**
795-
* @brief Whether or not to encrypt the streams.
840+
* @brief The advertising set to use for the broadcast source
796841
*
797-
* If set to true, then the broadcast code in @p broadcast_code
798-
* will be used to encrypt the streams.
842+
* This shall remain valid until the procedure has completed.
843+
* If the advertising set is not started at the time of calling
844+
* bt_cap_initiator_unicast_to_broadcast(),
845+
* the procedure will start the advertising set with @ref BT_LE_EXT_ADV_START_DEFAULT.
799846
*/
800-
bool encrypt;
847+
struct bt_le_ext_adv *ext_adv;
848+
849+
uint8_t sid;
850+
uint16_t pa_interval;
851+
uint32_t broadcast_id;
801852

802853
/**
803-
* @brief 16-octet broadcast code.
804-
*
805-
* Only valid if @p encrypt is true.
806-
*
807-
* If the value is a string or a the value is less than 16 octets,
808-
* the remaining octets shall be 0.
854+
* @brief Broadcast source parameters
809855
*
810-
* Example:
811-
* The string "Broadcast Code" shall be
812-
* [42 72 6F 61 64 63 61 73 74 20 43 6F 64 65 00 00]
856+
* These parameters shall remain valid until the procedure has completed.
813857
*/
814-
uint8_t broadcast_code[BT_ISO_BROADCAST_CODE_SIZE];
858+
struct bt_cap_initiator_broadcast_create_param *broadcast_create_param;
815859
};
816860

817861
/**
818862
* @brief Hands over the data streams in a unicast group to a broadcast source.
819863
*
820864
* The streams in the unicast group will be stopped and the unicast group
821-
* will be deleted. This can only be done for source streams.
865+
* will be deleted. This can only be done for sink streams.
822866
*
823-
* @note @kconfig{CONFIG_BT_CAP_INITIATOR},
824-
* @kconfig{CONFIG_BT_BAP_UNICAST_CLIENT} and
825-
* @kconfig{CONFIG_BT_BAP_BROADCAST_SOURCE} must be enabled for this function
826-
* to be enabled.
867+
* @kconfig_dep{CONFIG_BT_CAP_HANDOVER_SUPPORTED}
827868
*
828869
* @param param The parameters for the handover.
829-
* @param source The resulting broadcast source.
830870
*
831871
* @return 0 on success or negative error value on failure.
832872
*/
833-
int bt_cap_initiator_unicast_to_broadcast(const struct bt_cap_unicast_to_broadcast_param *param,
834-
struct bt_cap_broadcast_source **source);
873+
int bt_cap_initiator_unicast_to_broadcast(const struct bt_cap_unicast_to_broadcast_param *param);
835874

836875
/** Parameters for bt_cap_initiator_broadcast_to_unicast() */
837876
struct bt_cap_broadcast_to_unicast_param {
@@ -864,10 +903,7 @@ struct bt_cap_broadcast_to_unicast_param {
864903
* The streams in the broadcast source will be stopped and the broadcast source
865904
* will be deleted.
866905
*
867-
* @note @kconfig{CONFIG_BT_CAP_INITIATOR},
868-
* @kconfig{CONFIG_BT_BAP_UNICAST_CLIENT} and
869-
* @kconfig{CONFIG_BT_BAP_BROADCAST_SOURCE} must be enabled for this function
870-
* to be enabled.
906+
* @kconfig_dep{CONFIG_BT_CAP_HANDOVER_SUPPORTED}
871907
*
872908
* @param[in] param The parameters for the handover.
873909
* @param[out] unicast_group The resulting broadcast source.

subsys/bluetooth/audio/Kconfig.cap

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,3 +48,7 @@ config BT_CAP_COMMANDER
4848
BT_MCC
4949
help
5050
Enabling this will enable the CAP Initiator role.
51+
52+
config BT_CAP_HANDOVER_SUPPORTED
53+
def_bool BT_CAP_COMMANDER && BT_CAP_INITIATOR && BT_BAP_BROADCAST_ASSISTANT && \
54+
BT_BAP_BROADCAST_SOURCE && BT_BAP_UNICAST_CLIENT

subsys/bluetooth/audio/bap_unicast_client.c

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3146,6 +3146,33 @@ int bt_bap_unicast_group_delete(struct bt_bap_unicast_group *unicast_group)
31463146
return 0;
31473147
}
31483148

3149+
int bt_bap_unicast_group_foreach_stream(struct bt_bap_unicast_group *unicast_group,
3150+
bt_bap_unicast_group_foreach_stream_func_t func,
3151+
void *user_data)
3152+
{
3153+
struct bt_bap_stream *stream, *next;
3154+
3155+
if (unicast_group == NULL) {
3156+
LOG_DBG("unicast_group is NULL");
3157+
return -EINVAL;
3158+
}
3159+
3160+
if (func == NULL) {
3161+
LOG_DBG("func is NULL");
3162+
return -EINVAL;
3163+
}
3164+
3165+
SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&unicast_group->streams, stream, next, _node) {
3166+
const bool stop = func(stream, user_data);
3167+
3168+
if (stop) {
3169+
return -ECANCELED;
3170+
}
3171+
}
3172+
3173+
return 0;
3174+
}
3175+
31493176
int bt_bap_unicast_client_config(struct bt_bap_stream *stream,
31503177
const struct bt_audio_codec_cfg *codec_cfg)
31513178
{

subsys/bluetooth/audio/cap_commander.c

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2022-2023 Nordic Semiconductor ASA
2+
* Copyright (c) 2022-2025 Nordic Semiconductor ASA
33
*
44
* SPDX-License-Identifier: Apache-2.0
55
*/
@@ -28,6 +28,7 @@
2828
#include <zephyr/sys/__assert.h>
2929
#include <zephyr/sys/check.h>
3030
#include <zephyr/sys/util.h>
31+
#include <zephyr/sys/util_macro.h>
3132

3233
#include "audio_internal.h"
3334
#include "bap_endpoint.h"
@@ -338,7 +339,7 @@ int bt_cap_commander_broadcast_reception_start(
338339
return -EINVAL;
339340
}
340341

341-
if (bt_cap_common_test_and_set_proc_active()) {
342+
if (bt_cap_common_test_and_set_proc_active() && !bt_cap_common_handover_is_active()) {
342343
LOG_DBG("A CAP procedure is already in progress");
343344

344345
return -EBUSY;
@@ -798,7 +799,7 @@ int bt_cap_commander_distribute_broadcast_code(
798799
return -EINVAL;
799800
}
800801

801-
if (bt_cap_common_test_and_set_proc_active()) {
802+
if (bt_cap_common_test_and_set_proc_active() && !bt_cap_common_handover_is_active()) {
802803
LOG_DBG("A CAP procedure is already in progress");
803804

804805
return -EBUSY;
@@ -875,6 +876,18 @@ static void cap_commander_proc_complete(void)
875876
failed_conn = active_proc->failed_conn;
876877
err = active_proc->err;
877878
proc_type = active_proc->proc_type;
879+
880+
if (IS_ENABLED(CONFIG_BT_CAP_HANDOVER_SUPPORTED) && bt_cap_common_handover_is_active()) {
881+
/* Complete handover procedure. At this point we do not know if the remote
882+
* device will attempt to use PAST or scan for itself, so it's best to leave
883+
* this up to the application layer
884+
*/
885+
__ASSERT_NO_MSG(proc_type == BT_CAP_COMMON_PROC_TYPE_BROADCAST_RECEPTION_START);
886+
887+
bt_cap_handover_proc_complete();
888+
return;
889+
}
890+
878891
bt_cap_common_clear_active_proc();
879892

880893
if (cap_cb == NULL) {

subsys/bluetooth/audio/cap_common.c

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include <zephyr/sys/atomic.h>
2222
#include <zephyr/sys/check.h>
2323
#include <zephyr/sys/util.h>
24+
#include <zephyr/sys/util_macro.h>
2425

2526
#include "cap_internal.h"
2627
#include "csip_internal.h"
@@ -74,6 +75,23 @@ bool bt_cap_common_subproc_is_type(enum bt_cap_common_subproc_type subproc_type)
7475
}
7576
#endif /* CONFIG_BT_CAP_INITIATOR_UNICAST */
7677

78+
void bt_cap_common_set_handover_active(void)
79+
{
80+
if (IS_ENABLED(CONFIG_BT_CAP_HANDOVER_SUPPORTED)) {
81+
atomic_set_bit(active_proc.proc_state_flags, BT_CAP_COMMON_PROC_STATE_HANDOVER);
82+
}
83+
}
84+
85+
bool bt_cap_common_handover_is_active(void)
86+
{
87+
if (IS_ENABLED(CONFIG_BT_CAP_HANDOVER_SUPPORTED)) {
88+
return atomic_test_bit(active_proc.proc_state_flags,
89+
BT_CAP_COMMON_PROC_STATE_HANDOVER);
90+
} else {
91+
return false;
92+
}
93+
}
94+
7795
struct bt_conn *bt_cap_common_get_member_conn(enum bt_cap_set_type type,
7896
const union bt_cap_set_member *member)
7997
{

0 commit comments

Comments
 (0)