From db0d2fa9b877647b9c0abdd9fbc6e2a47d8b3409 Mon Sep 17 00:00:00 2001 From: Emil Gydesen Date: Wed, 19 Feb 2025 15:35:04 +0100 Subject: [PATCH] Bluetooth: CAP: Add cap_unicast_group API Adds a new abstract struct for unicast group that is specific for CAP. The difference between this and the BAP unicast group, is that the parameters are CAP streams and thus ensuring that the streams in the group adhere to the additional requirements that CAP has on top of BAP. This also adds foreach functions for both CAP and BAP to allow users to iterate on the streams in the abstract groups. Various samples, modules and tests have been updated to use the CAP struct and API. Signed-off-by: Emil Gydesen --- doc/releases/release-notes-4.2.rst | 6 + include/zephyr/bluetooth/audio/bap.h | 28 +- include/zephyr/bluetooth/audio/cap.h | 172 +++++++- .../cap_initiator/src/cap_initiator_unicast.c | 22 +- .../tmap_central/src/cap_initiator.c | 16 +- subsys/bluetooth/audio/bap_unicast_client.c | 48 ++- subsys/bluetooth/audio/cap_initiator.c | 274 +++++++++++- subsys/bluetooth/audio/shell/cap_initiator.c | 34 +- .../audio/cap_initiator/CMakeLists.txt | 1 + .../cap_initiator/src/test_unicast_group.c | 390 ++++++++++++++++++ .../cap_initiator/uut/bap_unicast_client.c | 104 ++++- tests/bluetooth/audio/mocks/src/kernel.c | 2 + .../audio/src/cap_initiator_unicast_test.c | 70 ++-- .../bsim/bluetooth/audio/src/gmap_ugg_test.c | 40 +- 14 files changed, 1105 insertions(+), 102 deletions(-) create mode 100644 tests/bluetooth/audio/cap_initiator/src/test_unicast_group.c 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