If, while storing app or net key, or changing cdb, we receive another mesh message that affects setting of the same key that the mesh currently stores, the new change won't be stored. The settings subsystem API has rescheduling point inside. The mesh settings are stored in the system workqueue and by default mesh messages are processed from BT RX thread which has higher priority than the system workqueue. When the case above happens, a new change will be written to the same cache entry. But this cache entry will be invalidated after leaving settings API, which in turns will invalidate the new change: - Receive Config AppKey Add message cfg_srv.c -> app_keys.c: bt_mesh_app_key_add() app_keys.c: update_app_key_settings() app_keys.c: bt_mesh_settings_store_schedule() - store_pending() in settings.c is scheduled settings.c -> app_keys.c: bt_mesh_app_key_pending_store() app_keys.c: store_app_key() called to store new app key app_keys.c: settings_save_one() calls flash driver and sleeps - Receive Config AppKey Delete message while in sleep, which wakes up BT RX thread before returning to the system workqueue cfg_srv.c -> app_keys.c: bt_mesh_app_key_del() app_keys.c: update_app_key_settings() app_keys.c: app_key_update_find() finds entry and returns it app_keys.c: update->clear = 1 app_keys.c: bt_mesh_settings_store_schedule() - returning back to bt_mesh_app_key_pending_store() from settings_save_one() app_keys.c: update->valid = 0 - the key won't be deleted next time store_pending() is scheduled. This change moves entry invalidation before calling settings API so that after returning from settings API, a new change won't be unintentionally invalidated. Signed-off-by: Pavel Vasilyev <pavel.vasilyev@nordicsemi.no>
1116 lines
24 KiB
C
1116 lines
24 KiB
C
/*
|
|
* Copyright (c) 2019 Tobias Svehagen
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#include <zephyr/kernel.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
|
|
#include <zephyr/settings/settings.h>
|
|
|
|
#include <zephyr/bluetooth/mesh.h>
|
|
|
|
#include "common/bt_str.h"
|
|
|
|
#include "cdb.h"
|
|
#include "mesh.h"
|
|
#include "net.h"
|
|
#include "rpl.h"
|
|
#include "settings.h"
|
|
|
|
#define LOG_LEVEL CONFIG_BT_MESH_CDB_LOG_LEVEL
|
|
#include <zephyr/logging/log.h>
|
|
LOG_MODULE_REGISTER(bt_mesh_cdb);
|
|
|
|
/* Tracking of what storage changes are pending for App and Net Keys. We
|
|
* track this in a separate array here instead of within the respective
|
|
* bt_mesh_app_key and bt_mesh_subnet structs themselves, since once a key
|
|
* gets deleted its struct becomes invalid and may be reused for other keys.
|
|
*/
|
|
struct key_update {
|
|
uint16_t key_idx:12, /* AppKey or NetKey Index */
|
|
valid:1, /* 1 if this entry is valid, 0 if not */
|
|
app_key:1, /* 1 if this is an AppKey, 0 if a NetKey */
|
|
clear:1; /* 1 if key needs clearing, 0 if storing */
|
|
};
|
|
|
|
/* Tracking of what storage changes are pending for node settings. */
|
|
struct node_update {
|
|
uint16_t addr;
|
|
bool clear;
|
|
};
|
|
|
|
/* Node information for persistent storage. */
|
|
struct node_val {
|
|
uint16_t net_idx;
|
|
uint8_t num_elem;
|
|
uint8_t flags;
|
|
#define F_NODE_CONFIGURED 0x01
|
|
uint8_t uuid[16];
|
|
uint8_t dev_key[16];
|
|
} __packed;
|
|
|
|
/* NetKey storage information */
|
|
struct net_key_val {
|
|
uint8_t kr_flag:1,
|
|
kr_phase:7;
|
|
uint8_t val[2][16];
|
|
} __packed;
|
|
|
|
/* AppKey information for persistent storage. */
|
|
struct app_key_val {
|
|
uint16_t net_idx;
|
|
bool updated;
|
|
uint8_t val[2][16];
|
|
} __packed;
|
|
|
|
/* Network information for persistent storage. */
|
|
struct net_val {
|
|
struct __packed {
|
|
uint32_t index;
|
|
bool update;
|
|
} iv;
|
|
uint16_t lowest_avail_addr;
|
|
} __packed;
|
|
|
|
static struct node_update cdb_node_updates[CONFIG_BT_MESH_CDB_NODE_COUNT];
|
|
static struct key_update cdb_key_updates[CONFIG_BT_MESH_CDB_SUBNET_COUNT +
|
|
CONFIG_BT_MESH_CDB_APP_KEY_COUNT];
|
|
|
|
struct bt_mesh_cdb bt_mesh_cdb = {
|
|
.nodes = {
|
|
[0 ... (CONFIG_BT_MESH_CDB_NODE_COUNT - 1)] = {
|
|
.addr = BT_MESH_ADDR_UNASSIGNED,
|
|
}
|
|
},
|
|
.subnets = {
|
|
[0 ... (CONFIG_BT_MESH_CDB_SUBNET_COUNT - 1)] = {
|
|
.net_idx = BT_MESH_KEY_UNUSED,
|
|
}
|
|
},
|
|
.app_keys = {
|
|
[0 ... (CONFIG_BT_MESH_CDB_APP_KEY_COUNT - 1)] = {
|
|
.net_idx = BT_MESH_KEY_UNUSED,
|
|
}
|
|
},
|
|
};
|
|
|
|
/*
|
|
* Check if an address range from addr_start for addr_start + num_elem - 1 is
|
|
* free for use. When a conflict is found, next will be set to the next address
|
|
* available after the conflicting range and -EAGAIN will be returned.
|
|
*/
|
|
static int addr_is_free(uint16_t addr_start, uint8_t num_elem, uint16_t *next)
|
|
{
|
|
uint16_t addr_end = addr_start + num_elem - 1;
|
|
uint16_t other_start, other_end;
|
|
int i;
|
|
|
|
if (!BT_MESH_ADDR_IS_UNICAST(addr_start) ||
|
|
!BT_MESH_ADDR_IS_UNICAST(addr_end) ||
|
|
num_elem == 0) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
for (i = 0; i < ARRAY_SIZE(bt_mesh_cdb.nodes); i++) {
|
|
struct bt_mesh_cdb_node *node = &bt_mesh_cdb.nodes[i];
|
|
|
|
if (node->addr == BT_MESH_ADDR_UNASSIGNED) {
|
|
continue;
|
|
}
|
|
|
|
other_start = node->addr;
|
|
other_end = other_start + node->num_elem - 1;
|
|
|
|
if (!(addr_end < other_start || addr_start > other_end)) {
|
|
if (next) {
|
|
*next = other_end + 1;
|
|
}
|
|
|
|
return -EAGAIN;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Find the lowest possible starting address that can fit num_elem elements. If
|
|
* a free address range cannot be found, BT_MESH_ADDR_UNASSIGNED will be
|
|
* returned. Otherwise the first address in the range is returned.
|
|
*
|
|
* NOTE: This is quite an ineffective algorithm as it might need to look
|
|
* through the array of nodes N+2 times. A more effective algorithm
|
|
* could be used if the nodes were stored in a sorted list.
|
|
*/
|
|
static uint16_t find_lowest_free_addr(uint8_t num_elem)
|
|
{
|
|
uint16_t addr = bt_mesh_cdb.lowest_avail_addr;
|
|
uint16_t next;
|
|
int err, i;
|
|
|
|
/*
|
|
* It takes a maximum of node count + 2 to find a free address if there
|
|
* is any. +1 for our own address and +1 for making sure that the
|
|
* address range is valid.
|
|
*/
|
|
for (i = 0; i < ARRAY_SIZE(bt_mesh_cdb.nodes) + 2; ++i) {
|
|
err = addr_is_free(addr, num_elem, &next);
|
|
if (err == 0) {
|
|
break;
|
|
} else if (err != -EAGAIN) {
|
|
addr = BT_MESH_ADDR_UNASSIGNED;
|
|
break;
|
|
}
|
|
|
|
addr = next;
|
|
}
|
|
|
|
return addr;
|
|
}
|
|
|
|
static int cdb_net_set(const char *name, size_t len_rd,
|
|
settings_read_cb read_cb, void *cb_arg)
|
|
{
|
|
struct net_val net;
|
|
int err;
|
|
|
|
if (len_rd == 0) {
|
|
LOG_DBG("val (null)");
|
|
return 0;
|
|
}
|
|
|
|
err = bt_mesh_settings_set(read_cb, cb_arg, &net, sizeof(net));
|
|
if (err) {
|
|
/* Try to recover previous version of the network settings without address. */
|
|
err = bt_mesh_settings_set(read_cb, cb_arg, &net, sizeof(net.iv));
|
|
if (err) {
|
|
LOG_ERR("Failed to set \'cdb_net\'");
|
|
return err;
|
|
}
|
|
|
|
net.lowest_avail_addr = 1;
|
|
}
|
|
|
|
bt_mesh_cdb.iv_index = net.iv.index;
|
|
|
|
if (net.iv.update) {
|
|
atomic_set_bit(bt_mesh_cdb.flags, BT_MESH_CDB_IVU_IN_PROGRESS);
|
|
}
|
|
|
|
bt_mesh_cdb.lowest_avail_addr = net.lowest_avail_addr;
|
|
|
|
atomic_set_bit(bt_mesh_cdb.flags, BT_MESH_CDB_VALID);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int cdb_node_set(const char *name, size_t len_rd,
|
|
settings_read_cb read_cb, void *cb_arg)
|
|
{
|
|
struct bt_mesh_cdb_node *node;
|
|
struct node_val val;
|
|
uint16_t addr;
|
|
int err;
|
|
|
|
if (!name) {
|
|
LOG_ERR("Insufficient number of arguments");
|
|
return -ENOENT;
|
|
}
|
|
|
|
addr = strtol(name, NULL, 16);
|
|
|
|
if (len_rd == 0) {
|
|
LOG_DBG("val (null)");
|
|
LOG_DBG("Deleting node 0x%04x", addr);
|
|
|
|
node = bt_mesh_cdb_node_get(addr);
|
|
if (node) {
|
|
bt_mesh_cdb_node_del(node, false);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
err = bt_mesh_settings_set(read_cb, cb_arg, &val, sizeof(val));
|
|
if (err) {
|
|
LOG_ERR("Failed to set \'node\'");
|
|
return err;
|
|
}
|
|
|
|
node = bt_mesh_cdb_node_get(addr);
|
|
if (!node) {
|
|
node = bt_mesh_cdb_node_alloc(val.uuid, addr, val.num_elem,
|
|
val.net_idx);
|
|
}
|
|
|
|
if (!node) {
|
|
LOG_ERR("No space for a new node");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
if (val.flags & F_NODE_CONFIGURED) {
|
|
atomic_set_bit(node->flags, BT_MESH_CDB_NODE_CONFIGURED);
|
|
}
|
|
|
|
memcpy(node->uuid, val.uuid, 16);
|
|
memcpy(node->dev_key, val.dev_key, 16);
|
|
|
|
LOG_DBG("Node 0x%04x recovered from storage", addr);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int cdb_subnet_set(const char *name, size_t len_rd,
|
|
settings_read_cb read_cb, void *cb_arg)
|
|
{
|
|
struct bt_mesh_cdb_subnet *sub;
|
|
struct net_key_val key;
|
|
uint16_t net_idx;
|
|
int err;
|
|
|
|
if (!name) {
|
|
LOG_ERR("Insufficient number of arguments");
|
|
return -ENOENT;
|
|
}
|
|
|
|
net_idx = strtol(name, NULL, 16);
|
|
sub = bt_mesh_cdb_subnet_get(net_idx);
|
|
|
|
if (len_rd == 0) {
|
|
LOG_DBG("val (null)");
|
|
if (!sub) {
|
|
LOG_ERR("No subnet with NetKeyIndex 0x%03x", net_idx);
|
|
return -ENOENT;
|
|
}
|
|
|
|
LOG_DBG("Deleting NetKeyIndex 0x%03x", net_idx);
|
|
bt_mesh_cdb_subnet_del(sub, false);
|
|
return 0;
|
|
}
|
|
|
|
err = bt_mesh_settings_set(read_cb, cb_arg, &key, sizeof(key));
|
|
if (err) {
|
|
LOG_ERR("Failed to set \'net-key\'");
|
|
return err;
|
|
}
|
|
|
|
if (sub) {
|
|
LOG_DBG("Updating existing NetKeyIndex 0x%03x", net_idx);
|
|
|
|
sub->kr_phase = key.kr_phase;
|
|
memcpy(sub->keys[0].net_key, &key.val[0], 16);
|
|
memcpy(sub->keys[1].net_key, &key.val[1], 16);
|
|
|
|
return 0;
|
|
}
|
|
|
|
sub = bt_mesh_cdb_subnet_alloc(net_idx);
|
|
if (!sub) {
|
|
LOG_ERR("No space to allocate a new subnet");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
sub->kr_phase = key.kr_phase;
|
|
memcpy(sub->keys[0].net_key, &key.val[0], 16);
|
|
memcpy(sub->keys[1].net_key, &key.val[1], 16);
|
|
|
|
LOG_DBG("NetKeyIndex 0x%03x recovered from storage", net_idx);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int cdb_app_key_set(const char *name, size_t len_rd,
|
|
settings_read_cb read_cb, void *cb_arg)
|
|
{
|
|
struct bt_mesh_cdb_app_key *app;
|
|
struct app_key_val key;
|
|
uint16_t app_idx;
|
|
int err;
|
|
|
|
if (!name) {
|
|
LOG_ERR("Insufficient number of arguments");
|
|
return -ENOENT;
|
|
}
|
|
|
|
app_idx = strtol(name, NULL, 16);
|
|
|
|
if (len_rd == 0) {
|
|
LOG_DBG("val (null)");
|
|
LOG_DBG("Deleting AppKeyIndex 0x%03x", app_idx);
|
|
|
|
app = bt_mesh_cdb_app_key_get(app_idx);
|
|
if (app) {
|
|
bt_mesh_cdb_app_key_del(app, false);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
err = bt_mesh_settings_set(read_cb, cb_arg, &key, sizeof(key));
|
|
if (err) {
|
|
LOG_ERR("Failed to set \'app-key\'");
|
|
return err;
|
|
}
|
|
|
|
app = bt_mesh_cdb_app_key_get(app_idx);
|
|
if (!app) {
|
|
app = bt_mesh_cdb_app_key_alloc(key.net_idx, app_idx);
|
|
}
|
|
|
|
if (!app) {
|
|
LOG_ERR("No space for a new app key");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
memcpy(app->keys[0].app_key, key.val[0], 16);
|
|
memcpy(app->keys[1].app_key, key.val[1], 16);
|
|
|
|
LOG_DBG("AppKeyIndex 0x%03x recovered from storage", app_idx);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int cdb_set(const char *name, size_t len_rd,
|
|
settings_read_cb read_cb, void *cb_arg)
|
|
{
|
|
int len;
|
|
const char *next;
|
|
|
|
if (!name) {
|
|
LOG_ERR("Insufficient number of arguments");
|
|
return -ENOENT;
|
|
}
|
|
|
|
if (!strcmp(name, "Net")) {
|
|
return cdb_net_set(name, len_rd, read_cb, cb_arg);
|
|
}
|
|
|
|
|
|
len = settings_name_next(name, &next);
|
|
|
|
if (!next) {
|
|
LOG_ERR("Insufficient number of arguments");
|
|
return -ENOENT;
|
|
}
|
|
|
|
if (!strncmp(name, "Node", len)) {
|
|
return cdb_node_set(next, len_rd, read_cb, cb_arg);
|
|
}
|
|
|
|
if (!strncmp(name, "Subnet", len)) {
|
|
return cdb_subnet_set(next, len_rd, read_cb, cb_arg);
|
|
}
|
|
|
|
if (!strncmp(name, "AppKey", len)) {
|
|
return cdb_app_key_set(next, len_rd, read_cb, cb_arg);
|
|
}
|
|
|
|
LOG_WRN("Unknown module key %s", name);
|
|
return -ENOENT;
|
|
}
|
|
|
|
BT_MESH_SETTINGS_DEFINE(cdb, "cdb", cdb_set);
|
|
|
|
static void store_cdb_node(const struct bt_mesh_cdb_node *node)
|
|
{
|
|
struct node_val val;
|
|
char path[30];
|
|
int err;
|
|
|
|
val.net_idx = node->net_idx;
|
|
val.num_elem = node->num_elem;
|
|
val.flags = 0;
|
|
|
|
if (atomic_test_bit(node->flags, BT_MESH_CDB_NODE_CONFIGURED)) {
|
|
val.flags |= F_NODE_CONFIGURED;
|
|
}
|
|
|
|
memcpy(val.uuid, node->uuid, 16);
|
|
memcpy(val.dev_key, node->dev_key, 16);
|
|
|
|
snprintk(path, sizeof(path), "bt/mesh/cdb/Node/%x", node->addr);
|
|
|
|
err = settings_save_one(path, &val, sizeof(val));
|
|
if (err) {
|
|
LOG_ERR("Failed to store Node %s value", path);
|
|
} else {
|
|
LOG_DBG("Stored Node %s value", path);
|
|
}
|
|
}
|
|
|
|
static void clear_cdb_node(uint16_t addr)
|
|
{
|
|
char path[30];
|
|
int err;
|
|
|
|
LOG_DBG("Node 0x%04x", addr);
|
|
|
|
snprintk(path, sizeof(path), "bt/mesh/cdb/Node/%x", addr);
|
|
err = settings_delete(path);
|
|
if (err) {
|
|
LOG_ERR("Failed to clear Node 0x%04x", addr);
|
|
} else {
|
|
LOG_DBG("Cleared Node 0x%04x", addr);
|
|
}
|
|
}
|
|
|
|
static void store_cdb_subnet(const struct bt_mesh_cdb_subnet *sub)
|
|
{
|
|
struct net_key_val key;
|
|
char path[30];
|
|
int err;
|
|
|
|
LOG_DBG("NetKeyIndex 0x%03x NetKey %s", sub->net_idx, bt_hex(sub->keys[0].net_key, 16));
|
|
|
|
memcpy(&key.val[0], sub->keys[0].net_key, 16);
|
|
memcpy(&key.val[1], sub->keys[1].net_key, 16);
|
|
key.kr_flag = 0U; /* Deprecated */
|
|
key.kr_phase = sub->kr_phase;
|
|
|
|
snprintk(path, sizeof(path), "bt/mesh/cdb/Subnet/%x", sub->net_idx);
|
|
|
|
err = settings_save_one(path, &key, sizeof(key));
|
|
if (err) {
|
|
LOG_ERR("Failed to store Subnet value");
|
|
} else {
|
|
LOG_DBG("Stored Subnet value");
|
|
}
|
|
}
|
|
|
|
static void clear_cdb_subnet(uint16_t net_idx)
|
|
{
|
|
char path[30];
|
|
int err;
|
|
|
|
LOG_DBG("NetKeyIndex 0x%03x", net_idx);
|
|
|
|
snprintk(path, sizeof(path), "bt/mesh/cdb/Subnet/%x", net_idx);
|
|
err = settings_delete(path);
|
|
if (err) {
|
|
LOG_ERR("Failed to clear NetKeyIndex 0x%03x", net_idx);
|
|
} else {
|
|
LOG_DBG("Cleared NetKeyIndex 0x%03x", net_idx);
|
|
}
|
|
}
|
|
|
|
static void store_cdb_app_key(const struct bt_mesh_cdb_app_key *app)
|
|
{
|
|
struct app_key_val key;
|
|
char path[30];
|
|
int err;
|
|
|
|
key.net_idx = app->net_idx;
|
|
key.updated = false;
|
|
memcpy(key.val[0], app->keys[0].app_key, 16);
|
|
memcpy(key.val[1], app->keys[1].app_key, 16);
|
|
|
|
snprintk(path, sizeof(path), "bt/mesh/cdb/AppKey/%x", app->app_idx);
|
|
|
|
err = settings_save_one(path, &key, sizeof(key));
|
|
if (err) {
|
|
LOG_ERR("Failed to store AppKey %s value", path);
|
|
} else {
|
|
LOG_DBG("Stored AppKey %s value", path);
|
|
}
|
|
}
|
|
|
|
static void clear_cdb_app_key(uint16_t app_idx)
|
|
{
|
|
char path[30];
|
|
int err;
|
|
|
|
snprintk(path, sizeof(path), "bt/mesh/cdb/AppKey/%x", app_idx);
|
|
err = settings_delete(path);
|
|
if (err) {
|
|
LOG_ERR("Failed to clear AppKeyIndex 0x%03x", app_idx);
|
|
} else {
|
|
LOG_DBG("Cleared AppKeyIndex 0x%03x", app_idx);
|
|
}
|
|
}
|
|
|
|
static void schedule_cdb_store(int flag)
|
|
{
|
|
atomic_set_bit(bt_mesh_cdb.flags, flag);
|
|
bt_mesh_settings_store_schedule(BT_MESH_SETTINGS_CDB_PENDING);
|
|
}
|
|
|
|
static void update_cdb_net_settings(void)
|
|
{
|
|
schedule_cdb_store(BT_MESH_CDB_SUBNET_PENDING);
|
|
}
|
|
|
|
static struct node_update *cdb_node_update_find(uint16_t addr,
|
|
struct node_update **free_slot)
|
|
{
|
|
struct node_update *match;
|
|
int i;
|
|
|
|
match = NULL;
|
|
*free_slot = NULL;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(cdb_node_updates); i++) {
|
|
struct node_update *update = &cdb_node_updates[i];
|
|
|
|
if (update->addr == BT_MESH_ADDR_UNASSIGNED) {
|
|
*free_slot = update;
|
|
continue;
|
|
}
|
|
|
|
if (update->addr == addr) {
|
|
match = update;
|
|
}
|
|
}
|
|
|
|
return match;
|
|
}
|
|
|
|
static void update_cdb_node_settings(const struct bt_mesh_cdb_node *node,
|
|
bool store)
|
|
{
|
|
struct node_update *update, *free_slot;
|
|
|
|
LOG_DBG("Node 0x%04x", node->addr);
|
|
|
|
update = cdb_node_update_find(node->addr, &free_slot);
|
|
if (update) {
|
|
update->clear = !store;
|
|
schedule_cdb_store(BT_MESH_CDB_NODES_PENDING);
|
|
return;
|
|
}
|
|
|
|
if (!free_slot) {
|
|
if (store) {
|
|
store_cdb_node(node);
|
|
} else {
|
|
clear_cdb_node(node->addr);
|
|
}
|
|
return;
|
|
}
|
|
|
|
free_slot->addr = node->addr;
|
|
free_slot->clear = !store;
|
|
|
|
schedule_cdb_store(BT_MESH_CDB_NODES_PENDING);
|
|
}
|
|
|
|
static struct key_update *cdb_key_update_find(bool app_key, uint16_t key_idx,
|
|
struct key_update **free_slot)
|
|
{
|
|
struct key_update *match;
|
|
int i;
|
|
|
|
match = NULL;
|
|
*free_slot = NULL;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(cdb_key_updates); i++) {
|
|
struct key_update *update = &cdb_key_updates[i];
|
|
|
|
if (!update->valid) {
|
|
*free_slot = update;
|
|
continue;
|
|
}
|
|
|
|
if (update->app_key != app_key) {
|
|
continue;
|
|
}
|
|
|
|
if (update->key_idx == key_idx) {
|
|
match = update;
|
|
}
|
|
}
|
|
|
|
return match;
|
|
}
|
|
|
|
static void update_cdb_subnet_settings(const struct bt_mesh_cdb_subnet *sub,
|
|
bool store)
|
|
{
|
|
struct key_update *update, *free_slot;
|
|
uint8_t clear = store ? 0U : 1U;
|
|
|
|
LOG_DBG("NetKeyIndex 0x%03x", sub->net_idx);
|
|
|
|
update = cdb_key_update_find(false, sub->net_idx, &free_slot);
|
|
if (update) {
|
|
update->clear = clear;
|
|
schedule_cdb_store(BT_MESH_CDB_KEYS_PENDING);
|
|
return;
|
|
}
|
|
|
|
if (!free_slot) {
|
|
if (store) {
|
|
store_cdb_subnet(sub);
|
|
} else {
|
|
clear_cdb_subnet(sub->net_idx);
|
|
}
|
|
return;
|
|
}
|
|
|
|
free_slot->valid = 1U;
|
|
free_slot->key_idx = sub->net_idx;
|
|
free_slot->app_key = 0U;
|
|
free_slot->clear = clear;
|
|
|
|
schedule_cdb_store(BT_MESH_CDB_KEYS_PENDING);
|
|
}
|
|
|
|
static void update_cdb_app_key_settings(const struct bt_mesh_cdb_app_key *key,
|
|
bool store)
|
|
{
|
|
struct key_update *update, *free_slot;
|
|
uint8_t clear = store ? 0U : 1U;
|
|
|
|
LOG_DBG("AppKeyIndex 0x%03x", key->app_idx);
|
|
|
|
update = cdb_key_update_find(true, key->app_idx, &free_slot);
|
|
if (update) {
|
|
update->clear = clear;
|
|
schedule_cdb_store(BT_MESH_CDB_KEYS_PENDING);
|
|
return;
|
|
}
|
|
|
|
if (!free_slot) {
|
|
if (store) {
|
|
store_cdb_app_key(key);
|
|
} else {
|
|
clear_cdb_app_key(key->app_idx);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
free_slot->valid = 1U;
|
|
free_slot->key_idx = key->app_idx;
|
|
free_slot->app_key = 1U;
|
|
free_slot->clear = clear;
|
|
|
|
schedule_cdb_store(BT_MESH_CDB_KEYS_PENDING);
|
|
}
|
|
|
|
int bt_mesh_cdb_create(const uint8_t key[16])
|
|
{
|
|
struct bt_mesh_cdb_subnet *sub;
|
|
|
|
if (atomic_test_and_set_bit(bt_mesh_cdb.flags,
|
|
BT_MESH_CDB_VALID)) {
|
|
return -EALREADY;
|
|
}
|
|
|
|
sub = bt_mesh_cdb_subnet_alloc(BT_MESH_KEY_PRIMARY);
|
|
if (sub == NULL) {
|
|
return -ENOMEM;
|
|
}
|
|
|
|
memcpy(sub->keys[0].net_key, key, 16);
|
|
bt_mesh_cdb.iv_index = 0;
|
|
bt_mesh_cdb.lowest_avail_addr = 1;
|
|
|
|
if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
|
|
update_cdb_net_settings();
|
|
update_cdb_subnet_settings(sub, true);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void bt_mesh_cdb_clear(void)
|
|
{
|
|
int i;
|
|
|
|
atomic_clear_bit(bt_mesh_cdb.flags, BT_MESH_CDB_VALID);
|
|
|
|
for (i = 0; i < ARRAY_SIZE(bt_mesh_cdb.nodes); ++i) {
|
|
if (bt_mesh_cdb.nodes[i].addr != BT_MESH_ADDR_UNASSIGNED) {
|
|
bt_mesh_cdb_node_del(&bt_mesh_cdb.nodes[i], true);
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < ARRAY_SIZE(bt_mesh_cdb.subnets); ++i) {
|
|
if (bt_mesh_cdb.subnets[i].net_idx != BT_MESH_KEY_UNUSED) {
|
|
bt_mesh_cdb_subnet_del(&bt_mesh_cdb.subnets[i], true);
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < ARRAY_SIZE(bt_mesh_cdb.app_keys); ++i) {
|
|
if (bt_mesh_cdb.app_keys[i].net_idx != BT_MESH_KEY_UNUSED) {
|
|
bt_mesh_cdb_app_key_del(&bt_mesh_cdb.app_keys[i], true);
|
|
}
|
|
}
|
|
|
|
if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
|
|
update_cdb_net_settings();
|
|
bt_mesh_settings_store_pending();
|
|
}
|
|
}
|
|
|
|
void bt_mesh_cdb_iv_update(uint32_t iv_index, bool iv_update)
|
|
{
|
|
LOG_DBG("Updating IV index to %d\n", iv_index);
|
|
|
|
/* Reset the last deleted addr when IV Index is updated or recovered. */
|
|
if (!iv_update || iv_index > bt_mesh_cdb.iv_index + 1) {
|
|
bt_mesh_cdb.lowest_avail_addr = 1;
|
|
}
|
|
|
|
bt_mesh_cdb.iv_index = iv_index;
|
|
|
|
atomic_set_bit_to(bt_mesh_cdb.flags, BT_MESH_CDB_IVU_IN_PROGRESS,
|
|
iv_update);
|
|
|
|
if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
|
|
update_cdb_net_settings();
|
|
}
|
|
}
|
|
|
|
struct bt_mesh_cdb_subnet *bt_mesh_cdb_subnet_alloc(uint16_t net_idx)
|
|
{
|
|
struct bt_mesh_cdb_subnet *sub;
|
|
int i;
|
|
|
|
if (bt_mesh_cdb_subnet_get(net_idx) != NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
for (i = 0; i < ARRAY_SIZE(bt_mesh_cdb.subnets); ++i) {
|
|
sub = &bt_mesh_cdb.subnets[i];
|
|
|
|
if (sub->net_idx != BT_MESH_KEY_UNUSED) {
|
|
continue;
|
|
}
|
|
|
|
sub->net_idx = net_idx;
|
|
|
|
return sub;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void bt_mesh_cdb_subnet_del(struct bt_mesh_cdb_subnet *sub, bool store)
|
|
{
|
|
LOG_DBG("NetIdx 0x%03x store %u", sub->net_idx, store);
|
|
|
|
if (IS_ENABLED(CONFIG_BT_SETTINGS) && store) {
|
|
update_cdb_subnet_settings(sub, false);
|
|
}
|
|
|
|
sub->net_idx = BT_MESH_KEY_UNUSED;
|
|
memset(sub->keys, 0, sizeof(sub->keys));
|
|
}
|
|
|
|
struct bt_mesh_cdb_subnet *bt_mesh_cdb_subnet_get(uint16_t net_idx)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(bt_mesh_cdb.subnets); ++i) {
|
|
if (bt_mesh_cdb.subnets[i].net_idx == net_idx) {
|
|
return &bt_mesh_cdb.subnets[i];
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void bt_mesh_cdb_subnet_store(const struct bt_mesh_cdb_subnet *sub)
|
|
{
|
|
if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
|
|
update_cdb_subnet_settings(sub, true);
|
|
}
|
|
}
|
|
|
|
uint8_t bt_mesh_cdb_subnet_flags(const struct bt_mesh_cdb_subnet *sub)
|
|
{
|
|
uint8_t flags = 0x00;
|
|
|
|
if (sub && SUBNET_KEY_TX_IDX(sub)) {
|
|
flags |= BT_MESH_NET_FLAG_KR;
|
|
}
|
|
|
|
if (atomic_test_bit(bt_mesh_cdb.flags, BT_MESH_CDB_IVU_IN_PROGRESS)) {
|
|
flags |= BT_MESH_NET_FLAG_IVU;
|
|
}
|
|
|
|
return flags;
|
|
}
|
|
|
|
struct bt_mesh_cdb_node *bt_mesh_cdb_node_alloc(const uint8_t uuid[16], uint16_t addr,
|
|
uint8_t num_elem, uint16_t net_idx)
|
|
{
|
|
int i;
|
|
|
|
if (addr == BT_MESH_ADDR_UNASSIGNED) {
|
|
addr = find_lowest_free_addr(num_elem);
|
|
if (addr == BT_MESH_ADDR_UNASSIGNED) {
|
|
return NULL;
|
|
}
|
|
} else if (addr < bt_mesh_cdb.lowest_avail_addr) {
|
|
return NULL;
|
|
} else if (addr_is_free(addr, num_elem, NULL) < 0) {
|
|
LOG_DBG("Address range 0x%04x-0x%04x is not free", addr, addr + num_elem - 1);
|
|
return NULL;
|
|
}
|
|
|
|
for (i = 0; i < ARRAY_SIZE(bt_mesh_cdb.nodes); i++) {
|
|
struct bt_mesh_cdb_node *node = &bt_mesh_cdb.nodes[i];
|
|
|
|
if (node->addr == BT_MESH_ADDR_UNASSIGNED) {
|
|
memcpy(node->uuid, uuid, 16);
|
|
node->addr = addr;
|
|
node->num_elem = num_elem;
|
|
node->net_idx = net_idx;
|
|
atomic_set(node->flags, 0);
|
|
return node;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void bt_mesh_cdb_node_del(struct bt_mesh_cdb_node *node, bool store)
|
|
{
|
|
LOG_DBG("Node addr 0x%04x store %u", node->addr, store);
|
|
|
|
if (IS_ENABLED(CONFIG_BT_SETTINGS) && store) {
|
|
update_cdb_node_settings(node, false);
|
|
}
|
|
|
|
if (store && node->addr + node->num_elem > bt_mesh_cdb.lowest_avail_addr) {
|
|
bt_mesh_cdb.lowest_avail_addr = node->addr + node->num_elem;
|
|
|
|
if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
|
|
update_cdb_net_settings();
|
|
}
|
|
}
|
|
|
|
node->addr = BT_MESH_ADDR_UNASSIGNED;
|
|
memset(node->dev_key, 0, sizeof(node->dev_key));
|
|
}
|
|
|
|
struct bt_mesh_cdb_node *bt_mesh_cdb_node_get(uint16_t addr)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(bt_mesh_cdb.nodes); i++) {
|
|
struct bt_mesh_cdb_node *node = &bt_mesh_cdb.nodes[i];
|
|
|
|
if (addr >= node->addr &&
|
|
addr <= node->addr + node->num_elem - 1) {
|
|
return node;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void bt_mesh_cdb_node_store(const struct bt_mesh_cdb_node *node)
|
|
{
|
|
if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
|
|
update_cdb_node_settings(node, true);
|
|
}
|
|
}
|
|
|
|
void bt_mesh_cdb_node_foreach(bt_mesh_cdb_node_func_t func, void *user_data)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(bt_mesh_cdb.nodes); ++i) {
|
|
if (bt_mesh_cdb.nodes[i].addr == BT_MESH_ADDR_UNASSIGNED) {
|
|
continue;
|
|
}
|
|
|
|
if (func(&bt_mesh_cdb.nodes[i], user_data) ==
|
|
BT_MESH_CDB_ITER_STOP) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
struct bt_mesh_cdb_app_key *bt_mesh_cdb_app_key_alloc(uint16_t net_idx,
|
|
uint16_t app_idx)
|
|
{
|
|
struct bt_mesh_cdb_app_key *key;
|
|
int i;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(bt_mesh_cdb.app_keys); ++i) {
|
|
key = &bt_mesh_cdb.app_keys[i];
|
|
|
|
if (key->net_idx != BT_MESH_KEY_UNUSED) {
|
|
continue;
|
|
}
|
|
|
|
key->net_idx = net_idx;
|
|
key->app_idx = app_idx;
|
|
|
|
return key;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void bt_mesh_cdb_app_key_del(struct bt_mesh_cdb_app_key *key, bool store)
|
|
{
|
|
LOG_DBG("AppIdx 0x%03x store %u", key->app_idx, store);
|
|
|
|
if (IS_ENABLED(CONFIG_BT_SETTINGS) && store) {
|
|
update_cdb_app_key_settings(key, false);
|
|
}
|
|
|
|
key->net_idx = BT_MESH_KEY_UNUSED;
|
|
memset(key->keys, 0, sizeof(key->keys));
|
|
}
|
|
|
|
struct bt_mesh_cdb_app_key *bt_mesh_cdb_app_key_get(uint16_t app_idx)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(bt_mesh_cdb.app_keys); i++) {
|
|
struct bt_mesh_cdb_app_key *key = &bt_mesh_cdb.app_keys[i];
|
|
|
|
if (key->net_idx != BT_MESH_KEY_UNUSED &&
|
|
key->app_idx == app_idx) {
|
|
return key;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void bt_mesh_cdb_app_key_store(const struct bt_mesh_cdb_app_key *key)
|
|
{
|
|
if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
|
|
update_cdb_app_key_settings(key, true);
|
|
}
|
|
}
|
|
|
|
static void clear_cdb_net(void)
|
|
{
|
|
int err;
|
|
|
|
err = settings_delete("bt/mesh/cdb/Net");
|
|
if (err) {
|
|
LOG_ERR("Failed to clear Network");
|
|
} else {
|
|
LOG_DBG("Cleared Network");
|
|
}
|
|
}
|
|
|
|
static void store_cdb_pending_net(void)
|
|
{
|
|
struct net_val net;
|
|
int err;
|
|
|
|
LOG_DBG("");
|
|
|
|
net.iv.index = bt_mesh_cdb.iv_index;
|
|
net.iv.update = atomic_test_bit(bt_mesh_cdb.flags,
|
|
BT_MESH_CDB_IVU_IN_PROGRESS);
|
|
net.lowest_avail_addr = bt_mesh_cdb.lowest_avail_addr;
|
|
|
|
err = settings_save_one("bt/mesh/cdb/Net", &net, sizeof(net));
|
|
if (err) {
|
|
LOG_ERR("Failed to store Network value");
|
|
} else {
|
|
LOG_DBG("Stored Network value");
|
|
}
|
|
}
|
|
|
|
static void store_cdb_pending_nodes(void)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(cdb_node_updates); ++i) {
|
|
struct node_update *update = &cdb_node_updates[i];
|
|
uint16_t addr;
|
|
|
|
if (update->addr == BT_MESH_ADDR_UNASSIGNED) {
|
|
continue;
|
|
}
|
|
|
|
addr = update->addr;
|
|
update->addr = BT_MESH_ADDR_UNASSIGNED;
|
|
|
|
LOG_DBG("addr: 0x%04x, clear: %d", addr, update->clear);
|
|
|
|
if (update->clear) {
|
|
clear_cdb_node(addr);
|
|
} else {
|
|
struct bt_mesh_cdb_node *node;
|
|
|
|
node = bt_mesh_cdb_node_get(addr);
|
|
if (node) {
|
|
store_cdb_node(node);
|
|
} else {
|
|
LOG_WRN("Node 0x%04x not found", addr);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void store_cdb_pending_keys(void)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(cdb_key_updates); i++) {
|
|
struct key_update *update = &cdb_key_updates[i];
|
|
|
|
if (!update->valid) {
|
|
continue;
|
|
}
|
|
|
|
update->valid = 0U;
|
|
|
|
if (update->clear) {
|
|
if (update->app_key) {
|
|
clear_cdb_app_key(update->key_idx);
|
|
} else {
|
|
clear_cdb_subnet(update->key_idx);
|
|
}
|
|
} else {
|
|
if (update->app_key) {
|
|
struct bt_mesh_cdb_app_key *key;
|
|
|
|
key = bt_mesh_cdb_app_key_get(update->key_idx);
|
|
if (key) {
|
|
store_cdb_app_key(key);
|
|
} else {
|
|
LOG_WRN("AppKeyIndex 0x%03x not found", update->key_idx);
|
|
}
|
|
} else {
|
|
struct bt_mesh_cdb_subnet *sub;
|
|
|
|
sub = bt_mesh_cdb_subnet_get(update->key_idx);
|
|
if (sub) {
|
|
store_cdb_subnet(sub);
|
|
} else {
|
|
LOG_WRN("NetKeyIndex 0x%03x not found", update->key_idx);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void bt_mesh_cdb_pending_store(void)
|
|
{
|
|
if (atomic_test_and_clear_bit(bt_mesh_cdb.flags,
|
|
BT_MESH_CDB_SUBNET_PENDING)) {
|
|
if (atomic_test_bit(bt_mesh_cdb.flags,
|
|
BT_MESH_CDB_VALID)) {
|
|
store_cdb_pending_net();
|
|
} else {
|
|
clear_cdb_net();
|
|
}
|
|
}
|
|
|
|
if (atomic_test_and_clear_bit(bt_mesh_cdb.flags,
|
|
BT_MESH_CDB_NODES_PENDING)) {
|
|
store_cdb_pending_nodes();
|
|
}
|
|
|
|
if (atomic_test_and_clear_bit(bt_mesh_cdb.flags,
|
|
BT_MESH_CDB_KEYS_PENDING)) {
|
|
store_cdb_pending_keys();
|
|
}
|
|
}
|