| /* |
| * Copyright (c) 2017 Intel Corporation |
| * Copyright (c) 2023 Nordic Semiconductor ASA |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #include <zephyr/kernel.h> |
| #include <errno.h> |
| #include <string.h> |
| #include <stdlib.h> |
| |
| #include <zephyr/settings/settings.h> |
| #include <zephyr/net_buf.h> |
| |
| #include <zephyr/bluetooth/mesh.h> |
| |
| #include "common/bt_str.h" |
| |
| #include "va.h" |
| #include "foundation.h" |
| #include "msg.h" |
| #include "net.h" |
| #include "crypto.h" |
| #include "settings.h" |
| |
| #define LOG_LEVEL CONFIG_BT_MESH_TRANS_LOG_LEVEL |
| #include <zephyr/logging/log.h> |
| LOG_MODULE_REGISTER(bt_mesh_va); |
| |
| static struct bt_mesh_va virtual_addrs[CONFIG_BT_MESH_LABEL_COUNT]; |
| |
| /* Virtual Address information for persistent storage. */ |
| struct va_val { |
| uint16_t ref; |
| uint16_t addr; |
| uint8_t uuid[16]; |
| } __packed; |
| |
| static void va_store(struct bt_mesh_va *store) |
| { |
| store->changed = 1U; |
| |
| if (IS_ENABLED(CONFIG_BT_SETTINGS)) { |
| bt_mesh_settings_store_schedule(BT_MESH_SETTINGS_VA_PENDING); |
| } |
| } |
| |
| uint8_t bt_mesh_va_add(const uint8_t uuid[16], const struct bt_mesh_va **entry) |
| { |
| struct bt_mesh_va *va = NULL; |
| int err; |
| |
| for (int i = 0; i < ARRAY_SIZE(virtual_addrs); i++) { |
| if (!virtual_addrs[i].ref) { |
| if (!va) { |
| va = &virtual_addrs[i]; |
| } |
| |
| continue; |
| } |
| |
| if (!memcmp(uuid, virtual_addrs[i].uuid, |
| ARRAY_SIZE(virtual_addrs[i].uuid))) { |
| if (entry) { |
| *entry = &virtual_addrs[i]; |
| } |
| virtual_addrs[i].ref++; |
| va_store(&virtual_addrs[i]); |
| return STATUS_SUCCESS; |
| } |
| } |
| |
| if (!va) { |
| return STATUS_INSUFF_RESOURCES; |
| } |
| |
| memcpy(va->uuid, uuid, ARRAY_SIZE(va->uuid)); |
| err = bt_mesh_virtual_addr(uuid, &va->addr); |
| if (err) { |
| va->addr = BT_MESH_ADDR_UNASSIGNED; |
| return STATUS_UNSPECIFIED; |
| } |
| |
| va->ref = 1; |
| va_store(va); |
| |
| if (entry) { |
| *entry = va; |
| } |
| |
| return STATUS_SUCCESS; |
| } |
| |
| uint8_t bt_mesh_va_del(const uint8_t *uuid) |
| { |
| struct bt_mesh_va *va; |
| |
| if (CONFIG_BT_MESH_LABEL_COUNT == 0) { |
| return STATUS_CANNOT_REMOVE; |
| } |
| |
| va = CONTAINER_OF(uuid, struct bt_mesh_va, uuid[0]); |
| |
| if (!PART_OF_ARRAY(virtual_addrs, va) || va->ref == 0) { |
| return STATUS_CANNOT_REMOVE; |
| } |
| |
| va->ref--; |
| va_store(va); |
| |
| return STATUS_SUCCESS; |
| } |
| |
| const uint8_t *bt_mesh_va_uuid_get(uint16_t addr, const uint8_t *uuid, uint16_t *retaddr) |
| { |
| int i = 0; |
| |
| if (CONFIG_BT_MESH_LABEL_COUNT == 0) { |
| return NULL; |
| } |
| |
| if (uuid != NULL) { |
| struct bt_mesh_va *va; |
| |
| va = CONTAINER_OF(uuid, struct bt_mesh_va, uuid[0]); |
| i = ARRAY_INDEX(virtual_addrs, va); |
| } |
| |
| for (; i < ARRAY_SIZE(virtual_addrs); i++) { |
| if (virtual_addrs[i].ref && |
| (virtual_addrs[i].addr == addr || addr == BT_MESH_ADDR_UNASSIGNED)) { |
| if (!uuid) { |
| LOG_DBG("Found Label UUID for 0x%04x: %s", addr, |
| bt_hex(virtual_addrs[i].uuid, 16)); |
| |
| if (retaddr) { |
| *retaddr = virtual_addrs[i].addr; |
| } |
| |
| return virtual_addrs[i].uuid; |
| } else if (uuid == virtual_addrs[i].uuid) { |
| uuid = NULL; |
| } |
| } |
| } |
| |
| LOG_WRN("No matching Label UUID for 0x%04x", addr); |
| |
| return NULL; |
| } |
| |
| bool bt_mesh_va_collision_check(uint16_t addr) |
| { |
| size_t count = 0; |
| const uint8_t *uuid = NULL; |
| |
| do { |
| uuid = bt_mesh_va_uuid_get(addr, uuid, NULL); |
| } while (uuid && ++count); |
| |
| return count > 1; |
| } |
| |
| const struct bt_mesh_va *bt_mesh_va_find(const uint8_t *uuid) |
| { |
| int i; |
| |
| for (i = 0; i < ARRAY_SIZE(virtual_addrs); i++) { |
| if (virtual_addrs[i].ref && !memcmp(virtual_addrs[i].uuid, uuid, 16)) { |
| return &virtual_addrs[i]; |
| } |
| } |
| |
| return NULL; |
| } |
| |
| static struct bt_mesh_va *va_get_by_idx(uint16_t index) |
| { |
| if (index >= ARRAY_SIZE(virtual_addrs)) { |
| return NULL; |
| } |
| |
| return &virtual_addrs[index]; |
| } |
| |
| const uint8_t *bt_mesh_va_get_uuid_by_idx(uint16_t idx) |
| { |
| struct bt_mesh_va *va; |
| |
| va = va_get_by_idx(idx); |
| return (va && va->ref > 0) ? va->uuid : NULL; |
| } |
| |
| int bt_mesh_va_get_idx_by_uuid(const uint8_t *uuid, uint16_t *uuidx) |
| { |
| struct bt_mesh_va *va; |
| |
| if (CONFIG_BT_MESH_LABEL_COUNT == 0) { |
| return -ENOENT; |
| } |
| |
| va = CONTAINER_OF(uuid, struct bt_mesh_va, uuid[0]); |
| |
| if (!PART_OF_ARRAY(virtual_addrs, va) || va->ref == 0) { |
| return -ENOENT; |
| } |
| |
| *uuidx = ARRAY_INDEX(virtual_addrs, va); |
| return 0; |
| } |
| |
| #if CONFIG_BT_MESH_LABEL_COUNT > 0 |
| static int va_set(const char *name, size_t len_rd, |
| settings_read_cb read_cb, void *cb_arg) |
| { |
| struct va_val va; |
| struct bt_mesh_va *lab; |
| uint16_t index; |
| int err; |
| |
| if (!name) { |
| LOG_ERR("Insufficient number of arguments"); |
| return -ENOENT; |
| } |
| |
| index = strtol(name, NULL, 16); |
| |
| if (len_rd == 0) { |
| LOG_WRN("Mesh Virtual Address length = 0"); |
| return 0; |
| } |
| |
| err = bt_mesh_settings_set(read_cb, cb_arg, &va, sizeof(va)); |
| if (err) { |
| LOG_ERR("Failed to set \'virtual address\'"); |
| return err; |
| } |
| |
| if (va.ref == 0) { |
| LOG_WRN("Ignore Mesh Virtual Address ref = 0"); |
| return 0; |
| } |
| |
| lab = va_get_by_idx(index); |
| if (lab == NULL) { |
| LOG_WRN("Out of labels buffers"); |
| return -ENOBUFS; |
| } |
| |
| memcpy(lab->uuid, va.uuid, 16); |
| lab->addr = va.addr; |
| lab->ref = va.ref; |
| |
| LOG_DBG("Restored Virtual Address, addr 0x%04x ref 0x%04x", lab->addr, lab->ref); |
| |
| return 0; |
| } |
| |
| BT_MESH_SETTINGS_DEFINE(va, "Va", va_set); |
| |
| #define IS_VA_DEL(_label) ((_label)->ref == 0) |
| void bt_mesh_va_pending_store(void) |
| { |
| struct bt_mesh_va *lab; |
| struct va_val va; |
| char path[18]; |
| uint16_t i; |
| int err; |
| |
| for (i = 0; (lab = va_get_by_idx(i)) != NULL; i++) { |
| if (!lab->changed) { |
| continue; |
| } |
| |
| lab->changed = 0U; |
| |
| snprintk(path, sizeof(path), "bt/mesh/Va/%x", i); |
| |
| if (IS_VA_DEL(lab)) { |
| err = settings_delete(path); |
| } else { |
| va.ref = lab->ref; |
| va.addr = lab->addr; |
| memcpy(va.uuid, lab->uuid, 16); |
| |
| err = settings_save_one(path, &va, sizeof(va)); |
| } |
| |
| if (err) { |
| LOG_ERR("Failed to %s %s value (err %d)", |
| IS_VA_DEL(lab) ? "delete" : "store", path, err); |
| } else { |
| LOG_DBG("%s %s value", IS_VA_DEL(lab) ? "Deleted" : "Stored", path); |
| } |
| } |
| } |
| #else |
| void bt_mesh_va_pending_store(void) |
| { |
| /* Do nothing. */ |
| } |
| #endif /* CONFIG_BT_MESH_LABEL_COUNT > 0 */ |
| |
| void bt_mesh_va_clear(void) |
| { |
| int i; |
| |
| if (CONFIG_BT_MESH_LABEL_COUNT == 0) { |
| return; |
| } |
| |
| for (i = 0; i < ARRAY_SIZE(virtual_addrs); i++) { |
| if (virtual_addrs[i].ref) { |
| virtual_addrs[i].ref = 0U; |
| virtual_addrs[i].changed = 1U; |
| } |
| } |
| |
| if (IS_ENABLED(CONFIG_BT_SETTINGS)) { |
| bt_mesh_settings_store_schedule(BT_MESH_SETTINGS_VA_PENDING); |
| } |
| } |