From 101ad56d433c89b64c138f8ae182979a8e4b4fbb Mon Sep 17 00:00:00 2001 From: Lingao Meng Date: Mon, 14 Oct 2019 04:09:55 -0700 Subject: [PATCH] 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 --- subsys/bluetooth/mesh/cfg_srv.c | 75 ++++++++++++++------ subsys/bluetooth/mesh/foundation.h | 13 ++++ subsys/bluetooth/mesh/net.h | 1 + subsys/bluetooth/mesh/settings.c | 107 +++++++++++++++++++++++++++++ subsys/bluetooth/mesh/settings.h | 1 + 5 files changed, 176 insertions(+), 21 deletions(-) diff --git a/subsys/bluetooth/mesh/cfg_srv.c b/subsys/bluetooth/mesh/cfg_srv.c index 42d576d47a3..0b6e9f01965 100644 --- a/subsys/bluetooth/mesh/cfg_srv.c +++ b/subsys/bluetooth/mesh/cfg_srv.c @@ -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) { diff --git a/subsys/bluetooth/mesh/foundation.h b/subsys/bluetooth/mesh/foundation.h index 25c24c0685d..3d337e2088f 100644 --- a/subsys/bluetooth/mesh/foundation.h +++ b/subsys/bluetooth/mesh/foundation.h @@ -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); diff --git a/subsys/bluetooth/mesh/net.h b/subsys/bluetooth/mesh/net.h index e8a2164d998..1481ec134a9 100644 --- a/subsys/bluetooth/mesh/net.h +++ b/subsys/bluetooth/mesh/net.h @@ -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, diff --git a/subsys/bluetooth/mesh/settings.c b/subsys/bluetooth/mesh/settings.c index 25c786db074..182330f9bf6 100644 --- a/subsys/bluetooth/mesh/settings.c +++ b/subsys/bluetooth/mesh/settings.c @@ -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) { diff --git a/subsys/bluetooth/mesh/settings.h b/subsys/bluetooth/mesh/settings.h index 7bd0284770d..094a1e58757 100644 --- a/subsys/bluetooth/mesh/settings.h +++ b/subsys/bluetooth/mesh/settings.h @@ -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);