zephyr/subsys/bluetooth/mesh/nodes.c
Tobias Svehagen 80669decce Bluetooth: Mesh: Add support for provisioner role over PB-ADV
Make it possible to provision devices over advertising bearer (PB-ADV).
Many messages in the provisioning protocol are the same for provisioner
and device so much of the code could be reused by only changing when
they are expected to arrive.

This introduces to concept of local and remote device keys. The models
for cfg_cli and cfg_srv have been updated to reflect this concept. Both
the send and receive path in the transport layer have been updated to
support encrypting/decrypting with local and remote device keys.

When a node has been provisioned it is stored in bt_mesh_net.nodes. If
CONFIG_BT_SETTINGS is enabled, they are also saved to settings. If the
callback node_added in bt_mesh_prov has been set, it will be called for
every node that gets provisioned. This includes when they are retrieved
from settings.

The configuration CONFIG_BT_MESH_NODE_COUNT controls how many nodes that
can be provisioned.

Signed-off-by: Tobias Svehagen <tobias.svehagen@gmail.com>
2019-10-30 13:08:09 +01:00

162 lines
3.6 KiB
C

/*
* 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));
}