1 /*
2 * Copyright (c) 2017 Intel Corporation
3 * Copyright (c) 2023 Nordic Semiconductor ASA
4 *
5 * SPDX-License-Identifier: Apache-2.0
6 */
7
8 #include <zephyr/kernel.h>
9 #include <errno.h>
10 #include <string.h>
11 #include <stdlib.h>
12
13 #include <zephyr/settings/settings.h>
14 #include <zephyr/net/buf.h>
15
16 #include <zephyr/bluetooth/mesh.h>
17
18 #include "common/bt_str.h"
19
20 #include "va.h"
21 #include "foundation.h"
22 #include "msg.h"
23 #include "net.h"
24 #include "crypto.h"
25 #include "settings.h"
26
27 #define LOG_LEVEL CONFIG_BT_MESH_TRANS_LOG_LEVEL
28 #include <zephyr/logging/log.h>
29 LOG_MODULE_REGISTER(bt_mesh_va);
30
31 static struct bt_mesh_va virtual_addrs[CONFIG_BT_MESH_LABEL_COUNT];
32
33 /* Virtual Address information for persistent storage. */
34 struct va_val {
35 uint16_t ref;
36 uint16_t addr;
37 uint8_t uuid[16];
38 } __packed;
39
va_store(struct bt_mesh_va * store)40 static void va_store(struct bt_mesh_va *store)
41 {
42 store->changed = 1U;
43
44 if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
45 bt_mesh_settings_store_schedule(BT_MESH_SETTINGS_VA_PENDING);
46 }
47 }
48
bt_mesh_va_add(const uint8_t uuid[16],const struct bt_mesh_va ** entry)49 uint8_t bt_mesh_va_add(const uint8_t uuid[16], const struct bt_mesh_va **entry)
50 {
51 struct bt_mesh_va *va = NULL;
52 int err;
53
54 for (int i = 0; i < ARRAY_SIZE(virtual_addrs); i++) {
55 if (!virtual_addrs[i].ref) {
56 if (!va) {
57 va = &virtual_addrs[i];
58 }
59
60 continue;
61 }
62
63 if (!memcmp(uuid, virtual_addrs[i].uuid,
64 ARRAY_SIZE(virtual_addrs[i].uuid))) {
65 if (entry) {
66 *entry = &virtual_addrs[i];
67 }
68 virtual_addrs[i].ref++;
69 va_store(&virtual_addrs[i]);
70 return STATUS_SUCCESS;
71 }
72 }
73
74 if (!va) {
75 return STATUS_INSUFF_RESOURCES;
76 }
77
78 memcpy(va->uuid, uuid, ARRAY_SIZE(va->uuid));
79 err = bt_mesh_virtual_addr(uuid, &va->addr);
80 if (err) {
81 va->addr = BT_MESH_ADDR_UNASSIGNED;
82 return STATUS_UNSPECIFIED;
83 }
84
85 va->ref = 1;
86 va_store(va);
87
88 if (entry) {
89 *entry = va;
90 }
91
92 return STATUS_SUCCESS;
93 }
94
bt_mesh_va_del(const uint8_t * uuid)95 uint8_t bt_mesh_va_del(const uint8_t *uuid)
96 {
97 struct bt_mesh_va *va;
98
99 if (CONFIG_BT_MESH_LABEL_COUNT == 0) {
100 return STATUS_CANNOT_REMOVE;
101 }
102
103 va = CONTAINER_OF(uuid, struct bt_mesh_va, uuid[0]);
104
105 if (!PART_OF_ARRAY(virtual_addrs, va) || va->ref == 0) {
106 return STATUS_CANNOT_REMOVE;
107 }
108
109 va->ref--;
110 va_store(va);
111
112 return STATUS_SUCCESS;
113 }
114
bt_mesh_va_uuid_get(uint16_t addr,const uint8_t * uuid,uint16_t * retaddr)115 const uint8_t *bt_mesh_va_uuid_get(uint16_t addr, const uint8_t *uuid, uint16_t *retaddr)
116 {
117 int i = 0;
118
119 if (CONFIG_BT_MESH_LABEL_COUNT == 0) {
120 return NULL;
121 }
122
123 if (uuid != NULL) {
124 struct bt_mesh_va *va;
125
126 va = CONTAINER_OF(uuid, struct bt_mesh_va, uuid[0]);
127 i = ARRAY_INDEX(virtual_addrs, va);
128 }
129
130 for (; i < ARRAY_SIZE(virtual_addrs); i++) {
131 if (virtual_addrs[i].ref &&
132 (virtual_addrs[i].addr == addr || addr == BT_MESH_ADDR_UNASSIGNED)) {
133 if (!uuid) {
134 LOG_DBG("Found Label UUID for 0x%04x: %s", addr,
135 bt_hex(virtual_addrs[i].uuid, 16));
136
137 if (retaddr) {
138 *retaddr = virtual_addrs[i].addr;
139 }
140
141 return virtual_addrs[i].uuid;
142 } else if (uuid == virtual_addrs[i].uuid) {
143 uuid = NULL;
144 }
145 }
146 }
147
148 LOG_WRN("No matching Label UUID for 0x%04x", addr);
149
150 return NULL;
151 }
152
bt_mesh_va_collision_check(uint16_t addr)153 bool bt_mesh_va_collision_check(uint16_t addr)
154 {
155 size_t count = 0;
156 const uint8_t *uuid = NULL;
157
158 do {
159 uuid = bt_mesh_va_uuid_get(addr, uuid, NULL);
160 } while (uuid && ++count);
161
162 return count > 1;
163 }
164
bt_mesh_va_find(const uint8_t * uuid)165 const struct bt_mesh_va *bt_mesh_va_find(const uint8_t *uuid)
166 {
167 int i;
168
169 for (i = 0; i < ARRAY_SIZE(virtual_addrs); i++) {
170 if (virtual_addrs[i].ref && !memcmp(virtual_addrs[i].uuid, uuid, 16)) {
171 return &virtual_addrs[i];
172 }
173 }
174
175 return NULL;
176 }
177
va_get_by_idx(uint16_t index)178 static struct bt_mesh_va *va_get_by_idx(uint16_t index)
179 {
180 if (index >= ARRAY_SIZE(virtual_addrs)) {
181 return NULL;
182 }
183
184 return &virtual_addrs[index];
185 }
186
bt_mesh_va_get_uuid_by_idx(uint16_t idx)187 const uint8_t *bt_mesh_va_get_uuid_by_idx(uint16_t idx)
188 {
189 struct bt_mesh_va *va;
190
191 va = va_get_by_idx(idx);
192 return (va && va->ref > 0) ? va->uuid : NULL;
193 }
194
bt_mesh_va_get_idx_by_uuid(const uint8_t * uuid,uint16_t * uuidx)195 int bt_mesh_va_get_idx_by_uuid(const uint8_t *uuid, uint16_t *uuidx)
196 {
197 struct bt_mesh_va *va;
198
199 if (CONFIG_BT_MESH_LABEL_COUNT == 0) {
200 return -ENOENT;
201 }
202
203 va = CONTAINER_OF(uuid, struct bt_mesh_va, uuid[0]);
204
205 if (!PART_OF_ARRAY(virtual_addrs, va) || va->ref == 0) {
206 return -ENOENT;
207 }
208
209 *uuidx = ARRAY_INDEX(virtual_addrs, va);
210 return 0;
211 }
212
213 #if CONFIG_BT_MESH_LABEL_COUNT > 0
va_set(const char * name,size_t len_rd,settings_read_cb read_cb,void * cb_arg)214 static int va_set(const char *name, size_t len_rd,
215 settings_read_cb read_cb, void *cb_arg)
216 {
217 struct va_val va;
218 struct bt_mesh_va *lab;
219 uint16_t index;
220 int err;
221
222 if (!name) {
223 LOG_ERR("Insufficient number of arguments");
224 return -ENOENT;
225 }
226
227 index = strtol(name, NULL, 16);
228
229 if (len_rd == 0) {
230 LOG_WRN("Mesh Virtual Address length = 0");
231 return 0;
232 }
233
234 err = bt_mesh_settings_set(read_cb, cb_arg, &va, sizeof(va));
235 if (err) {
236 LOG_ERR("Failed to set \'virtual address\'");
237 return err;
238 }
239
240 if (va.ref == 0) {
241 LOG_WRN("Ignore Mesh Virtual Address ref = 0");
242 return 0;
243 }
244
245 lab = va_get_by_idx(index);
246 if (lab == NULL) {
247 LOG_WRN("Out of labels buffers");
248 return -ENOBUFS;
249 }
250
251 memcpy(lab->uuid, va.uuid, 16);
252 lab->addr = va.addr;
253 lab->ref = va.ref;
254
255 LOG_DBG("Restored Virtual Address, addr 0x%04x ref 0x%04x", lab->addr, lab->ref);
256
257 return 0;
258 }
259
260 BT_MESH_SETTINGS_DEFINE(va, "Va", va_set);
261
262 #define IS_VA_DEL(_label) ((_label)->ref == 0)
bt_mesh_va_pending_store(void)263 void bt_mesh_va_pending_store(void)
264 {
265 struct bt_mesh_va *lab;
266 struct va_val va;
267 char path[18];
268 uint16_t i;
269 int err;
270
271 for (i = 0; (lab = va_get_by_idx(i)) != NULL; i++) {
272 if (!lab->changed) {
273 continue;
274 }
275
276 lab->changed = 0U;
277
278 snprintk(path, sizeof(path), "bt/mesh/Va/%x", i);
279
280 if (IS_VA_DEL(lab)) {
281 err = settings_delete(path);
282 } else {
283 va.ref = lab->ref;
284 va.addr = lab->addr;
285 memcpy(va.uuid, lab->uuid, 16);
286
287 err = settings_save_one(path, &va, sizeof(va));
288 }
289
290 if (err) {
291 LOG_ERR("Failed to %s %s value (err %d)",
292 IS_VA_DEL(lab) ? "delete" : "store", path, err);
293 } else {
294 LOG_DBG("%s %s value", IS_VA_DEL(lab) ? "Deleted" : "Stored", path);
295 }
296 }
297 }
298 #else
bt_mesh_va_pending_store(void)299 void bt_mesh_va_pending_store(void)
300 {
301 /* Do nothing. */
302 }
303 #endif /* CONFIG_BT_MESH_LABEL_COUNT > 0 */
304
bt_mesh_va_clear(void)305 void bt_mesh_va_clear(void)
306 {
307 int i;
308
309 if (CONFIG_BT_MESH_LABEL_COUNT == 0) {
310 return;
311 }
312
313 for (i = 0; i < ARRAY_SIZE(virtual_addrs); i++) {
314 if (virtual_addrs[i].ref) {
315 virtual_addrs[i].ref = 0U;
316 virtual_addrs[i].changed = 1U;
317 }
318 }
319
320 if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
321 bt_mesh_settings_store_schedule(BT_MESH_SETTINGS_VA_PENDING);
322 }
323 }
324