Bluetooth: CSIP: Add support for dynamic SIRKs

Add support for dynamically change the SIRK in a CSIS.

Signed-off-by: Emil Gydesen <emil.gydesen@nordicsemi.no>
This commit is contained in:
Emil Gydesen 2024-01-08 23:26:53 +01:00 committed by Anas Nashif
commit 41a589c5fa
13 changed files with 537 additions and 174 deletions

View file

@ -28,12 +28,38 @@ register callbacks.
lock :Lock the set
release :Release the set [force]
print_sirk :Print the currently used SIRK
set_sirk :Set the currently used SIRK <sirk>
get_sirk :Get the currently used SIRK
set_sirk_rsp :Set the response used in SIRK requests <accept, accept_enc,
reject, oob>
Besides initializing the CAS and the CSIS, there are also commands to lock and release the CSIS
instance, as well as printing and modifying access to the SIRK of the CSIS.
Setting a new SIRK
------------------
This command can modify the currently used SIRK. To get the new RSI to advertise on air,
:code:`bt adv-data`` or :code:`bt advertise` must be called again to set the new advertising data.
If :code:`CONFIG_BT_CSIP_SET_MEMBER_NOTIFIABLE` is enabled, this will also notify connected
clients.
.. code-block:: console
uart:~$ cap_acceptor set_sirk 00112233445566778899aabbccddeeff
Set SIRK updated
Getting the current SIRK
------------------------
This command can get the currently used SIRK.
.. code-block:: console
uart:~$ cap_acceptor get_sirk
Set SIRK
36 04 9a dc 66 3a a1 a1 |6...f:..
1d 9a 2f 41 01 73 3e 01 |../A.s>.
CAP Initiator
*************

View file

@ -146,10 +146,11 @@ Using the Set Member
Subcommands:
register :Initialize the service and register callbacks [size <int>]
[rank <int>] [not-lockable] [sirk <data>]
update_psri :Update the advertised PSRI
lock :Lock the set
release :Release the set [force]
print_sirk :Print the currently used SIRK
set_sirk :Set the currently used SIRK <sirk>
get_sirk :Get the currently used SIRK
set_sirk_rsp :Set the response used in SIRK requests <accept, accept_enc,
reject, oob>
@ -163,3 +164,29 @@ Setup
uart:~$ bt init
uart:~$ csip_set_member register
Setting a new SIRK
------------------
This command can modify the currently used SIRK. To get the new RSI to advertise on air,
:code:`bt adv-data`` or :code:`bt advertise` must be called again to set the new advertising data.
If :code:`CONFIG_BT_CSIP_SET_MEMBER_NOTIFIABLE` is enabled, this will also notify connected
clients.
.. code-block:: console
uart:~$ csip_set_member set_sirk 00112233445566778899aabbccddeeff
Set SIRK updated
Getting the current SIRK
------------------------
This command can get the currently used SIRK.
.. code-block:: console
uart:~$ csip_set_member get_sirk
Set SIRK
36 04 9a dc 66 3a a1 a1 |6...f:..
1d 9a 2f 41 01 73 3e 01 |../A.s>.

View file

@ -197,6 +197,24 @@ int bt_csip_set_member_register(const struct bt_csip_set_member_register_param *
*/
int bt_csip_set_member_unregister(struct bt_csip_set_member_svc_inst *svc_inst);
/**
* @brief Set the SIRK of a service instance
*
* @param svc_inst Pointer to the registered Coordinated Set Identification Service.
* @param sirk The new SIRK.
*/
int bt_csip_set_member_set_sirk(struct bt_csip_set_member_svc_inst *svc_inst,
const uint8_t sirk[BT_CSIP_SET_SIRK_SIZE]);
/**
* @brief Get the SIRK of a service instance
*
* @param[in] svc_inst Pointer to the registered Coordinated Set Identification Service.
* @param[out] sirk Array to store the SIRK in.
*/
int bt_csip_set_member_get_sirk(struct bt_csip_set_member_svc_inst *svc_inst,
uint8_t sirk[BT_CSIP_SET_SIRK_SIZE]);
/**
* @brief Print the SIRK to the debug output
*
@ -323,6 +341,16 @@ typedef void (*bt_csip_set_coordinator_lock_set_cb)(int err);
typedef void (*bt_csip_set_coordinator_lock_changed_cb)(
struct bt_csip_set_coordinator_csis_inst *inst, bool locked);
/**
* @typedef bt_csip_set_coordinator_sirk_changed_cb
* @brief Callback when the SIRK value of a set of a connected device changes.
*
* @param inst The Coordinated Set Identification Service instance that was changed.
* The new SIRK can be accessed via the @p inst.info.
*/
typedef void (*bt_csip_set_coordinator_sirk_changed_cb)(
struct bt_csip_set_coordinator_csis_inst *inst);
/**
* @typedef bt_csip_set_coordinator_ordered_access_cb_t
* @brief Callback for bt_csip_set_coordinator_ordered_access()
@ -347,6 +375,7 @@ struct bt_csip_set_coordinator_cb {
bt_csip_set_coordinator_lock_set_cb lock_set;
bt_csip_set_coordinator_lock_set_cb release_set;
bt_csip_set_coordinator_lock_changed_cb lock_changed;
bt_csip_set_coordinator_sirk_changed_cb sirk_changed;
/* Device specific callbacks */
bt_csip_set_coordinator_discover_cb discover;
@ -455,7 +484,6 @@ int bt_csip_set_coordinator_release(const struct bt_csip_set_coordinator_set_mem
uint8_t count,
const struct bt_csip_set_coordinator_set_info *set_info);
#ifdef __cplusplus
}
#endif

View file

@ -47,6 +47,11 @@ config BT_CSIP_SET_MEMBER_MAX_INSTANCE_COUNT
Enabling BT_CAP_ACCEPTOR_SET_MEMBER will take one of the allocated
instances.
config BT_CSIP_SET_MEMBER_NOTIFIABLE
bool "SIRK notifiable Support"
help
This option enables support for clients to be notified on SIRK changes.
endif # BT_CSIP_SET_MEMBER
#################### Coordinated Set Identification Client ####################

View file

@ -300,6 +300,17 @@ static void lock_changed(struct bt_csip_set_coordinator_csis_inst *inst, bool lo
}
}
static void sirk_changed(struct bt_csip_set_coordinator_csis_inst *inst)
{
struct bt_csip_set_coordinator_cb *listener;
SYS_SLIST_FOR_EACH_CONTAINER(&csip_set_coordinator_cbs, listener, _node) {
if (listener->sirk_changed) {
listener->sirk_changed(inst);
}
}
}
static void release_set_complete(int err)
{
struct bt_csip_set_coordinator_cb *listener;
@ -389,10 +400,12 @@ static uint8_t sirk_notify_func(struct bt_conn *conn,
struct bt_csip_set_sirk *sirk =
(struct bt_csip_set_sirk *)data;
struct bt_csip_set_coordinator_inst *client;
struct bt_csip_set_coordinator_csis_inst *inst;
uint8_t *dst_sirk;
client = &client_insts[bt_conn_index(conn)];
dst_sirk = client->set_member.insts[svc_inst->idx].info.set_sirk;
inst = &client->set_member.insts[svc_inst->idx];
dst_sirk = inst->info.set_sirk;
LOG_DBG("Set SIRK %sencrypted",
sirk->type == BT_CSIP_SIRK_TYPE_PLAIN ? "not " : "");
@ -422,7 +435,7 @@ static uint8_t sirk_notify_func(struct bt_conn *conn,
LOG_HEXDUMP_DBG(dst_sirk, BT_CSIP_SET_SIRK_SIZE,
"Set SIRK");
/* TODO: Notify app */
sirk_changed(inst);
} else {
LOG_DBG("Invalid length %u", length);
}

View file

@ -39,9 +39,10 @@
LOG_MODULE_REGISTER(bt_csip_set_member, CONFIG_BT_CSIP_SET_MEMBER_LOG_LEVEL);
enum csip_pending_notify_flag {
enum csip_flag {
FLAG_ACTIVE,
FLAG_SET_MEMBER_LOCK,
FLAG_NOTIFY_LOCK,
FLAG_NOTIFY_SIRK,
FLAG_NUM,
};
@ -67,16 +68,9 @@ struct bt_csip_set_member_svc_inst {
static struct bt_csip_set_member_svc_inst svc_insts[CONFIG_BT_CSIP_SET_MEMBER_MAX_INSTANCE_COUNT];
static bt_addr_le_t server_dummy_addr; /* 0'ed address */
static atomic_t notify_in_progress;
static void deferred_nfy_work_handler(struct k_work *work);
static K_WORK_DEFINE(deferred_nfy_work, deferred_nfy_work_handler);
struct csip_notify_foreach {
struct bt_conn *excluded_client;
struct bt_csip_set_member_svc_inst *svc_inst;
};
static K_WORK_DELAYABLE_DEFINE(deferred_nfy_work, deferred_nfy_work_handler);
static bool is_last_client_to_write(const struct bt_csip_set_member_svc_inst *svc_inst,
const struct bt_conn *conn)
@ -90,114 +84,27 @@ static bool is_last_client_to_write(const struct bt_csip_set_member_svc_inst *sv
}
}
static void csip_gatt_notify_complete_cb(struct bt_conn *conn, void *user_data)
{
/* Notification done, clear bit and reschedule work */
atomic_clear(&notify_in_progress);
k_work_submit(&deferred_nfy_work);
}
static int csip_gatt_notify_set_lock(struct bt_conn *conn,
const struct bt_gatt_attr *attr,
const void *data,
uint16_t len)
static void notify_work_reschedule(k_timeout_t delay)
{
int err;
struct bt_gatt_notify_params params;
memset(&params, 0, sizeof(params));
params.uuid = BT_UUID_CSIS_SET_LOCK;
params.attr = attr;
params.data = data;
params.len = len;
params.func = csip_gatt_notify_complete_cb;
/* Mark notification in progress */
atomic_set(&notify_in_progress, 1);
err = bt_gatt_notify_cb(conn, &params);
if (err != 0) {
atomic_clear(&notify_in_progress);
if (err != -ENOTCONN) {
return err;
}
}
return 0;
}
static void csip_set_notify_bit(struct bt_csip_set_member_svc_inst *svc_inst,
enum csip_pending_notify_flag flag)
{
for (size_t i = 0U; i < ARRAY_SIZE(svc_inst->clients); i++) {
struct csip_client *client;
client = &svc_inst->clients[i];
if (atomic_test_bit(client->flags, FLAG_ACTIVE)) {
atomic_set_bit(client->flags, flag);
}
}
}
static int notify_lock_value(const struct bt_csip_set_member_svc_inst *svc_inst,
struct bt_conn *conn)
{
LOG_DBG("");
if (svc_inst->service_p != NULL) {
return csip_gatt_notify_set_lock(conn, svc_inst->service_p->attrs,
&svc_inst->set_lock, sizeof(svc_inst->set_lock));
} else {
return -EINVAL;
}
}
static void notify_client(struct bt_conn *conn, void *data)
{
struct csip_notify_foreach *csip_data = (struct csip_notify_foreach *)data;
struct bt_csip_set_member_svc_inst *svc_inst = csip_data->svc_inst;
struct bt_conn *excluded_conn = csip_data->excluded_client;
if (excluded_conn != NULL && conn == excluded_conn) {
/* If it is already scheduled, don't reschedule */
if (k_work_delayable_remaining_get(&deferred_nfy_work) > 0) {
return;
}
for (size_t i = 0U; i < ARRAY_SIZE(svc_inst->clients); i++) {
struct csip_client *client;
client = &svc_inst->clients[i];
if (atomic_test_bit(client->flags, FLAG_SET_MEMBER_LOCK) &&
bt_addr_le_eq(bt_conn_get_dst(conn), &client->addr)) {
/* First try to send the notification directly, and if it fails add it
* to system workqueue for retry. We do it like this here as the client
* wants the lock notification asap to begin ordered access procedure
*/
if (notify_lock_value(svc_inst, conn) != 0) {
csip_set_notify_bit(svc_inst, FLAG_SET_MEMBER_LOCK);
k_work_submit(&deferred_nfy_work);
} else {
atomic_clear_bit(client->flags, FLAG_SET_MEMBER_LOCK);
break;
}
}
err = k_work_reschedule(&deferred_nfy_work, delay);
if (err < 0) {
LOG_ERR("Failed to reschedule notification work err %d", err);
}
}
static void notify_clients(struct bt_csip_set_member_svc_inst *svc_inst,
struct bt_conn *excluded_client)
struct bt_conn *excluded_client, enum csip_flag flag)
{
struct csip_notify_foreach data = {
.excluded_client = excluded_client,
.svc_inst = svc_inst,
};
bool submit_work = false;
/* Mark all bonded devices as pending notifications, and clear those
* that are notified in `notify_client`
*/
/* Mark all bonded devices (except the excluded one) as pending notifications */
for (size_t i = 0U; i < ARRAY_SIZE(svc_inst->clients); i++) {
struct csip_client *client;
@ -209,11 +116,15 @@ static void notify_clients(struct bt_csip_set_member_svc_inst *svc_inst,
continue;
}
atomic_set_bit(client->flags, FLAG_SET_MEMBER_LOCK);
atomic_set_bit(client->flags, flag);
submit_work = true;
}
}
bt_conn_foreach(BT_CONN_TYPE_LE, notify_client, &data);
/* Reschedule work for notifying */
if (submit_work) {
notify_work_reschedule(K_NO_WAIT);
}
}
static int sirk_encrypt(struct bt_conn *conn,
@ -373,11 +284,13 @@ static ssize_t read_set_sirk(struct bt_conn *conn,
sirk, sizeof(*sirk));
}
#if defined(CONFIG_BT_CSIP_SET_MEMBER_NOTIFIABLE)
static void set_sirk_cfg_changed(const struct bt_gatt_attr *attr,
uint16_t value)
{
LOG_DBG("value 0x%04x", value);
}
#endif /* CONFIG_BT_CSIP_SET_MEMBER_NOTIFIABLE */
static ssize_t read_set_size(struct bt_conn *conn,
const struct bt_gatt_attr *attr,
@ -467,7 +380,7 @@ static uint8_t set_lock(struct bt_conn *conn,
* client writing the value, shall be notified
* (if subscribed)
*/
notify_clients(svc_inst, conn);
notify_clients(svc_inst, conn, FLAG_NOTIFY_LOCK);
if (svc_inst->cb != NULL && svc_inst->cb->lock_changed != NULL) {
bool locked = svc_inst->set_lock == BT_CSIP_LOCK_VALUE;
@ -534,7 +447,7 @@ static void set_lock_timer_handler(struct k_work *work)
LOG_DBG("Lock timeout, releasing");
svc_inst->set_lock = BT_CSIP_RELEASE_VALUE;
notify_clients(svc_inst, NULL);
notify_clients(svc_inst, NULL, FLAG_NOTIFY_LOCK);
if (svc_inst->cb != NULL && svc_inst->cb->lock_changed != NULL) {
bool locked = svc_inst->set_lock == BT_CSIP_LOCK_VALUE;
@ -562,9 +475,9 @@ static void csip_security_changed(struct bt_conn *conn, bt_security_t level,
client = &svc_inst->clients[i];
if (atomic_test_bit(client->flags, FLAG_SET_MEMBER_LOCK) &&
if (atomic_test_bit(client->flags, FLAG_NOTIFY_LOCK) &&
bt_addr_le_eq(bt_conn_get_dst(conn), &client->addr)) {
k_work_submit(&deferred_nfy_work);
notify_work_reschedule(K_NO_WAIT);
break;
}
}
@ -579,7 +492,7 @@ static void handle_csip_disconnect(struct bt_csip_set_member_svc_inst *svc_inst,
(void)memset(&svc_inst->lock_client_addr, 0,
sizeof(svc_inst->lock_client_addr));
svc_inst->set_lock = BT_CSIP_RELEASE_VALUE;
notify_clients(svc_inst, NULL);
notify_clients(svc_inst, NULL, FLAG_NOTIFY_LOCK);
if (svc_inst->cb != NULL && svc_inst->cb->lock_changed != NULL) {
bool locked = svc_inst->set_lock == BT_CSIP_LOCK_VALUE;
@ -640,7 +553,7 @@ static void handle_csip_auth_complete(struct bt_csip_set_member_svc_inst *svc_in
memcpy(&client->addr, bt_conn_get_dst(conn), sizeof(bt_addr_le_t));
/* Send out all pending notifications */
k_work_submit(&deferred_nfy_work);
notify_work_reschedule(K_NO_WAIT);
return;
}
}
@ -701,13 +614,20 @@ static struct bt_conn_auth_info_cb auth_callbacks = {
.bond_deleted = csip_bond_deleted
};
#if defined(CONFIG_BT_CSIP_SET_MEMBER_NOTIFIABLE)
#define BT_CSIS_CHR_SIRK(_csip) \
BT_AUDIO_CHRC(BT_UUID_CSIS_SET_SIRK, BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY, \
BT_GATT_PERM_READ_ENCRYPT, read_set_sirk, NULL, &_csip), \
BT_AUDIO_CCC(set_sirk_cfg_changed)
#else
#define BT_CSIS_CHR_SIRK(_csip) \
BT_AUDIO_CHRC(BT_UUID_CSIS_SET_SIRK, BT_GATT_CHRC_READ, BT_GATT_PERM_READ_ENCRYPT, \
read_set_sirk, NULL, &_csip)
#endif /* CONFIG_BT_CSIP_SET_MEMBER_NOTIFIABLE */
#define BT_CSIP_SERVICE_DEFINITION(_csip) {\
BT_GATT_PRIMARY_SERVICE(BT_UUID_CSIS), \
BT_AUDIO_CHRC(BT_UUID_CSIS_SET_SIRK, \
BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY, \
BT_GATT_PERM_READ_ENCRYPT, \
read_set_sirk, NULL, &_csip), \
BT_AUDIO_CCC(set_sirk_cfg_changed), \
BT_CSIS_CHR_SIRK(_csip), \
BT_AUDIO_CHRC(BT_UUID_CSIS_SET_SIZE, \
BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY, \
BT_GATT_PERM_READ_ENCRYPT, \
@ -805,6 +725,25 @@ static void remove_csis_char(const struct bt_uuid *uuid, struct bt_gatt_service
__ASSERT(false, "Failed to remove CSIS char %s", bt_uuid_str(uuid));
}
static void notify(struct bt_csip_set_member_svc_inst *svc_inst, struct bt_conn *conn,
const struct bt_uuid *uuid, const void *data, uint16_t len)
{
int err;
if (svc_inst->service_p == NULL) {
return;
}
err = bt_gatt_notify_uuid(conn, uuid, svc_inst->service_p->attrs, data, len);
if (err) {
if (err == -ENOTCONN) {
LOG_DBG("Notification error: ENOTCONN (%d)", err);
} else {
LOG_ERR("Notification error: %d", err);
}
}
}
static void notify_cb(struct bt_conn *conn, void *data)
{
struct bt_conn_info info;
@ -817,7 +756,7 @@ static void notify_cb(struct bt_conn *conn, void *data)
if (info.state != BT_CONN_STATE_CONNECTED) {
/* Not connected */
LOG_DBG("Not connected");
LOG_DBG("Not connected: %u", info.state);
return;
}
@ -825,22 +764,21 @@ static void notify_cb(struct bt_conn *conn, void *data)
struct bt_csip_set_member_svc_inst *svc_inst = &svc_insts[i];
struct csip_client *client = &svc_inst->clients[bt_conn_index(conn)];
if (atomic_test_bit(client->flags, FLAG_SET_MEMBER_LOCK)) {
err = notify_lock_value(svc_inst, conn);
if (!err) {
atomic_clear_bit(client->flags, FLAG_SET_MEMBER_LOCK);
if (atomic_test_and_clear_bit(client->flags, FLAG_NOTIFY_LOCK)) {
notify(svc_inst, conn, BT_UUID_CSIS_SET_LOCK, &svc_inst->set_lock,
sizeof(svc_inst->set_lock));
}
if (IS_ENABLED(CONFIG_BT_CSIP_SET_MEMBER_NOTIFIABLE) &&
atomic_test_and_clear_bit(client->flags, FLAG_NOTIFY_SIRK)) {
notify(svc_inst, conn, BT_UUID_CSIS_SET_SIRK, &svc_inst->set_sirk,
sizeof(svc_inst->set_sirk));
}
}
}
static void deferred_nfy_work_handler(struct k_work *work)
{
/* Check if we have unverified notifications in progress */
if (atomic_get(&notify_in_progress)) {
return;
}
bt_conn_foreach(BT_CONN_TYPE_LE, notify_cb, NULL);
}
@ -973,6 +911,44 @@ int bt_csip_set_member_unregister(struct bt_csip_set_member_svc_inst *svc_inst)
return 0;
}
int bt_csip_set_member_set_sirk(struct bt_csip_set_member_svc_inst *svc_inst,
const uint8_t sirk[BT_CSIP_SET_SIRK_SIZE])
{
CHECKIF(svc_inst == NULL) {
LOG_DBG("NULL svc_inst");
return -EINVAL;
}
CHECKIF(sirk == NULL) {
LOG_DBG("NULL SIRK");
return -EINVAL;
}
memcpy(svc_inst->set_sirk.value, sirk, BT_CSIP_SET_SIRK_SIZE);
notify_clients(svc_inst, NULL, FLAG_NOTIFY_SIRK);
return 0;
}
int bt_csip_set_member_get_sirk(struct bt_csip_set_member_svc_inst *svc_inst,
uint8_t sirk[BT_CSIP_SET_SIRK_SIZE])
{
CHECKIF(svc_inst == NULL) {
LOG_DBG("NULL svc_inst");
return -EINVAL;
}
CHECKIF(sirk == NULL) {
LOG_DBG("NULL SIRK");
return -EINVAL;
}
memcpy(sirk, svc_inst->set_sirk.value, BT_CSIP_SET_SIRK_SIZE);
return 0;
}
int bt_csip_set_member_lock(struct bt_csip_set_member_svc_inst *svc_inst,
bool lock, bool force)
{
@ -987,7 +963,7 @@ int bt_csip_set_member_lock(struct bt_csip_set_member_svc_inst *svc_inst,
if (!lock && force) {
svc_inst->set_lock = BT_CSIP_RELEASE_VALUE;
notify_clients(svc_inst, NULL);
notify_clients(svc_inst, NULL, FLAG_NOTIFY_LOCK);
if (svc_inst->cb != NULL && svc_inst->cb->lock_changed != NULL) {
svc_inst->cb->lock_changed(NULL, &svc_insts[0], false);

View file

@ -228,6 +228,59 @@ static int cmd_cap_acceptor_release(const struct shell *sh, size_t argc,
return 0;
}
static int cmd_cap_acceptor_set_sirk(const struct shell *sh, size_t argc, char *argv[])
{
uint8_t sirk[BT_CSIP_SET_SIRK_SIZE];
size_t len;
int err;
if (cap_csip_svc_inst == NULL) {
shell_error(sh, "CSIS not registered");
return -ENOEXEC;
}
len = hex2bin(argv[1], strlen(argv[1]), sirk, sizeof(sirk));
if (len != sizeof(sirk)) {
shell_error(sh, "Invalid SIRK Length: %zu", len);
return -ENOEXEC;
}
err = bt_csip_set_member_set_sirk(cap_csip_svc_inst, sirk);
if (err != 0) {
shell_error(sh, "Failed to set SIRK: %d", err);
return -ENOEXEC;
}
shell_print(sh, "SIRK updated");
return 0;
}
static int cmd_cap_acceptor_get_sirk(const struct shell *sh, size_t argc, char *argv[])
{
uint8_t sirk[BT_CSIP_SET_SIRK_SIZE];
int err;
if (cap_csip_svc_inst == NULL) {
shell_error(sh, "CSIS not registered");
return -ENOEXEC;
}
err = bt_csip_set_member_get_sirk(cap_csip_svc_inst, sirk);
if (err != 0) {
shell_error(sh, "Failed to get SIRK: %d", err);
return -ENOEXEC;
}
shell_print(sh, "Set SIRK");
shell_hexdump(sh, sirk, sizeof(sirk));
return 0;
}
static int cmd_cap_acceptor_set_sirk_rsp(const struct shell *sh, size_t argc,
char *argv[])
{
@ -268,6 +321,10 @@ SHELL_STATIC_SUBCMD_SET_CREATE(cap_acceptor_cmds,
SHELL_CMD_ARG(print_sirk, NULL,
"Print the currently used SIRK",
cmd_cap_acceptor_print_sirk, 1, 0),
SHELL_CMD_ARG(set_sirk, NULL, "Set the currently used SIRK <sirk>",
cmd_cap_acceptor_set_sirk, 2, 0),
SHELL_CMD_ARG(get_sirk, NULL, "Get the currently used SIRK", cmd_cap_acceptor_get_sirk,
1, 0),
SHELL_CMD_ARG(set_sirk_rsp, NULL,
"Set the response used in SIRK requests "
"<accept, accept_enc, reject, oob>",

View file

@ -164,6 +164,59 @@ static int cm_csip_set_member_print_sirk(const struct shell *sh, size_t argc,
return 0;
}
static int cmd_csip_set_member_set_sirk(const struct shell *sh, size_t argc, char *argv[])
{
uint8_t sirk[BT_CSIP_SET_SIRK_SIZE];
size_t len;
int err;
if (svc_inst == NULL) {
shell_error(sh, "CSIS not registered yet");
return -ENOEXEC;
}
len = hex2bin(argv[1], strlen(argv[1]), sirk, sizeof(sirk));
if (len != sizeof(sirk)) {
shell_error(sh, "Invalid SIRK Length: %zu", len);
return -ENOEXEC;
}
err = bt_csip_set_member_set_sirk(svc_inst, sirk);
if (err != 0) {
shell_error(sh, "Failed to set SIRK: %d", err);
return -ENOEXEC;
}
shell_print(sh, "SIRK updated");
return 0;
}
static int cmd_csip_set_member_get_sirk(const struct shell *sh, size_t argc, char *argv[])
{
uint8_t sirk[BT_CSIP_SET_SIRK_SIZE];
int err;
if (svc_inst == NULL) {
shell_error(sh, "CSIS not registered yet");
return -ENOEXEC;
}
err = bt_csip_set_member_get_sirk(svc_inst, sirk);
if (err != 0) {
shell_error(sh, "Failed to get SIRK: %d", err);
return -ENOEXEC;
}
shell_print(sh, "Set SIRK");
shell_hexdump(sh, sirk, sizeof(sirk));
return 0;
}
static int cm_csip_set_member_lock(const struct shell *sh, size_t argc, char *argv[])
{
int err;
@ -232,20 +285,20 @@ static int cm_csip_set_member(const struct shell *sh, size_t argc, char **argv)
return -ENOEXEC;
}
SHELL_STATIC_SUBCMD_SET_CREATE(csip_set_member_cmds,
SHELL_STATIC_SUBCMD_SET_CREATE(
csip_set_member_cmds,
SHELL_CMD_ARG(register, NULL,
"Initialize the service and register callbacks "
"[size <int>] [rank <int>] [not-lockable] [sirk <data>]",
cm_csip_set_member_register, 1, 4),
SHELL_CMD_ARG(lock, NULL,
"Lock the set",
cm_csip_set_member_lock, 1, 0),
SHELL_CMD_ARG(release, NULL,
"Release the set [force]",
cm_csip_set_member_release, 1, 1),
SHELL_CMD_ARG(print_sirk, NULL,
"Print the currently used SIRK",
SHELL_CMD_ARG(lock, NULL, "Lock the set", cm_csip_set_member_lock, 1, 0),
SHELL_CMD_ARG(release, NULL, "Release the set [force]", cm_csip_set_member_release, 1, 1),
SHELL_CMD_ARG(print_sirk, NULL, "Print the currently used SIRK",
cm_csip_set_member_print_sirk, 1, 0),
SHELL_CMD_ARG(set_sirk, NULL, "Set the currently used SIRK <sirk>",
cmd_csip_set_member_set_sirk, 2, 0),
SHELL_CMD_ARG(get_sirk, NULL, "Get the currently used SIRK", cmd_csip_set_member_get_sirk,
1, 0),
SHELL_CMD_ARG(set_sirk_rsp, NULL,
"Set the response used in SIRK requests "
"<accept, accept_enc, reject, oob>",

View file

@ -69,6 +69,7 @@ CONFIG_BT_MICP_MIC_CTLR_MAX_AICS_INST=2
# Coordinated Set Identification
CONFIG_BT_CSIP_SET_MEMBER=y
CONFIG_BT_CSIP_SET_MEMBER_TEST_SAMPLE_DATA=y
CONFIG_BT_CSIP_SET_MEMBER_NOTIFIABLE=y
CONFIG_BT_CSIP_SET_COORDINATOR=y
CONFIG_BT_CSIP_SET_COORDINATOR_TEST_SAMPLE_DATA=y

View file

@ -1,6 +1,6 @@
/*
* Copyright (c) 2019 Bose Corporation
* Copyright (c) 2020-2022 Nordic Semiconductor ASA
* Copyright (c) 2020-2024 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -22,7 +22,9 @@ static volatile bool set_unlocked;
static volatile bool ordered_access_locked;
static volatile bool ordered_access_unlocked;
static const struct bt_csip_set_coordinator_csis_inst *primary_inst;
CREATE_FLAG(flag_sirk_changed);
static uint8_t connected_member_count;
static uint8_t members_found;
static struct k_work_delayable discover_members_timer;
static bt_addr_le_t addr_found[CONFIG_BT_MAX_CONN];
@ -113,6 +115,13 @@ static void csip_lock_changed_cb(struct bt_csip_set_coordinator_csis_inst *inst,
printk("inst %p %s\n", inst, locked ? "locked" : "released");
}
static void csip_sirk_changed_cb(struct bt_csip_set_coordinator_csis_inst *inst)
{
printk("Inst %p SIRK changed\n", inst);
SET_FLAG(flag_sirk_changed);
}
static void csip_set_coordinator_ordered_access_cb(
const struct bt_csip_set_coordinator_set_info *set_info, int err,
bool locked, struct bt_csip_set_coordinator_set_member *member)
@ -133,7 +142,8 @@ static struct bt_csip_set_coordinator_cb cbs = {
.release_set = csip_set_coordinator_lock_release_cb,
.discover = csip_discover_cb,
.lock_changed = csip_lock_changed_cb,
.ordered_access = csip_set_coordinator_ordered_access_cb
.sirk_changed = csip_sirk_changed_cb,
.ordered_access = csip_set_coordinator_ordered_access_cb,
};
static bool csip_set_coordinator_oap_cb(const struct bt_csip_set_coordinator_set_info *set_info,
@ -263,12 +273,9 @@ static void discover_csis(struct bt_conn *conn)
WAIT_FOR_COND(discovered);
}
static void test_main(void)
static void init(void)
{
int err;
char addr[BT_ADDR_LE_STR_LEN];
const struct bt_csip_set_coordinator_set_member *locked_members[CONFIG_BT_MAX_CONN];
uint8_t connected_member_count = 0;
err = bt_enable(NULL);
if (err != 0) {
@ -282,21 +289,31 @@ static void test_main(void)
k_work_init_delayable(&discover_members_timer,
discover_members_timer_handler);
bt_le_scan_cb_register(&csip_set_coordinator_scan_callbacks);
}
static void connect_set(void)
{
char addr[BT_ADDR_LE_STR_LEN];
int err;
connected_member_count = 0U;
err = bt_le_scan_start(BT_LE_SCAN_PASSIVE, NULL);
if (err != 0) {
FAIL("Scanning failed to start (err %d)\n", err);
return;
}
printk("Scanning successfully started\n");
WAIT_FOR_COND(members_found == 1);
WAIT_FOR_COND(members_found == 1U);
printk("Stopping scan\n");
err = bt_le_scan_stop();
if (err != 0) {
FAIL("Could not stop scan");
return;
}
@ -305,6 +322,7 @@ static void test_main(void)
BT_LE_CONN_PARAM_DEFAULT, &conns[0]);
if (err != 0) {
FAIL("Failed to connect to %s: %d\n", err);
return;
}
printk("Connecting to %s\n", addr);
@ -318,6 +336,7 @@ static void test_main(void)
err = bt_le_scan_start(BT_LE_SCAN_ACTIVE, NULL);
if (err != 0) {
FAIL("Could not start scan: %d", err);
return;
}
@ -325,10 +344,11 @@ static void test_main(void)
BT_CSIP_SET_COORDINATOR_DISCOVER_TIMER_VALUE);
if (err < 0) { /* Can return 0, 1 and 2 for success */
FAIL("Could not schedule discover_members_timer %d", err);
return;
}
if (primary_inst->info.set_size > 0) {
if (primary_inst->info.set_size > 0U) {
WAIT_FOR_COND(members_found == primary_inst->info.set_size);
(void)k_work_cancel_delayable(&discover_members_timer);
@ -339,6 +359,7 @@ static void test_main(void)
err = bt_le_scan_stop();
if (err != 0) {
FAIL("Scanning failed to stop (err %d)\n", err);
return;
}
@ -347,12 +368,11 @@ static void test_main(void)
UNSET_FLAG(flag_connected);
printk("Connecting to member[%d] (%s)", i, addr);
err = bt_conn_le_create(&addr_found[i],
BT_CONN_LE_CREATE_CONN,
BT_LE_CONN_PARAM_DEFAULT,
&conns[i]);
err = bt_conn_le_create(&addr_found[i], BT_CONN_LE_CREATE_CONN,
BT_LE_CONN_PARAM_DEFAULT, &conns[i]);
if (err != 0) {
FAIL("Failed to connect to %s: %d\n", addr, err);
return;
}
@ -363,6 +383,33 @@ static void test_main(void)
printk("Doing discovery on member[%u]", i);
discover_csis(conns[i]);
}
}
static void disconnect_set(void)
{
for (uint8_t i = 0; i < connected_member_count; i++) {
char addr[BT_ADDR_LE_STR_LEN];
int err;
bt_addr_le_to_str(&addr_found[i], addr, sizeof(addr));
printk("Disconnecting member[%u] (%s)", i, addr);
err = bt_conn_disconnect(conns[i], BT_HCI_ERR_REMOTE_USER_TERM_CONN);
(void)memset(&set_members[i], 0, sizeof(set_members[i]));
if (err != 0) {
FAIL("Failed to do disconnect\n", err);
return;
}
}
}
static void test_main(void)
{
const struct bt_csip_set_coordinator_set_member *locked_members[CONFIG_BT_MAX_CONN];
int err;
init();
connect_set();
for (size_t i = 0; i < ARRAY_SIZE(locked_members); i++) {
locked_members[i] = set_members[i];
@ -436,16 +483,22 @@ static void test_main(void)
WAIT_FOR_COND(set_unlocked);
}
for (uint8_t i = 0; i < members_found; i++) {
printk("Disconnecting member[%u] (%s)", i, addr);
err = bt_conn_disconnect(conns[i],
BT_HCI_ERR_REMOTE_USER_TERM_CONN);
(void)memset(&set_members[i], 0, sizeof(set_members[i]));
if (err != 0) {
FAIL("Failed to do disconnect\n", err);
return;
}
}
disconnect_set();
PASS("All members disconnected\n");
}
static void test_new_sirk(void)
{
init();
connect_set();
backchannel_sync_send_all();
backchannel_sync_wait_all();
WAIT_FOR_FLAG(flag_sirk_changed);
disconnect_set();
PASS("All members disconnected\n");
}
@ -468,7 +521,6 @@ static void test_args(int argc, char *argv[])
}
static const struct bst_test_instance test_connect[] = {
{
.test_id = "csip_set_coordinator",
.test_post_init_f = test_init,
@ -476,8 +528,15 @@ static const struct bst_test_instance test_connect[] = {
.test_main_f = test_main,
.test_args_f = test_args,
},
BSTEST_END_MARKER};
{
.test_id = "csip_set_coordinator_new_sirk",
.test_post_init_f = test_init,
.test_tick_f = test_tick,
.test_main_f = test_new_sirk,
.test_args_f = test_args,
},
BSTEST_END_MARKER,
};
struct bst_test_list *test_csip_set_coordinator_install(struct bst_test_list *tests)
{

View file

@ -1,6 +1,6 @@
/*
* Copyright (c) 2019 Bose Corporation
* Copyright (c) 2020-2022 Nordic Semiconductor ASA
* Copyright (c) 2020-2024 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -76,6 +76,35 @@ static void bt_ready(int err)
}
}
static void test_set_sirk(void)
{
const uint8_t new_set_sirk[] = {0xff, 0xcc, 0x72, 0xdd, 0x86, 0x8c, 0xcd, 0xce,
0x22, 0xfd, 0xa1, 0x21, 0x09, 0x7d, 0x7d, 0x45};
uint8_t tmp_sirk[BT_CSIP_SET_SIRK_SIZE];
int err;
printk("Setting new SIRK\n");
err = bt_csip_set_member_set_sirk(svc_inst, new_set_sirk);
if (err != 0) {
FAIL("Failed to set SIRK: %d\n", err);
return;
}
printk("Getting new SIRK\n");
err = bt_csip_set_member_get_sirk(svc_inst, tmp_sirk);
if (err != 0) {
FAIL("Failed to get SIRK: %d\n", err);
return;
}
if (memcmp(new_set_sirk, tmp_sirk, BT_CSIP_SET_SIRK_SIZE) != 0) {
FAIL("The SIRK set and the SIRK set were different\n");
return;
}
printk("New SIRK correctly set and retrieved\n");
}
static void test_main(void)
{
int err;
@ -88,6 +117,18 @@ static void test_main(void)
}
WAIT_FOR_FLAG(flag_connected);
if (param.lockable) {
/* Waiting for lock */
WAIT_FOR_COND(g_locked);
/* Waiting for lock release */
WAIT_FOR_COND(!g_locked);
/* Waiting for lock */
WAIT_FOR_COND(g_locked);
/* Waiting for lock release */
WAIT_FOR_COND(!g_locked);
}
WAIT_FOR_UNSET_FLAG(flag_connected);
err = bt_csip_set_member_unregister(svc_inst);
@ -136,6 +177,36 @@ static void test_csip_enc(void)
test_main();
}
static void test_new_sirk(void)
{
int err;
err = bt_enable(bt_ready);
if (err != 0) {
FAIL("Bluetooth init failed (err %d)\n", err);
return;
}
WAIT_FOR_FLAG(flag_connected);
backchannel_sync_send_all();
backchannel_sync_wait_all();
test_set_sirk();
WAIT_FOR_UNSET_FLAG(flag_connected);
err = bt_csip_set_member_unregister(svc_inst);
if (err != 0) {
FAIL("Could not unregister CSIP (err %d)\n", err);
return;
}
svc_inst = NULL;
PASS("CSIP Set member passed: Client successfully disconnected\n");
}
static void test_args(int argc, char *argv[])
{
for (size_t argn = 0; argn < argc; argn++) {
@ -186,8 +257,14 @@ static const struct bst_test_instance test_connect[] = {
.test_main_f = test_csip_enc,
.test_args_f = test_args,
},
BSTEST_END_MARKER
{
.test_id = "csip_set_member_new_sirk",
.test_post_init_f = test_init,
.test_tick_f = test_tick,
.test_main_f = test_new_sirk,
.test_args_f = test_args,
},
BSTEST_END_MARKER,
};
struct bst_test_list *test_csip_set_member_install(struct bst_test_list *tests)

View file

@ -1,6 +1,6 @@
#!/usr/bin/env bash
#
# Copyright (c) 2023 Nordic Semiconductor ASA
# Copyright (c) 2023-2024 Nordic Semiconductor ASA
#
# SPDX-License-Identifier: Apache-2.0
@ -12,6 +12,8 @@ $dir_path/csip_encrypted_sirk.sh
$dir_path/csip_forced_release.sh
$dir_path/csip_new_sirk.sh
$dir_path/csip_no_lock.sh
$dir_path/csip_no_rank.sh

View file

@ -0,0 +1,39 @@
#!/usr/bin/env bash
#
# Copyright (c) 2024 Nordic Semiconductor ASA
#
# SPDX-License-Identifier: Apache-2.0
# Basic CSIP test. A set coordinator connects to multiple set members
# lock thems, unlocks them and disconnects.
source ${ZEPHYR_BASE}/tests/bsim/sh_common.source
VERBOSITY_LEVEL=2
EXECUTE_TIMEOUT=30
cd ${BSIM_OUT_PATH}/bin
SIMULATION_ID="csip_new_sirk"
Execute ./bs_${BOARD}_tests_bsim_bluetooth_audio_prj_conf \
-v=${VERBOSITY_LEVEL} -s=${SIMULATION_ID} -d=0 -testid=csip_set_coordinator_new_sirk \
-RealEncryption=1 -rs=1 -D=4
Execute ./bs_${BOARD}_tests_bsim_bluetooth_audio_prj_conf \
-v=${VERBOSITY_LEVEL} -s=${SIMULATION_ID} -d=1 -testid=csip_set_member_new_sirk \
-RealEncryption=1 -rs=2 -D=4 -argstest rank 1
Execute ./bs_${BOARD}_tests_bsim_bluetooth_audio_prj_conf \
-v=${VERBOSITY_LEVEL} -s=${SIMULATION_ID} -d=2 -testid=csip_set_member_new_sirk \
-RealEncryption=1 -rs=3 -D=4 -argstest rank 2
Execute ./bs_${BOARD}_tests_bsim_bluetooth_audio_prj_conf \
-v=${VERBOSITY_LEVEL} -s=${SIMULATION_ID} -d=3 -testid=csip_set_member_new_sirk \
-RealEncryption=1 -rs=4 -D=4 -argstest rank 3
# Simulation time should be larger than the WAIT_TIME in common.h
Execute ./bs_2G4_phy_v1 -v=${VERBOSITY_LEVEL} -s=${SIMULATION_ID} \
-D=4 -sim_length=60e6 $@
wait_for_background_jobs