| /* |
| * Copyright (c) 2019 Tobias Svehagen |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #include <zephyr.h> |
| #include <string.h> |
| |
| #include <bluetooth/mesh.h> |
| |
| #define BT_DBG_ENABLED IS_ENABLED(CONFIG_BT_MESH_DEBUG_CDB) |
| #define LOG_MODULE_NAME bt_mesh_cdb |
| #include "common/log.h" |
| |
| #include "mesh.h" |
| #include "net.h" |
| #include "settings.h" |
| |
| 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(u16_t addr_start, u8_t num_elem, u16_t *next) |
| { |
| u16_t addr_end = addr_start + num_elem - 1; |
| u16_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 u16_t find_lowest_free_addr(u8_t num_elem) |
| { |
| u16_t addr = 1, 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; |
| } |
| |
| int bt_mesh_cdb_create(const u8_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; |
| |
| if (IS_ENABLED(CONFIG_BT_SETTINGS)) { |
| bt_mesh_store_cdb(); |
| bt_mesh_store_cdb_subnet(sub); |
| } |
| |
| 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)) { |
| bt_mesh_store_cdb(); |
| } |
| } |
| |
| void bt_mesh_cdb_iv_update(u32_t iv_index, bool iv_update) |
| { |
| BT_DBG("Updating IV index to %d\n", iv_index); |
| |
| 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)) { |
| bt_mesh_store_cdb(); |
| } |
| } |
| |
| struct bt_mesh_cdb_subnet *bt_mesh_cdb_subnet_alloc(u16_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) |
| { |
| BT_DBG("NetIdx 0x%03x store %u", sub->net_idx, store); |
| |
| if (IS_ENABLED(CONFIG_BT_SETTINGS) && store) { |
| bt_mesh_clear_cdb_subnet(sub); |
| } |
| |
| sub->net_idx = BT_MESH_KEY_UNUSED; |
| memset(sub->keys, 0, sizeof(sub->keys)); |
| } |
| |
| struct bt_mesh_cdb_subnet *bt_mesh_cdb_subnet_get(u16_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) |
| { |
| bt_mesh_store_cdb_subnet(sub); |
| } |
| |
| u8_t bt_mesh_cdb_subnet_flags(const struct bt_mesh_cdb_subnet *sub) |
| { |
| u8_t flags = 0x00; |
| |
| if (sub && sub->kr_flag) { |
| 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 u8_t uuid[16], u16_t addr, |
| u8_t num_elem, u16_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_is_free(addr, num_elem, NULL) < 0) { |
| BT_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) |
| { |
| BT_DBG("Node addr 0x%04x store %u", node->addr, store); |
| |
| if (IS_ENABLED(CONFIG_BT_SETTINGS) && store) { |
| bt_mesh_clear_cdb_node(node); |
| } |
| |
| 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(u16_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) |
| { |
| bt_mesh_store_cdb_node(node); |
| } |
| |
| 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(u16_t net_idx, |
| u16_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) |
| { |
| BT_DBG("AppIdx 0x%03x store %u", key->app_idx, store); |
| |
| if (IS_ENABLED(CONFIG_BT_SETTINGS) && store) { |
| bt_mesh_clear_cdb_app_key(key); |
| } |
| |
| key->net_idx = BT_MESH_ADDR_UNASSIGNED; |
| memset(key->keys, 0, sizeof(key->keys)); |
| } |
| |
| struct bt_mesh_cdb_app_key *bt_mesh_cdb_app_key_get(u16_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) |
| { |
| bt_mesh_store_cdb_app_key(key); |
| } |