Skip to content

Commit 3157de2

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 5a06edf commit 3157de2

File tree

11 files changed

+1404
-68
lines changed

11 files changed

+1404
-68
lines changed

include/zephyr/bluetooth/audio/bap.h

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1672,6 +1672,32 @@ int bt_bap_unicast_group_add_streams(struct bt_bap_unicast_group *unicast_group,
16721672
*/
16731673
int bt_bap_unicast_group_delete(struct bt_bap_unicast_group *unicast_group);
16741674

1675+
/** Callback function for bt_bap_unicast_group_foreach_stream()
1676+
*
1677+
* @param stream The audio stream
1678+
* @param user_data User data
1679+
*
1680+
* @retval true Stop iterating.
1681+
* @retval false Continue iterating.
1682+
*/
1683+
typedef bool (*bt_bap_unicast_group_foreach_stream_func_t)(struct bt_bap_stream *stream,
1684+
void *user_data);
1685+
1686+
/**
1687+
* @brief Iterate through all streams in a unicast group
1688+
*
1689+
* @param unicast_group The unicast group
1690+
* @param func The callback function
1691+
* @param user_data User specified data that sent to the callback function
1692+
*
1693+
* @retval 0 Success (even if no streams exists in the group).
1694+
* @retval -ECANCELED Iteration was stopped by the callback function before complete.
1695+
* @retval -EINVAL @p unicast_group or @p func were NULL.
1696+
*/
1697+
int bt_bap_unicast_group_foreach_stream(struct bt_bap_unicast_group *unicast_group,
1698+
bt_bap_unicast_group_foreach_stream_func_t func,
1699+
void *user_data);
1700+
16751701
/** Unicast Client callback structure */
16761702
struct bt_bap_unicast_client_cb {
16771703
/**

include/zephyr/bluetooth/audio/cap.h

Lines changed: 49 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,12 @@ 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+
void (*unicast_to_broadcast_complete)(int err, struct bt_conn *conn);
155+
#endif /* CONFIG_BT_CAP_HANDOVER_SUPPORTED */
150156
};
151157

152158
/**
@@ -398,6 +404,32 @@ int bt_cap_unicast_group_add_streams(struct bt_cap_unicast_group *unicast_group,
398404
*/
399405
int bt_cap_unicast_group_delete(struct bt_cap_unicast_group *unicast_group);
400406

407+
/** Callback function for bt_bap_unicast_group_foreach_stream()
408+
*
409+
* @param stream The audio stream
410+
* @param user_data User data
411+
*
412+
* @retval true Stop iterating.
413+
* @retval false Continue iterating.
414+
*/
415+
typedef bool (*bt_cap_unicast_group_foreach_stream_func_t)(struct bt_cap_stream *stream,
416+
void *user_data);
417+
418+
/**
419+
* @brief Iterate through all streams in a unicast group
420+
*
421+
* @param unicast_group The unicast group
422+
* @param func The callback function
423+
* @param user_data User specified data that sent to the callback function
424+
*
425+
* @retval 0 Success (even if no streams exists in the group).
426+
* @retval -ECANCELED Iteration was stopped by the callback function before complete.
427+
* @retval -EINVAL @p unicast_group or @p func were NULL.
428+
*/
429+
int bt_cap_unicast_group_foreach_stream(struct bt_cap_unicast_group *unicast_group,
430+
bt_cap_unicast_group_foreach_stream_func_t func,
431+
void *user_data);
432+
401433
/** Stream specific parameters for the bt_cap_initiator_unicast_audio_start() function */
402434
struct bt_cap_unicast_audio_start_stream_param {
403435
/** Coordinated or ad-hoc set member. */
@@ -788,30 +820,30 @@ int bt_cap_initiator_broadcast_get_base(struct bt_cap_broadcast_source *broadcas
788820

789821
/** Parameters for bt_cap_initiator_unicast_to_broadcast() */
790822
struct bt_cap_unicast_to_broadcast_param {
823+
/** The type of the set. */
824+
enum bt_cap_set_type type;
825+
791826
/** The source unicast group with the streams. */
792-
struct bt_bap_unicast_group *unicast_group;
827+
struct bt_cap_unicast_group *unicast_group;
793828

794829
/**
795-
* @brief Whether or not to encrypt the streams.
830+
* @brief The advertising set to use for the broadcast source
796831
*
797-
* If set to true, then the broadcast code in @p broadcast_code
798-
* will be used to encrypt the streams.
832+
* This shall remain valid until the procedure has completed.
799833
*/
800-
bool encrypt;
834+
/* TBD: Is this necessary? Should we, or the app, start the source? */
835+
struct bt_le_ext_adv *ext_adv;
836+
837+
uint8_t sid;
838+
uint16_t pa_interval;
839+
uint32_t broadcast_id;
801840

802841
/**
803-
* @brief 16-octet broadcast code.
804-
*
805-
* Only valid if @p encrypt is true.
842+
* @brief Broadcast source parameters
806843
*
807-
* If the value is a string or a the value is less than 16 octets,
808-
* the remaining octets shall be 0.
809-
*
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]
844+
* These parameters shall remain valid until the procedure has completed.
813845
*/
814-
uint8_t broadcast_code[BT_ISO_BROADCAST_CODE_SIZE];
846+
struct bt_cap_initiator_broadcast_create_param *broadcast_create_param;
815847
};
816848

817849
/**
@@ -820,10 +852,7 @@ struct bt_cap_unicast_to_broadcast_param {
820852
* The streams in the unicast group will be stopped and the unicast group
821853
* will be deleted. This can only be done for source streams.
822854
*
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.
855+
* @kconfig_dep{CONFIG_BT_CAP_HANDOVER_SUPPORTED}
827856
*
828857
* @param param The parameters for the handover.
829858
* @param source The resulting broadcast source.
@@ -864,10 +893,7 @@ struct bt_cap_broadcast_to_unicast_param {
864893
* The streams in the broadcast source will be stopped and the broadcast source
865894
* will be deleted.
866895
*
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.
896+
* @kconfig_dep{CONFIG_BT_CAP_HANDOVER_SUPPORTED}
871897
*
872898
* @param[in] param The parameters for the handover.
873899
* @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
@@ -3143,6 +3143,33 @@ int bt_bap_unicast_group_delete(struct bt_bap_unicast_group *unicast_group)
31433143
return 0;
31443144
}
31453145

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

subsys/bluetooth/audio/cap_commander.c

Lines changed: 21 additions & 13 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
*/
@@ -338,7 +338,7 @@ int bt_cap_commander_broadcast_reception_start(
338338
return -EINVAL;
339339
}
340340

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

344344
return -EBUSY;
@@ -798,7 +798,7 @@ int bt_cap_commander_distribute_broadcast_code(
798798
return -EINVAL;
799799
}
800800

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

804804
return -EBUSY;
@@ -868,66 +868,74 @@ int bt_cap_commander_distribute_broadcast_code(
868868
static void cap_commander_proc_complete(void)
869869
{
870870
struct bt_cap_common_proc *active_proc = bt_cap_common_get_active_proc();
871+
const bool is_handover = bt_cap_common_handover_is_active();
871872
enum bt_cap_common_proc_type proc_type;
872873
struct bt_conn *failed_conn;
873874
int err;
874875

875876
failed_conn = active_proc->failed_conn;
876877
err = active_proc->err;
877878
proc_type = active_proc->proc_type;
878-
bt_cap_common_clear_active_proc();
879879

880-
if (cap_cb == NULL) {
880+
if (is_handover && err != 0) {
881+
bt_cap_handover_proc_complete();
881882
return;
883+
} else if (!is_handover) {
884+
bt_cap_common_clear_active_proc();
882885
}
883886

884887
switch (proc_type) {
885888
#if defined(CONFIG_BT_VCP_VOL_CTLR)
886889
case BT_CAP_COMMON_PROC_TYPE_VOLUME_CHANGE:
887-
if (cap_cb->volume_changed != NULL) {
890+
if (cap_cb != NULL && cap_cb->volume_changed != NULL) {
888891
cap_cb->volume_changed(failed_conn, err);
889892
}
890893
break;
891894
case BT_CAP_COMMON_PROC_TYPE_VOLUME_MUTE_CHANGE:
892-
if (cap_cb->volume_mute_changed != NULL) {
895+
if (cap_cb != NULL && cap_cb->volume_mute_changed != NULL) {
893896
cap_cb->volume_mute_changed(failed_conn, err);
894897
}
895898
break;
896899
#if defined(CONFIG_BT_VCP_VOL_CTLR_VOCS)
897900
case BT_CAP_COMMON_PROC_TYPE_VOLUME_OFFSET_CHANGE:
898-
if (cap_cb->volume_offset_changed != NULL) {
901+
if (cap_cb != NULL && cap_cb->volume_offset_changed != NULL) {
899902
cap_cb->volume_offset_changed(failed_conn, err);
900903
}
901904
break;
902905
#endif /* CONFIG_BT_VCP_VOL_CTLR_VOCS */
903906
#endif /* CONFIG_BT_VCP_VOL_CTLR */
904907
#if defined(CONFIG_BT_MICP_MIC_CTLR)
905908
case BT_CAP_COMMON_PROC_TYPE_MICROPHONE_MUTE_CHANGE:
906-
if (cap_cb->microphone_mute_changed != NULL) {
909+
if (cap_cb != NULL && cap_cb->microphone_mute_changed != NULL) {
907910
cap_cb->microphone_mute_changed(failed_conn, err);
908911
}
909912
break;
910913
#if defined(CONFIG_BT_MICP_MIC_CTLR_AICS)
911914
case BT_CAP_COMMON_PROC_TYPE_MICROPHONE_GAIN_CHANGE:
912-
if (cap_cb->microphone_gain_changed != NULL) {
915+
if (cap_cb != NULL && cap_cb->microphone_gain_changed != NULL) {
913916
cap_cb->microphone_gain_changed(failed_conn, err);
914917
}
915918
break;
916919
#endif /* CONFIG_BT_MICP_MIC_CTLR_AICS */
917920
#endif /* CONFIG_BT_MICP_MIC_CTLR */
918921
#if defined(CONFIG_BT_BAP_BROADCAST_ASSISTANT)
919922
case BT_CAP_COMMON_PROC_TYPE_BROADCAST_RECEPTION_START:
920-
if (cap_cb->broadcast_reception_start != NULL) {
923+
if (is_handover) {
924+
/* continue */
925+
/* TODO: If PAST, send PAST */
926+
LOG_ERR("");
927+
bt_cap_handover_proc_complete();
928+
} else if (cap_cb != NULL && cap_cb->broadcast_reception_start != NULL) {
921929
cap_cb->broadcast_reception_start(failed_conn, err);
922930
}
923931
break;
924932
case BT_CAP_COMMON_PROC_TYPE_BROADCAST_RECEPTION_STOP:
925-
if (cap_cb->broadcast_reception_stop != NULL) {
933+
if (cap_cb != NULL && cap_cb->broadcast_reception_stop != NULL) {
926934
cap_cb->broadcast_reception_stop(failed_conn, err);
927935
}
928936
break;
929937
case BT_CAP_COMMON_PROC_TYPE_DISTRIBUTE_BROADCAST_CODE:
930-
if (cap_cb->distribute_broadcast_code != NULL) {
938+
if (cap_cb != NULL && cap_cb->distribute_broadcast_code != NULL) {
931939
cap_cb->distribute_broadcast_code(failed_conn, err);
932940
}
933941
break;

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)