Bluetooth: Mesh: Persistent storage of Virtual Addresses

The 16-bit format group addresses will be stored,
but we don't store (or restore) the virtual label UUIDs,
i.e. after a power cycle the 16-bit group addresses
would be meaningless.

Fixes #19342

Signed-off-by: Lingao Meng <mengabc1086@gmail.com>
This commit is contained in:
Lingao Meng 2019-10-14 04:09:55 -07:00 committed by Johan Hedberg
commit 101ad56d43
5 changed files with 176 additions and 21 deletions

View file

@ -41,11 +41,7 @@
static struct bt_mesh_cfg_srv *conf;
static struct label {
u16_t ref;
u16_t addr;
u8_t uuid[16];
} labels[CONFIG_BT_MESH_LABEL_COUNT];
static struct label labels[CONFIG_BT_MESH_LABEL_COUNT];
static int comp_add_elem(struct net_buf_simple *buf, struct bt_mesh_elem *elem,
bool primary)
@ -1090,25 +1086,61 @@ send_status:
status, mod_id);
}
#if CONFIG_BT_MESH_LABEL_COUNT > 0
static u8_t va_add(u8_t *label_uuid, u16_t *addr)
struct label *get_label(u16_t index)
{
struct label *free_slot = NULL;
if (index >= ARRAY_SIZE(labels)) {
return NULL;
}
return &labels[index];
}
#if CONFIG_BT_MESH_LABEL_COUNT > 0
static inline void va_store(struct label *store)
{
atomic_set_bit(store->flags, BT_MESH_VA_CHANGED);
if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
bt_mesh_store_label();
}
}
static struct label *va_find(const u8_t *label_uuid,
struct label **free_slot)
{
struct label *match = NULL;
int i;
if (free_slot != NULL) {
*free_slot = NULL;
}
for (i = 0; i < ARRAY_SIZE(labels); i++) {
if (!labels[i].ref) {
free_slot = &labels[i];
if (labels[i].ref == 0) {
if (free_slot != NULL) {
*free_slot = &labels[i];
}
continue;
}
if (!memcmp(labels[i].uuid, label_uuid, 16)) {
*addr = labels[i].addr;
labels[i].ref++;
return STATUS_SUCCESS;
match = &labels[i];
}
}
return match;
}
static u8_t va_add(u8_t *label_uuid, u16_t *addr)
{
struct label *update, *free_slot = NULL;
update = va_find(label_uuid, &free_slot);
if (update) {
update->ref++;
va_store(update);
return 0;
}
if (!free_slot) {
return STATUS_INSUFF_RESOURCES;
}
@ -1120,23 +1152,24 @@ static u8_t va_add(u8_t *label_uuid, u16_t *addr)
free_slot->ref = 1U;
free_slot->addr = *addr;
memcpy(free_slot->uuid, label_uuid, 16);
va_store(free_slot);
return STATUS_SUCCESS;
}
static u8_t va_del(u8_t *label_uuid, u16_t *addr)
{
int i;
struct label *update;
for (i = 0; i < ARRAY_SIZE(labels); i++) {
if (!memcmp(labels[i].uuid, label_uuid, 16)) {
if (addr) {
*addr = labels[i].addr;
}
update = va_find(label_uuid, NULL);
if (update) {
update->ref--;
labels[i].ref--;
return STATUS_SUCCESS;
if (addr) {
*addr = update->addr;
}
va_store(update);
}
if (addr) {

View file

@ -112,12 +112,25 @@
#define STATUS_UNSPECIFIED 0x10
#define STATUS_INVALID_BINDING 0x11
enum {
BT_MESH_VA_CHANGED, /* Label information changed */
};
struct label {
u16_t ref;
u16_t addr;
u8_t uuid[16];
atomic_t flags[1];
};
void bt_mesh_cfg_reset(void);
void bt_mesh_heartbeat(u16_t src, u16_t dst, u8_t hops, u16_t feat);
void bt_mesh_attention(struct bt_mesh_model *model, u8_t time);
struct label *get_label(u16_t index);
u8_t *bt_mesh_label_uuid_get(u16_t addr);
struct bt_mesh_hb_pub *bt_mesh_hb_pub_get(void);

View file

@ -215,6 +215,7 @@ enum {
BT_MESH_HB_PUB_PENDING,
BT_MESH_CFG_PENDING,
BT_MESH_MOD_PENDING,
BT_MESH_VA_PENDING,
/* Don't touch - intentionally last */
BT_MESH_FLAG_COUNT,

View file

@ -116,6 +116,13 @@ struct mod_pub_val {
cred:1;
};
/* Virtual Address information */
struct va_val {
u16_t ref;
u16_t addr;
u8_t uuid[16];
} __packed;
/* We need this so we don't overwrite app-hardcoded values in case FCB
* contains a history of changes but then has a NULL at the end.
*/
@ -684,6 +691,55 @@ static int vnd_mod_set(const char *name, size_t len_rd,
return mod_set(true, name, len_rd, read_cb, cb_arg);
}
#if CONFIG_BT_MESH_LABEL_COUNT > 0
static int va_set(const char *name, size_t len_rd,
settings_read_cb read_cb, void *cb_arg)
{
struct va_val va;
struct label *lab;
u16_t index;
int err;
if (!name) {
BT_ERR("Insufficient number of arguments");
return -ENOENT;
}
index = strtol(name, NULL, 16);
if (len_rd == 0) {
BT_WARN("Mesh Virtual Address length = 0");
return 0;
}
err = mesh_x_set(read_cb, cb_arg, &va, sizeof(va));
if (err) {
BT_ERR("Failed to set \'virtual address\'");
return err;
}
if (va.ref == 0) {
BT_WARN("Ignore Mesh Virtual Address ref = 0");
return 0;
}
lab = get_label(index);
if (lab == NULL) {
BT_WARN("Out of labels buffers");
return -ENOBUFS;
}
memcpy(lab->uuid, va.uuid, 16);
lab->addr = va.addr;
lab->ref = va.ref;
BT_DBG("Restored Virtual Address, addr 0x%04x ref 0x%04x",
lab->addr, lab->ref);
return 0;
}
#endif
const struct mesh_setting {
const char *name;
int (*func)(const char *name, size_t len_rd,
@ -699,6 +755,9 @@ const struct mesh_setting {
{ "Cfg", cfg_set },
{ "s", sig_mod_set },
{ "v", vnd_mod_set },
#if CONFIG_BT_MESH_LABEL_COUNT > 0
{ "Va", va_set },
#endif
};
static int mesh_set(const char *name, size_t len_rd,
@ -1354,6 +1413,45 @@ static void store_pending_mod(struct bt_mesh_model *mod,
}
}
#define IS_VA_DEL(_label) ((_label)->ref == 0)
static void store_pending_va(void)
{
struct label *lab;
struct va_val va;
char path[18];
u16_t i;
int err;
for (i = 0; (lab = get_label(i)) != NULL; i++) {
if (!atomic_test_and_clear_bit(lab->flags,
BT_MESH_VA_CHANGED)) {
continue;
}
snprintk(path, sizeof(path), "bt/mesh/Va/%x", i);
if (IS_VA_DEL(lab)) {
err = settings_delete(path);
} else {
va.ref = lab->ref;
va.addr = lab->addr;
memcpy(va.uuid, lab->uuid, 16);
err = settings_save_one(path, &va, sizeof(va));
}
if (err) {
BT_ERR("Failed to %s %s value (err %d)",
IS_VA_DEL(lab) ? "delete" : "store",
log_strdup(path), err);
} else {
BT_DBG("%s %s value",
IS_VA_DEL(lab) ? "Deleted" : "Stored",
log_strdup(path));
}
}
}
static void store_pending(struct k_work *work)
{
BT_DBG("");
@ -1405,6 +1503,10 @@ static void store_pending(struct k_work *work)
if (atomic_test_and_clear_bit(bt_mesh.flags, BT_MESH_MOD_PENDING)) {
bt_mesh_model_foreach(store_pending_mod, NULL);
}
if (atomic_test_and_clear_bit(bt_mesh.flags, BT_MESH_VA_PENDING)) {
store_pending_va();
}
}
void bt_mesh_store_rpl(struct bt_mesh_rpl *entry)
@ -1586,6 +1688,11 @@ void bt_mesh_store_mod_pub(struct bt_mesh_model *mod)
schedule_store(BT_MESH_MOD_PENDING);
}
void bt_mesh_store_label(void)
{
schedule_store(BT_MESH_VA_PENDING);
}
int bt_mesh_model_data_store(struct bt_mesh_model *mod, bool vnd,
const void *data, size_t data_len)
{

View file

@ -15,6 +15,7 @@ void bt_mesh_store_cfg(void);
void bt_mesh_store_mod_bind(struct bt_mesh_model *mod);
void bt_mesh_store_mod_sub(struct bt_mesh_model *mod);
void bt_mesh_store_mod_pub(struct bt_mesh_model *mod);
void bt_mesh_store_label(void);
void bt_mesh_clear_net(void);
void bt_mesh_clear_subnet(struct bt_mesh_subnet *sub);