diff --git a/doc/releases/release-notes-4.2.rst b/doc/releases/release-notes-4.2.rst index b2dcd604aab4..03921a240a32 100644 --- a/doc/releases/release-notes-4.2.rst +++ b/doc/releases/release-notes-4.2.rst @@ -158,6 +158,12 @@ New APIs and options * :c:macro:`BT_BAP_PER_ADV_PARAM_BROADCAST_SLOW` * :c:func:`bt_csip_set_member_set_size_and_rank` * :c:func:`bt_csip_set_member_get_info` + * :c:func:`bt_bap_unicast_group_foreach_stream` + * :c:func:`bt_cap_unicast_group_create` + * :c:func:`bt_cap_unicast_group_reconfig` + * :c:func:`bt_cap_unicast_group_add_streams` + * :c:func:`bt_cap_unicast_group_delete` + * :c:func:`bt_cap_unicast_group_foreach_stream` * Host diff --git a/include/zephyr/bluetooth/audio/bap.h b/include/zephyr/bluetooth/audio/bap.h index 7c7ccdc39f19..74b7fc7d7daa 100644 --- a/include/zephyr/bluetooth/audio/bap.h +++ b/include/zephyr/bluetooth/audio/bap.h @@ -3,7 +3,7 @@ * @brief Header for Bluetooth BAP. * * Copyright (c) 2020 Bose Corporation - * Copyright (c) 2021-2024 Nordic Semiconductor ASA + * Copyright (c) 2021-2025 Nordic Semiconductor ASA * * SPDX-License-Identifier: Apache-2.0 */ @@ -1720,6 +1720,32 @@ int bt_bap_unicast_group_add_streams(struct bt_bap_unicast_group *unicast_group, */ int bt_bap_unicast_group_delete(struct bt_bap_unicast_group *unicast_group); +/** Callback function for bt_bap_unicast_group_foreach_stream() + * + * @param stream The audio stream + * @param user_data User data + * + * @retval true Stop iterating. + * @retval false Continue iterating. + */ +typedef bool (*bt_bap_unicast_group_foreach_stream_func_t)(struct bt_bap_stream *stream, + void *user_data); + +/** + * @brief Iterate through all streams in a unicast group + * + * @param unicast_group The unicast group + * @param func The callback function + * @param user_data User specified data that sent to the callback function + * + * @retval 0 Success (even if no streams exists in the group). + * @retval -ECANCELED Iteration was stopped by the callback function before complete. + * @retval -EINVAL @p unicast_group or @p func were NULL. + */ +int bt_bap_unicast_group_foreach_stream(struct bt_bap_unicast_group *unicast_group, + bt_bap_unicast_group_foreach_stream_func_t func, + void *user_data); + /** Unicast Client callback structure */ struct bt_bap_unicast_client_cb { /** diff --git a/include/zephyr/bluetooth/audio/cap.h b/include/zephyr/bluetooth/audio/cap.h index 046002318dbf..13c4155ea5c4 100644 --- a/include/zephyr/bluetooth/audio/cap.h +++ b/include/zephyr/bluetooth/audio/cap.h @@ -4,7 +4,7 @@ */ /* - * Copyright (c) 2022-2024 Nordic Semiconductor ASA + * Copyright (c) 2022-2025 Nordic Semiconductor ASA * * SPDX-License-Identifier: Apache-2.0 */ @@ -50,6 +50,9 @@ extern "C" { /** @brief Abstract Audio Broadcast Source structure. */ struct bt_cap_broadcast_source; +/** @brief Abstract CAP Unicast Group structure. */ +struct bt_cap_unicast_group; + /** * @brief Register the Common Audio Service. * @@ -254,6 +257,173 @@ int bt_cap_stream_send_ts(struct bt_cap_stream *stream, struct net_buf *buf, uin */ int bt_cap_stream_get_tx_sync(struct bt_cap_stream *stream, struct bt_iso_tx_info *info); +/** Parameter struct for each stream in the unicast group */ +struct bt_cap_unicast_group_stream_param { + /** Pointer to a stream object. */ + struct bt_cap_stream *stream; + + /** The QoS settings for the stream object. */ + struct bt_bap_qos_cfg *qos_cfg; +}; + +/** + * @brief Parameter struct for the unicast group functions + * + * Parameter struct for the bt_cap_unicast_group_create() and + * bt_cap_unicast_group_add_streams() functions. + */ +struct bt_cap_unicast_group_stream_pair_param { + /** Pointer to a receiving stream parameters. */ + struct bt_cap_unicast_group_stream_param *rx_param; + + /** Pointer to a transmitting stream parameters. */ + struct bt_cap_unicast_group_stream_param *tx_param; +}; + +/** Parameters for the creating unicast groups with bt_cap_unicast_group_create() */ +struct bt_cap_unicast_group_param { + /** The number of parameters in @p params */ + size_t params_count; + + /** Array of stream parameters */ + struct bt_cap_unicast_group_stream_pair_param *params; + + /** + * @brief Unicast Group packing mode. + * + * @ref BT_ISO_PACKING_SEQUENTIAL or @ref BT_ISO_PACKING_INTERLEAVED. + * + * @note This is a recommendation to the controller, which the controller may ignore. + */ + uint8_t packing; + +#if defined(CONFIG_BT_ISO_TEST_PARAMS) || defined(__DOXYGEN__) + /** + * @brief Central to Peripheral flush timeout + * + * The flush timeout in multiples of ISO_Interval for each payload sent + * from the Central to Peripheral. + * + * Value range from @ref BT_ISO_FT_MIN to @ref BT_ISO_FT_MAX + */ + uint8_t c_to_p_ft; + + /** + * @brief Peripheral to Central flush timeout + * + * The flush timeout in multiples of ISO_Interval for each payload sent + * from the Peripheral to Central. + * + * Value range from @ref BT_ISO_FT_MIN to @ref BT_ISO_FT_MAX. + */ + uint8_t p_to_c_ft; + + /** + * @brief ISO interval + * + * Time between consecutive CIS anchor points. + * + * Value range from @ref BT_ISO_ISO_INTERVAL_MIN to @ref BT_ISO_ISO_INTERVAL_MAX. + */ + uint16_t iso_interval; +#endif /* CONFIG_BT_ISO_TEST_PARAMS */ +}; + +/** + * @brief Create unicast group. + * + * Create a new audio unicast group with one or more audio streams as a unicast client. + * All streams shall share the same framing. + * All streams in the same direction shall share the same interval and latency (see + * @ref bt_bap_qos_cfg). + * + * @param[in] param The unicast group create parameters. + * @param[out] unicast_group Pointer to the unicast group created. + * + * @return Zero on success or (negative) error code otherwise. + */ +int bt_cap_unicast_group_create(const struct bt_cap_unicast_group_param *param, + struct bt_cap_unicast_group **unicast_group); + +/** + * @brief Reconfigure unicast group. + * + * Reconfigure a unicast group with one or more audio streams as a unicast client. + * All streams shall share the same framing. + * All streams in the same direction shall share the same interval and latency (see + * @ref bt_bap_qos_cfg). + * All streams in @p param shall already belong to @p unicast_group. + * Use bt_cap_unicast_group_add_streams() to add additional streams. + * + * @param unicast_group Pointer to the unicast group created. + * @param param The unicast group reconfigure parameters. + * + * @return Zero on success or (negative) error code otherwise. + */ +int bt_cap_unicast_group_reconfig(struct bt_cap_unicast_group *unicast_group, + const struct bt_cap_unicast_group_param *param); + +/** + * @brief Add streams to a unicast group as a unicast client + * + * This function can be used to add additional streams to a bt_cap_unicast_group. + * + * This can be called at any time before any of the streams in the group has been started + * (see bt_bap_stream_ops.started()). + * This can also be called after the streams have been stopped (see bt_bap_stream_ops.stopped()). + * + * Once a stream has been added to a unicast group, it cannot be removed. To remove a stream from a + * group, the group must be deleted with bt_cap_unicast_group_delete(), but this will require all + * streams in the group to be released first. + * + * @param unicast_group Pointer to the unicast group + * @param params Array of stream parameters with streams being added to the group. + * @param num_param Number of parameters in @p params. + * + * @return 0 in case of success or negative value in case of error. + */ +int bt_cap_unicast_group_add_streams(struct bt_cap_unicast_group *unicast_group, + const struct bt_cap_unicast_group_stream_pair_param params[], + size_t num_param); + +/** + * @brief Delete audio unicast group. + * + * Delete a audio unicast group as a client. All streams in the group shall + * be in the idle or configured state. + * + * @param unicast_group Pointer to the unicast group to delete + * + * @return Zero on success or (negative) error code otherwise. + */ +int bt_cap_unicast_group_delete(struct bt_cap_unicast_group *unicast_group); + +/** Callback function for bt_bap_unicast_group_foreach_stream() + * + * @param stream The audio stream + * @param user_data User data + * + * @retval true Stop iterating. + * @retval false Continue iterating. + */ +typedef bool (*bt_cap_unicast_group_foreach_stream_func_t)(struct bt_cap_stream *stream, + void *user_data); + +/** + * @brief Iterate through all streams in a unicast group + * + * @param unicast_group The unicast group + * @param func The callback function + * @param user_data User specified data that sent to the callback function + * + * @retval 0 Success (even if no streams exists in the group). + * @retval -ECANCELED Iteration was stopped by the callback function before complete. + * @retval -EINVAL @p unicast_group or @p func were NULL. + */ +int bt_cap_unicast_group_foreach_stream(struct bt_cap_unicast_group *unicast_group, + bt_cap_unicast_group_foreach_stream_func_t func, + void *user_data); + /** Stream specific parameters for the bt_cap_initiator_unicast_audio_start() function */ struct bt_cap_unicast_audio_start_stream_param { /** Coordinated or ad-hoc set member. */ diff --git a/samples/bluetooth/cap_initiator/src/cap_initiator_unicast.c b/samples/bluetooth/cap_initiator/src/cap_initiator_unicast.c index df45dfcadaa4..1d17a0cd5022 100644 --- a/samples/bluetooth/cap_initiator/src/cap_initiator_unicast.c +++ b/samples/bluetooth/cap_initiator/src/cap_initiator_unicast.c @@ -44,7 +44,7 @@ LOG_MODULE_REGISTER(cap_initiator_unicast, LOG_LEVEL_INF); */ static struct bt_bap_lc3_preset unicast_preset_16_2_1 = BT_BAP_LC3_UNICAST_PRESET_16_2_1( BT_AUDIO_LOCATION_MONO_AUDIO, BT_AUDIO_CONTEXT_TYPE_UNSPECIFIED); -static struct bt_bap_unicast_group *unicast_group; +static struct bt_cap_unicast_group *unicast_group; uint64_t total_rx_iso_packet_count; /* This value is exposed to test code */ uint64_t total_unicast_tx_iso_packet_count; /* This value is exposed to test code */ @@ -336,16 +336,16 @@ static int discover_sources(void) static int unicast_group_create(void) { - struct bt_bap_unicast_group_stream_param source_stream_param = { - .qos = &unicast_preset_16_2_1.qos, - .stream = &peer.source_stream.bap_stream, + struct bt_cap_unicast_group_stream_param source_stream_param = { + .qos_cfg = &unicast_preset_16_2_1.qos, + .stream = &peer.source_stream, }; - struct bt_bap_unicast_group_stream_param sink_stream_param = { - .qos = &unicast_preset_16_2_1.qos, - .stream = &peer.sink_stream.bap_stream, + struct bt_cap_unicast_group_stream_param sink_stream_param = { + .qos_cfg = &unicast_preset_16_2_1.qos, + .stream = &peer.sink_stream, }; - struct bt_bap_unicast_group_stream_pair_param pair_params = {0}; - struct bt_bap_unicast_group_param group_param = {0}; + struct bt_cap_unicast_group_stream_pair_param pair_params = {0}; + struct bt_cap_unicast_group_param group_param = {0}; int err; if (peer.source_ep != NULL) { @@ -359,7 +359,7 @@ static int unicast_group_create(void) group_param.params_count = 1U; group_param.params = &pair_params; - err = bt_bap_unicast_group_create(&group_param, &unicast_group); + err = bt_cap_unicast_group_create(&group_param, &unicast_group); if (err != 0) { LOG_ERR("Failed to create group: %d", err); return err; @@ -374,7 +374,7 @@ static int unicast_group_delete(void) { int err; - err = bt_bap_unicast_group_delete(unicast_group); + err = bt_cap_unicast_group_delete(unicast_group); if (err != 0) { LOG_ERR("Failed to delete group: %d", err); return err; diff --git a/samples/bluetooth/tmap_central/src/cap_initiator.c b/samples/bluetooth/tmap_central/src/cap_initiator.c index e2722db64f5e..ee129bf1f132 100644 --- a/samples/bluetooth/tmap_central/src/cap_initiator.c +++ b/samples/bluetooth/tmap_central/src/cap_initiator.c @@ -313,15 +313,15 @@ static struct bt_bap_unicast_client_cb unicast_client_cbs = { .endpoint = endpoint_cb, }; -static int unicast_group_create(struct bt_bap_unicast_group **out_unicast_group) +static int unicast_group_create(struct bt_cap_unicast_group **out_unicast_group) { int err = 0; - struct bt_bap_unicast_group_stream_param group_stream_params; - struct bt_bap_unicast_group_stream_pair_param pair_params; - struct bt_bap_unicast_group_param group_param; + struct bt_cap_unicast_group_stream_param group_stream_params; + struct bt_cap_unicast_group_stream_pair_param pair_params; + struct bt_cap_unicast_group_param group_param; - group_stream_params.qos = &unicast_preset_48_2_1.qos; - group_stream_params.stream = &unicast_streams[0].bap_stream; + group_stream_params.qos_cfg = &unicast_preset_48_2_1.qos; + group_stream_params.stream = &unicast_streams[0]; pair_params.tx_param = &group_stream_params; pair_params.rx_param = NULL; @@ -329,7 +329,7 @@ static int unicast_group_create(struct bt_bap_unicast_group **out_unicast_group) group_param.params_count = 1; group_param.params = &pair_params; - err = bt_bap_unicast_group_create(&group_param, out_unicast_group); + err = bt_cap_unicast_group_create(&group_param, out_unicast_group); if (err != 0) { printk("Failed to create group: %d\n", err); return err; @@ -444,7 +444,7 @@ int cap_initiator_init(void) int cap_initiator_setup(struct bt_conn *conn) { int err = 0; - struct bt_bap_unicast_group *unicast_group; + struct bt_cap_unicast_group *unicast_group; k_sem_reset(&sem_cas_discovery); k_sem_reset(&sem_discover_sink); diff --git a/subsys/bluetooth/audio/bap_unicast_client.c b/subsys/bluetooth/audio/bap_unicast_client.c index 1f081ee805b1..2e2b40957def 100644 --- a/subsys/bluetooth/audio/bap_unicast_client.c +++ b/subsys/bluetooth/audio/bap_unicast_client.c @@ -2733,30 +2733,35 @@ static bool valid_unicast_group_stream_param(const struct bt_bap_unicast_group * { const struct bt_bap_qos_cfg *qos; - CHECKIF(param->stream == NULL) { + if (param->stream == NULL) { LOG_DBG("param->stream is NULL"); - return -EINVAL; + return false; } CHECKIF(param->qos == NULL) { LOG_DBG("param->qos is NULL"); - return -EINVAL; + return false; } - if (param->stream != NULL && param->stream->group != NULL) { - if (unicast_group != NULL && param->stream->group != unicast_group) { + /* If unicast_group is non-NULL then we are doing a reconfigure */ + if (unicast_group != NULL) { + if (param->stream->group != unicast_group) { LOG_DBG("stream %p not part of group %p (%p)", param->stream, unicast_group, param->stream->group); - } else { + return false; + } + } else { + if (param->stream->group != NULL) { LOG_DBG("stream %p already part of group %p", param->stream, param->stream->group); + + return false; } - return -EALREADY; } CHECKIF(bt_audio_verify_qos(param->qos) != BT_BAP_ASCS_REASON_NONE) { LOG_DBG("Invalid QoS"); - return -EINVAL; + return false; } qos = param->qos; @@ -3141,6 +3146,33 @@ int bt_bap_unicast_group_delete(struct bt_bap_unicast_group *unicast_group) return 0; } +int bt_bap_unicast_group_foreach_stream(struct bt_bap_unicast_group *unicast_group, + bt_bap_unicast_group_foreach_stream_func_t func, + void *user_data) +{ + struct bt_bap_stream *stream, *next; + + if (unicast_group == NULL) { + LOG_DBG("unicast_group is NULL"); + return -EINVAL; + } + + if (func == NULL) { + LOG_DBG("func is NULL"); + return -EINVAL; + } + + SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&unicast_group->streams, stream, next, _node) { + const bool stop = func(stream, user_data); + + if (stop) { + return -ECANCELED; + } + } + + return 0; +} + int bt_bap_unicast_client_config(struct bt_bap_stream *stream, const struct bt_audio_codec_cfg *codec_cfg) { diff --git a/subsys/bluetooth/audio/cap_initiator.c b/subsys/bluetooth/audio/cap_initiator.c index 4d7c3f06f27b..811709620da4 100644 --- a/subsys/bluetooth/audio/cap_initiator.c +++ b/subsys/bluetooth/audio/cap_initiator.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022-2024 Nordic Semiconductor ASA + * Copyright (c) 2022-2025 Nordic Semiconductor ASA * * SPDX-License-Identifier: Apache-2.0 */ @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -420,6 +421,9 @@ int bt_cap_initiator_broadcast_get_base(struct bt_cap_broadcast_source *broadcas #endif /* CONFIG_BT_BAP_BROADCAST_SOURCE */ #if defined(CONFIG_BT_BAP_UNICAST_CLIENT) +static struct bt_cap_unicast_group { + struct bt_bap_unicast_group *bap_unicast_group; +} unicast_groups[CONFIG_BT_BAP_UNICAST_CLIENT_GROUP_COUNT]; static enum bt_bap_ep_state stream_get_state(const struct bt_bap_stream *bap_stream) { @@ -777,6 +781,274 @@ int bt_cap_initiator_unicast_discover(struct bt_conn *conn) return bt_cap_common_discover(conn, bt_cap_initiator_discover_complete); } +/** Validates that the stream parameter does not contain invalid values */ +static bool valid_unicast_group_stream_param(const struct bt_cap_unicast_group_stream_param *param) +{ + if (param->stream == NULL) { + LOG_DBG("param->stream is NULL"); + return false; + } + + /* Remaining values will be validated by BAP */ + + return true; +} + +static bool +valid_group_stream_pair_param(const struct bt_cap_unicast_group_stream_pair_param *pair_param) +{ + if (pair_param->rx_param != NULL && + !valid_unicast_group_stream_param(pair_param->rx_param)) { + return false; + } + + if (pair_param->tx_param != NULL && + !valid_unicast_group_stream_param(pair_param->tx_param)) { + return false; + } + + return true; +} + +static bool valid_unicast_group_param(const struct bt_cap_unicast_group_param *param) +{ + if (param == NULL) { + LOG_DBG("param is NULL"); + return false; + } + + if (param->params_count > CONFIG_BT_BAP_UNICAST_CLIENT_GROUP_STREAM_COUNT) { + LOG_DBG("Too many streams provided: %u/%u", param->params_count, + UNICAST_GROUP_STREAM_CNT); + return false; + } + + for (size_t i = 0U; i < param->params_count; i++) { + if (!valid_group_stream_pair_param(¶m->params[i])) { + return false; + } + } + + return true; +} + +struct cap_to_bap_unicast_params { + struct bt_bap_unicast_group_stream_param + stream_params[CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SNK_COUNT + + CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SRC_COUNT]; + struct bt_bap_unicast_group_stream_pair_param + pair_params[CONFIG_BT_BAP_UNICAST_CLIENT_GROUP_STREAM_COUNT]; + struct bt_bap_unicast_group_param group_param; +}; + +static void cap_to_bap_unicast_group_pair_param( + const struct bt_cap_unicast_group_stream_pair_param pair_params[], + struct cap_to_bap_unicast_params *bap_params) +{ + size_t stream_param_idx = 0U; + + for (size_t i = 0U; i < bap_params->group_param.params_count; i++) { + const struct bt_cap_unicast_group_stream_pair_param *pair_param = &pair_params[i]; + struct bt_bap_unicast_group_stream_pair_param *bap_pair_param = + &bap_params->pair_params[i]; + struct bt_bap_unicast_group_stream_param *bap_stream_param; + + if (pair_param->rx_param != NULL) { + bap_stream_param = &bap_params->stream_params[stream_param_idx++]; + + bap_stream_param->stream = &pair_param->rx_param->stream->bap_stream; + bap_stream_param->qos = pair_param->rx_param->qos_cfg; + + bap_pair_param->rx_param = bap_stream_param; + } else { + bap_pair_param->rx_param = NULL; + } + + if (pair_param->tx_param != NULL) { + bap_stream_param = &bap_params->stream_params[stream_param_idx++]; + + bap_stream_param->stream = &pair_param->tx_param->stream->bap_stream; + bap_stream_param->qos = pair_param->tx_param->qos_cfg; + + bap_pair_param->tx_param = bap_stream_param; + } else { + bap_pair_param->tx_param = NULL; + } + } +} + +static void cap_to_bap_unicast_group_param(const struct bt_cap_unicast_group_param *param, + struct cap_to_bap_unicast_params *bap_params) +{ + bap_params->group_param.params_count = param->params_count; + bap_params->group_param.packing = param->packing; +#if defined(CONFIG_BT_ISO_TEST_PARAMS) + bap_params->group_param.c_to_p_ft = param->c_to_p_ft; + bap_params->group_param.p_to_c_ft = param->p_to_c_ft; + bap_params->group_param.iso_interval = param->iso_interval; +#endif /* CONFIG_BT_ISO_TEST_PARAMS */ + bap_params->group_param.params = bap_params->pair_params; + + cap_to_bap_unicast_group_pair_param(param->params, bap_params); +} + +int bt_cap_unicast_group_create(const struct bt_cap_unicast_group_param *param, + struct bt_cap_unicast_group **unicast_group) +{ + struct cap_to_bap_unicast_params bap_param = {0}; + int err; + + static K_MUTEX_DEFINE(list_mutex); + + if (unicast_group == NULL) { + LOG_DBG("unicast_group is NULL"); + return -EINVAL; + } + + if (!valid_unicast_group_param(param)) { + return -EINVAL; + } + + *unicast_group = NULL; + + (void)k_mutex_lock(&list_mutex, K_FOREVER); + for (size_t i = 0; i < ARRAY_SIZE(unicast_groups); i++) { + if (unicast_groups[i].bap_unicast_group == NULL) { + *unicast_group = &unicast_groups[i]; + break; + } + } + + if (*unicast_group == NULL) { + LOG_DBG("Could not allocate more unicast groups"); + (void)k_mutex_unlock(&list_mutex); + return -ENOMEM; + } + + /* Populate the BAP group params based on the CAP params (mostly just a copy) */ + cap_to_bap_unicast_group_param(param, &bap_param); + + err = bt_bap_unicast_group_create(&bap_param.group_param, + &(*unicast_group)->bap_unicast_group); + if (err != 0) { + LOG_DBG("Failed to create unicast group: %d", err); + } + (void)k_mutex_unlock(&list_mutex); + + return err; +} + +int bt_cap_unicast_group_reconfig(struct bt_cap_unicast_group *unicast_group, + const struct bt_cap_unicast_group_param *param) +{ + struct cap_to_bap_unicast_params bap_param = {0}; + + if (unicast_group == NULL) { + LOG_DBG("unicast_group is NULL"); + return -EINVAL; + } + + if (!valid_unicast_group_param(param)) { + return -EINVAL; + } + + /* Populate the BAP group params based on the CAP params (mostly just a copy) */ + cap_to_bap_unicast_group_param(param, &bap_param); + + return bt_bap_unicast_group_reconfig(unicast_group->bap_unicast_group, + &bap_param.group_param); +} + +int bt_cap_unicast_group_add_streams(struct bt_cap_unicast_group *unicast_group, + const struct bt_cap_unicast_group_stream_pair_param params[], + size_t num_param) +{ + struct cap_to_bap_unicast_params bap_param = {.group_param.params_count = num_param}; + + if (unicast_group == NULL) { + LOG_DBG("unicast_group is NULL"); + return -EINVAL; + } + + if (params == NULL) { + LOG_DBG("params is NULL"); + return -EINVAL; + } + + if (num_param == 0U) { + LOG_DBG("num_param is 0"); + return -EINVAL; + } + + for (size_t i = 0U; i < num_param; i++) { + if (!valid_group_stream_pair_param(¶ms[i])) { + return -EINVAL; + } + } + + cap_to_bap_unicast_group_pair_param(params, &bap_param); + + return bt_bap_unicast_group_add_streams(unicast_group->bap_unicast_group, + bap_param.pair_params, num_param); +} + +int bt_cap_unicast_group_delete(struct bt_cap_unicast_group *unicast_group) +{ + int err; + + if (unicast_group == NULL) { + LOG_DBG("unicast_group is NULL"); + return -EINVAL; + } + + err = bt_bap_unicast_group_delete(unicast_group->bap_unicast_group); + if (err == 0) { + unicast_group->bap_unicast_group = NULL; + } + + return err; +} + +struct cap_unicast_group_foreach_stream_data { + bt_cap_unicast_group_foreach_stream_func_t func; + void *user_data; +}; + +static bool bap_unicast_group_foreach_stream_cb(struct bt_bap_stream *bap_stream, void *user_data) +{ + struct cap_unicast_group_foreach_stream_data *data = user_data; + + /* Since we are iterating on a CAP unicast group, we can assume that all streams are CAP + * streams + */ + data->func(CONTAINER_OF(bap_stream, struct bt_cap_stream, bap_stream), data->user_data); + + return false; +} + +int bt_cap_unicast_group_foreach_stream(struct bt_cap_unicast_group *unicast_group, + bt_cap_unicast_group_foreach_stream_func_t func, + void *user_data) +{ + struct cap_unicast_group_foreach_stream_data data = { + .func = func, + .user_data = user_data, + }; + + if (unicast_group == NULL) { + LOG_DBG("unicast_group is NULL"); + return -EINVAL; + } + + if (func == NULL) { + LOG_DBG("func is NULL"); + return -EINVAL; + } + + return bt_bap_unicast_group_foreach_stream(unicast_group->bap_unicast_group, + bap_unicast_group_foreach_stream_cb, &data); +} + static bool valid_unicast_audio_start_param(const struct bt_cap_unicast_audio_start_param *param) { struct bt_bap_unicast_group *unicast_group = NULL; diff --git a/subsys/bluetooth/audio/shell/cap_initiator.c b/subsys/bluetooth/audio/shell/cap_initiator.c index bfcba65f70e0..d9a076984966 100644 --- a/subsys/bluetooth/audio/shell/cap_initiator.c +++ b/subsys/bluetooth/audio/shell/cap_initiator.c @@ -42,6 +42,8 @@ #define UNICAST_SINK_SUPPORTED (CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SNK_COUNT > 0) #define UNICAST_SRC_SUPPORTED (CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SRC_COUNT > 0) +struct bt_cap_unicast_group *cap_unicast_group; + #define CAP_UNICAST_CLIENT_STREAM_COUNT ARRAY_SIZE(unicast_streams) static void cap_discover_cb(struct bt_conn *conn, int err, @@ -88,13 +90,13 @@ static void unicast_stop_complete_cb(int err, struct bt_conn *conn) } else { bt_shell_print("Unicast stop completed"); - if (default_unicast_group != NULL) { - err = bt_bap_unicast_group_delete(default_unicast_group); + if (cap_unicast_group != NULL) { + err = bt_cap_unicast_group_delete(cap_unicast_group); if (err != 0) { bt_shell_error("Failed to delete unicast group %p: %d", - default_unicast_group, err); + cap_unicast_group, err); } else { - default_unicast_group = NULL; + cap_unicast_group = NULL; } } } @@ -146,15 +148,15 @@ static void populate_connected_conns(struct bt_conn *conn, void *data) static int cmd_cap_initiator_unicast_start(const struct shell *sh, size_t argc, char *argv[]) { - struct bt_bap_unicast_group_stream_param + struct bt_cap_unicast_group_stream_param group_stream_params[CAP_UNICAST_CLIENT_STREAM_COUNT] = {0}; - struct bt_bap_unicast_group_stream_pair_param + struct bt_cap_unicast_group_stream_pair_param pair_params[CAP_UNICAST_CLIENT_STREAM_COUNT] = {0}; struct bt_cap_unicast_audio_start_stream_param stream_param[CAP_UNICAST_CLIENT_STREAM_COUNT] = {0}; struct bt_conn *connected_conns[CONFIG_BT_MAX_CONN] = {0}; struct bt_cap_unicast_audio_start_param start_param = {0}; - struct bt_bap_unicast_group_param group_param = {0}; + struct bt_cap_unicast_group_param group_param = {0}; size_t source_cnt = 1U; ssize_t conn_cnt = 1U; size_t sink_cnt = 1U; @@ -262,8 +264,8 @@ static int cmd_cap_initiator_unicast_start(const struct shell *sh, size_t argc, stream_param[start_param.count].codec_cfg = &uni_stream->codec_cfg; group_stream_params[start_param.count].stream = - &stream_param[start_param.count].stream->bap_stream; - group_stream_params[start_param.count].qos = &uni_stream->qos; + stream_param[start_param.count].stream; + group_stream_params[start_param.count].qos_cfg = &uni_stream->qos; pair_params[pair_cnt + j].tx_param = &group_stream_params[start_param.count]; @@ -293,8 +295,8 @@ static int cmd_cap_initiator_unicast_start(const struct shell *sh, size_t argc, copy_unicast_stream_preset(uni_stream, &default_source_preset); stream_param[start_param.count].codec_cfg = &uni_stream->codec_cfg; group_stream_params[start_param.count].stream = - &stream_param[start_param.count].stream->bap_stream; - group_stream_params[start_param.count].qos = &uni_stream->qos; + stream_param[start_param.count].stream; + group_stream_params[start_param.count].qos_cfg = &uni_stream->qos; pair_params[pair_cnt + j].rx_param = &group_stream_params[start_param.count]; @@ -318,8 +320,8 @@ static int cmd_cap_initiator_unicast_start(const struct shell *sh, size_t argc, group_param.params_count = pair_cnt; group_param.params = pair_params; - if (default_unicast_group == NULL) { - err = bt_bap_unicast_group_create(&group_param, &default_unicast_group); + if (cap_unicast_group == NULL) { + err = bt_cap_unicast_group_create(&group_param, &cap_unicast_group); if (err != 0) { shell_print(sh, "Failed to create group: %d", err); @@ -748,7 +750,7 @@ int cap_ac_unicast(const struct shell *sh, const struct bap_unicast_ac_param *pa size_t src_cnt = 0; int err; - if (default_unicast_group != NULL) { + if (cap_unicast_group != NULL) { shell_error(sh, "Unicast Group already exist, please delete first"); return -ENOEXEC; } @@ -840,11 +842,11 @@ int cap_ac_unicast(const struct shell *sh, const struct bap_unicast_ac_param *pa if (err != 0) { shell_error(sh, "Failed to start unicast audio: %d", err); - err = bt_bap_unicast_group_delete(default_unicast_group); + err = bt_cap_unicast_group_delete(cap_unicast_group); if (err != 0) { shell_error(sh, "Failed to delete group: %d", err); } else { - default_unicast_group = NULL; + cap_unicast_group = NULL; } return -ENOEXEC; diff --git a/tests/bluetooth/audio/cap_initiator/CMakeLists.txt b/tests/bluetooth/audio/cap_initiator/CMakeLists.txt index 2e4b1d275f0b..29d56f22a283 100644 --- a/tests/bluetooth/audio/cap_initiator/CMakeLists.txt +++ b/tests/bluetooth/audio/cap_initiator/CMakeLists.txt @@ -16,6 +16,7 @@ target_sources(testbinary PRIVATE src/main.c src/test_common.c + src/test_unicast_group.c src/test_unicast_start.c src/test_unicast_stop.c ) diff --git a/tests/bluetooth/audio/cap_initiator/src/test_unicast_group.c b/tests/bluetooth/audio/cap_initiator/src/test_unicast_group.c new file mode 100644 index 000000000000..ee6da9e53401 --- /dev/null +++ b/tests/bluetooth/audio/cap_initiator/src/test_unicast_group.c @@ -0,0 +1,390 @@ +/* test_unicast_group.c - unit test for unicast group functions */ + +/* + * Copyright (c) 2025 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "bap_endpoint.h" +#include "test_common.h" +#include "ztest_assert.h" +#include "ztest_test.h" + +struct cap_initiator_test_unicast_group_fixture { + struct bt_cap_unicast_group_param *group_param; + struct bt_cap_unicast_group *unicast_group; + struct bt_bap_qos_cfg *qos_cfg; +}; + +static void *cap_initiator_test_unicast_group_setup(void) +{ + struct cap_initiator_test_unicast_group_fixture *fixture; + + fixture = malloc(sizeof(*fixture)); + zassert_not_null(fixture); + + return fixture; +} + +static void cap_initiator_test_unicast_group_before(void *f) +{ + + struct cap_initiator_test_unicast_group_fixture *fixture = f; + struct bt_cap_unicast_group_stream_pair_param *pair_params; + struct bt_cap_unicast_group_stream_param *stream_params; + struct bt_cap_stream *cap_streams; + size_t pair_cnt = 0U; + size_t str_cnt = 0U; + + memset(f, 0, sizeof(struct cap_initiator_test_unicast_group_fixture)); + + fixture->group_param = calloc(sizeof(struct bt_cap_unicast_group_param), 1); + zassert_not_null(fixture->group_param); + pair_params = calloc(sizeof(struct bt_cap_unicast_group_stream_pair_param), + DIV_ROUND_UP(CONFIG_BT_BAP_UNICAST_CLIENT_GROUP_STREAM_COUNT, 2U)); + zassert_not_null(pair_params); + stream_params = calloc(sizeof(struct bt_cap_unicast_group_stream_param), + CONFIG_BT_BAP_UNICAST_CLIENT_GROUP_STREAM_COUNT); + zassert_not_null(stream_params); + cap_streams = calloc(sizeof(struct bt_cap_stream), + CONFIG_BT_BAP_UNICAST_CLIENT_GROUP_STREAM_COUNT); + zassert_not_null(cap_streams); + fixture->qos_cfg = calloc(sizeof(struct bt_bap_qos_cfg), 1); + zassert_not_null(fixture->qos_cfg); + + *fixture->qos_cfg = BT_BAP_QOS_CFG_UNFRAMED(10000u, 40u, 2u, 10u, 40000u); /* 16_2_1 */ + + while (str_cnt < CONFIG_BT_BAP_UNICAST_CLIENT_GROUP_STREAM_COUNT) { + stream_params[str_cnt].stream = &cap_streams[str_cnt]; + stream_params[str_cnt].qos_cfg = fixture->qos_cfg; + + if (str_cnt & 1) { + pair_params[pair_cnt].tx_param = &stream_params[str_cnt]; + } else { + pair_params[pair_cnt].rx_param = &stream_params[str_cnt]; + } + + str_cnt++; + pair_cnt = str_cnt / 2U; + } + + fixture->group_param->packing = BT_ISO_PACKING_SEQUENTIAL; + fixture->group_param->params_count = pair_cnt; + fixture->group_param->params = pair_params; +} + +static void cap_initiator_test_unicast_group_after(void *f) +{ + struct cap_initiator_test_unicast_group_fixture *fixture = f; + struct bt_cap_unicast_group_param *group_param; + + /* In the case of a test failing, we delete the group so that subsequent tests won't fail */ + if (fixture->unicast_group != NULL) { + bt_cap_unicast_group_delete(fixture->unicast_group); + } + + group_param = fixture->group_param; + + free(group_param->params[0].rx_param->stream); + free(group_param->params[0].rx_param); + free(group_param->params); + free(group_param); + free(fixture->qos_cfg); +} + +static void cap_initiator_test_unicast_group_teardown(void *f) +{ + free(f); +} + +ZTEST_SUITE(cap_initiator_test_unicast_group, NULL, cap_initiator_test_unicast_group_setup, + cap_initiator_test_unicast_group_before, cap_initiator_test_unicast_group_after, + cap_initiator_test_unicast_group_teardown); + +static ZTEST_F(cap_initiator_test_unicast_group, test_initiator_unicast_group_create) +{ + int err = 0; + + err = bt_cap_unicast_group_create(fixture->group_param, &fixture->unicast_group); + zassert_equal(err, 0, "Unexpected return value %d", err); +} + +static ZTEST_F(cap_initiator_test_unicast_group, + test_initiator_unicast_group_create_inval_null_param) +{ + int err = 0; + + err = bt_cap_unicast_group_create(NULL, &fixture->unicast_group); + zassert_equal(err, -EINVAL, "Unexpected return value %d", err); +} + +static ZTEST_F(cap_initiator_test_unicast_group, + test_initiator_unicast_group_create_inval_null_rx_stream) +{ + int err = 0; + + if (fixture->group_param->params[0].rx_param->stream == NULL) { + ztest_test_skip(); + } + fixture->group_param->params[0].rx_param->stream = NULL; + + err = bt_cap_unicast_group_create(fixture->group_param, &fixture->unicast_group); + zassert_equal(err, -EINVAL, "Unexpected return value %d", err); +} + +static ZTEST_F(cap_initiator_test_unicast_group, + test_initiator_unicast_group_create_inval_null_tx_stream) +{ + int err = 0; + + if (fixture->group_param->params[0].tx_param->stream == NULL) { + ztest_test_skip(); + } + fixture->group_param->params[0].tx_param->stream = NULL; + + err = bt_cap_unicast_group_create(fixture->group_param, &fixture->unicast_group); + zassert_equal(err, -EINVAL, "Unexpected return value %d", err); +} + +static ZTEST_F(cap_initiator_test_unicast_group, + test_initiator_unicast_group_create_inval_too_many_streams) +{ + int err = 0; + + fixture->group_param->params_count = CONFIG_BT_BAP_UNICAST_CLIENT_GROUP_STREAM_COUNT + 1; + + err = bt_cap_unicast_group_create(fixture->group_param, &fixture->unicast_group); + zassert_equal(err, -EINVAL, "Unexpected return value %d", err); +} + +static ZTEST_F(cap_initiator_test_unicast_group, test_initiator_unicast_group_reconfig) +{ + int err = 0; + + err = bt_cap_unicast_group_create(fixture->group_param, &fixture->unicast_group); + zassert_equal(err, 0, "Unexpected return value %d", err); + + err = bt_cap_unicast_group_reconfig(fixture->unicast_group, fixture->group_param); + zassert_equal(err, 0, "Unexpected return value %d", err); +} + +static ZTEST_F(cap_initiator_test_unicast_group, + test_initiator_unicast_group_reconfig_inval_null_group) +{ + int err = 0; + + err = bt_cap_unicast_group_create(fixture->group_param, &fixture->unicast_group); + zassert_equal(err, 0, "Unexpected return value %d", err); + + err = bt_cap_unicast_group_reconfig(NULL, fixture->group_param); + zassert_equal(err, -EINVAL, "Unexpected return value %d", err); +} + +static ZTEST_F(cap_initiator_test_unicast_group, + test_initiator_unicast_group_reconfig_inval_null_param) +{ + int err = 0; + + err = bt_cap_unicast_group_create(fixture->group_param, &fixture->unicast_group); + zassert_equal(err, 0, "Unexpected return value %d", err); + + err = bt_cap_unicast_group_reconfig(fixture->unicast_group, NULL); + zassert_equal(err, -EINVAL, "Unexpected return value %d", err); +} + +static ZTEST_F(cap_initiator_test_unicast_group, test_initiator_unicast_group_add_streams) +{ + struct bt_cap_stream stream = {}; + struct bt_cap_unicast_group_stream_param stream_param = { + .stream = &stream, + .qos_cfg = fixture->qos_cfg, + }; + const struct bt_cap_unicast_group_stream_pair_param pair_param = { + .rx_param = &stream_param, + }; + int err = 0; + + err = bt_cap_unicast_group_create(fixture->group_param, &fixture->unicast_group); + zassert_equal(err, 0, "Unexpected return value %d", err); + + err = bt_cap_unicast_group_add_streams(fixture->unicast_group, &pair_param, 1); + zassert_equal(err, 0, "Unexpected return value %d", err); +} + +static ZTEST_F(cap_initiator_test_unicast_group, + test_initiator_unicast_group_add_streams_inval_null_group) +{ + struct bt_cap_stream stream = {}; + struct bt_cap_unicast_group_stream_param stream_param = { + .stream = &stream, + .qos_cfg = fixture->qos_cfg, + }; + const struct bt_cap_unicast_group_stream_pair_param pair_param = { + .rx_param = &stream_param, + }; + int err = 0; + + err = bt_cap_unicast_group_create(fixture->group_param, &fixture->unicast_group); + zassert_equal(err, 0, "Unexpected return value %d", err); + + err = bt_cap_unicast_group_add_streams(NULL, &pair_param, 1); + zassert_equal(err, -EINVAL, "Unexpected return value %d", err); +} + +static ZTEST_F(cap_initiator_test_unicast_group, + test_initiator_unicast_group_add_streams_inval_null_param) +{ + int err = 0; + + err = bt_cap_unicast_group_create(fixture->group_param, &fixture->unicast_group); + zassert_equal(err, 0, "Unexpected return value %d", err); + + err = bt_cap_unicast_group_add_streams(fixture->unicast_group, NULL, 1); + zassert_equal(err, -EINVAL, "Unexpected return value %d", err); +} + +static ZTEST_F(cap_initiator_test_unicast_group, + test_initiator_unicast_group_add_streams_inval_0_param) +{ + struct bt_cap_stream stream = {}; + struct bt_cap_unicast_group_stream_param stream_param = { + .stream = &stream, + .qos_cfg = fixture->qos_cfg, + }; + const struct bt_cap_unicast_group_stream_pair_param pair_param = { + .rx_param = &stream_param, + }; + int err = 0; + + err = bt_cap_unicast_group_create(fixture->group_param, &fixture->unicast_group); + zassert_equal(err, 0, "Unexpected return value %d", err); + + err = bt_cap_unicast_group_add_streams(fixture->unicast_group, &pair_param, 0); + zassert_equal(err, -EINVAL, "Unexpected return value %d", err); +} + +static ZTEST_F(cap_initiator_test_unicast_group, test_initiator_unicast_group_delete) +{ + int err = 0; + + err = bt_cap_unicast_group_create(fixture->group_param, &fixture->unicast_group); + zassert_equal(err, 0, "Unexpected return value %d", err); + + err = bt_cap_unicast_group_delete(fixture->unicast_group); + zassert_equal(err, 0, "Unexpected return value %d", err); + fixture->unicast_group = NULL; +} + +static ZTEST_F(cap_initiator_test_unicast_group, + test_initiator_unicast_group_delete_inval_null_group) +{ + int err = 0; + + err = bt_cap_unicast_group_create(fixture->group_param, &fixture->unicast_group); + zassert_equal(err, 0, "Unexpected return value %d", err); + + err = bt_cap_unicast_group_delete(NULL); + zassert_equal(err, -EINVAL, "Unexpected return value %d", err); +} + +static ZTEST_F(cap_initiator_test_unicast_group, + test_initiator_unicast_group_delete_inval_double_delete) +{ + int err = 0; + + err = bt_cap_unicast_group_create(fixture->group_param, &fixture->unicast_group); + zassert_equal(err, 0, "Unexpected return value %d", err); + + err = bt_cap_unicast_group_delete(fixture->unicast_group); + zassert_equal(err, 0, "Unexpected return value %d", err); + + err = bt_cap_unicast_group_delete(fixture->unicast_group); + zassert_equal(err, -EINVAL, "Unexpected return value %d", err); + fixture->unicast_group = NULL; +} + +static bool unicast_group_foreach_stream_cb(struct bt_cap_stream *cap_stream, void *user_data) +{ + size_t *cnt = user_data; + + (*cnt)++; + + return false; +} + +static ZTEST_F(cap_initiator_test_unicast_group, test_initiator_unicast_group_foreach_stream) +{ + size_t expect_cnt = 0U; + size_t cnt = 0U; + int err; + + err = bt_cap_unicast_group_create(fixture->group_param, &fixture->unicast_group); + zassert_equal(err, 0, "Unexpected return value %d", err); + + err = bt_cap_unicast_group_foreach_stream(fixture->unicast_group, + unicast_group_foreach_stream_cb, &cnt); + zassert_equal(err, 0, "Unexpected return value %d", err); + + for (size_t i = 0; i < fixture->group_param->params_count; i++) { + if (fixture->group_param->params[i].rx_param != NULL) { + expect_cnt++; + } + + if (fixture->group_param->params[i].tx_param != NULL) { + expect_cnt++; + } + } + + zassert_equal(cnt, expect_cnt, "Unexpected cnt (%zu != %zu)", cnt, expect_cnt); +} + +static ZTEST_F(cap_initiator_test_unicast_group, + test_initiator_unicast_group_foreach_stream_inval_null_group) +{ + size_t expect_cnt = 0U; + size_t cnt = 0U; + int err; + + err = bt_cap_unicast_group_create(fixture->group_param, &fixture->unicast_group); + zassert_equal(err, 0, "Unexpected return value %d", err); + + err = bt_cap_unicast_group_foreach_stream(NULL, unicast_group_foreach_stream_cb, &cnt); + zassert_equal(err, -EINVAL, "Unexpected return value %d", err); + + zassert_equal(cnt, expect_cnt, "Unexpected cnt (%zu != %zu)", cnt, expect_cnt); +} + +static ZTEST_F(cap_initiator_test_unicast_group, + test_initiator_unicast_group_foreach_stream_inval_null_func) +{ + size_t expect_cnt = 0U; + size_t cnt = 0U; + int err; + + err = bt_cap_unicast_group_create(fixture->group_param, &fixture->unicast_group); + zassert_equal(err, 0, "Unexpected return value %d", err); + + err = bt_cap_unicast_group_foreach_stream(fixture->unicast_group, NULL, &cnt); + zassert_equal(err, -EINVAL, "Unexpected return value %d", err); + + zassert_equal(cnt, expect_cnt, "Unexpected cnt (%zu != %zu)", cnt, expect_cnt); +} diff --git a/tests/bluetooth/audio/cap_initiator/uut/bap_unicast_client.c b/tests/bluetooth/audio/cap_initiator/uut/bap_unicast_client.c index 5a8994f36cdf..b054ea26e460 100644 --- a/tests/bluetooth/audio/cap_initiator/uut/bap_unicast_client.c +++ b/tests/bluetooth/audio/cap_initiator/uut/bap_unicast_client.c @@ -1,6 +1,6 @@ /* * Copyright (c) 2023 Codecoup - * Copyright (c) 2024 Nordic Semiconductor ASA + * Copyright (c) 2024-2025 Nordic Semiconductor ASA * * SPDX-License-Identifier: Apache-2.0 */ @@ -23,6 +23,7 @@ #include "bap_iso.h" static struct bt_bap_unicast_client_cb *unicast_client_cb; +static struct bt_bap_unicast_group bap_unicast_group; bool bt_bap_ep_is_unicast_client(const struct bt_bap_ep *ep) { @@ -361,3 +362,104 @@ int bt_bap_unicast_client_register_cb(struct bt_bap_unicast_client_cb *cb) return 0; } + +int bt_bap_unicast_group_create(struct bt_bap_unicast_group_param *param, + struct bt_bap_unicast_group **unicast_group) +{ + if (bap_unicast_group.allocated) { + return -ENOMEM; + } + + bap_unicast_group.allocated = true; + *unicast_group = &bap_unicast_group; + + sys_slist_init(&bap_unicast_group.streams); + for (size_t i = 0U; i < param->params_count; i++) { + if (param->params[i].rx_param != NULL) { + sys_slist_append(&bap_unicast_group.streams, + ¶m->params[i].rx_param->stream->_node); + } + + if (param->params[i].tx_param != NULL) { + sys_slist_append(&bap_unicast_group.streams, + ¶m->params[i].tx_param->stream->_node); + } + } + + return 0; +} + +int bt_bap_unicast_group_reconfig(struct bt_bap_unicast_group *unicast_group, + const struct bt_bap_unicast_group_param *param) +{ + if (unicast_group == NULL || param == NULL) { + return -EINVAL; + } + + return 0; +} + +int bt_bap_unicast_group_add_streams(struct bt_bap_unicast_group *unicast_group, + struct bt_bap_unicast_group_stream_pair_param params[], + size_t num_param) +{ + if (unicast_group == NULL || params == NULL) { + return -EINVAL; + } + + for (size_t i = 0U; i < num_param; i++) { + if (params[i].rx_param != NULL) { + sys_slist_append(&unicast_group->streams, + ¶ms[i].rx_param->stream->_node); + } + + if (params[i].tx_param != NULL) { + sys_slist_append(&unicast_group->streams, + ¶ms[i].tx_param->stream->_node); + } + } + + return 0; +} + +int bt_bap_unicast_group_delete(struct bt_bap_unicast_group *unicast_group) +{ + struct bt_bap_stream *stream, *next; + + if (unicast_group == NULL) { + return -EINVAL; + } + + unicast_group->allocated = false; + + SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&unicast_group->streams, stream, next, _node) { + sys_slist_remove(&unicast_group->streams, NULL, &stream->_node); + } + + return 0; +} + +int bt_bap_unicast_group_foreach_stream(struct bt_bap_unicast_group *unicast_group, + bt_bap_unicast_group_foreach_stream_func_t func, + void *user_data) +{ + struct bt_bap_stream *stream, *next; + + if (unicast_group == NULL) { + return -EINVAL; + } + + if (func == NULL) { + return -EINVAL; + } + + SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&unicast_group->streams, stream, next, _node) { + const bool stop = func(stream, user_data); + + if (stop) { + return -ECANCELED; + } + } + + return 0; +} diff --git a/tests/bluetooth/audio/mocks/src/kernel.c b/tests/bluetooth/audio/mocks/src/kernel.c index f150d5cb5cff..4b84b9a94ad4 100644 --- a/tests/bluetooth/audio/mocks/src/kernel.c +++ b/tests/bluetooth/audio/mocks/src/kernel.c @@ -21,6 +21,8 @@ #define FFF_FAKES_LIST(FAKE) \ FAKE(z_timeout_remaining) \ FAKE(k_work_cancel_delayable_sync) \ + FAKE(k_sem_take) \ + FAKE(k_sem_give) /* List of k_work items to be worked. */ static sys_slist_t work_pending; diff --git a/tests/bsim/bluetooth/audio/src/cap_initiator_unicast_test.c b/tests/bsim/bluetooth/audio/src/cap_initiator_unicast_test.c index d3e415ac3967..5a073f2b8257 100644 --- a/tests/bsim/bluetooth/audio/src/cap_initiator_unicast_test.c +++ b/tests/bsim/bluetooth/audio/src/cap_initiator_unicast_test.c @@ -616,20 +616,20 @@ static void discover_cas(struct bt_conn *conn) WAIT_FOR_FLAG(flag_discovered); } -static void unicast_group_create(struct bt_bap_unicast_group **out_unicast_group) +static void unicast_group_create(struct bt_cap_unicast_group **out_unicast_group) { - struct bt_bap_unicast_group_stream_param group_source_stream_params; - struct bt_bap_unicast_group_stream_param group_sink_stream_params; - struct bt_bap_unicast_group_stream_pair_param pair_params; - struct bt_bap_unicast_group_param group_param; + struct bt_cap_unicast_group_stream_param group_source_stream_params; + struct bt_cap_unicast_group_stream_param group_sink_stream_params; + struct bt_cap_unicast_group_stream_pair_param pair_params; + struct bt_cap_unicast_group_param group_param; int err; - group_sink_stream_params.qos = &unicast_preset_16_2_1.qos; + group_sink_stream_params.qos_cfg = &unicast_preset_16_2_1.qos; group_sink_stream_params.stream = - bap_stream_from_audio_test_stream(&unicast_client_sink_streams[0]); - group_source_stream_params.qos = &unicast_preset_16_2_1.qos; + cap_stream_from_audio_test_stream(&unicast_client_sink_streams[0]); + group_source_stream_params.qos_cfg = &unicast_preset_16_2_1.qos; group_source_stream_params.stream = - bap_stream_from_audio_test_stream(&unicast_client_source_streams[0]); + cap_stream_from_audio_test_stream(&unicast_client_source_streams[0]); pair_params.tx_param = &group_sink_stream_params; pair_params.rx_param = &group_source_stream_params; @@ -637,14 +637,14 @@ static void unicast_group_create(struct bt_bap_unicast_group **out_unicast_group group_param.params_count = 1; group_param.params = &pair_params; - err = bt_bap_unicast_group_create(&group_param, out_unicast_group); + err = bt_cap_unicast_group_create(&group_param, out_unicast_group); if (err != 0) { FAIL("Failed to create group: %d\n", err); return; } } -static void unicast_audio_start(struct bt_bap_unicast_group *unicast_group, bool wait) +static void unicast_audio_start(struct bt_cap_unicast_group *unicast_group, bool wait) { struct bt_cap_unicast_audio_start_stream_param stream_param[2]; struct bt_cap_unicast_audio_start_param param; @@ -761,7 +761,7 @@ static void unicast_audio_update(void) WAIT_FOR_FLAG(flag_updated); } -static void unicast_audio_stop(struct bt_bap_unicast_group *unicast_group) +static void unicast_audio_stop(struct bt_cap_unicast_group *unicast_group) { struct bt_cap_unicast_audio_stop_param param; int err; @@ -828,27 +828,27 @@ static void unicast_group_delete_inval(void) { int err; - err = bt_bap_unicast_group_delete(NULL); + err = bt_cap_unicast_group_delete(NULL); if (err == 0) { - FAIL("bt_bap_unicast_group_delete with NULL group did not fail\n"); + FAIL("bt_cap_unicast_group_delete with NULL group did not fail\n"); return; } } -static void unicast_group_delete(struct bt_bap_unicast_group *unicast_group) +static void unicast_group_delete(struct bt_cap_unicast_group *unicast_group) { int err; - err = bt_bap_unicast_group_delete(unicast_group); + err = bt_cap_unicast_group_delete(unicast_group); if (err != 0) { FAIL("Failed to create group: %d\n", err); return; } /* Verify that it cannot be deleted twice */ - err = bt_bap_unicast_group_delete(unicast_group); + err = bt_cap_unicast_group_delete(unicast_group); if (err == 0) { - FAIL("bt_bap_unicast_group_delete with already-deleted unicast group did not " + FAIL("bt_cap_unicast_group_delete with already-deleted unicast group did not " "fail\n"); return; } @@ -856,7 +856,7 @@ static void unicast_group_delete(struct bt_bap_unicast_group *unicast_group) static void test_main_cap_initiator_unicast(void) { - struct bt_bap_unicast_group *unicast_group; + struct bt_cap_unicast_group *unicast_group; const size_t iterations = 2; init(); @@ -909,7 +909,7 @@ static void test_main_cap_initiator_unicast(void) static void test_main_cap_initiator_unicast_inval(void) { - struct bt_bap_unicast_group *unicast_group; + struct bt_cap_unicast_group *unicast_group; init(); @@ -948,7 +948,7 @@ static void test_main_cap_initiator_unicast_inval(void) static void test_cap_initiator_unicast_timeout(void) { - struct bt_bap_unicast_group *unicast_group; + struct bt_cap_unicast_group *unicast_group; const k_timeout_t timeout = K_SECONDS(10); const size_t iterations = 2; @@ -1013,7 +1013,7 @@ static void unset_invalid_metadata_type(uint8_t type) static void test_cap_initiator_unicast_ase_error(void) { - struct bt_bap_unicast_group *unicast_group; + struct bt_cap_unicast_group *unicast_group; const uint8_t inval_type = 0xFD; init(); @@ -1071,12 +1071,12 @@ static int cap_initiator_ac_create_unicast_group(const struct cap_initiator_ac_p size_t snk_cnt, struct unicast_stream *src_uni_streams[], size_t src_cnt, - struct bt_bap_unicast_group **unicast_group) + struct bt_cap_unicast_group **unicast_group) { - struct bt_bap_unicast_group_stream_param snk_group_stream_params[CAP_AC_MAX_SNK] = {0}; - struct bt_bap_unicast_group_stream_param src_group_stream_params[CAP_AC_MAX_SRC] = {0}; - struct bt_bap_unicast_group_stream_pair_param pair_params[CAP_AC_MAX_PAIR] = {0}; - struct bt_bap_unicast_group_param group_param = {0}; + struct bt_cap_unicast_group_stream_param snk_group_stream_params[CAP_AC_MAX_SNK] = {0}; + struct bt_cap_unicast_group_stream_param src_group_stream_params[CAP_AC_MAX_SRC] = {0}; + struct bt_cap_unicast_group_stream_pair_param pair_params[CAP_AC_MAX_PAIR] = {0}; + struct bt_cap_unicast_group_param group_param = {0}; struct bt_bap_qos_cfg *snk_qos[CAP_AC_MAX_SNK]; struct bt_bap_qos_cfg *src_qos[CAP_AC_MAX_SRC]; size_t snk_stream_cnt = 0U; @@ -1097,14 +1097,14 @@ static int cap_initiator_ac_create_unicast_group(const struct cap_initiator_ac_p * and direction */ for (size_t i = 0U; i < snk_cnt; i++) { - snk_group_stream_params[i].qos = snk_qos[i]; + snk_group_stream_params[i].qos_cfg = snk_qos[i]; snk_group_stream_params[i].stream = - bap_stream_from_audio_test_stream(&snk_uni_streams[i]->stream); + cap_stream_from_audio_test_stream(&snk_uni_streams[i]->stream); } for (size_t i = 0U; i < src_cnt; i++) { - src_group_stream_params[i].qos = src_qos[i]; + src_group_stream_params[i].qos_cfg = src_qos[i]; src_group_stream_params[i].stream = - bap_stream_from_audio_test_stream(&src_uni_streams[i]->stream); + cap_stream_from_audio_test_stream(&src_uni_streams[i]->stream); } for (size_t i = 0U; i < param->conn_cnt; i++) { @@ -1131,7 +1131,7 @@ static int cap_initiator_ac_create_unicast_group(const struct cap_initiator_ac_p group_param.params = pair_params; group_param.params_count = pair_cnt; - return bt_bap_unicast_group_create(&group_param, unicast_group); + return bt_cap_unicast_group_create(&group_param, unicast_group); } static int cap_initiator_ac_cap_unicast_start(const struct cap_initiator_ac_param *param, @@ -1139,7 +1139,7 @@ static int cap_initiator_ac_cap_unicast_start(const struct cap_initiator_ac_para size_t snk_cnt, struct unicast_stream *src_uni_streams[], size_t src_cnt, - struct bt_bap_unicast_group *unicast_group) + struct bt_cap_unicast_group *unicast_group) { struct bt_cap_unicast_audio_start_stream_param stream_params[CAP_AC_MAX_STREAM] = {0}; struct bt_audio_codec_cfg *snk_codec_cfgs[CAP_AC_MAX_SNK] = {0}; @@ -1271,7 +1271,7 @@ static int cap_initiator_ac_cap_unicast_start(const struct cap_initiator_ac_para } static int cap_initiator_ac_unicast(const struct cap_initiator_ac_param *param, - struct bt_bap_unicast_group **unicast_group) + struct bt_cap_unicast_group **unicast_group) { /* Allocate params large enough for any params, but only use what is required */ struct unicast_stream *snk_uni_streams[CAP_AC_MAX_SNK]; @@ -1376,7 +1376,7 @@ static int cap_initiator_ac_unicast(const struct cap_initiator_ac_param *param, static void test_cap_initiator_ac(const struct cap_initiator_ac_param *param) { - struct bt_bap_unicast_group *unicast_group; + struct bt_cap_unicast_group *unicast_group; bool expect_tx = false; bool expect_rx = false; diff --git a/tests/bsim/bluetooth/audio/src/gmap_ugg_test.c b/tests/bsim/bluetooth/audio/src/gmap_ugg_test.c index c1d608058712..aa465de57b32 100644 --- a/tests/bsim/bluetooth/audio/src/gmap_ugg_test.c +++ b/tests/bsim/bluetooth/audio/src/gmap_ugg_test.c @@ -613,14 +613,14 @@ static int gmap_unicast_ac_create_unicast_group(const struct gmap_unicast_ac_par size_t snk_cnt, struct unicast_stream *src_uni_streams[], size_t src_cnt, - struct bt_bap_unicast_group **unicast_group) + struct bt_cap_unicast_group **unicast_group) { - struct bt_bap_unicast_group_stream_param + struct bt_cap_unicast_group_stream_param snk_group_stream_params[GMAP_UNICAST_AC_MAX_SNK] = {0}; - struct bt_bap_unicast_group_stream_param + struct bt_cap_unicast_group_stream_param src_group_stream_params[GMAP_UNICAST_AC_MAX_SRC] = {0}; - struct bt_bap_unicast_group_stream_pair_param pair_params[GMAP_UNICAST_AC_MAX_PAIR] = {0}; - struct bt_bap_unicast_group_param group_param = {0}; + struct bt_cap_unicast_group_stream_pair_param pair_params[GMAP_UNICAST_AC_MAX_PAIR] = {0}; + struct bt_cap_unicast_group_param group_param = {0}; size_t snk_stream_cnt = 0U; size_t src_stream_cnt = 0U; size_t pair_cnt = 0U; @@ -631,14 +631,14 @@ static int gmap_unicast_ac_create_unicast_group(const struct gmap_unicast_ac_par * and direction */ for (size_t i = 0U; i < snk_cnt; i++) { - snk_group_stream_params[i].qos = &snk_uni_streams[i]->qos; + snk_group_stream_params[i].qos_cfg = &snk_uni_streams[i]->qos; snk_group_stream_params[i].stream = - bap_stream_from_audio_test_stream(&snk_uni_streams[i]->stream); + cap_stream_from_audio_test_stream(&snk_uni_streams[i]->stream); } for (size_t i = 0U; i < src_cnt; i++) { - src_group_stream_params[i].qos = &src_uni_streams[i]->qos; + src_group_stream_params[i].qos_cfg = &src_uni_streams[i]->qos; src_group_stream_params[i].stream = - bap_stream_from_audio_test_stream(&src_uni_streams[i]->stream); + cap_stream_from_audio_test_stream(&src_uni_streams[i]->stream); } for (size_t i = 0U; i < param->conn_cnt; i++) { @@ -665,13 +665,13 @@ static int gmap_unicast_ac_create_unicast_group(const struct gmap_unicast_ac_par group_param.params = pair_params; group_param.params_count = pair_cnt; - return bt_bap_unicast_group_create(&group_param, unicast_group); + return bt_cap_unicast_group_create(&group_param, unicast_group); } static int gmap_ac_cap_unicast_start(const struct gmap_unicast_ac_param *param, struct unicast_stream *snk_uni_streams[], size_t snk_cnt, struct unicast_stream *src_uni_streams[], size_t src_cnt, - struct bt_bap_unicast_group *unicast_group) + struct bt_cap_unicast_group *unicast_group) { struct bt_cap_unicast_audio_start_stream_param stream_params[GMAP_UNICAST_AC_MAX_STREAM] = { 0}; @@ -813,7 +813,7 @@ static int gmap_ac_cap_unicast_start(const struct gmap_unicast_ac_param *param, } static int gmap_ac_unicast(const struct gmap_unicast_ac_param *param, - struct bt_bap_unicast_group **unicast_group) + struct bt_cap_unicast_group **unicast_group) { /* Allocate params large enough for any params, but only use what is required */ struct unicast_stream *snk_uni_streams[GMAP_UNICAST_AC_MAX_SNK]; @@ -900,7 +900,7 @@ static int gmap_ac_unicast(const struct gmap_unicast_ac_param *param, return 0; } -static void unicast_audio_stop(struct bt_bap_unicast_group *unicast_group) +static void unicast_audio_stop(struct bt_cap_unicast_group *unicast_group) { struct bt_cap_unicast_audio_stop_param param; int err; @@ -924,11 +924,11 @@ static void unicast_audio_stop(struct bt_bap_unicast_group *unicast_group) memset(started_unicast_streams, 0, sizeof(started_unicast_streams)); } -static void unicast_group_delete(struct bt_bap_unicast_group *unicast_group) +static void unicast_group_delete(struct bt_cap_unicast_group *unicast_group) { int err; - err = bt_bap_unicast_group_delete(unicast_group); + err = bt_cap_unicast_group_delete(unicast_group); if (err != 0) { FAIL("Failed to create group: %d\n", err); return; @@ -937,7 +937,7 @@ static void unicast_group_delete(struct bt_bap_unicast_group *unicast_group) static void test_gmap_ugg_unicast_ac(const struct gmap_unicast_ac_param *param) { - struct bt_bap_unicast_group *unicast_group; + struct bt_cap_unicast_group *unicast_group; bool expect_tx = false; bool expect_rx = false; @@ -1165,10 +1165,10 @@ static int test_gmap_ugg_broadcast_ac(const struct gmap_broadcast_ac_param *para uint8_t stereo_data[] = { BT_AUDIO_CODEC_DATA(BT_AUDIO_CODEC_CFG_CHAN_ALLOC, BT_AUDIO_LOCATION_FRONT_RIGHT | BT_AUDIO_LOCATION_FRONT_LEFT)}; - uint8_t right_data[] = {BT_AUDIO_CODEC_DATA(BT_AUDIO_CODEC_CFG_CHAN_ALLOC, - BT_AUDIO_LOCATION_FRONT_RIGHT)}; - uint8_t left_data[] = {BT_AUDIO_CODEC_DATA(BT_AUDIO_CODEC_CFG_CHAN_ALLOC, - BT_AUDIO_LOCATION_FRONT_LEFT)}; + uint8_t right_data[] = { + BT_AUDIO_CODEC_DATA(BT_AUDIO_CODEC_CFG_CHAN_ALLOC, BT_AUDIO_LOCATION_FRONT_RIGHT)}; + uint8_t left_data[] = { + BT_AUDIO_CODEC_DATA(BT_AUDIO_CODEC_CFG_CHAN_ALLOC, BT_AUDIO_LOCATION_FRONT_LEFT)}; struct bt_cap_initiator_broadcast_subgroup_param subgroup_param = {0}; struct bt_cap_initiator_broadcast_create_param create_param = {0}; struct bt_cap_initiator_broadcast_stream_param