summaryrefslogtreecommitdiff
path: root/src/libs/mynewt-nimble/nimble/host/mesh/src/nodes.c
blob: 127ef21e4a65ad953e6cf20d3a8309d16b701e55 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
/*
 * Copyright (c) 2019 Tobias Svehagen
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#include "syscfg/syscfg.h"
#define MESH_LOG_MODULE BLE_MESH_PROV_LOG

#if MYNEWT_VAL(BLE_MESH_PROVISIONER)

#include "mesh/mesh.h"

#include "mesh_priv.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));
}

#endif