Bluetooth: Mesh: Support for storing model bindings persistently

Add support for storing the bound App Keys for each model
persistently. The bindings are stored under the settings key
bt/mesh/s/<mod id>/bind for SIG models and bt/mesh/v/<mod id>/bind for
vendor models.

Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
This commit is contained in:
Johan Hedberg 2018-05-09 21:34:52 +03:00 committed by Johan Hedberg
commit c3d5a0da62
5 changed files with 153 additions and 0 deletions

View file

@ -6,6 +6,11 @@
* SPDX-License-Identifier: Apache-2.0
*/
/* bt_mesh_model.flags */
enum {
BT_MESH_MOD_BIND_PENDING = BIT(0),
};
void bt_mesh_elem_register(struct bt_mesh_elem *elem, u8_t count);
u8_t bt_mesh_elem_count(void);

View file

@ -322,6 +322,11 @@ static u8_t mod_bind(struct bt_mesh_model *model, u16_t key_idx)
for (i = 0; i < ARRAY_SIZE(model->keys); i++) {
if (model->keys[i] == BT_MESH_KEY_UNUSED) {
model->keys[i] = key_idx;
if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
bt_mesh_store_mod_bind(model);
}
return STATUS_SUCCESS;
}
}
@ -346,6 +351,10 @@ static u8_t mod_unbind(struct bt_mesh_model *model, u16_t key_idx)
model->keys[i] = BT_MESH_KEY_UNUSED;
if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
bt_mesh_store_mod_bind(model);
}
if (model->pub && model->pub->key == key_idx) {
_mod_pub_set(model, BT_MESH_ADDR_UNASSIGNED,
0, 0, 0, 0, 0);

View file

@ -204,6 +204,7 @@ enum {
BT_MESH_NET_PENDING,
BT_MESH_IV_PENDING,
BT_MESH_SEQ_PENDING,
BT_MESH_MOD_PENDING,
/* Don't touch - intentionally last */
BT_MESH_FLAG_COUNT,

View file

@ -409,6 +409,74 @@ static int app_key_set(int argc, char **argv, char *val)
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(bool vnd, int argc, char **argv, char *val)
{
struct bt_mesh_model *mod;
u8_t elem_idx, mod_idx;
u16_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);
}
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);
}
const struct mesh_setting {
const char *name;
int (*func)(int argc, char **argv, char *val);
@ -419,6 +487,8 @@ const struct mesh_setting {
{ "RPL", rpl_set },
{ "NetKey", net_key_set },
{ "AppKey", app_key_set },
{ "s", sig_mod_set },
{ "v", vnd_mod_set },
};
static int mesh_set(int argc, char **argv, char *val)
@ -821,6 +891,63 @@ static void store_pending_keys(void)
}
}
static void encode_mod_path(struct bt_mesh_model *mod, bool vnd,
const char *key, char *path, size_t path_len)
{
u16_t mod_key = (((u16_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)
{
u16_t keys[CONFIG_BT_MESH_MODEL_KEY_COUNT];
char buf[BT_SETTINGS_SIZE(sizeof(keys))];
char path[20];
int i, count;
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)");
settings_save_one(path, val);
}
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);
}
}
static void store_pending(struct k_work *work)
{
BT_DBG("");
@ -856,6 +983,10 @@ static void store_pending(struct k_work *work)
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_MOD_PENDING)) {
bt_mesh_model_foreach(store_pending_mod, NULL);
}
}
void bt_mesh_store_rpl(struct bt_mesh_rpl *entry)
@ -1008,6 +1139,12 @@ 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_settings_init(void)
{
k_delayed_work_init(&pending_store, store_pending);

View file

@ -10,6 +10,7 @@ void bt_mesh_store_seq(void);
void bt_mesh_store_rpl(struct bt_mesh_rpl *rpl);
void bt_mesh_store_subnet(struct bt_mesh_subnet *sub);
void bt_mesh_store_app_key(struct bt_mesh_app_key *key);
void bt_mesh_store_mod_bind(struct bt_mesh_model *mod);
void bt_mesh_clear_net(void);
void bt_mesh_clear_subnet(struct bt_mesh_subnet *sub);