From 21586d016fffeb479241e04fe8b8620c7ec98732 Mon Sep 17 00:00:00 2001 From: Emil Gydesen Date: Mon, 22 Sep 2025 14:14:40 +0200 Subject: [PATCH 1/6] Bluetooth: BAP: Add bt_bap_ep_get_conn Add a new function, bt_bap_ep_get_conn, which returns the ACL connection for the endpoint. This works because endpoints are specific to an ACL in BAP. The function returns a pointer with a new reference similar to the lookup functions from conn.h The conn pointer was not added to the bt_bap_ep_info struct, as doing so would be more likely to cause reference leaks if the caller did not care about the conn pointer when using bt_bap_ep_get_info. It can be added later if that is requested. Signed-off-by: Emil Gydesen --- include/zephyr/bluetooth/audio/bap.h | 15 +++++++++ subsys/bluetooth/audio/ascs.c | 12 +++++++ subsys/bluetooth/audio/ascs_internal.h | 1 + subsys/bluetooth/audio/bap_internal.h | 2 ++ subsys/bluetooth/audio/bap_stream.c | 25 +++++++++++++++ subsys/bluetooth/audio/bap_unicast_client.c | 35 +++++++++++++++++++++ subsys/bluetooth/audio/bap_unicast_server.c | 5 +++ 7 files changed, 95 insertions(+) diff --git a/include/zephyr/bluetooth/audio/bap.h b/include/zephyr/bluetooth/audio/bap.h index d480837c97138..64f3b83d20bcc 100644 --- a/include/zephyr/bluetooth/audio/bap.h +++ b/include/zephyr/bluetooth/audio/bap.h @@ -882,6 +882,21 @@ struct bt_bap_ep_info { */ int bt_bap_ep_get_info(const struct bt_bap_ep *ep, struct bt_bap_ep_info *info); +/** + * @brief Get the pointer to the ACL connection of an endpoint + * + * The caller gets a new reference to the connection object, if not NULL, which must be + * released with bt_conn_unref() once done using the object. + * + * @param ep The endpoint to get the ACL connection of + * + * @return The ACL connection pointer. + * Will always be NULL for broadcast endpoints. + * Will be NULL for Unicast Server endpoints if the endpoint is not configured by a client. + * Will be NULL for Unicast Client endpoints if @p does not match a discovered endpoint. + */ +struct bt_conn *bt_bap_ep_get_conn(const struct bt_bap_ep *ep); + /** * @brief Basic Audio Profile stream structure. * diff --git a/subsys/bluetooth/audio/ascs.c b/subsys/bluetooth/audio/ascs.c index 4636ca605a378..1f0045afe1048 100644 --- a/subsys/bluetooth/audio/ascs.c +++ b/subsys/bluetooth/audio/ascs.c @@ -3259,4 +3259,16 @@ int bt_ascs_unregister(void) return err; } +struct bt_conn *bt_ascs_ep_get_conn(const struct bt_bap_ep *ep) +{ + struct bt_ascs_ase *ase = CONTAINER_OF(ep, struct bt_ascs_ase, ep); + + __ASSERT_NO_MSG(bt_ascs_has_ep(ep)); + + if (ase->conn == NULL) { + return NULL; + } + + return bt_conn_ref(ase->conn); +} #endif /* BT_BAP_UNICAST_SERVER */ diff --git a/subsys/bluetooth/audio/ascs_internal.h b/subsys/bluetooth/audio/ascs_internal.h index 649b5c1706571..041727712dfc4 100644 --- a/subsys/bluetooth/audio/ascs_internal.h +++ b/subsys/bluetooth/audio/ascs_internal.h @@ -364,5 +364,6 @@ void bt_ascs_foreach_ep(struct bt_conn *conn, bt_bap_ep_func_t func, void *user_ int bt_ascs_register(uint8_t snk_cnt, uint8_t src_cnt); int bt_ascs_unregister(void); +struct bt_conn *bt_ascs_ep_get_conn(const struct bt_bap_ep *ep); #endif /* BT_ASCS_INTERNAL_H */ diff --git a/subsys/bluetooth/audio/bap_internal.h b/subsys/bluetooth/audio/bap_internal.h index 6d7ff219b3543..d6a20ef769af2 100644 --- a/subsys/bluetooth/audio/bap_internal.h +++ b/subsys/bluetooth/audio/bap_internal.h @@ -147,3 +147,5 @@ bool bt_bap_broadcast_sink_has_ep(const struct bt_bap_ep *ep); bool bt_bap_broadcast_source_has_ep(const struct bt_bap_ep *ep); bool bt_bap_unicast_client_has_ep(const struct bt_bap_ep *ep); bool bt_bap_unicast_server_has_ep(const struct bt_bap_ep *ep); +struct bt_conn *bt_bap_unicast_client_ep_get_conn(const struct bt_bap_ep *ep); +struct bt_conn *bt_bap_unicast_server_ep_get_conn(const struct bt_bap_ep *ep); diff --git a/subsys/bluetooth/audio/bap_stream.c b/subsys/bluetooth/audio/bap_stream.c index e33a7d79f6bde..b463d74113c2b 100644 --- a/subsys/bluetooth/audio/bap_stream.c +++ b/subsys/bluetooth/audio/bap_stream.c @@ -173,6 +173,31 @@ int bt_bap_ep_get_info(const struct bt_bap_ep *ep, struct bt_bap_ep_info *info) return 0; } +struct bt_conn *bt_bap_ep_get_conn(const struct bt_bap_ep *ep) +{ + struct bt_conn *conn; + + if (ep == NULL) { + LOG_DBG("ep is NULL"); + + return NULL; + } + + if ((IS_ENABLED(CONFIG_BT_BAP_BROADCAST_SOURCE) && bt_bap_broadcast_source_has_ep(ep)) || + (IS_ENABLED(CONFIG_BT_BAP_BROADCAST_SINK) && bt_bap_broadcast_sink_has_ep(ep))) { + conn = NULL; + } else if (IS_ENABLED(CONFIG_BT_BAP_UNICAST_CLIENT) && bt_bap_unicast_client_has_ep(ep)) { + conn = bt_bap_unicast_client_ep_get_conn(ep); + } else if (IS_ENABLED(CONFIG_BT_BAP_UNICAST_SERVER) && bt_bap_unicast_server_has_ep(ep)) { + conn = bt_bap_unicast_server_ep_get_conn(ep); + } else { + LOG_DBG("Invalid endpoint %p", ep); + conn = NULL; + } + + return conn; +} + enum bt_bap_ascs_reason bt_audio_verify_qos(const struct bt_bap_qos_cfg *qos) { if (qos->interval < BT_ISO_SDU_INTERVAL_MIN || diff --git a/subsys/bluetooth/audio/bap_unicast_client.c b/subsys/bluetooth/audio/bap_unicast_client.c index cbbc56088740b..e3545d842019c 100644 --- a/subsys/bluetooth/audio/bap_unicast_client.c +++ b/subsys/bluetooth/audio/bap_unicast_client.c @@ -460,6 +460,41 @@ bool bt_bap_unicast_client_has_ep(const struct bt_bap_ep *ep) return false; } +struct bt_conn *bt_bap_unicast_client_ep_get_conn(const struct bt_bap_ep *ep) +{ + for (size_t i = 0U; i < ARRAY_SIZE(uni_cli_insts); i++) { +#if CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SNK_COUNT > 0 + if (PART_OF_ARRAY(uni_cli_insts[i].snks, ep)) { + ARRAY_FOR_EACH_PTR(uni_cli_insts[i].snks, client_ep) { + if (&client_ep->ep == ep) { + if (client_ep->handle == BAP_HANDLE_UNUSED) { + return NULL; + } + + return bt_conn_lookup_index((uint8_t)i); + } + } + } +#endif /* CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SNK_COUNT > 0 */ + +#if CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SRC_COUNT > 0 + if (PART_OF_ARRAY(uni_cli_insts[i].srcs, ep)) { + ARRAY_FOR_EACH_PTR(uni_cli_insts[i].srcs, client_ep) { + if (&client_ep->ep == ep) { + if (client_ep->handle == BAP_HANDLE_UNUSED) { + return NULL; + } + + return bt_conn_lookup_index((uint8_t)i); + } + } + } +#endif /* CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SRC_COUNT > 0 */ + } + + return NULL; +} + static void unicast_client_ep_init(struct bt_bap_ep *ep, uint16_t handle, uint8_t dir) { struct bt_bap_unicast_client_ep *client_ep; diff --git a/subsys/bluetooth/audio/bap_unicast_server.c b/subsys/bluetooth/audio/bap_unicast_server.c index e46ed4ff4b282..f7edb918b0297 100644 --- a/subsys/bluetooth/audio/bap_unicast_server.c +++ b/subsys/bluetooth/audio/bap_unicast_server.c @@ -227,3 +227,8 @@ bool bt_bap_unicast_server_has_ep(const struct bt_bap_ep *ep) { return bt_ascs_has_ep(ep); } + +struct bt_conn *bt_bap_unicast_server_ep_get_conn(const struct bt_bap_ep *ep) +{ + return bt_ascs_ep_get_conn(ep); +} From fbfa3d3d13dbae6039e82f42b77d962ca64927bc Mon Sep 17 00:00:00 2001 From: Emil Gydesen Date: Mon, 22 Sep 2025 14:15:50 +0200 Subject: [PATCH 2/6] doc: releases: Add entry for bt_bap_ep_get_conn Add entry for the new function bt_bap_ep_get_conn. Signed-off-by: Emil Gydesen --- doc/releases/release-notes-4.3.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/releases/release-notes-4.3.rst b/doc/releases/release-notes-4.3.rst index 8a3692e6aea90..c185f0846052e 100644 --- a/doc/releases/release-notes-4.3.rst +++ b/doc/releases/release-notes-4.3.rst @@ -114,6 +114,7 @@ New APIs and options * :c:struct:`bt_bap_stream` now contains an ``iso`` field as a reference to the ISO channel * :c:func:`bt_bap_unicast_group_get_info` * :c:func:`bt_cap_unicast_group_get_info` + * :c:func:`bt_bap_ep_get_conn` * Host From ad34ce18b102ddfbd2dfa22fe239e511831239a8 Mon Sep 17 00:00:00 2001 From: Emil Gydesen Date: Mon, 22 Sep 2025 14:16:18 +0200 Subject: [PATCH 3/6] tests: Bluetooth: BAP: Test bt_bap_ep_get_conn in BSIM Add steps in the BAP tests to verify the correctness of bt_bap_ep_get_conn. Signed-off-by: Emil Gydesen --- .../audio/src/bap_broadcast_sink_test.c | 7 +++++++ .../audio/src/bap_broadcast_source_test.c | 7 +++++++ .../audio/src/bap_unicast_client_test.c | 9 +++++++++ .../audio/src/bap_unicast_server_test.c | 20 +++++++++++++++++-- 4 files changed, 41 insertions(+), 2 deletions(-) diff --git a/tests/bsim/bluetooth/audio/src/bap_broadcast_sink_test.c b/tests/bsim/bluetooth/audio/src/bap_broadcast_sink_test.c index 32522d79cba5c..1d677609a949c 100644 --- a/tests/bsim/bluetooth/audio/src/bap_broadcast_sink_test.c +++ b/tests/bsim/bluetooth/audio/src/bap_broadcast_sink_test.c @@ -565,6 +565,7 @@ static void stream_started_cb(struct bt_bap_stream *stream) { struct audio_test_stream *test_stream = audio_test_stream_from_bap_stream(stream); struct bt_bap_ep_info info; + struct bt_conn *ep_conn; int err; memset(&test_stream->last_info, 0, sizeof(test_stream->last_info)); @@ -602,6 +603,12 @@ static void stream_started_cb(struct bt_bap_stream *stream) return; } + ep_conn = bt_bap_ep_get_conn(stream->ep); + if (ep_conn != NULL) { + FAIL("Invalid conn from endpoint: %p", ep_conn); + return; + } + printk("Stream %p started\n", stream); k_sem_give(&sem_stream_started); diff --git a/tests/bsim/bluetooth/audio/src/bap_broadcast_source_test.c b/tests/bsim/bluetooth/audio/src/bap_broadcast_source_test.c index 9f6417eee2901..7613d07cdb8d2 100644 --- a/tests/bsim/bluetooth/audio/src/bap_broadcast_source_test.c +++ b/tests/bsim/bluetooth/audio/src/bap_broadcast_source_test.c @@ -178,6 +178,7 @@ static void stream_started_cb(struct bt_bap_stream *stream) { struct audio_test_stream *test_stream = audio_test_stream_from_bap_stream(stream); struct bt_bap_ep_info info; + struct bt_conn *ep_conn; int err; test_stream->seq_num = 0U; @@ -214,6 +215,12 @@ static void stream_started_cb(struct bt_bap_stream *stream) return; } + ep_conn = bt_bap_ep_get_conn(stream->ep); + if (ep_conn != NULL) { + FAIL("Invalid conn from endpoint: %p", ep_conn); + return; + } + err = bap_stream_tx_register(stream); if (err != 0) { FAIL("Failed to register stream %p for TX: %d\n", stream, err); diff --git a/tests/bsim/bluetooth/audio/src/bap_unicast_client_test.c b/tests/bsim/bluetooth/audio/src/bap_unicast_client_test.c index 69da47a4e2e5b..86de4b6424430 100644 --- a/tests/bsim/bluetooth/audio/src/bap_unicast_client_test.c +++ b/tests/bsim/bluetooth/audio/src/bap_unicast_client_test.c @@ -69,12 +69,21 @@ CREATE_FLAG(flag_operation_success); static void stream_configured(struct bt_bap_stream *stream, const struct bt_bap_qos_cfg_pref *pref) { + struct bt_conn *ep_conn; + printk("Configured stream %p\n", stream); /* TODO: The preference should be used/taken into account when * setting the QoS */ + ep_conn = bt_bap_ep_get_conn(stream->ep); + if (ep_conn == NULL || stream->conn != ep_conn) { + FAIL("Invalid conn from endpoint: %p", ep_conn); + return; + } + bt_conn_unref(ep_conn); + SET_FLAG(flag_stream_codec_configured); } diff --git a/tests/bsim/bluetooth/audio/src/bap_unicast_server_test.c b/tests/bsim/bluetooth/audio/src/bap_unicast_server_test.c index 980915d21b8c3..e9d3d8a6af1d0 100644 --- a/tests/bsim/bluetooth/audio/src/bap_unicast_server_test.c +++ b/tests/bsim/bluetooth/audio/src/bap_unicast_server_test.c @@ -114,8 +114,6 @@ static int lc3_config(struct bt_conn *conn, const struct bt_bap_ep *ep, enum bt_ bt_bap_unicast_server_foreach_ep(conn, print_ase_info, NULL); - SET_FLAG(flag_stream_configured); - *pref = qos_pref; return 0; @@ -222,6 +220,23 @@ static const struct bt_bap_unicast_server_cb unicast_server_cb = { .release = lc3_release, }; +static void stream_configured_cb(struct bt_bap_stream *stream, + const struct bt_bap_qos_cfg_pref *pref) +{ + struct bt_conn *ep_conn; + + printk("Configured stream %p\n", stream); + + ep_conn = bt_bap_ep_get_conn(stream->ep); + if (ep_conn == NULL || stream->conn != ep_conn) { + FAIL("Invalid conn from endpoint: %p", ep_conn); + return; + } + bt_conn_unref(ep_conn); + + SET_FLAG(flag_stream_configured); +} + static void stream_enabled_cb(struct bt_bap_stream *stream) { struct bt_bap_ep_info ep_info; @@ -279,6 +294,7 @@ static void stream_stopped_cb(struct bt_bap_stream *stream, uint8_t reason) } static struct bt_bap_stream_ops stream_ops = { + .configured = stream_configured_cb, .enabled = stream_enabled_cb, .started = stream_started_cb, .stopped = stream_stopped_cb, From 549bc7a694ba66b6eae3efe39b24b24e44754961 Mon Sep 17 00:00:00 2001 From: Emil Gydesen Date: Mon, 22 Sep 2025 15:13:47 +0200 Subject: [PATCH 4/6] Bluetooth: CAP: Add ep conn check in unicast audio start Add check in valid_unicast_audio_start_param to verify that the ep connection matches the member_conn. Signed-off-by: Emil Gydesen --- subsys/bluetooth/audio/cap_initiator.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/subsys/bluetooth/audio/cap_initiator.c b/subsys/bluetooth/audio/cap_initiator.c index 550da6b5f5b2f..2e799e1145954 100644 --- a/subsys/bluetooth/audio/cap_initiator.c +++ b/subsys/bluetooth/audio/cap_initiator.c @@ -1158,9 +1158,11 @@ bool bt_cap_initiator_valid_unicast_audio_start_param( const union bt_cap_set_member *member = &stream_param->member; const struct bt_cap_stream *cap_stream = stream_param->stream; const struct bt_audio_codec_cfg *codec_cfg = stream_param->codec_cfg; + const struct bt_bap_ep *ep = stream_param->ep; const struct bt_bap_stream *bap_stream; const struct bt_conn *member_conn = bt_cap_common_get_member_conn(param->type, member); + struct bt_conn *ep_conn; if (member == NULL) { LOG_DBG("param->members[%zu] is NULL", i); @@ -1182,11 +1184,26 @@ bool bt_cap_initiator_valid_unicast_audio_start_param( return false; } - CHECKIF(stream_param->ep == NULL) { + if (ep == NULL) { LOG_DBG("param->stream_params[%zu].ep is NULL", i); return false; } + ep_conn = bt_bap_ep_get_conn(ep); + if (ep_conn == NULL) { + LOG_DBG("param->stream_params[%zu].ep is invalid", i); + return false; + } + if (ep_conn != member_conn) { + LOG_DBG("param->stream_params[%zu].ep conn %p does not match " + "param->members[%zu] %p", + i, ep_conn, i, member_conn); + bt_conn_unref(ep_conn); + + return false; + } + bt_conn_unref(ep_conn); + CHECKIF(member == NULL) { LOG_DBG("param->stream_params[%zu].member is NULL", i); return false; From 4b30370b571b0858d648b4c6bee2fd980e9367f2 Mon Sep 17 00:00:00 2001 From: Emil Gydesen Date: Tue, 30 Sep 2025 15:31:26 +0200 Subject: [PATCH 5/6] Bluetooth: BAP: Add bt_bap_unicast_client_unregister_cb Add bt_bap_unicast_client_unregister_cb to unregister BAP unicast client callbacks. This is required for unittests to clean up between runs. Signed-off-by: Emil Gydesen --- include/zephyr/bluetooth/audio/bap.h | 15 +++++++++++---- subsys/bluetooth/audio/bap_unicast_client.c | 15 +++++++++++++++ 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/include/zephyr/bluetooth/audio/bap.h b/include/zephyr/bluetooth/audio/bap.h index 64f3b83d20bcc..54beef46022ee 100644 --- a/include/zephyr/bluetooth/audio/bap.h +++ b/include/zephyr/bluetooth/audio/bap.h @@ -1982,10 +1982,7 @@ struct bt_bap_unicast_client_cb { /** * @brief Register unicast client callbacks. * - * Only one callback structure can be registered, and attempting to - * registering more than one will result in an error. - * - * @param cb Unicast client callback structure. + * @param cb Unicast client callback structure to register. * * @retval 0 Success * @retval -EINVAL @p cb is NULL. @@ -1993,6 +1990,16 @@ struct bt_bap_unicast_client_cb { */ int bt_bap_unicast_client_register_cb(struct bt_bap_unicast_client_cb *cb); +/** + * @brief Unregister unicast client callbacks. + * + * @param cb Unicast client callback structure to unregister. + * + * @retval 0 Success + * @retval -EINVAL @p cb is NULL or @p cb was not registered + */ +int bt_bap_unicast_client_unregister_cb(struct bt_bap_unicast_client_cb *cb); + /** * @brief Discover remote capabilities and endpoints * diff --git a/subsys/bluetooth/audio/bap_unicast_client.c b/subsys/bluetooth/audio/bap_unicast_client.c index e3545d842019c..81fa2ae957d8b 100644 --- a/subsys/bluetooth/audio/bap_unicast_client.c +++ b/subsys/bluetooth/audio/bap_unicast_client.c @@ -4719,3 +4719,18 @@ int bt_bap_unicast_client_register_cb(struct bt_bap_unicast_client_cb *cb) return 0; } + +int bt_bap_unicast_client_unregister_cb(struct bt_bap_unicast_client_cb *cb) +{ + if (cb == NULL) { + LOG_DBG("cb was NULL"); + return -EINVAL; + } + + if (!sys_slist_find_and_remove(&unicast_client_cbs, &cb->_node)) { + LOG_DBG("cb was not registered"); + return -EALREADY; + } + + return 0; +} From 80f9c25facb101ed2cef2fcfeda42cea48a07f11 Mon Sep 17 00:00:00 2001 From: Emil Gydesen Date: Tue, 30 Sep 2025 15:33:30 +0200 Subject: [PATCH 6/6] tests: Bluetooth: CAP: Refactor unittests to use discover Instead of declaring the endpoints in the unit tests, we not define them in the mock unicast client and use the discover function to get pointers. This allows for a proper implementation of bt_bap_unicast_client_has_ep as well as makes the tests more similar to a normal application. Signed-off-by: Emil Gydesen --- .../audio/cap_initiator/include/test_common.h | 5 +- .../bluetooth/audio/cap_initiator/src/main.c | 2 +- .../audio/cap_initiator/src/test_common.c | 8 +- .../cap_initiator/src/test_unicast_start.c | 161 ++++++++++++--- .../cap_initiator/src/test_unicast_stop.c | 2 +- .../cap_initiator/uut/bap_unicast_client.c | 191 +++++++++++++++--- 6 files changed, 306 insertions(+), 63 deletions(-) diff --git a/tests/bluetooth/audio/cap_initiator/include/test_common.h b/tests/bluetooth/audio/cap_initiator/include/test_common.h index 53a2d1a403198..16328f0518f1b 100644 --- a/tests/bluetooth/audio/cap_initiator/include/test_common.h +++ b/tests/bluetooth/audio/cap_initiator/include/test_common.h @@ -1,10 +1,11 @@ /* test_common.h */ /* - * Copyright (c) 2024 Nordic Semiconductor ASA + * Copyright (c) 2024-2025 Nordic Semiconductor ASA * * SPDX-License-Identifier: Apache-2.0 */ +#include #include #include @@ -14,7 +15,7 @@ void test_mocks_init(void); void test_mocks_cleanup(void); -void test_conn_init(struct bt_conn *conn); +void test_conn_init(struct bt_conn *conn, uint8_t index); void test_unicast_set_state(struct bt_cap_stream *cap_stream, struct bt_conn *conn, struct bt_bap_ep *ep, struct bt_bap_lc3_preset *preset, diff --git a/tests/bluetooth/audio/cap_initiator/src/main.c b/tests/bluetooth/audio/cap_initiator/src/main.c index a15c1fc1c1f04..256f982d87e9a 100644 --- a/tests/bluetooth/audio/cap_initiator/src/main.c +++ b/tests/bluetooth/audio/cap_initiator/src/main.c @@ -43,7 +43,7 @@ struct cap_initiator_test_suite_fixture { static void cap_initiator_test_suite_fixture_init(struct cap_initiator_test_suite_fixture *fixture) { for (size_t i = 0; i < ARRAY_SIZE(fixture->conns); i++) { - test_conn_init(&fixture->conns[i]); + test_conn_init(&fixture->conns[i], i); } } diff --git a/tests/bluetooth/audio/cap_initiator/src/test_common.c b/tests/bluetooth/audio/cap_initiator/src/test_common.c index fe3650c7c1160..d7c161c68ad6b 100644 --- a/tests/bluetooth/audio/cap_initiator/src/test_common.c +++ b/tests/bluetooth/audio/cap_initiator/src/test_common.c @@ -1,11 +1,13 @@ /* test_common.c - common procedures for unit test of CAP initiator */ /* - * Copyright (c) 2024 Nordic Semiconductor ASA + * Copyright (c) 2024-2025 Nordic Semiconductor ASA * * SPDX-License-Identifier: Apache-2.0 */ +#include + #include #include #include @@ -32,9 +34,9 @@ void test_mocks_cleanup(void) mock_cap_initiator_cleanup(); } -void test_conn_init(struct bt_conn *conn) +void test_conn_init(struct bt_conn *conn, uint8_t index) { - conn->index = 0; + conn->index = index; conn->info.type = BT_CONN_TYPE_LE; conn->info.role = BT_CONN_ROLE_CENTRAL; conn->info.state = BT_CONN_STATE_CONNECTED; diff --git a/tests/bluetooth/audio/cap_initiator/src/test_unicast_start.c b/tests/bluetooth/audio/cap_initiator/src/test_unicast_start.c index 09e85780ffdd3..fc6801ed34d34 100644 --- a/tests/bluetooth/audio/cap_initiator/src/test_unicast_start.c +++ b/tests/bluetooth/audio/cap_initiator/src/test_unicast_start.c @@ -36,9 +36,22 @@ #include "ztest_assert.h" #include "ztest_test.h" +BUILD_ASSERT(CONFIG_BT_MAX_CONN *(CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SNK_COUNT + + CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SRC_COUNT) >= + CONFIG_BT_BAP_UNICAST_CLIENT_GROUP_STREAM_COUNT); + +/* Either BT_AUDIO_DIR_SINK or BT_AUDIO_DIR_SOURCE */ +#define INDEX_TO_DIR(_idx) (((_idx) & 1U) + 1U) + +DECLARE_FAKE_VOID_FUNC(mock_bap_discover_endpoint, struct bt_conn *, enum bt_audio_dir, + struct bt_bap_ep *); +DEFINE_FAKE_VOID_FUNC(mock_bap_discover_endpoint, struct bt_conn *, enum bt_audio_dir, + struct bt_bap_ep *); + struct cap_initiator_test_unicast_start_fixture { struct bt_cap_stream cap_streams[CONFIG_BT_BAP_UNICAST_CLIENT_GROUP_STREAM_COUNT]; - struct bt_bap_ep eps[CONFIG_BT_BAP_UNICAST_CLIENT_GROUP_STREAM_COUNT]; + struct bt_bap_ep *snk_eps[CONFIG_BT_MAX_CONN][CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SNK_COUNT]; + struct bt_bap_ep *src_eps[CONFIG_BT_MAX_CONN][CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SRC_COUNT]; struct bt_cap_unicast_audio_start_stream_param audio_start_stream_params[CONFIG_BT_BAP_UNICAST_CLIENT_GROUP_STREAM_COUNT]; struct bt_cap_unicast_audio_start_param audio_start_param; @@ -51,9 +64,9 @@ static void cap_initiator_test_unicast_start_fixture_init( struct cap_initiator_test_unicast_start_fixture *fixture) { struct bt_cap_unicast_group_stream_pair_param - pair_params[CONFIG_BT_BAP_UNICAST_CLIENT_GROUP_STREAM_COUNT] = {0}; + group_pair_params[CONFIG_BT_BAP_UNICAST_CLIENT_GROUP_STREAM_COUNT] = {0}; struct bt_cap_unicast_group_stream_param - stream_params[CONFIG_BT_BAP_UNICAST_CLIENT_GROUP_STREAM_COUNT] = {0}; + group_stream_param[CONFIG_BT_BAP_UNICAST_CLIENT_GROUP_STREAM_COUNT] = {0}; struct bt_cap_unicast_group_param group_param = {0}; size_t stream_cnt = 0U; size_t pair_cnt = 0U; @@ -63,24 +76,20 @@ static void cap_initiator_test_unicast_start_fixture_init( BT_AUDIO_LOCATION_MONO_AUDIO, BT_AUDIO_CONTEXT_TYPE_UNSPECIFIED); for (size_t i = 0U; i < ARRAY_SIZE(fixture->conns); i++) { - test_conn_init(&fixture->conns[i]); + test_conn_init(&fixture->conns[i], i); } - for (size_t i = 0U; i < ARRAY_SIZE(fixture->eps); i++) { - const uint8_t dir = (i & 1) + 1; /* Makes it either 1 or 2 (sink or source)*/ + while (stream_cnt < ARRAY_SIZE(group_stream_param)) { + const enum bt_audio_dir dir = INDEX_TO_DIR(stream_cnt); - fixture->eps[i].dir = dir; - } - - while (stream_cnt < ARRAY_SIZE(stream_params)) { - stream_params[stream_cnt].stream = &fixture->cap_streams[stream_cnt]; - stream_params[stream_cnt].qos_cfg = &fixture->preset.qos; + group_stream_param[stream_cnt].stream = &fixture->cap_streams[stream_cnt]; + group_stream_param[stream_cnt].qos_cfg = &fixture->preset.qos; /* Switch between sink and source depending on index*/ - if ((stream_cnt & 1) == 0) { - pair_params[pair_cnt].tx_param = &stream_params[stream_cnt]; + if (dir == BT_AUDIO_DIR_SINK) { + group_pair_params[pair_cnt].tx_param = &group_stream_param[stream_cnt]; } else { - pair_params[pair_cnt].rx_param = &stream_params[stream_cnt]; + group_pair_params[pair_cnt].rx_param = &group_stream_param[stream_cnt]; } pair_cnt = DIV_ROUND_UP(stream_cnt, 2U); @@ -89,25 +98,126 @@ static void cap_initiator_test_unicast_start_fixture_init( group_param.packing = BT_ISO_PACKING_SEQUENTIAL; group_param.params_count = pair_cnt; - group_param.params = pair_params; + group_param.params = group_pair_params; err = bt_cap_unicast_group_create(&group_param, &fixture->unicast_group); zassert_equal(err, 0, "Unexpected return value %d", err); +} + +static void *cap_initiator_test_unicast_start_setup(void) +{ + struct cap_initiator_test_unicast_start_fixture *fixture; + + fixture = malloc(sizeof(*fixture)); + zassert_not_null(fixture); + + return fixture; +} + +static void mock_discover(struct cap_initiator_test_unicast_start_fixture *fixture) +{ + struct bt_bap_unicast_client_cb unicast_client_cb = { + .endpoint = mock_bap_discover_endpoint, + }; + int err; + + err = bt_bap_unicast_client_register_cb(&unicast_client_cb); + zassert_equal(0, err, "Unexpected return value %d", err); + + for (size_t i = 0U; i < ARRAY_SIZE(fixture->conns); i++) { + RESET_FAKE(mock_bap_discover_endpoint); + err = bt_bap_unicast_client_discover(&fixture->conns[i], BT_AUDIO_DIR_SINK); + zassert_equal(0, err, "Unexpected return value %d", err); + + /* TODO: use callback to populate eps */ + + zexpect_call_count("unicast_client_cb.bap_discover_endpoint", + CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SNK_COUNT, + mock_bap_discover_endpoint_fake.call_count); + for (size_t j = 0U; j < mock_bap_discover_endpoint_fake.call_count; j++) { + /* Verify conn */ + zassert_equal(mock_bap_discover_endpoint_fake.arg0_history[j], + &fixture->conns[i], "%p", + mock_bap_discover_endpoint_fake.arg0_history[j]); + + /* Verify dir */ + zassert_equal(mock_bap_discover_endpoint_fake.arg1_history[j], + BT_AUDIO_DIR_SINK, "%d", + mock_bap_discover_endpoint_fake.arg1_history[j]); + + /* Verify and store ep */ + zassert_not_equal(mock_bap_discover_endpoint_fake.arg2_history[j], NULL, + "%p", mock_bap_discover_endpoint_fake.arg2_history[j]); + + fixture->snk_eps[fixture->conns[i].index][j] = + mock_bap_discover_endpoint_fake.arg2_history[j]; + } + RESET_FAKE(mock_bap_discover_endpoint); + err = bt_bap_unicast_client_discover(&fixture->conns[i], BT_AUDIO_DIR_SOURCE); + zassert_equal(0, err, "Unexpected return value %d", err); + + zexpect_call_count("unicast_client_cb.bap_discover_endpoint", + CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SRC_COUNT, + mock_bap_discover_endpoint_fake.call_count); + for (size_t j = 0U; j < mock_bap_discover_endpoint_fake.call_count; j++) { + /* Verify conn */ + zassert_equal(mock_bap_discover_endpoint_fake.arg0_history[j], + &fixture->conns[i], "%p", + mock_bap_discover_endpoint_fake.arg0_history[j]); + + /* Verify dir */ + zassert_equal(mock_bap_discover_endpoint_fake.arg1_history[j], + BT_AUDIO_DIR_SOURCE, "%d", + mock_bap_discover_endpoint_fake.arg1_history[j]); + + /* Verify and store ep */ + zassert_not_equal(mock_bap_discover_endpoint_fake.arg2_history[j], NULL, + "%p", mock_bap_discover_endpoint_fake.arg2_history[j]); + + fixture->src_eps[fixture->conns[i].index][j] = + mock_bap_discover_endpoint_fake.arg2_history[j]; + } + } + + /* We don't need the callbacks anymore */ + err = bt_bap_unicast_client_unregister_cb(&unicast_client_cb); + zassert_equal(0, err, "Unexpected return value %d", err); +} + +static void init_default_params(struct cap_initiator_test_unicast_start_fixture *fixture) +{ /* Setup default params */ ARRAY_FOR_EACH(fixture->audio_start_stream_params, i) { struct bt_cap_unicast_audio_start_stream_param *stream_param = &fixture->audio_start_stream_params[i]; + /* We pair 2 streams, so only increase conn_index every 2nd stream and otherwise * round robin on all conns */ - const size_t conn_index = (i / 2) % ARRAY_SIZE(fixture->conns); + const size_t conn_index = (i / 2U) % ARRAY_SIZE(fixture->conns); + const size_t ep_index = i / (ARRAY_SIZE(fixture->conns) * 2U); + const enum bt_audio_dir dir = INDEX_TO_DIR(i); stream_param->stream = &fixture->cap_streams[i]; stream_param->codec_cfg = &fixture->preset.codec_cfg; - /* Distribute the streams equally among the connections */ + + /* Distribute the streams like + * [0]: conn[0] src[0] + * [1]: conn[0] snk[0] + * [2]: conn[1] src[0] + * [3]: conn[0] snk[0] + * [4]: conn[0] src[1] + * [5]: conn[0] snk[1] + * [6]: conn[1] src[1] + * [7]: conn[0] snk[1] + */ stream_param->member.member = &fixture->conns[conn_index]; - stream_param->ep = &fixture->eps[i]; + if (dir == BT_AUDIO_DIR_SINK) { + stream_param->ep = fixture->snk_eps[conn_index][ep_index]; + } else { + stream_param->ep = fixture->src_eps[conn_index][ep_index]; + } } fixture->audio_start_param.type = BT_CAP_SET_TYPE_AD_HOC; @@ -115,16 +225,6 @@ static void cap_initiator_test_unicast_start_fixture_init( fixture->audio_start_param.stream_params = fixture->audio_start_stream_params; } -static void *cap_initiator_test_unicast_start_setup(void) -{ - struct cap_initiator_test_unicast_start_fixture *fixture; - - fixture = malloc(sizeof(*fixture)); - zassert_not_null(fixture); - - return fixture; -} - static void cap_initiator_test_unicast_start_before(void *f) { struct cap_initiator_test_unicast_start_fixture *fixture = f; @@ -135,6 +235,9 @@ static void cap_initiator_test_unicast_start_before(void *f) err = bt_cap_initiator_register_cb(&mock_cap_initiator_cb); zassert_equal(0, err, "Unexpected return value %d", err); + + mock_discover(fixture); + init_default_params(fixture); } static void cap_initiator_test_unicast_start_after(void *f) diff --git a/tests/bluetooth/audio/cap_initiator/src/test_unicast_stop.c b/tests/bluetooth/audio/cap_initiator/src/test_unicast_stop.c index dd456d16a6e09..6bd881fc9d7c7 100644 --- a/tests/bluetooth/audio/cap_initiator/src/test_unicast_stop.c +++ b/tests/bluetooth/audio/cap_initiator/src/test_unicast_stop.c @@ -47,7 +47,7 @@ static void cap_initiator_test_unicast_stop_fixture_init( BT_AUDIO_LOCATION_MONO_AUDIO, BT_AUDIO_CONTEXT_TYPE_UNSPECIFIED); for (size_t i = 0U; i < ARRAY_SIZE(fixture->conns); i++) { - test_conn_init(&fixture->conns[i]); + test_conn_init(&fixture->conns[i], i); } for (size_t i = 0U; i < ARRAY_SIZE(fixture->eps); i++) { 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 eb34b97b4a048..130d9152c3075 100644 --- a/tests/bluetooth/audio/cap_initiator/uut/bap_unicast_client.c +++ b/tests/bluetooth/audio/cap_initiator/uut/bap_unicast_client.c @@ -24,18 +24,64 @@ #include "bap_endpoint.h" #include "bap_iso.h" +#include "conn.h" -static struct bt_bap_unicast_client_cb *unicast_client_cb; +static sys_slist_t unicast_client_cbs = SYS_SLIST_STATIC_INIT(&unicast_client_cbs); static struct bt_bap_unicast_group bap_unicast_group; +static struct unicast_client { +#if CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SNK_COUNT > 0 + struct bt_bap_ep snks[CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SNK_COUNT]; +#endif /* CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SNK_COUNT > 0 */ +#if CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SRC_COUNT > 0 + struct bt_bap_ep srcs[CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SRC_COUNT]; +#endif /* CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SRC_COUNT > 0 */ + struct bt_conn *conn; +} uni_cli_insts[CONFIG_BT_MAX_CONN]; + bool bt_bap_unicast_client_has_ep(const struct bt_bap_ep *ep) { - return true; + ARRAY_FOR_EACH_PTR(uni_cli_insts, uni_cli) { +#if CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SNK_COUNT > 0 + if (IS_ARRAY_ELEMENT(uni_cli->snks, ep)) { + return true; + } +#endif /* CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SNK_COUNT > 0 */ + +#if CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SRC_COUNT > 0 + if (IS_ARRAY_ELEMENT(uni_cli->srcs, ep)) { + return true; + } +#endif /* CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SRC_COUNT > 0 */ + } + + return false; +} + +struct bt_conn *bt_bap_unicast_client_ep_get_conn(const struct bt_bap_ep *ep) +{ + ARRAY_FOR_EACH_PTR(uni_cli_insts, uni_cli) { +#if CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SNK_COUNT > 0 + if (IS_ARRAY_ELEMENT(uni_cli->snks, ep)) { + return uni_cli->conn; + } +#endif /* CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SNK_COUNT > 0 */ + +#if CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SRC_COUNT > 0 + if (IS_ARRAY_ELEMENT(uni_cli->srcs, ep)) { + return uni_cli->conn; + } +#endif /* CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SRC_COUNT > 0 */ + } + + return NULL; } int bt_bap_unicast_client_config(struct bt_bap_stream *stream, const struct bt_audio_codec_cfg *codec_cfg) { + struct bt_bap_unicast_client_cb *listener, *next; + if (stream == NULL || stream->ep == NULL || codec_cfg == NULL) { return -EINVAL; } @@ -48,9 +94,11 @@ int bt_bap_unicast_client_config(struct bt_bap_stream *stream, return -EINVAL; } - if (unicast_client_cb != NULL && unicast_client_cb->config != NULL) { - unicast_client_cb->config(stream, BT_BAP_ASCS_RSP_CODE_SUCCESS, - BT_BAP_ASCS_REASON_NONE); + SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&unicast_client_cbs, listener, next, _node) { + if (listener->config != NULL) { + listener->config(stream, BT_BAP_ASCS_RSP_CODE_SUCCESS, + BT_BAP_ASCS_REASON_NONE); + } } stream->ep->state = BT_BAP_EP_STATE_CODEC_CONFIGURED; @@ -66,6 +114,7 @@ int bt_bap_unicast_client_config(struct bt_bap_stream *stream, int bt_bap_unicast_client_qos(struct bt_conn *conn, struct bt_bap_unicast_group *group) { + struct bt_bap_unicast_client_cb *listener, *next; struct bt_bap_stream *stream; if (conn == NULL || group == NULL) { @@ -86,9 +135,12 @@ int bt_bap_unicast_client_qos(struct bt_conn *conn, struct bt_bap_unicast_group SYS_SLIST_FOR_EACH_CONTAINER(&group->streams, stream, _node) { if (stream->conn == conn) { - if (unicast_client_cb != NULL && unicast_client_cb->qos != NULL) { - unicast_client_cb->qos(stream, BT_BAP_ASCS_RSP_CODE_SUCCESS, - BT_BAP_ASCS_REASON_NONE); + SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&unicast_client_cbs, listener, next, + _node) { + if (listener->qos != NULL) { + listener->qos(stream, BT_BAP_ASCS_RSP_CODE_SUCCESS, + BT_BAP_ASCS_REASON_NONE); + } } stream->ep->state = BT_BAP_EP_STATE_QOS_CONFIGURED; @@ -105,6 +157,8 @@ int bt_bap_unicast_client_qos(struct bt_conn *conn, struct bt_bap_unicast_group int bt_bap_unicast_client_enable(struct bt_bap_stream *stream, const uint8_t meta[], size_t meta_len) { + struct bt_bap_unicast_client_cb *listener, *next; + if (stream == NULL) { return -EINVAL; } @@ -116,9 +170,11 @@ int bt_bap_unicast_client_enable(struct bt_bap_stream *stream, const uint8_t met return -EINVAL; } - if (unicast_client_cb != NULL && unicast_client_cb->enable != NULL) { - unicast_client_cb->enable(stream, BT_BAP_ASCS_RSP_CODE_SUCCESS, - BT_BAP_ASCS_REASON_NONE); + SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&unicast_client_cbs, listener, next, _node) { + if (listener->enable != NULL) { + listener->enable(stream, BT_BAP_ASCS_RSP_CODE_SUCCESS, + BT_BAP_ASCS_REASON_NONE); + } } stream->ep->state = BT_BAP_EP_STATE_ENABLING; @@ -133,6 +189,8 @@ int bt_bap_unicast_client_enable(struct bt_bap_stream *stream, const uint8_t met int bt_bap_unicast_client_metadata(struct bt_bap_stream *stream, const uint8_t meta[], size_t meta_len) { + struct bt_bap_unicast_client_cb *listener, *next; + if (stream == NULL) { return -EINVAL; } @@ -145,9 +203,11 @@ int bt_bap_unicast_client_metadata(struct bt_bap_stream *stream, const uint8_t m return -EINVAL; } - if (unicast_client_cb != NULL && unicast_client_cb->metadata != NULL) { - unicast_client_cb->metadata(stream, BT_BAP_ASCS_RSP_CODE_SUCCESS, - BT_BAP_ASCS_REASON_NONE); + SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&unicast_client_cbs, listener, next, _node) { + if (listener->metadata != NULL) { + listener->metadata(stream, BT_BAP_ASCS_RSP_CODE_SUCCESS, + BT_BAP_ASCS_REASON_NONE); + } } if (stream->ops != NULL && stream->ops->metadata_updated != NULL) { @@ -194,6 +254,8 @@ int bt_bap_unicast_client_connect(struct bt_bap_stream *stream) int bt_bap_unicast_client_start(struct bt_bap_stream *stream) { + struct bt_bap_unicast_client_cb *listener, *next; + /* As per the ASCS spec, only source streams can be started by the client */ if (stream == NULL || stream->ep == NULL || stream->ep->dir == BT_AUDIO_DIR_SINK) { return -EINVAL; @@ -206,9 +268,11 @@ int bt_bap_unicast_client_start(struct bt_bap_stream *stream) return -EINVAL; } - if (unicast_client_cb != NULL && unicast_client_cb->start != NULL) { - unicast_client_cb->start(stream, BT_BAP_ASCS_RSP_CODE_SUCCESS, - BT_BAP_ASCS_REASON_NONE); + SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&unicast_client_cbs, listener, next, _node) { + if (listener->start != NULL) { + listener->start(stream, BT_BAP_ASCS_RSP_CODE_SUCCESS, + BT_BAP_ASCS_REASON_NONE); + } } stream->ep->state = BT_BAP_EP_STATE_STREAMING; @@ -222,6 +286,8 @@ int bt_bap_unicast_client_start(struct bt_bap_stream *stream) int bt_bap_unicast_client_disable(struct bt_bap_stream *stream) { + struct bt_bap_unicast_client_cb *listener, *next; + if (stream == NULL || stream->ep == NULL) { return -EINVAL; } @@ -241,9 +307,11 @@ int bt_bap_unicast_client_disable(struct bt_bap_stream *stream) * when leaving the streaming state in a non-release manner */ - if (unicast_client_cb != NULL && unicast_client_cb->disable != NULL) { - unicast_client_cb->disable(stream, BT_BAP_ASCS_RSP_CODE_SUCCESS, - BT_BAP_ASCS_REASON_NONE); + SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&unicast_client_cbs, listener, next, _node) { + if (listener->disable != NULL) { + listener->disable(stream, BT_BAP_ASCS_RSP_CODE_SUCCESS, + BT_BAP_ASCS_REASON_NONE); + } } /* Disabled sink ASEs go directly to the QoS configured state */ @@ -274,6 +342,8 @@ int bt_bap_unicast_client_disable(struct bt_bap_stream *stream) int bt_bap_unicast_client_stop(struct bt_bap_stream *stream) { + struct bt_bap_unicast_client_cb *listener, *next; + printk("%s %p\n", __func__, stream); /* As per the ASCS spec, only source streams can be stopped by the client */ @@ -288,9 +358,11 @@ int bt_bap_unicast_client_stop(struct bt_bap_stream *stream) return -EINVAL; } - if (unicast_client_cb != NULL && unicast_client_cb->stop != NULL) { - unicast_client_cb->stop(stream, BT_BAP_ASCS_RSP_CODE_SUCCESS, - BT_BAP_ASCS_REASON_NONE); + SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&unicast_client_cbs, listener, next, _node) { + if (listener->stop != NULL) { + listener->stop(stream, BT_BAP_ASCS_RSP_CODE_SUCCESS, + BT_BAP_ASCS_REASON_NONE); + } } stream->ep->state = BT_BAP_EP_STATE_QOS_CONFIGURED; @@ -332,6 +404,8 @@ int bt_bap_unicast_client_stop(struct bt_bap_stream *stream) int bt_bap_unicast_client_release(struct bt_bap_stream *stream) { + struct bt_bap_unicast_client_cb *listener, *next; + printk("%s %p\n", __func__, stream); if (stream == NULL || stream->ep == NULL) { @@ -349,9 +423,11 @@ int bt_bap_unicast_client_release(struct bt_bap_stream *stream) return -EINVAL; } - if (unicast_client_cb != NULL && unicast_client_cb->release != NULL) { - unicast_client_cb->release(stream, BT_BAP_ASCS_RSP_CODE_SUCCESS, - BT_BAP_ASCS_REASON_NONE); + SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&unicast_client_cbs, listener, next, _node) { + if (listener->release != NULL) { + listener->release(stream, BT_BAP_ASCS_RSP_CODE_SUCCESS, + BT_BAP_ASCS_REASON_NONE); + } } stream->ep->state = BT_BAP_EP_STATE_IDLE; @@ -366,7 +442,28 @@ int bt_bap_unicast_client_release(struct bt_bap_stream *stream) int bt_bap_unicast_client_register_cb(struct bt_bap_unicast_client_cb *cb) { - unicast_client_cb = cb; + if (cb == NULL) { + return -EINVAL; + } + + if (sys_slist_find(&unicast_client_cbs, &cb->_node, NULL)) { + return -EEXIST; + } + + sys_slist_append(&unicast_client_cbs, &cb->_node); + + return 0; +} + +int bt_bap_unicast_client_unregister_cb(struct bt_bap_unicast_client_cb *cb) +{ + if (cb == NULL) { + return -EINVAL; + } + + if (!sys_slist_find_and_remove(&unicast_client_cbs, &cb->_node)) { + return -EALREADY; + } return 0; } @@ -656,3 +753,43 @@ int bt_bap_unicast_group_foreach_stream(struct bt_bap_unicast_group *unicast_gro return 0; } + +int bt_bap_unicast_client_discover(struct bt_conn *conn, enum bt_audio_dir dir) +{ + struct bt_bap_unicast_client_cb *listener, *next; + struct unicast_client *client; + + if (conn == NULL) { + return -ENOTCONN; + } + + client = &uni_cli_insts[conn->index]; + client->conn = conn; + + SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&unicast_client_cbs, listener, next, _node) { + if (listener->endpoint != NULL) { +#if CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SNK_COUNT > 0 + if (dir == BT_AUDIO_DIR_SINK) { + ARRAY_FOR_EACH_PTR(client->snks, snk) { + snk->dir = BT_AUDIO_DIR_SINK; + listener->endpoint(conn, dir, snk); + } + } +#endif /* CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SNK_COUNT > 0 */ +#if CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SRC_COUNT > 0 + if (dir == BT_AUDIO_DIR_SOURCE) { + ARRAY_FOR_EACH_PTR(client->srcs, src) { + src->dir = BT_AUDIO_DIR_SOURCE; + listener->endpoint(conn, dir, src); + } + } +#endif /* CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SRC_COUNT > 0 */ + } + + if (listener->discover != NULL) { + listener->discover(conn, 0, dir); + } + } + + return 0; +}