/* * Copyright (c) 2018 Intel Corporation * * SPDX-License-Identifier: Apache-2.0 */ #include "syscfg/syscfg.h" #define MESH_LOG_MODULE BLE_MESH_SETTINGS_LOG #if MYNEWT_VAL(BLE_MESH_SETTINGS) #include "mesh_priv.h" #include "mesh/mesh.h" #include "mesh/glue.h" #include "subnet.h" #include "app_keys.h" #include "net.h" #include "rpl.h" #include "crypto.h" #include "transport.h" #include "heartbeat.h" #include "access.h" #include "foundation.h" #include "proxy.h" #include "settings.h" #include "lpn.h" #include "cfg.h" #include "config/config.h" /* 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 */ }; static struct key_update key_updates[CONFIG_BT_MESH_APP_KEY_COUNT + CONFIG_BT_MESH_SUBNET_COUNT]; static struct k_delayed_work pending_store; /* Mesh network storage information */ struct net_val { uint16_t primary_addr; uint8_t dev_key[16]; } __packed; /* Sequence number storage */ struct seq_val { uint8_t val[3]; } __packed; /* Heartbeat Publication storage */ struct hb_pub_val { uint16_t dst; uint8_t period; uint8_t ttl; uint16_t feat; uint16_t net_idx:12, indefinite:1; }; /* Miscelaneous configuration server model states */ struct cfg_val { uint8_t net_transmit; uint8_t relay; uint8_t relay_retransmit; uint8_t beacon; uint8_t gatt_proxy; uint8_t frnd; uint8_t default_ttl; }; /* IV Index & IV Update storage */ struct iv_val { uint32_t iv_index; uint8_t iv_update:1, iv_duration:7; } __packed; /* Replay Protection List storage */ struct rpl_val { uint32_t seq:24, old_iv:1; }; /* NetKey storage information */ struct net_key_val { uint8_t kr_flag:1, kr_phase:7; uint8_t val[2][16]; } __packed; /* AppKey storage information */ struct app_key_val { uint16_t net_idx; bool updated; uint8_t val[2][16]; } __packed; struct mod_pub_val { uint16_t addr; uint16_t key; uint8_t ttl; uint8_t retransmit; uint8_t period; uint8_t period_div:4, cred:1; }; /* Virtual Address information */ struct va_val { uint16_t ref; uint16_t addr; uint8_t uuid[16]; } __packed; struct cdb_net_val { uint32_t iv_index; bool iv_update; } __packed; /* Node storage information */ 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; struct node_update { uint16_t addr; bool clear; }; #if MYNEWT_VAL(BLE_MESH_CDB) static struct node_update cdb_node_updates[MYNEWT_VAL(BLE_MESH_CDB_NODE_COUNT)]; static struct key_update cdb_key_updates[ MYNEWT_VAL(BLE_MESH_CDB_SUBNET_COUNT) + MYNEWT_VAL(BLE_MESH_CDB_APP_KEY_COUNT)]; #else static struct node_update cdb_node_updates[0]; static struct key_update cdb_key_updates[0]; #endif int settings_name_next(char *name, char **next) { int rc = 0; if (next) { *next = NULL; } if (!name) { return 0; } /* name might come from flash directly, in flash the name would end * with '=' or '\0' depending how storage is done. Flash reading is * limited to what can be read */ while ((*name != '\0') && (*name != '=') && (*name != '/')) { rc++; name++; } if (*name == '/') { if (next) { *next = name + 1; } return rc; } return rc; } static int net_set(int argc, char **argv, char *val) { struct net_val net; int len, err; BT_DBG("val %s", val ? val : "(null)"); if (!val) { bt_mesh_comp_unprovision(); memset(bt_mesh.dev_key, 0, sizeof(bt_mesh.dev_key)); return 0; } len = sizeof(net); err = settings_bytes_from_str(val, &net, &len); if (err) { BT_ERR("Failed to decode value %s (err %d)", val, err); return err; } if (len != sizeof(net)) { BT_ERR("Unexpected value length (%d != %zu)", len, sizeof(net)); return -EINVAL; } memcpy(bt_mesh.dev_key, net.dev_key, sizeof(bt_mesh.dev_key)); bt_mesh_comp_provision(net.primary_addr); BT_DBG("Provisioned with primary address 0x%04x", net.primary_addr); BT_DBG("Recovered DevKey %s", bt_hex(bt_mesh.dev_key, 16)); return 0; } static int iv_set(int argc, char **argv, char *val) { struct iv_val iv; int len, err; BT_DBG("val %s", val ? val : "(null)"); if (!val) { bt_mesh.iv_index = 0U; atomic_clear_bit(bt_mesh.flags, BT_MESH_IVU_IN_PROGRESS); return 0; } len = sizeof(iv); err = settings_bytes_from_str(val, &iv, &len); if (err) { BT_ERR("Failed to decode value %s (err %d)", val, err); return err; } if (len != sizeof(iv)) { BT_ERR("Unexpected value length (%d != %zu)", len, sizeof(iv)); return -EINVAL; } bt_mesh.iv_index = iv.iv_index; atomic_set_bit_to(bt_mesh.flags, BT_MESH_IVU_IN_PROGRESS, iv.iv_update); bt_mesh.ivu_duration = iv.iv_duration; BT_DBG("IV Index 0x%04x (IV Update Flag %u) duration %u hours", (unsigned) iv.iv_index, iv.iv_update, iv.iv_duration); return 0; } static int seq_set(int argc, char **argv, char *val) { struct seq_val seq; int len, err; BT_DBG("val %s", val ? val : "(null)"); if (!val) { bt_mesh.seq = 0; return 0; } len = sizeof(seq); err = settings_bytes_from_str(val, &seq, &len); if (err) { BT_ERR("Failed to decode value %s (err %d)", val, err); return err; } if (len != sizeof(seq)) { BT_ERR("Unexpected value length (%d != %zu)", len, sizeof(seq)); return -EINVAL; } bt_mesh.seq = sys_get_le24(seq.val); if (CONFIG_BT_MESH_SEQ_STORE_RATE > 0) { /* Make sure we have a large enough sequence number. We * subtract 1 so that the first transmission causes a write * to the settings storage. */ bt_mesh.seq += (CONFIG_BT_MESH_SEQ_STORE_RATE - (bt_mesh.seq % CONFIG_BT_MESH_SEQ_STORE_RATE)); bt_mesh.seq--; } BT_DBG("Sequence Number 0x%06x", bt_mesh.seq); return 0; } static int rpl_set(int argc, char **argv, char *val) { struct bt_mesh_rpl *entry; struct rpl_val rpl; int len, err; uint16_t src; if (argc < 1) { BT_ERR("Invalid argc (%d)", argc); return -ENOENT; } BT_DBG("argv[0] %s val %s", argv[0], val ? val : "(null)"); src = strtol(argv[0], NULL, 16); entry = bt_mesh_rpl_find(src); if (!val) { if (entry) { memset(entry, 0, sizeof(*entry)); } else { BT_WARN("Unable to find RPL entry for 0x%04x", src); } return 0; } if (!entry) { entry = bt_mesh_rpl_alloc(src); if (!entry) { BT_ERR("Unable to allocate RPL entry for 0x%04x", src); return -ENOMEM; } } len = sizeof(rpl); err = settings_bytes_from_str(val, &rpl, &len); if (err) { BT_ERR("Failed to decode value %s (err %d)", val, err); return err; } if (len != sizeof(rpl)) { BT_ERR("Unexpected value length (%d != %zu)", len, sizeof(rpl)); return -EINVAL; } entry->seq = rpl.seq; entry->old_iv = rpl.old_iv; BT_DBG("RPL entry for 0x%04x: Seq 0x%06x old_iv %u", entry->src, (unsigned) entry->seq, entry->old_iv); return 0; } static int net_key_set(int argc, char **argv, char *val) { struct net_key_val key; int len, err; uint16_t net_idx; BT_DBG("argv[0] %s val %s", argv[0], val ? val : "(null)"); net_idx = strtol(argv[0], NULL, 16); len = sizeof(key); err = settings_bytes_from_str(val, &key, &len); if (err) { BT_ERR("Failed to decode value %s (err %d)", val, err); return err; } if (len != sizeof(key)) { BT_ERR("Unexpected value length (%d != %zu)", len, sizeof(key)); return -EINVAL; } BT_DBG("NetKeyIndex 0x%03x recovered from storage", net_idx); return bt_mesh_subnet_set( net_idx, key.kr_phase, key.val[0], (key.kr_phase != BT_MESH_KR_NORMAL) ? key.val[1] : NULL); } static int app_key_set(int argc, char **argv, char *val) { struct app_key_val key; uint16_t app_idx; int len_rd, err; BT_DBG("argv[0] %s val %s", argv[0], val ? val : "(null)"); app_idx = strtol(argv[0], NULL, 16); len_rd = strtol(argv[1], NULL, 16); if (!len_rd) { return 0; } err = settings_bytes_from_str(val, &key, &len_rd); if (err) { BT_ERR("Failed to decode value %s (err %d)", val, err); return err; } err = bt_mesh_app_key_set(app_idx, key.net_idx, key.val[0], key.updated ? key.val[1] : NULL); if (err) { BT_ERR("Failed to set \'app-key\'"); return err; } BT_DBG("AppKeyIndex 0x%03x recovered from storage", app_idx); return 0; } static int hb_pub_set(int argc, char **argv, char *val) { struct bt_mesh_hb_pub pub; struct hb_pub_val hb_val; int len, err; BT_DBG("val %s", val ? val : "(null)"); len = sizeof(hb_val); err = settings_bytes_from_str(val, &hb_val, &len); if (err) { BT_ERR("Failed to decode value %s (err %d)", val, err); return err; } if (len != sizeof(hb_val)) { BT_ERR("Unexpected value length (%d != %zu)", len, sizeof(hb_val)); return -EINVAL; } pub.dst = hb_val.dst; pub.period = bt_mesh_hb_pwr2(hb_val.period); pub.ttl = hb_val.ttl; pub.feat = hb_val.feat; pub.net_idx = hb_val.net_idx; if (hb_val.indefinite) { pub.count = 0xffff; } else { pub.count = 0; } (void)bt_mesh_hb_pub_set(&pub); BT_DBG("Restored heartbeat publication"); return 0; } static int cfg_set(int argc, char **argv, char *val) { struct cfg_val cfg; int len, err; BT_DBG("val %s", val ? val : "(null)"); if (!val) { BT_DBG("Cleared configuration state"); return 0; } len = sizeof(cfg); err = settings_bytes_from_str(val, &cfg, &len); if (err) { BT_ERR("Failed to decode value %s (err %d)", val, err); return err; } if (len != sizeof(cfg)) { BT_ERR("Unexpected value length (%d != %zu)", len, sizeof(cfg)); return -EINVAL; } bt_mesh_net_transmit_set(cfg.net_transmit); bt_mesh_relay_set(cfg.relay, cfg.relay_retransmit); bt_mesh_beacon_set(cfg.beacon); bt_mesh_gatt_proxy_set(cfg.gatt_proxy); bt_mesh_friend_set(cfg.frnd); bt_mesh_default_ttl_set(cfg.default_ttl); BT_DBG("Restored configuration state"); return 0; } static int mod_set_bind(struct bt_mesh_model *mod, char *val) { int len, err, i; /* Start with empty array regardless of cleared or set value */ for (i = 0; i < ARRAY_SIZE(mod->keys); i++) { mod->keys[i] = BT_MESH_KEY_UNUSED; } if (!val) { BT_DBG("Cleared bindings for model"); return 0; } len = sizeof(mod->keys); err = settings_bytes_from_str(val, mod->keys, &len); if (err) { BT_ERR("Failed to decode value %s (err %d)", val, err); return -EINVAL; } BT_DBG("Decoded %u bound keys for model", len / sizeof(mod->keys[0])); return 0; } static int mod_set_sub(struct bt_mesh_model *mod, char *val) { int len, err; /* Start with empty array regardless of cleared or set value */ memset(mod->groups, 0, sizeof(mod->groups)); if (!val) { BT_DBG("Cleared subscriptions for model"); return 0; } len = sizeof(mod->groups); err = settings_bytes_from_str(val, mod->groups, &len); if (err) { BT_ERR("Failed to decode value %s (err %d)", val, err); return -EINVAL; } BT_DBG("Decoded %u subscribed group addresses for model", len / sizeof(mod->groups[0])); return 0; } static int mod_set_pub(struct bt_mesh_model *mod, char *val) { struct mod_pub_val pub; int len, err; if (!mod->pub) { BT_WARN("Model has no publication context!"); return -EINVAL; } if (!val) { mod->pub->addr = BT_MESH_ADDR_UNASSIGNED; mod->pub->key = 0; mod->pub->cred = 0; mod->pub->ttl = 0; mod->pub->period = 0; mod->pub->retransmit = 0; mod->pub->count = 0; BT_DBG("Cleared publication for model"); return 0; } len = sizeof(pub); err = settings_bytes_from_str(val, &pub, &len); if (err) { BT_ERR("Failed to decode value %s (err %d)", val, err); return -EINVAL; } if (len != sizeof(pub)) { BT_ERR("Invalid length for model publication"); return -EINVAL; } mod->pub->addr = pub.addr; mod->pub->key = pub.key; mod->pub->cred = pub.cred; mod->pub->ttl = pub.ttl; mod->pub->period = pub.period; mod->pub->retransmit = pub.retransmit; mod->pub->count = 0; BT_DBG("Restored model publication, dst 0x%04x app_idx 0x%03x", pub.addr, pub.key); return 0; } static int mod_data_set(struct bt_mesh_model *mod, char *name, char *len_rd) { char *next; settings_name_next(name, &next); if (mod->cb && mod->cb->settings_set) { return mod->cb->settings_set(mod, next, len_rd); } return 0; } static int mod_set(bool vnd, int argc, char **argv, char *val) { struct bt_mesh_model *mod; uint8_t elem_idx, mod_idx; uint16_t mod_key; if (argc < 2) { BT_ERR("Too small argc (%d)", argc); return -ENOENT; } mod_key = strtol(argv[0], NULL, 16); elem_idx = mod_key >> 8; mod_idx = mod_key; BT_DBG("Decoded mod_key 0x%04x as elem_idx %u mod_idx %u", mod_key, elem_idx, mod_idx); mod = bt_mesh_model_get(vnd, elem_idx, mod_idx); if (!mod) { BT_ERR("Failed to get model for elem_idx %u mod_idx %u", elem_idx, mod_idx); return -ENOENT; } if (!strcmp(argv[1], "bind")) { return mod_set_bind(mod, val); } if (!strcmp(argv[1], "sub")) { return mod_set_sub(mod, val); } if (!strcmp(argv[1], "pub")) { return mod_set_pub(mod, val); } if (!strcmp(argv[1], "data")) { return mod_data_set(mod, argv[1], val); } BT_WARN("Unknown module key %s", argv[1]); return -ENOENT; } static int sig_mod_set(int argc, char **argv, char *val) { return mod_set(false, argc, argv, val); } static int vnd_mod_set(int argc, char **argv, char *val) { return mod_set(true, argc, argv, val); } #if CONFIG_BT_MESH_LABEL_COUNT > 0 static int va_set(int argc, char **argv, char *val) { struct va_val va; struct bt_mesh_va *lab; uint16_t index; int len, err; if (argc < 1) { BT_ERR("Insufficient number of arguments"); return -ENOENT; } index = strtol(argv[0], NULL, 16); if (val == NULL) { BT_WARN("Mesh Virtual Address length = 0"); return 0; } err = settings_bytes_from_str(val, &va, &len); if (err) { BT_ERR("Failed to decode value %s (err %d)", val, err); return -EINVAL; } if (len != sizeof(struct va_val)) { BT_ERR("Invalid length for virtual address"); return -EINVAL; } if (va.ref == 0) { BT_WARN("Ignore Mesh Virtual Address ref = 0"); return 0; } lab = bt_mesh_va_get(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 #if MYNEWT_VAL(BLE_MESH_CDB) static int cdb_net_set(int argc, char *val) { struct cdb_net_val net; int len, err; len = sizeof(net); err = settings_bytes_from_str(val, &net, &len); if (err) { BT_ERR("Failed to set \'cdb_net\'"); return err; } 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); } atomic_set_bit(bt_mesh_cdb.flags, BT_MESH_CDB_VALID); return 0; } static int cdb_node_set(int argc, char *str) { struct bt_mesh_cdb_node *node; struct node_val val; uint16_t addr; int len, err; if (argc < 1) { BT_ERR("Insufficient number of arguments"); return -ENOENT; } addr = strtol(str, NULL, 16); len = sizeof(str); if (argc < 1) { BT_DBG("val (null)"); BT_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 = settings_bytes_from_str(str, &val, &len); if (err) { BT_ERR("Failed to decode value %s (err %d)", val, err); return -EINVAL; } if (len != sizeof(struct node_val)) { BT_ERR("Invalid length for node_val"); return -EINVAL; } 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) { BT_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); BT_DBG("Node 0x%04x recovered from storage", addr); return 0; } static int cdb_subnet_set(int argc, char *name) { struct bt_mesh_cdb_subnet *sub; struct net_key_val key; uint16_t net_idx; int len, len_rd, err; if (!name) { BT_ERR("Insufficient number of arguments"); return -ENOENT; } len_rd = sizeof(sub); net_idx = strtol(name, NULL, 16); sub = bt_mesh_cdb_subnet_get(net_idx); if (len_rd == 0) { BT_DBG("val (null)"); if (!sub) { BT_ERR("No subnet with NetKeyIndex 0x%03x", net_idx); return -ENOENT; } BT_DBG("Deleting NetKeyIndex 0x%03x", net_idx); bt_mesh_cdb_subnet_del(sub, false); return 0; } len = sizeof(key); err = settings_bytes_from_str(name, &key, &len); if (err) { BT_ERR("Failed to set \'net-key\'"); return err; } if (sub) { BT_DBG("Updating existing NetKeyIndex 0x%03x", net_idx); sub->kr_flag = key.kr_flag; 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) { BT_ERR("No space to allocate a new subnet"); return -ENOMEM; } sub->kr_flag = key.kr_flag; 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); BT_DBG("NetKeyIndex 0x%03x recovered from storage", net_idx); return 0; } static int cdb_app_key_set(int argc, char *name) { struct bt_mesh_cdb_app_key *app; struct app_key_val key; uint16_t app_idx; int len_rd, err; app_idx = strtol(name, NULL, 16); len_rd = sizeof(key); if (len_rd == 0) { BT_DBG("val (null)"); BT_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 = settings_bytes_from_str(name, &key, &len_rd); if (err) { BT_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) { BT_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); BT_DBG("AppKeyIndex 0x%03x recovered from storage", app_idx); return 0; } static int cdb_set(int argc, char **argv, char *name) { int len; char *next; if (argc < 1) { BT_ERR("Insufficient number of arguments"); return -ENOENT; } if (!strcmp(name, "Net")) { return cdb_net_set(1, name); } len = settings_name_next(name, &next); if (!next) { BT_ERR("Insufficient number of arguments"); return -ENOENT; } if (!strncmp(name, "Node", len)) { return cdb_node_set(1, next); } if (!strncmp(name, "Subnet", len)) { return cdb_subnet_set(1, next); } if (!strncmp(name, "AppKey", len)) { return cdb_app_key_set(1, next); } BT_WARN("Unknown module key %s", name); return -ENOENT; } #endif const struct mesh_setting { const char *name; int (*func)(int argc, char **argv, char *val); } settings[] = { { "Net", net_set }, { "IV", iv_set }, { "Seq", seq_set }, { "RPL", rpl_set }, { "NetKey", net_key_set }, { "AppKey", app_key_set }, { "HBPub", hb_pub_set }, { "Cfg", cfg_set }, { "s", sig_mod_set }, { "v", vnd_mod_set }, #if CONFIG_BT_MESH_LABEL_COUNT > 0 { "Va", va_set }, #endif #if MYNEWT_VAL(BLE_MESH_CDB) { "cdb", cdb_set }, #endif }; static int mesh_set(int argc, char **argv, char *val) { int i; if (argc < 1) { BT_ERR("Insufficient number of arguments"); return -EINVAL; } BT_DBG("argv[0] %s val %s", argv[0], val ? val : "(null)"); for (i = 0; i < ARRAY_SIZE(settings); i++) { if (!strcmp(settings[i].name, argv[0])) { argc--; argv++; return settings[i].func(argc, argv, val); } } BT_WARN("No matching handler for key %s", argv[0]); return -ENOENT; } static void commit_mod(struct bt_mesh_model *mod, struct bt_mesh_elem *elem, bool vnd, bool primary, void *user_data) { if (mod->pub && mod->pub->update && mod->pub->addr != BT_MESH_ADDR_UNASSIGNED) { int32_t ms = bt_mesh_model_pub_period_get(mod); if (ms) { BT_DBG("Starting publish timer (period %u ms)", (unsigned) ms); k_delayed_work_submit(&mod->pub->timer, ms); } } if (!IS_ENABLED(CONFIG_BT_MESH_LOW_POWER)) { return; } for (int i = 0; i < ARRAY_SIZE(mod->groups); i++) { if (mod->groups[i] != BT_MESH_ADDR_UNASSIGNED) { bt_mesh_lpn_group_add(mod->groups[i]); } } } static int mesh_commit(void) { if (!bt_mesh_subnet_next(NULL)) { /* Nothing to do since we're not yet provisioned */ return 0; } if (IS_ENABLED(CONFIG_BT_MESH_PB_GATT)) { bt_mesh_proxy_prov_disable(true); } if (bt_mesh.ivu_duration < BT_MESH_IVU_MIN_HOURS) { k_delayed_work_submit(&bt_mesh.ivu_timer, BT_MESH_IVU_TIMEOUT); } bt_mesh_model_foreach(commit_mod, NULL); atomic_set_bit(bt_mesh.flags, BT_MESH_VALID); bt_mesh_start(); return 0; } /* Pending flags that use K_NO_WAIT as the storage timeout */ #define NO_WAIT_PENDING_BITS (BIT(BT_MESH_NET_PENDING) | \ BIT(BT_MESH_IV_PENDING) | \ BIT(BT_MESH_SEQ_PENDING)) /* Pending flags that use CONFIG_BT_MESH_STORE_TIMEOUT */ #define GENERIC_PENDING_BITS (BIT(BT_MESH_KEYS_PENDING) | \ BIT(BT_MESH_HB_PUB_PENDING) | \ BIT(BT_MESH_CFG_PENDING) | \ BIT(BT_MESH_MOD_PENDING)) static void schedule_store(int flag) { int32_t timeout, remaining; atomic_set_bit(bt_mesh.flags, flag); if (atomic_get(bt_mesh.flags) & NO_WAIT_PENDING_BITS) { timeout = K_NO_WAIT; } else if (atomic_test_bit(bt_mesh.flags, BT_MESH_RPL_PENDING) && (!(atomic_get(bt_mesh.flags) & GENERIC_PENDING_BITS) || (CONFIG_BT_MESH_RPL_STORE_TIMEOUT < CONFIG_BT_MESH_STORE_TIMEOUT))) { timeout = K_SECONDS(CONFIG_BT_MESH_RPL_STORE_TIMEOUT); } else { timeout = K_SECONDS(CONFIG_BT_MESH_STORE_TIMEOUT); } remaining = k_delayed_work_remaining_get(&pending_store); if (remaining && remaining < timeout) { BT_DBG("Not rescheduling due to existing earlier deadline"); return; } BT_DBG("Waiting %d seconds", (int) (timeout / MSEC_PER_SEC)); k_delayed_work_submit(&pending_store, timeout); } static void clear_iv(void) { int err; err = settings_save_one("bt_mesh/IV", NULL); if (err) { BT_ERR("Failed to clear IV"); } else { BT_DBG("Cleared IV"); } } static void clear_net(void) { int err; err = settings_save_one("bt_mesh/Net", NULL); if (err) { BT_ERR("Failed to clear Network"); } else { BT_DBG("Cleared Network"); } } static void store_pending_net(void) { char buf[BT_SETTINGS_SIZE(sizeof(struct net_val))]; struct net_val net; char *str; int err; BT_DBG("addr 0x%04x DevKey %s", bt_mesh_primary_addr(), bt_hex(bt_mesh.dev_key, 16)); net.primary_addr = bt_mesh_primary_addr(); memcpy(net.dev_key, bt_mesh.dev_key, 16); str = settings_str_from_bytes(&net, sizeof(net), buf, sizeof(buf)); if (!str) { BT_ERR("Unable to encode Network as value"); return; } BT_DBG("Saving Network as value %s", str); err = settings_save_one("bt_mesh/Net", str); if (err) { BT_ERR("Failed to store Network"); } else { BT_DBG("Stored Network"); } } void bt_mesh_store_net(void) { schedule_store(BT_MESH_NET_PENDING); } static void store_pending_iv(void) { char buf[BT_SETTINGS_SIZE(sizeof(struct iv_val))]; struct iv_val iv; char *str; int err; iv.iv_index = bt_mesh.iv_index; iv.iv_update = atomic_test_bit(bt_mesh.flags, BT_MESH_IVU_IN_PROGRESS); iv.iv_duration = bt_mesh.ivu_duration; str = settings_str_from_bytes(&iv, sizeof(iv), buf, sizeof(buf)); if (!str) { BT_ERR("Unable to encode IV as value"); return; } BT_DBG("Saving IV as value %s", str); err = settings_save_one("bt_mesh/IV", str); if (err) { BT_ERR("Failed to store IV"); } else { BT_DBG("Stored IV"); } } void bt_mesh_store_iv(bool only_duration) { schedule_store(BT_MESH_IV_PENDING); if (!only_duration) { /* Always update Seq whenever IV changes */ schedule_store(BT_MESH_SEQ_PENDING); } } static void store_pending_seq(void) { char buf[BT_SETTINGS_SIZE(sizeof(struct seq_val))]; struct seq_val seq; char *str; int err; sys_put_le24(bt_mesh.seq, seq.val); str = settings_str_from_bytes(&seq, sizeof(seq), buf, sizeof(buf)); if (!str) { BT_ERR("Unable to encode Seq as value"); return; } BT_DBG("Saving Seq as value %s", str); err = settings_save_one("bt_mesh/Seq", str); if (err) { BT_ERR("Failed to store Seq"); } else { BT_DBG("Stored Seq"); } } void bt_mesh_store_seq(void) { if (CONFIG_BT_MESH_SEQ_STORE_RATE && (bt_mesh.seq % CONFIG_BT_MESH_SEQ_STORE_RATE)) { return; } schedule_store(BT_MESH_SEQ_PENDING); } static void store_rpl(struct bt_mesh_rpl *entry) { char buf[BT_SETTINGS_SIZE(sizeof(struct rpl_val))]; struct rpl_val rpl; char path[18]; char *str; int err; BT_DBG("src 0x%04x seq 0x%06x old_iv %u", entry->src, (unsigned) entry->seq, entry->old_iv); rpl.seq = entry->seq; rpl.old_iv = entry->old_iv; str = settings_str_from_bytes(&rpl, sizeof(rpl), buf, sizeof(buf)); if (!str) { BT_ERR("Unable to encode RPL as value"); return; } snprintk(path, sizeof(path), "bt_mesh/RPL/%x", entry->src); BT_DBG("Saving RPL %s as value %s", path, str); err = settings_save_one(path, str); if (err) { BT_ERR("Failed to store RPL"); } else { BT_DBG("Stored RPL"); } } static void clear_rpl(struct bt_mesh_rpl *rpl, void *user_data) { int err; char path[18]; if (!rpl->src) { return; } snprintk(path, sizeof(path), "bt/mesh/RPL/%x", rpl->src); err = settings_save_one(path, NULL); if (err) { BT_ERR("Failed to clear RPL"); } else { BT_DBG("Cleared RPL"); } (void)memset(rpl, 0, sizeof(*rpl)); } static void store_pending_rpl(struct bt_mesh_rpl *rpl, void *user_data) { BT_DBG(""); if (rpl->store) { rpl->store = false; store_rpl(rpl); } } static void store_pending_hb_pub(void) { char buf[BT_SETTINGS_SIZE(sizeof(struct hb_pub_val))]; struct bt_mesh_hb_pub pub; struct hb_pub_val val; char *str; int err; bt_mesh_hb_pub_get(&pub); if (pub.dst == BT_MESH_ADDR_UNASSIGNED) { str = NULL; } else { val.indefinite = (pub.count == 0xffff); val.dst = pub.dst; val.period = bt_mesh_hb_log(pub.period); val.ttl = pub.ttl; val.feat = pub.feat; val.net_idx = pub.net_idx; str = settings_str_from_bytes(&val, sizeof(val), buf, sizeof(buf)); if (!str) { BT_ERR("Unable to encode hb pub as value"); return; } } BT_DBG("Saving Heartbeat Publication as value %s", str ? str : "(null)"); err = settings_save_one("bt_mesh/HBPub", str); if (err) { BT_ERR("Failed to store Heartbeat Publication"); } else { BT_DBG("Stored Heartbeat Publication"); } } static void store_pending_cfg(void) { char buf[BT_SETTINGS_SIZE(sizeof(struct cfg_val))]; struct cfg_val val; char *str; int err; val.net_transmit = bt_mesh_net_transmit_get(); val.relay = bt_mesh_relay_get(); val.relay_retransmit = bt_mesh_relay_retransmit_get(); val.beacon = bt_mesh_beacon_enabled(); val.gatt_proxy = bt_mesh_gatt_proxy_get(); val.frnd = bt_mesh_friend_get(); val.default_ttl = bt_mesh_default_ttl_get(); str = settings_str_from_bytes(&val, sizeof(val), buf, sizeof(buf)); if (!str) { BT_ERR("Unable to encode configuration as value"); return; } BT_DBG("Saving configuration as value %s", str); err = settings_save_one("bt_mesh/Cfg", str); if (err) { BT_ERR("Failed to store configuration"); } else { BT_DBG("Stored configuration"); } } static void clear_cfg(void) { int err; err = settings_save_one("bt_mesh/Cfg", NULL); if (err) { BT_ERR("Failed to clear configuration"); } else { BT_DBG("Cleared configuration"); } } static void clear_app_key(uint16_t app_idx) { char path[20]; int err; BT_DBG("AppKeyIndex 0x%03x", app_idx); snprintk(path, sizeof(path), "bt_mesh/AppKey/%x", app_idx); err = settings_save_one(path, NULL); if (err) { BT_ERR("Failed to clear AppKeyIndex 0x%03x", app_idx); } else { BT_DBG("Cleared AppKeyIndex 0x%03x", app_idx); } } static void clear_net_key(uint16_t net_idx) { char path[20]; int err; BT_DBG("NetKeyIndex 0x%03x", net_idx); snprintk(path, sizeof(path), "bt_mesh/NetKey/%x", net_idx); err = settings_save_one(path, NULL); if (err) { BT_ERR("Failed to clear NetKeyIndex 0x%03x", net_idx); } else { BT_DBG("Cleared NetKeyIndex 0x%03x", net_idx); } } static void store_subnet(uint16_t net_idx) { const struct bt_mesh_subnet *sub; struct net_key_val key; char buf[BT_SETTINGS_SIZE(sizeof(struct app_key_val))]; char path[20]; char *str; int err; sub = bt_mesh_subnet_get(net_idx); if (!sub) { BT_WARN("NetKeyIndex 0x%03x not found", net_idx); return; } BT_DBG("NetKeyIndex 0x%03x", net_idx); snprintk(path, sizeof(path), "bt/mesh/NetKey/%x", net_idx); memcpy(&key.val[0], sub->keys[0].net, 16); memcpy(&key.val[1], sub->keys[1].net, 16); key.kr_flag = 0U; /* Deprecated */ key.kr_phase = sub->kr_phase; str = settings_str_from_bytes(&key, sizeof(key), buf, sizeof(buf)); if (!str) { BT_ERR("Unable to encode AppKey as value"); return; } err = settings_save_one(path, str); if (err) { BT_ERR("Failed to store NetKey"); } else { BT_DBG("Stored NetKey"); } } static void store_app(uint16_t app_idx) { const struct bt_mesh_app_key *app; char buf[BT_SETTINGS_SIZE(sizeof(struct app_key_val))]; struct app_key_val key; char path[20]; char *str; int err; snprintk(path, sizeof(path), "bt/mesh/AppKey/%x", app_idx); app = bt_mesh_app_key_get(app_idx); if (!app) { BT_WARN("ApKeyIndex 0x%03x not found", app_idx); return; } key.net_idx = app->net_idx, key.updated = app->updated, memcpy(key.val[0], app->keys[0].val, 16); memcpy(key.val[1], app->keys[1].val, 16); str = settings_str_from_bytes(&key, sizeof(key), buf, sizeof(buf)); if (!str) { BT_ERR("Unable to encode AppKey as value"); return; } snprintk(path, sizeof(path), "bt_mesh/AppKey/%x", app->app_idx); BT_DBG("Saving AppKey %s as value %s", path, str); err = settings_save_one(path, str); if (err) { BT_ERR("Failed to store AppKey"); } else { BT_DBG("Stored AppKey"); } } static void store_pending_keys(void) { int i; for (i = 0; i < ARRAY_SIZE(key_updates); i++) { struct key_update *update = &key_updates[i]; if (!update->valid) { continue; } if (update->clear) { if (update->app_key) { clear_app_key(update->key_idx); } else { clear_net_key(update->key_idx); } } else { store_subnet(update->key_idx); } update->valid = 0; } } static void clear_cdb(void) { int err; err = settings_save_one("bt/mesh/cdb/Net", NULL); if (err) { BT_ERR("Failed to clear Network"); } else { BT_DBG("Cleared Network"); } } static void store_pending_cdb(void) { char buf[BT_SETTINGS_SIZE(sizeof(struct cdb_net_val))]; struct cdb_net_val net; int err; char *str; BT_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); str = settings_str_from_bytes(&net, sizeof(net), buf, sizeof(buf)); if (!str) { BT_ERR("Unable to encode Network as value"); return; } err = settings_save_one("bt/mesh/cdb/Net", str); if (err) { BT_ERR("Failed to store Network value"); } else { BT_DBG("Stored Network value"); } } static void store_cdb_node(const struct bt_mesh_cdb_node *node) { char buf[BT_SETTINGS_SIZE(sizeof(struct node_val))]; struct node_val val; char path[30]; char *str; 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); str = settings_str_from_bytes(&val, sizeof(val), buf, sizeof(buf)); if (!str) { BT_ERR("Unable to encode Node as value"); return; } err = settings_save_one(path, str); if (err) { BT_ERR("Failed to store Node %s value", path); } else { BT_DBG("Stored Node %s value", path); } } static void clear_cdb_node(uint16_t addr) { char path[30]; int err; BT_DBG("Node 0x%04x", addr); snprintk(path, sizeof(path), "bt/mesh/cdb/Node/%x", addr); err = settings_save_one(path, NULL); if (err) { BT_ERR("Failed to clear Node 0x%04x", addr); } else { BT_DBG("Cleared Node 0x%04x", addr); } } static void store_pending_cdb_nodes(void) { int i; 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) { continue; } BT_DBG("addr: 0x%04x, clear: %d", update->addr, update->clear); if (update->clear) { clear_cdb_node(update->addr); } else { struct bt_mesh_cdb_node *node; node = bt_mesh_cdb_node_get(update->addr); if (node) { store_cdb_node(node); } else { BT_WARN("Node 0x%04x not found", update->addr); } } update->addr = BT_MESH_ADDR_UNASSIGNED; } } static void store_cdb_subnet(const struct bt_mesh_cdb_subnet *sub) { char buf[BT_SETTINGS_SIZE(sizeof(struct net_key_val))]; struct net_key_val key; char path[30]; int err; char *str; BT_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 = sub->kr_flag; key.kr_phase = sub->kr_phase; snprintk(path, sizeof(path), "bt/mesh/cdb/Subnet/%x", sub->net_idx); str = settings_str_from_bytes(&key, sizeof(key), buf, sizeof(buf)); if (!str) { BT_ERR("Unable to encode Subnet as value"); return; } err = settings_save_one(path, str); if (err) { BT_ERR("Failed to store Subnet value"); } else { BT_DBG("Stored Subnet value"); } } static void clear_cdb_subnet(uint16_t net_idx) { char path[30]; int err; BT_DBG("NetKeyIndex 0x%03x", net_idx); snprintk(path, sizeof(path), "bt/mesh/cdb/Subnet/%x", net_idx); err = settings_save_one(path, NULL); if (err) { BT_ERR("Failed to clear NetKeyIndex 0x%03x", net_idx); } else { BT_DBG("Cleared NetKeyIndex 0x%03x", net_idx); } } static void store_cdb_app_key(const struct bt_mesh_cdb_app_key *app) { char buf[BT_SETTINGS_SIZE(sizeof(struct app_key_val))]; struct app_key_val key; char path[30]; int err; char *str; 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); str = settings_str_from_bytes(&key, sizeof(key), buf, sizeof(buf)); err = settings_save_one(path, str); if (err) { BT_ERR("Failed to store AppKey"); } else { BT_DBG("Stored AppKey"); } } 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_save_one(path, NULL); if (err) { BT_ERR("Failed to clear AppKeyIndex 0x%03x", app_idx); } else { BT_DBG("Cleared AppKeyIndex 0x%03x", app_idx); } } static void store_pending_cdb_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; } 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 { BT_WARN("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 { BT_WARN("NetKeyIndex 0x%03x not found", update->key_idx); } } } update->valid = 0U; } } 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 encode_mod_path(struct bt_mesh_model *mod, bool vnd, const char *key, char *path, size_t path_len) { uint16_t mod_key = (((uint16_t)mod->elem_idx << 8) | mod->mod_idx); if (vnd) { snprintk(path, path_len, "bt_mesh/v/%x/%s", mod_key, key); } else { snprintk(path, path_len, "bt_mesh/s/%x/%s", mod_key, key); } } static void store_pending_mod_bind(struct bt_mesh_model *mod, bool vnd) { uint16_t keys[CONFIG_BT_MESH_MODEL_KEY_COUNT]; char buf[BT_SETTINGS_SIZE(sizeof(keys))]; char path[20]; int i, count, err; char *val; for (i = 0, count = 0; i < ARRAY_SIZE(mod->keys); i++) { if (mod->keys[i] != BT_MESH_KEY_UNUSED) { keys[count++] = mod->keys[i]; } } if (count) { val = settings_str_from_bytes(keys, count * sizeof(keys[0]), buf, sizeof(buf)); if (!val) { BT_ERR("Unable to encode model bindings as value"); return; } } else { val = NULL; } encode_mod_path(mod, vnd, "bind", path, sizeof(path)); BT_DBG("Saving %s as %s", path, val ? val : "(null)"); err = settings_save_one(path, val); if (err) { BT_ERR("Failed to store bind"); } else { BT_DBG("Stored bind"); } } static void store_pending_mod_sub(struct bt_mesh_model *mod, bool vnd) { uint16_t groups[CONFIG_BT_MESH_MODEL_GROUP_COUNT]; char buf[BT_SETTINGS_SIZE(sizeof(groups))]; char path[20]; int i, count, err; char *val; for (i = 0, count = 0; i < CONFIG_BT_MESH_MODEL_GROUP_COUNT; i++) { if (mod->groups[i] != BT_MESH_ADDR_UNASSIGNED) { groups[count++] = mod->groups[i]; } } if (count) { val = settings_str_from_bytes(groups, count * sizeof(groups[0]), buf, sizeof(buf)); if (!val) { BT_ERR("Unable to encode model subscription as value"); return; } } else { val = NULL; } encode_mod_path(mod, vnd, "sub", path, sizeof(path)); BT_DBG("Saving %s as %s", path, val ? val : "(null)"); err = settings_save_one(path, val); if (err) { BT_ERR("Failed to store sub"); } else { BT_DBG("Stored sub"); } } static void store_pending_mod_pub(struct bt_mesh_model *mod, bool vnd) { char buf[BT_SETTINGS_SIZE(sizeof(struct mod_pub_val))]; struct mod_pub_val pub; char path[20]; char *val; int err; if (!mod->pub || mod->pub->addr == BT_MESH_ADDR_UNASSIGNED) { val = NULL; } else { pub.addr = mod->pub->addr; pub.key = mod->pub->key; pub.ttl = mod->pub->ttl; pub.retransmit = mod->pub->retransmit; pub.period = mod->pub->period; pub.period_div = mod->pub->period_div; pub.cred = mod->pub->cred; val = settings_str_from_bytes(&pub, sizeof(pub), buf, sizeof(buf)); if (!val) { BT_ERR("Unable to encode model publication as value"); return; } } encode_mod_path(mod, vnd, "pub", path, sizeof(path)); BT_DBG("Saving %s as %s", path, val ? val : "(null)"); err = settings_save_one(path, val); if (err) { BT_ERR("Failed to store pub"); } else { BT_DBG("Stored pub"); } } static void store_pending_mod(struct bt_mesh_model *mod, struct bt_mesh_elem *elem, bool vnd, bool primary, void *user_data) { if (!mod->flags) { return; } if (mod->flags & BT_MESH_MOD_BIND_PENDING) { mod->flags &= ~BT_MESH_MOD_BIND_PENDING; store_pending_mod_bind(mod, vnd); } if (mod->flags & BT_MESH_MOD_SUB_PENDING) { mod->flags &= ~BT_MESH_MOD_SUB_PENDING; store_pending_mod_sub(mod, vnd); } if (mod->flags & BT_MESH_MOD_PUB_PENDING) { mod->flags &= ~BT_MESH_MOD_PUB_PENDING; store_pending_mod_pub(mod, vnd); } } #define IS_VA_DEL(_label) ((_label)->ref == 0) static void store_pending_va(void) { char buf[BT_SETTINGS_SIZE(sizeof(struct va_val))]; struct bt_mesh_va *lab; struct va_val va; char path[18]; char *val; uint16_t i; int err = 0; for (i = 0; (lab = bt_mesh_va_get(i)) != NULL; i++) { if (!lab->changed) { continue; } lab->changed = 0U; snprintk(path, sizeof(path), "bt_mesh/Va/%x", i); if (IS_VA_DEL(lab)) { val = NULL; } else { va.ref = lab->ref; va.addr = lab->addr; memcpy(va.uuid, lab->uuid, 16); val = settings_str_from_bytes(&va, sizeof(va), buf, sizeof(buf)); if (!val) { BT_ERR("Unable to encode model publication as value"); return; } err = settings_save_one(path, val); } if (err) { BT_ERR("Failed to %s %s value (err %d)", IS_VA_DEL(lab) ? "delete" : "store", path, err); } else { BT_DBG("%s %s value", IS_VA_DEL(lab) ? "Deleted" : "Stored", path); } } } static void store_pending(struct ble_npl_event *work) { BT_DBG(""); if (atomic_test_and_clear_bit(bt_mesh.flags, BT_MESH_RPL_PENDING)) { if (atomic_test_bit(bt_mesh.flags, BT_MESH_VALID)) { bt_mesh_rpl_foreach(store_pending_rpl, NULL); } else { bt_mesh_rpl_foreach(clear_rpl, NULL); } } if (atomic_test_and_clear_bit(bt_mesh.flags, BT_MESH_KEYS_PENDING)) { store_pending_keys(); } if (atomic_test_and_clear_bit(bt_mesh.flags, BT_MESH_NET_PENDING)) { if (atomic_test_bit(bt_mesh.flags, BT_MESH_VALID)) { store_pending_net(); } else { clear_net(); } } if (atomic_test_and_clear_bit(bt_mesh.flags, BT_MESH_IV_PENDING)) { if (atomic_test_bit(bt_mesh.flags, BT_MESH_VALID)) { store_pending_iv(); } else { clear_iv(); } } if (atomic_test_and_clear_bit(bt_mesh.flags, BT_MESH_SEQ_PENDING)) { store_pending_seq(); } if (atomic_test_and_clear_bit(bt_mesh.flags, BT_MESH_HB_PUB_PENDING)) { store_pending_hb_pub(); } if (atomic_test_and_clear_bit(bt_mesh.flags, BT_MESH_CFG_PENDING)) { if (atomic_test_bit(bt_mesh.flags, BT_MESH_VALID)) { store_pending_cfg(); } else { clear_cfg(); } } 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(); } if (IS_ENABLED(CONFIG_BT_MESH_CDB)) { 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_pending_cdb(); } else { clear_cdb(); } } if (atomic_test_and_clear_bit(bt_mesh_cdb.flags, BT_MESH_CDB_NODES_PENDING)) { store_pending_cdb_nodes(); } if (atomic_test_and_clear_bit(bt_mesh_cdb.flags, BT_MESH_CDB_KEYS_PENDING)) { store_pending_cdb_keys(); } } } void bt_mesh_store_rpl(struct bt_mesh_rpl *entry) { entry->store = true; schedule_store(BT_MESH_RPL_PENDING); } static struct key_update *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(key_updates); i++) { struct key_update *update = &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; } void bt_mesh_store_subnet(uint16_t net_idx) { struct key_update *update, *free_slot; BT_DBG("NetKeyIndex 0x%03x", net_idx); update = key_update_find(false, net_idx, &free_slot); if (update) { update->clear = 0; schedule_store(BT_MESH_KEYS_PENDING); return; } if (!free_slot) { store_subnet(net_idx); return; } free_slot->valid = 1; free_slot->key_idx = net_idx; free_slot->app_key = 0; free_slot->clear = 0; schedule_store(BT_MESH_KEYS_PENDING); } void bt_mesh_store_app_key(uint16_t app_idx) { struct key_update *update, *free_slot; BT_DBG("AppKeyIndex 0x%03x", app_idx); update = key_update_find(true, app_idx, &free_slot); if (update) { update->clear = 0; schedule_store(BT_MESH_KEYS_PENDING); return; } if (!free_slot) { store_app(app_idx); return; } free_slot->valid = 1; free_slot->key_idx = app_idx; free_slot->app_key = 1; free_slot->clear = 0; schedule_store(BT_MESH_KEYS_PENDING); } void bt_mesh_store_hb_pub(void) { schedule_store(BT_MESH_HB_PUB_PENDING); } void bt_mesh_store_cfg(void) { schedule_store(BT_MESH_CFG_PENDING); } void bt_mesh_clear_net(void) { schedule_store(BT_MESH_NET_PENDING); schedule_store(BT_MESH_IV_PENDING); schedule_store(BT_MESH_CFG_PENDING); } void bt_mesh_clear_subnet(uint16_t net_idx) { struct key_update *update, *free_slot; BT_DBG("NetKeyIndex 0x%03x", net_idx); update = key_update_find(false, net_idx, &free_slot); if (update) { update->clear = 1; schedule_store(BT_MESH_KEYS_PENDING); return; } if (!free_slot) { clear_net_key(net_idx); return; } free_slot->valid = 1; free_slot->key_idx = net_idx; free_slot->app_key = 0; free_slot->clear = 1; schedule_store(BT_MESH_KEYS_PENDING); } void bt_mesh_clear_app_key(uint16_t app_idx) { struct key_update *update, *free_slot; BT_DBG("AppKeyIndex 0x%03x", app_idx); update = key_update_find(true, app_idx, &free_slot); if (update) { update->clear = 1; schedule_store(BT_MESH_KEYS_PENDING); return; } if (!free_slot) { clear_app_key(app_idx); return; } free_slot->valid = 1; free_slot->key_idx = app_idx; free_slot->app_key = 1; free_slot->clear = 1; schedule_store(BT_MESH_KEYS_PENDING); } void bt_mesh_clear_rpl(void) { schedule_store(BT_MESH_RPL_PENDING); } void bt_mesh_store_mod_bind(struct bt_mesh_model *mod) { mod->flags |= BT_MESH_MOD_BIND_PENDING; schedule_store(BT_MESH_MOD_PENDING); } void bt_mesh_store_mod_sub(struct bt_mesh_model *mod) { mod->flags |= BT_MESH_MOD_SUB_PENDING; schedule_store(BT_MESH_MOD_PENDING); } void bt_mesh_store_mod_pub(struct bt_mesh_model *mod) { mod->flags |= BT_MESH_MOD_PUB_PENDING; schedule_store(BT_MESH_MOD_PENDING); } void bt_mesh_store_label(void) { schedule_store(BT_MESH_VA_PENDING); } static void schedule_cdb_store(int flag) { atomic_set_bit(bt_mesh_cdb.flags, flag); k_delayed_work_submit(&pending_store, K_NO_WAIT); } void bt_mesh_store_cdb(void) { schedule_cdb_store(BT_MESH_CDB_SUBNET_PENDING); } void bt_mesh_store_cdb_node(const struct bt_mesh_cdb_node *node) { struct node_update *update, *free_slot; BT_DBG("Node 0x%04x", node->addr); update = cdb_node_update_find(node->addr, &free_slot); if (update) { update->clear = false; schedule_cdb_store(BT_MESH_CDB_NODES_PENDING); return; } if (!free_slot) { store_cdb_node(node); return; } free_slot->addr = node->addr; free_slot->clear = false; schedule_cdb_store(BT_MESH_CDB_NODES_PENDING); } void bt_mesh_clear_cdb_node(struct bt_mesh_cdb_node *node) { struct node_update *update, *free_slot; BT_DBG("Node 0x%04x", node->addr); update = cdb_node_update_find(node->addr, &free_slot); if (update) { update->clear = true; schedule_cdb_store(BT_MESH_CDB_NODES_PENDING); return; } if (!free_slot) { clear_cdb_node(node->addr); return; } free_slot->addr = node->addr; free_slot->clear = true; schedule_cdb_store(BT_MESH_CDB_NODES_PENDING); } /* TODO: Could be shared with key_update_find? */ 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; } void bt_mesh_store_cdb_subnet(const struct bt_mesh_cdb_subnet *sub) { struct key_update *update, *free_slot; BT_DBG("NetKeyIndex 0x%03x", sub->net_idx); update = cdb_key_update_find(false, sub->net_idx, &free_slot); if (update) { update->clear = 0U; schedule_cdb_store(BT_MESH_CDB_KEYS_PENDING); return; } if (!free_slot) { store_cdb_subnet(sub); return; } free_slot->valid = 1U; free_slot->key_idx = sub->net_idx; free_slot->app_key = 0U; free_slot->clear = 0U; schedule_cdb_store(BT_MESH_CDB_KEYS_PENDING); } void bt_mesh_clear_cdb_subnet(struct bt_mesh_cdb_subnet *sub) { struct key_update *update, *free_slot; BT_DBG("NetKeyIndex 0x%03x", sub->net_idx); update = cdb_key_update_find(false, sub->net_idx, &free_slot); if (update) { update->clear = 1U; schedule_cdb_store(BT_MESH_CDB_KEYS_PENDING); return; } if (!free_slot) { 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 = 1U; schedule_cdb_store(BT_MESH_CDB_KEYS_PENDING); } void bt_mesh_store_cdb_app_key(const struct bt_mesh_cdb_app_key *key) { struct key_update *update, *free_slot; BT_DBG("AppKeyIndex 0x%03x", key->app_idx); update = cdb_key_update_find(true, key->app_idx, &free_slot); if (update) { update->clear = 0U; schedule_cdb_store(BT_MESH_CDB_KEYS_PENDING); return; } if (!free_slot) { store_cdb_app_key(key); return; } free_slot->valid = 1U; free_slot->key_idx = key->app_idx; free_slot->app_key = 1U; free_slot->clear = 0U; schedule_cdb_store(BT_MESH_CDB_KEYS_PENDING); } void bt_mesh_clear_cdb_app_key(struct bt_mesh_cdb_app_key *key) { struct key_update *update, *free_slot; BT_DBG("AppKeyIndex 0x%03x", key->app_idx); update = cdb_key_update_find(true, key->app_idx, &free_slot); if (update) { update->clear = 1U; schedule_cdb_store(BT_MESH_CDB_KEYS_PENDING); return; } if (!free_slot) { 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 = 1U; schedule_cdb_store(BT_MESH_CDB_KEYS_PENDING); } int bt_mesh_model_data_store(struct bt_mesh_model *mod, bool vnd, const char *name, const void *data, size_t data_len) { char path[30]; char buf[BT_SETTINGS_SIZE(sizeof(struct mod_pub_val))]; char *val; int err; encode_mod_path(mod, vnd, "data", path, sizeof(path)); if (name) { strcat(path, "/"); strncat(path, name, 8); } if (data_len) { val = settings_str_from_bytes(data, data_len, buf, sizeof(buf)); if (!val) { BT_ERR("Unable to encode model publication as value"); return -EINVAL; } err = settings_save_one(path, val); } else { err = settings_save_one(path, NULL); } if (err) { BT_ERR("Failed to store %s value", path); } else { BT_DBG("Stored %s value", path); } return err; } static struct conf_handler bt_mesh_settings_conf_handler = { .ch_name = "bt_mesh", .ch_get = NULL, .ch_set = mesh_set, .ch_commit = mesh_commit, .ch_export = NULL, }; void bt_mesh_settings_init(void) { int rc; rc = conf_register(&bt_mesh_settings_conf_handler); SYSINIT_PANIC_ASSERT_MSG(rc == 0, "Failed to register bt_mesh_settings conf"); k_delayed_work_init(&pending_store, store_pending); } #endif /* MYNEWT_VAL(BLE_MESH_SETTINGS) */