From b54a7eabad88cd229b355b2f1aa5b1085c0afb8d Mon Sep 17 00:00:00 2001 From: Andries Kruithof Date: Thu, 31 Oct 2024 13:36:10 +0100 Subject: [PATCH] Bluetooth: Audio: implement the distribute broadcast code procedure Implemenation of the distribute broadcast code CAP procedure, as well as unittesting Signed-off-by: Andries Kruithof --- include/zephyr/bluetooth/audio/cap.h | 11 + subsys/bluetooth/audio/cap_commander.c | 170 ++++++++++- subsys/bluetooth/audio/cap_common.c | 1 + subsys/bluetooth/audio/cap_internal.h | 14 + .../audio/cap_commander/CMakeLists.txt | 1 + .../cap_commander/include/cap_commander.h | 1 + .../include/test_broadcast_reception.h | 11 + .../include/test_distribute_broadcast_code.h | 9 + .../src/test_broadcast_reception.c | 18 +- .../src/test_distribute_broadcast_code.c | 274 ++++++++++++++++++ .../uut/bap_broadcast_assistant.c | 27 ++ .../audio/cap_commander/uut/cap_commander.c | 5 +- 12 files changed, 533 insertions(+), 9 deletions(-) create mode 100644 tests/bluetooth/audio/cap_commander/include/test_broadcast_reception.h create mode 100644 tests/bluetooth/audio/cap_commander/include/test_distribute_broadcast_code.h create mode 100644 tests/bluetooth/audio/cap_commander/src/test_distribute_broadcast_code.c diff --git a/include/zephyr/bluetooth/audio/cap.h b/include/zephyr/bluetooth/audio/cap.h index 65fbb8a78c31c98..bd6a96923cd5a41 100644 --- a/include/zephyr/bluetooth/audio/cap.h +++ b/include/zephyr/bluetooth/audio/cap.h @@ -820,6 +820,17 @@ struct bt_cap_commander_cb { * by bt_cap_commander_cancel(). */ void (*broadcast_reception_stop)(struct bt_conn *conn, int err); + /** + * @brief Callback for bt_cap_commander_distribute_broadcast_code(). + * + * @param conn Pointer to the connection where the error + * occurred. NULL if @p err is 0 or if cancelled by + * bt_cap_commander_cancel() + * @param err 0 on success, BT_GATT_ERR() with a + * specific ATT (BT_ATT_ERR_*) error code or -ECANCELED if cancelled + * by bt_cap_commander_cancel(). + */ + void (*distribute_broadcast_code)(struct bt_conn *conn, int err); #endif /* CONFIG_BT_BAP_BROADCAST_ASSISTANT */ }; diff --git a/subsys/bluetooth/audio/cap_commander.c b/subsys/bluetooth/audio/cap_commander.c index 6725bb478633d4f..fc68187a2707d47 100644 --- a/subsys/bluetooth/audio/cap_commander.c +++ b/subsys/bluetooth/audio/cap_commander.c @@ -671,10 +671,173 @@ int bt_cap_commander_broadcast_reception_stop( return 0; } +static void cap_commander_broadcast_assistant_set_broadcast_code_cb(struct bt_conn *conn, int err) +{ + struct bt_cap_common_proc *active_proc = bt_cap_common_get_active_proc(); + + LOG_DBG("conn %p", (void *)conn); + + if (!bt_cap_common_conn_in_active_proc(conn)) { + /* State change happened outside of a procedure; ignore */ + return; + } + + if (err != 0) { + LOG_DBG("Failed to distribute broadcast code: %d", err); + LOG_DBG("Aborting the proc %d %d", active_proc->proc_done_cnt, + active_proc->proc_initiated_cnt); + + bt_cap_common_abort_proc(conn, err); + } else { + active_proc->proc_done_cnt++; + + LOG_DBG("Conn %p broadcast code set (%zu/%zu streams done)", (void *)conn, + active_proc->proc_done_cnt, active_proc->proc_cnt); + } + + if (bt_cap_common_proc_is_aborted()) { + if (bt_cap_common_proc_all_handled()) { + cap_commander_proc_complete(); + } + + return; + } + + if (!bt_cap_common_proc_is_done()) { + struct bt_cap_commander_proc_param *proc_param; + + proc_param = &active_proc->proc_param.commander[active_proc->proc_done_cnt]; + conn = proc_param->conn; + + active_proc->proc_initiated_cnt++; + err = bt_bap_broadcast_assistant_set_broadcast_code( + conn, proc_param->distribute_broadcast_code.src_id, + proc_param->distribute_broadcast_code.broadcast_code); + if (err != 0) { + LOG_DBG("Failed to perform set broadcast code for conn %p: %d", + (void *)conn, err); + cap_commander_proc_complete(); + } + } else { + cap_commander_proc_complete(); + } +} + +static bool valid_distribute_broadcast_code_param( + const struct bt_cap_commander_distribute_broadcast_code_param *param) +{ + CHECKIF(param == NULL) { + LOG_DBG("param is NULL"); + return false; + } + + CHECKIF(param->count == 0) { + LOG_DBG("Invalid param->count: %zu", param->count); + return false; + } + + CHECKIF(param->count > CONFIG_BT_MAX_CONN) { + LOG_DBG("param->count (%zu) is larger than CONFIG_BT_MAX_CONN (%d)", param->count, + CONFIG_BT_MAX_CONN); + return false; + } + + CHECKIF(param->param == NULL) { + LOG_DBG("param->param is NULL"); + return false; + } + + for (size_t i = 0; i < param->count; i++) { + const union bt_cap_set_member *member = ¶m->param[i].member; + const struct bt_conn *member_conn = + bt_cap_common_get_member_conn(param->type, member); + + if (member == NULL) { + LOG_DBG("param->param[%zu].member is NULL", i); + return false; + } + + if (member_conn == NULL) { + LOG_DBG("Invalid param->param[%zu].member", i); + return false; + } + } + + return true; +} + int bt_cap_commander_distribute_broadcast_code( const struct bt_cap_commander_distribute_broadcast_code_param *param) { - return -ENOSYS; + struct bt_cap_commander_proc_param *proc_param; + struct bt_cap_common_proc *active_proc; + struct bt_conn *conn; + int err; + + if (bt_cap_common_proc_is_active()) { + LOG_DBG("A CAP procedure is already in progress"); + + return -EBUSY; + } + + if (!valid_distribute_broadcast_code_param(param)) { + return -EINVAL; + } + + bt_cap_common_start_proc(BT_CAP_COMMON_PROC_TYPE_DISTRIBUTE_BROADCAST_CODE, param->count); + + broadcast_assistant_cb.broadcast_code = + cap_commander_broadcast_assistant_set_broadcast_code_cb; + if (!broadcast_assistant_cb_registered && + cap_commander_register_broadcast_assistant_cb() != 0) { + LOG_DBG("Failed to register broadcast assistant callbacks"); + + return -ENOEXEC; + } + + active_proc = bt_cap_common_get_active_proc(); + + for (size_t i = 0U; i < param->count; i++) { + const struct bt_cap_commander_distribute_broadcast_code_member_param *member_param = + ¶m->param[i]; + struct bt_cap_commander_proc_param *stored_param; + struct bt_conn *member_conn = + bt_cap_common_get_member_conn(param->type, &member_param->member); + + if (member_conn == NULL) { + LOG_DBG("Invalid param->member[%zu]", i); + + return -EINVAL; + } + + /* Store the necessary parameters as we cannot assume that the + * supplied parameters are kept valid + */ + stored_param = &active_proc->proc_param.commander[i]; + stored_param->conn = member_conn; + stored_param->distribute_broadcast_code.src_id = member_param->src_id; + memcpy(stored_param->distribute_broadcast_code.broadcast_code, + param->broadcast_code, sizeof(uint8_t) * BT_ISO_BROADCAST_CODE_SIZE); + } + + active_proc->proc_initiated_cnt++; + + proc_param = &active_proc->proc_param.commander[0]; + + conn = proc_param->conn; + + err = bt_bap_broadcast_assistant_set_broadcast_code( + conn, proc_param->distribute_broadcast_code.src_id, + proc_param->distribute_broadcast_code.broadcast_code); + + if (err != 0) { + LOG_DBG("Failed to start distribute broadcast code for conn %p: %d", (void *)conn, + err); + + return -ENOEXEC; + } + + return 0; } #endif /* CONFIG_BT_BAP_BROADCAST_ASSISTANT */ @@ -740,6 +903,11 @@ static void cap_commander_proc_complete(void) cap_cb->broadcast_reception_stop(failed_conn, err); } break; + case BT_CAP_COMMON_PROC_TYPE_DISTRIBUTE_BROADCAST_CODE: + if (cap_cb->distribute_broadcast_code != NULL) { + cap_cb->distribute_broadcast_code(failed_conn, err); + } + break; #endif /* CONFIG_BT_BAP_BROADCAST_ASSISTANT */ case BT_CAP_COMMON_PROC_TYPE_NONE: default: diff --git a/subsys/bluetooth/audio/cap_common.c b/subsys/bluetooth/audio/cap_common.c index 5d277bbf19a3036..cd52428eb2b47f3 100644 --- a/subsys/bluetooth/audio/cap_common.c +++ b/subsys/bluetooth/audio/cap_common.c @@ -164,6 +164,7 @@ static bool active_proc_is_commander(void) case BT_CAP_COMMON_PROC_TYPE_MICROPHONE_MUTE_CHANGE: case BT_CAP_COMMON_PROC_TYPE_BROADCAST_RECEPTION_START: case BT_CAP_COMMON_PROC_TYPE_BROADCAST_RECEPTION_STOP: + case BT_CAP_COMMON_PROC_TYPE_DISTRIBUTE_BROADCAST_CODE: return true; default: return false; diff --git a/subsys/bluetooth/audio/cap_internal.h b/subsys/bluetooth/audio/cap_internal.h index 7d8d530628d43f8..c83c4af0e948d6b 100644 --- a/subsys/bluetooth/audio/cap_internal.h +++ b/subsys/bluetooth/audio/cap_internal.h @@ -51,6 +51,7 @@ enum bt_cap_common_proc_type { BT_CAP_COMMON_PROC_TYPE_STOP, BT_CAP_COMMON_PROC_TYPE_BROADCAST_RECEPTION_START, BT_CAP_COMMON_PROC_TYPE_BROADCAST_RECEPTION_STOP, + BT_CAP_COMMON_PROC_TYPE_DISTRIBUTE_BROADCAST_CODE, BT_CAP_COMMON_PROC_TYPE_VOLUME_CHANGE, BT_CAP_COMMON_PROC_TYPE_VOLUME_OFFSET_CHANGE, BT_CAP_COMMON_PROC_TYPE_VOLUME_MUTE_CHANGE, @@ -109,6 +110,18 @@ struct cap_broadcast_reception_stop { uint8_t num_subgroups; struct bt_bap_bass_subgroup subgroups[CONFIG_BT_BAP_BASS_MAX_SUBGROUPS]; }; + +/* Note that although the broadcast_code will be the same for all + * we nevertheless store a separate copy for each sink, for + * consistensy in the struct bt_cap_commander_proc_param + * There is no memory savings by not having broadcast_code part of the + * union: struct cap_broadcast_reception_start uses minimum 20 bytes + * and struct cap_distribute_broadcast_code uses 17 bytes + */ +struct cap_distribute_broadcast_code { + uint8_t src_id; + uint8_t broadcast_code[BT_ISO_BROADCAST_CODE_SIZE]; +}; #endif /* CONFIG_BT_BAP_BROADCAST_ASSISTANT */ struct bt_cap_commander_proc_param { @@ -131,6 +144,7 @@ struct bt_cap_commander_proc_param { #if defined(CONFIG_BT_BAP_BROADCAST_ASSISTANT) struct cap_broadcast_reception_start broadcast_reception_start; struct cap_broadcast_reception_stop broadcast_reception_stop; + struct cap_distribute_broadcast_code distribute_broadcast_code; #endif /* CONFIG_BT_BAP_BROADCAST_ASSISTANT */ #if defined(CONFIG_BT_MICP_MIC_CTLR) struct { diff --git a/tests/bluetooth/audio/cap_commander/CMakeLists.txt b/tests/bluetooth/audio/cap_commander/CMakeLists.txt index ebda30a7c7a2c18..f5a82565f5183dc 100644 --- a/tests/bluetooth/audio/cap_commander/CMakeLists.txt +++ b/tests/bluetooth/audio/cap_commander/CMakeLists.txt @@ -19,4 +19,5 @@ target_sources(testbinary src/test_vcp.c src/test_micp.c src/test_broadcast_reception.c + src/test_distribute_broadcast_code.c ) diff --git a/tests/bluetooth/audio/cap_commander/include/cap_commander.h b/tests/bluetooth/audio/cap_commander/include/cap_commander.h index 68690073d6eff52..6b19ce4ca02c4fe 100644 --- a/tests/bluetooth/audio/cap_commander/include/cap_commander.h +++ b/tests/bluetooth/audio/cap_commander/include/cap_commander.h @@ -25,5 +25,6 @@ DECLARE_FAKE_VOID_FUNC(mock_cap_commander_microphone_mute_changed_cb, struct bt_ DECLARE_FAKE_VOID_FUNC(mock_cap_commander_microphone_gain_changed_cb, struct bt_conn *, int); DECLARE_FAKE_VOID_FUNC(mock_cap_commander_broadcast_reception_start_cb, struct bt_conn *, int); DECLARE_FAKE_VOID_FUNC(mock_cap_commander_broadcast_reception_stop_cb, struct bt_conn *, int); +DECLARE_FAKE_VOID_FUNC(mock_cap_commander_distribute_broadcast_code_cb, struct bt_conn *, int); #endif /* MOCKS_CAP_COMMANDER_H_ */ diff --git a/tests/bluetooth/audio/cap_commander/include/test_broadcast_reception.h b/tests/bluetooth/audio/cap_commander/include/test_broadcast_reception.h new file mode 100644 index 000000000000000..8f4b610660e5dd7 --- /dev/null +++ b/tests/bluetooth/audio/cap_commander/include/test_broadcast_reception.h @@ -0,0 +1,11 @@ +/* test_broadcast_reception.h */ + +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +void test_start_param_init(void *f); +void test_broadcast_reception_start( + struct bt_cap_commander_broadcast_reception_start_param *start_param); diff --git a/tests/bluetooth/audio/cap_commander/include/test_distribute_broadcast_code.h b/tests/bluetooth/audio/cap_commander/include/test_distribute_broadcast_code.h new file mode 100644 index 000000000000000..9af88629f62b3d4 --- /dev/null +++ b/tests/bluetooth/audio/cap_commander/include/test_distribute_broadcast_code.h @@ -0,0 +1,9 @@ +/* test_distribute_broadcast_code.h */ + +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define BROADCAST_CODE "BroadcastCode" diff --git a/tests/bluetooth/audio/cap_commander/src/test_broadcast_reception.c b/tests/bluetooth/audio/cap_commander/src/test_broadcast_reception.c index 61827719bb5fd7c..bf54a611d8af6ff 100644 --- a/tests/bluetooth/audio/cap_commander/src/test_broadcast_reception.c +++ b/tests/bluetooth/audio/cap_commander/src/test_broadcast_reception.c @@ -9,7 +9,6 @@ #include #include -#include #include #include "bluetooth.h" @@ -18,6 +17,7 @@ #include "expects_util.h" #include "cap_mocks.h" #include "test_common.h" +#include "test_broadcast_reception.h" #include @@ -47,7 +47,6 @@ struct cap_commander_test_broadcast_reception_fixture { /* src_id can not be part of the fixture since it is accessed in the callback function */ static uint8_t src_id[CONFIG_BT_MAX_CONN]; -static void test_start_param_init(void *f); static void test_stop_param_init(void *f); static void cap_commander_broadcast_assistant_recv_state_cb( @@ -104,13 +103,12 @@ static void cap_commander_test_broadcast_reception_before(void *f) static void cap_commander_test_broadcast_reception_after(void *f) { struct cap_commander_test_broadcast_reception_fixture *fixture = f; - int err; bt_cap_commander_unregister_cb(&mock_cap_commander_cb); bt_bap_broadcast_assistant_unregister_cb(&fixture->broadcast_assistant_cb); /* We need to cleanup since the CAP commander remembers state */ - err = bt_cap_commander_cancel(); + bt_cap_commander_cancel(); for (size_t i = 0; i < ARRAY_SIZE(fixture->conns); i++) { mock_bt_conn_disconnected(&fixture->conns[i], BT_HCI_ERR_REMOTE_USER_TERM_CONN); @@ -122,7 +120,10 @@ static void cap_commander_test_broadcast_reception_teardown(void *f) free(f); } -static void test_start_param_init(void *f) +/* Note that test_start_param_init is re-used for the + * distribute_broadcast_code test + */ +void test_start_param_init(void *f) { struct cap_commander_test_broadcast_reception_fixture *fixture = f; int err; @@ -169,8 +170,11 @@ static void test_stop_param_init(void *f) } } -static void -test_broadcast_reception_start(struct bt_cap_commander_broadcast_reception_start_param *start_param) +/* Note that the broadcast_reception_start test is re-used for the + * distribute_broadcast_code test + */ +void test_broadcast_reception_start( + struct bt_cap_commander_broadcast_reception_start_param *start_param) { int err; diff --git a/tests/bluetooth/audio/cap_commander/src/test_distribute_broadcast_code.c b/tests/bluetooth/audio/cap_commander/src/test_distribute_broadcast_code.c new file mode 100644 index 000000000000000..75f4915c98b63b7 --- /dev/null +++ b/tests/bluetooth/audio/cap_commander/src/test_distribute_broadcast_code.c @@ -0,0 +1,274 @@ +/* test_distribute_broadcast_code.c - unit test for distribute broadcast code */ + +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#include +#include + +#include "bluetooth.h" +#include "cap_commander.h" +#include "conn.h" +#include "expects_util.h" +#include "cap_mocks.h" +#include "test_common.h" +#include "test_broadcast_reception.h" +#include "test_distribute_broadcast_code.h" + +#include + +LOG_MODULE_REGISTER(bt_distribute_broadcast_code, CONFIG_BT_CAP_COMMANDER_LOG_LEVEL); + +#define FFF_GLOBALS + +struct cap_commander_test_distribute_broadcast_code_fixture { + struct bt_conn conns[CONFIG_BT_MAX_CONN]; + + struct bt_bap_bass_subgroup subgroups[CONFIG_BT_BAP_BASS_MAX_SUBGROUPS]; + struct bt_cap_commander_broadcast_reception_start_member_param + start_member_params[CONFIG_BT_MAX_CONN]; + struct bt_cap_commander_broadcast_reception_start_param start_param; + struct bt_cap_commander_distribute_broadcast_code_member_param + broadcast_code_member_params[CONFIG_BT_MAX_CONN]; + struct bt_cap_commander_distribute_broadcast_code_param distribute_broadcast_code_param; + struct bt_bap_broadcast_assistant_cb broadcast_assistant_cb; +}; + +/* src_id can not be part of the fixture since it is accessed in the callback function */ +static uint8_t src_id[CONFIG_BT_MAX_CONN]; + +static void cap_commander_broadcast_assistant_recv_state_cb( + struct bt_conn *conn, int err, const struct bt_bap_scan_delegator_recv_state *state) +{ + uint8_t index; + + index = bt_conn_index(conn); + src_id[index] = state->src_id; +} + +static void cap_commander_test_distribute_broadcast_code_fixture_init( + struct cap_commander_test_distribute_broadcast_code_fixture *fixture) +{ + int err; + + for (size_t i = 0; i < ARRAY_SIZE(fixture->conns); i++) { + test_conn_init(&fixture->conns[i]); + fixture->conns[i].index = i; + } + + test_start_param_init(fixture); + + fixture->distribute_broadcast_code_param.type = BT_CAP_SET_TYPE_AD_HOC; + fixture->distribute_broadcast_code_param.param = fixture->broadcast_code_member_params; + fixture->distribute_broadcast_code_param.count = + ARRAY_SIZE(fixture->broadcast_code_member_params); + memcpy(fixture->distribute_broadcast_code_param.broadcast_code, BROADCAST_CODE, + sizeof(BROADCAST_CODE)); + for (size_t i = 0; i < ARRAY_SIZE(fixture->broadcast_code_member_params); i++) { + fixture->broadcast_code_member_params[i].member.member = &fixture->conns[i]; + fixture->broadcast_code_member_params[i].src_id = 0; + } + + fixture->broadcast_assistant_cb.recv_state = + cap_commander_broadcast_assistant_recv_state_cb; + err = bt_bap_broadcast_assistant_register_cb(&fixture->broadcast_assistant_cb); + zassert_equal(0, err, "Failed registering broadcast assistant callback functions %d", err); +} + +static void *cap_commander_test_distribute_broadcast_code_setup(void) +{ + struct cap_commander_test_distribute_broadcast_code_fixture *fixture; + + fixture = malloc(sizeof(*fixture)); + zassert_not_null(fixture); + + return fixture; +} + +static void cap_commander_test_distribute_broadcast_code_before(void *f) +{ + int err; + struct cap_commander_test_distribute_broadcast_code_fixture *fixture = f; + + memset(f, 0, sizeof(struct cap_commander_test_distribute_broadcast_code_fixture)); + cap_commander_test_distribute_broadcast_code_fixture_init(fixture); + + for (size_t i = 0; i < ARRAY_SIZE(fixture->conns); i++) { + err = bt_cap_commander_discover(&fixture->conns[i]); + zassert_equal(0, err, "Unexpected return value %d", err); + } +} + +static void cap_commander_test_distribute_broadcast_code_after(void *f) +{ + struct cap_commander_test_distribute_broadcast_code_fixture *fixture = f; + + bt_cap_commander_unregister_cb(&mock_cap_commander_cb); + bt_bap_broadcast_assistant_unregister_cb(&fixture->broadcast_assistant_cb); + + /* We need to cleanup since the CAP commander remembers state */ + bt_cap_commander_cancel(); + + for (size_t i = 0; i < ARRAY_SIZE(fixture->conns); i++) { + mock_bt_conn_disconnected(&fixture->conns[i], BT_HCI_ERR_REMOTE_USER_TERM_CONN); + } +} + +static void cap_commander_test_distribute_broadcast_code_teardown(void *f) +{ + free(f); +} + +static void test_distribute_broadcast_code( + struct bt_cap_commander_distribute_broadcast_code_param *distribute_broadcast_code_param) +{ + int err; + + err = bt_cap_commander_distribute_broadcast_code(distribute_broadcast_code_param); + zassert_equal(0, err, "Unexpected return value %d", err); + + zexpect_call_count("bt_cap_commander_cb.distribute_broadcast_code", 1, + mock_cap_commander_distribute_broadcast_code_cb_fake.call_count); + zassert_equal_ptr(NULL, + mock_cap_commander_distribute_broadcast_code_cb_fake.arg0_history[0]); + zassert_equal(0, mock_cap_commander_distribute_broadcast_code_cb_fake.arg1_history[0]); +} + +ZTEST_SUITE(cap_commander_test_distribute_broadcast_code, NULL, + cap_commander_test_distribute_broadcast_code_setup, + cap_commander_test_distribute_broadcast_code_before, + cap_commander_test_distribute_broadcast_code_after, + cap_commander_test_distribute_broadcast_code_teardown); + +ZTEST_F(cap_commander_test_distribute_broadcast_code, test_commander_distribute_broadcast_code) +{ + int err; + + err = bt_cap_commander_register_cb(&mock_cap_commander_cb); + zassert_equal(0, err, "Unexpected return value %d", err); + + /* we perform a broadcast_reception_start to ensure we get a valid src_id */ + test_broadcast_reception_start(&fixture->start_param); + + for (size_t i = 0U; i < CONFIG_BT_MAX_CONN; i++) { + fixture->distribute_broadcast_code_param.param[i].src_id = src_id[i]; + } + + test_distribute_broadcast_code(&fixture->distribute_broadcast_code_param); +} + +ZTEST_F(cap_commander_test_distribute_broadcast_code, + test_commander_reception_distribute_broadcast_code_double) +{ + int err; + + err = bt_cap_commander_register_cb(&mock_cap_commander_cb); + zassert_equal(0, err, "Unexpected return value %d", err); + + test_broadcast_reception_start(&fixture->start_param); + + for (size_t i = 0U; i < CONFIG_BT_MAX_CONN; i++) { + fixture->distribute_broadcast_code_param.param[i].src_id = src_id[i]; + } + + test_distribute_broadcast_code(&fixture->distribute_broadcast_code_param); + + /* + * We can not use test_distribute_broadcast_code because of the check on how often the + * callback function is called + */ + err = bt_cap_commander_distribute_broadcast_code(&fixture->distribute_broadcast_code_param); + zassert_equal(0, err, "Unexpected return value %d", err); + + zexpect_call_count("bt_cap_commander_cb.distribute_broadcast_code", 2, + mock_cap_commander_distribute_broadcast_code_cb_fake.call_count); +} + +ZTEST_F(cap_commander_test_distribute_broadcast_code, + test_commander_reception_distribute_broadcast_code_param_null) +{ + int err; + + err = bt_cap_commander_distribute_broadcast_code(NULL); + zassert_equal(-EINVAL, err, "Unexpected return value %d", err); + + zexpect_call_count("bt_cap_commander_cb.distribute_broadcast_code", 0, + mock_cap_commander_distribute_broadcast_code_cb_fake.call_count); +} + +ZTEST_F(cap_commander_test_distribute_broadcast_code, + test_commander_distribute_broadcast_code_param_zero_count) +{ + int err; + + fixture->distribute_broadcast_code_param.count = 0; + + err = bt_cap_commander_distribute_broadcast_code(&fixture->distribute_broadcast_code_param); + zassert_equal(-EINVAL, err, "Unexpected return value %d", err); + + zexpect_call_count("bt_cap_commander_cb.distribute_broadcast_code", 0, + mock_cap_commander_distribute_broadcast_code_cb_fake.call_count); +} + +ZTEST_F(cap_commander_test_distribute_broadcast_code, + test_commander_distribute_broadcast_code_param_high_count) +{ + int err; + + fixture->distribute_broadcast_code_param.count = CONFIG_BT_MAX_CONN + 1; + + err = bt_cap_commander_distribute_broadcast_code(&fixture->distribute_broadcast_code_param); + zassert_equal(-EINVAL, err, "Unexpected return value %d", err); + + zexpect_call_count("bt_cap_commander_cb.broadcast_distribute_broadcast_code", 0, + mock_cap_commander_distribute_broadcast_code_cb_fake.call_count); +} + +ZTEST_F(cap_commander_test_distribute_broadcast_code, + test_commander_distribute_broadcast_code_inval_param_null_param) +{ + int err; + + fixture->distribute_broadcast_code_param.type = BT_CAP_SET_TYPE_AD_HOC; + fixture->distribute_broadcast_code_param.param = NULL; + fixture->distribute_broadcast_code_param.count = ARRAY_SIZE(fixture->conns); + + err = bt_cap_commander_distribute_broadcast_code(&fixture->distribute_broadcast_code_param); + zassert_equal(-EINVAL, err, "Unexpected return value %d", err); + + zexpect_call_count("bt_cap_commander_cb.distribute_broadcast_code", 0, + mock_cap_commander_distribute_broadcast_code_cb_fake.call_count); +} + +ZTEST_F(cap_commander_test_distribute_broadcast_code, + test_commander_distribute_broadcast_code_inval_null_member) +{ + int err; + + fixture->distribute_broadcast_code_param.param[0].member.member = NULL; + + err = bt_cap_commander_distribute_broadcast_code(&fixture->distribute_broadcast_code_param); + zassert_equal(-EINVAL, err, "Unexpected return value %d", err); + + zexpect_call_count("bt_cap_commander_cb.distribute_broadcast_code", 0, + mock_cap_commander_distribute_broadcast_code_cb_fake.call_count); +} + +ZTEST_F(cap_commander_test_distribute_broadcast_code, + test_commander_distribute_broadcast_code_inval_missing_cas) +{ + int err; + + fixture->distribute_broadcast_code_param.type = BT_CAP_SET_TYPE_CSIP; + + err = bt_cap_commander_distribute_broadcast_code(&fixture->distribute_broadcast_code_param); + zassert_equal(-EINVAL, err, "Unexpected return value %d", err); + + zexpect_call_count("bt_cap_commander_cb.distribute_broadcast_code", 0, + mock_cap_commander_distribute_broadcast_code_cb_fake.call_count); +} diff --git a/tests/bluetooth/audio/cap_commander/uut/bap_broadcast_assistant.c b/tests/bluetooth/audio/cap_commander/uut/bap_broadcast_assistant.c index 95dcf032189d597..375bfa7a344e53a 100644 --- a/tests/bluetooth/audio/cap_commander/uut/bap_broadcast_assistant.c +++ b/tests/bluetooth/audio/cap_commander/uut/bap_broadcast_assistant.c @@ -5,6 +5,7 @@ */ #include "zephyr/bluetooth/audio/bap.h" +#include "test_distribute_broadcast_code.h" static sys_slist_t broadcast_assistant_cbs = SYS_SLIST_STATIC_INIT(&broadcast_assistant_cbs); @@ -176,3 +177,29 @@ int bt_bap_broadcast_assistant_rem_src(struct bt_conn *conn, uint8_t src_id) return 0; } + +int bt_bap_broadcast_assistant_set_broadcast_code( + struct bt_conn *conn, uint8_t src_id, + const uint8_t broadcast_code[BT_ISO_BROADCAST_CODE_SIZE]) +{ + struct bap_broadcast_assistant_instance *inst; + struct bt_bap_broadcast_assistant_cb *listener, *next; + int err; + + zassert_not_null(conn, "conn is NULL"); + + inst = inst_by_conn(conn); + + zassert_equal(src_id, inst->recv_states.src_id, "Invalid src_id"); + + err = strncmp((const char *)broadcast_code, BROADCAST_CODE, sizeof(BROADCAST_CODE)); + zassert_equal(0, err, "Unexpected broadcast code"); + + SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&broadcast_assistant_cbs, listener, next, _node) { + if (listener->broadcast_code != NULL) { + listener->broadcast_code(conn, 0); + } + } + + return 0; +} diff --git a/tests/bluetooth/audio/cap_commander/uut/cap_commander.c b/tests/bluetooth/audio/cap_commander/uut/cap_commander.c index 1e1de370b73e0a0..702618e1594f883 100644 --- a/tests/bluetooth/audio/cap_commander/uut/cap_commander.c +++ b/tests/bluetooth/audio/cap_commander/uut/cap_commander.c @@ -17,7 +17,8 @@ FAKE(mock_cap_commander_microphone_mute_changed_cb) \ FAKE(mock_cap_commander_microphone_gain_changed_cb) \ FAKE(mock_cap_commander_broadcast_reception_start_cb) \ - FAKE(mock_cap_commander_broadcast_reception_stop_cb) + FAKE(mock_cap_commander_broadcast_reception_stop_cb) \ + FAKE(mock_cap_commander_distribute_broadcast_code_cb) DEFINE_FAKE_VOID_FUNC(mock_cap_commander_discovery_complete_cb, struct bt_conn *, int, const struct bt_csip_set_coordinator_set_member *, @@ -30,6 +31,7 @@ DEFINE_FAKE_VOID_FUNC(mock_cap_commander_microphone_mute_changed_cb, struct bt_c DEFINE_FAKE_VOID_FUNC(mock_cap_commander_microphone_gain_changed_cb, struct bt_conn *, int); DEFINE_FAKE_VOID_FUNC(mock_cap_commander_broadcast_reception_start_cb, struct bt_conn *, int); DEFINE_FAKE_VOID_FUNC(mock_cap_commander_broadcast_reception_stop_cb, struct bt_conn *, int); +DEFINE_FAKE_VOID_FUNC(mock_cap_commander_distribute_broadcast_code_cb, struct bt_conn *, int); const struct bt_cap_commander_cb mock_cap_commander_cb = { .discovery_complete = mock_cap_commander_discovery_complete_cb, @@ -49,6 +51,7 @@ const struct bt_cap_commander_cb mock_cap_commander_cb = { #if defined(CONFIG_BT_BAP_BROADCAST_ASSISTANT) .broadcast_reception_start = mock_cap_commander_broadcast_reception_start_cb, .broadcast_reception_stop = mock_cap_commander_broadcast_reception_stop_cb, + .distribute_broadcast_code = mock_cap_commander_distribute_broadcast_code_cb, #endif /* CONFIG_BT_BAP_BROADCAST_ASSISTANT */ };