blob: 4d6f3d639bfa64e9a6d0420698ad9cdbe91dabf5 [file] [log] [blame]
/*
* 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_PROV)
#define LOG_MODULE_NAME bt_mesh_node
#include "common/log.h"
#include "mesh.h"
#include "net.h"
#include "access.h"
#include "settings.h"
/*
* 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)
{
const struct bt_mesh_comp *comp = bt_mesh_comp_get();
u16_t addr_end = addr_start + num_elem - 1;
u16_t other_start, other_end;
int i;
if (comp == NULL) {
return -EINVAL;
}
if (!BT_MESH_ADDR_IS_UNICAST(addr_start) ||
!BT_MESH_ADDR_IS_UNICAST(addr_end) ||
num_elem == 0 || next == NULL) {
return -EINVAL;
}
other_start = bt_mesh_primary_addr();
other_end = other_start + comp->elem_count - 1;
/* Compare with local element addresses */
if (!(addr_end < other_start || addr_start > other_end)) {
*next = other_end + 1;
return -EAGAIN;
}
for (i = 0; i < ARRAY_SIZE(bt_mesh.nodes); i++) {
struct bt_mesh_node *node = &bt_mesh.nodes[i];
if (node->net_idx == BT_MESH_KEY_UNUSED) {
continue;
}
other_start = node->addr;
other_end = other_start + node->num_elem - 1;
if (!(addr_end < other_start || addr_start > other_end)) {
*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.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;
}
struct bt_mesh_node *bt_mesh_node_find(u16_t addr)
{
int i;
for (i = 0; i < ARRAY_SIZE(bt_mesh.nodes); i++) {
struct bt_mesh_node *node = &bt_mesh.nodes[i];
if (addr >= node->addr &&
addr <= node->addr + node->num_elem - 1) {
return node;
}
}
return NULL;
}
struct bt_mesh_node *bt_mesh_node_alloc(u16_t addr, u8_t num_elem,
u16_t net_idx)
{
int i;
BT_DBG("");
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)) {
return NULL;
}
for (i = 0; i < ARRAY_SIZE(bt_mesh.nodes); i++) {
struct bt_mesh_node *node = &bt_mesh.nodes[i];
if (node->addr == BT_MESH_ADDR_UNASSIGNED) {
node->addr = addr;
node->num_elem = num_elem;
node->net_idx = net_idx;
return node;
}
}
return NULL;
}
void bt_mesh_node_del(struct bt_mesh_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_node(node);
}
node->addr = BT_MESH_ADDR_UNASSIGNED;
(void)memset(node->dev_key, 0, sizeof(node->dev_key));
}