Skip to content

Commit

Permalink
Bluetooth: Audio: implement the distribute broadcast code procedure
Browse files Browse the repository at this point in the history
Implemenation of the distribute broadcast code CAP procedure, as well
as unittesting

Signed-off-by: Andries Kruithof <[email protected]>
  • Loading branch information
kruithofa committed Nov 8, 2024
1 parent 7ab1fe0 commit b54a7ea
Show file tree
Hide file tree
Showing 12 changed files with 533 additions and 9 deletions.
11 changes: 11 additions & 0 deletions include/zephyr/bluetooth/audio/cap.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
};

Expand Down
170 changes: 169 additions & 1 deletion subsys/bluetooth/audio/cap_commander.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 = &param->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 =
&param->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 */
Expand Down Expand Up @@ -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:
Expand Down
1 change: 1 addition & 0 deletions subsys/bluetooth/audio/cap_common.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
14 changes: 14 additions & 0 deletions subsys/bluetooth/audio/cap_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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 {
Expand All @@ -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 {
Expand Down
1 change: 1 addition & 0 deletions tests/bluetooth/audio/cap_commander/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
)
Original file line number Diff line number Diff line change
Expand Up @@ -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_ */
Original file line number Diff line number Diff line change
@@ -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);
Original file line number Diff line number Diff line change
@@ -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"
18 changes: 11 additions & 7 deletions tests/bluetooth/audio/cap_commander/src/test_broadcast_reception.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
#include <stdlib.h>

#include <zephyr/bluetooth/audio/cap.h>
#include <zephyr/bluetooth/audio/vcp.h>
#include <zephyr/fff.h>

#include "bluetooth.h"
Expand All @@ -18,6 +17,7 @@
#include "expects_util.h"
#include "cap_mocks.h"
#include "test_common.h"
#include "test_broadcast_reception.h"

#include <zephyr/logging/log.h>

Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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);
Expand All @@ -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;
Expand Down Expand Up @@ -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;

Expand Down
Loading

0 comments on commit b54a7ea

Please sign in to comment.