text: /*
   * 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_CDB)
  #define LOG_MODULE_NAME bt_mesh_cdb
  #include "common/log.h"
  
  #include "mesh.h"
  #include "net.h"
  #include "rpl.h"
  #include "settings.h"
  
  struct bt_mesh_cdb bt_mesh_cdb = {
  	.nodes = {
  		[0 ... (CONFIG_BT_MESH_CDB_NODE_COUNT - 1)] = {
  			.addr = BT_MESH_ADDR_UNASSIGNED,
  		}
  	},
  	.subnets = {
  		[0 ... (CONFIG_BT_MESH_CDB_SUBNET_COUNT - 1)] = {
  			.net_idx = BT_MESH_KEY_UNUSED,
  		}
  	},
  	.app_keys = {
  		[0 ... (CONFIG_BT_MESH_CDB_APP_KEY_COUNT - 1)] = {
  			.net_idx = BT_MESH_KEY_UNUSED,
  		}
  	},
  };
  
  /*
   * 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(uint16_t addr_start, uint8_t num_elem, uint16_t *next)
  {
  	uint16_t addr_end = addr_start + num_elem - 1;
  	uint16_t other_start, other_end;
  	int i;
  
  	if (!BT_MESH_ADDR_IS_UNICAST(addr_start) ||
  	    !BT_MESH_ADDR_IS_UNICAST(addr_end) ||
  	    num_elem == 0) {
  		return -EINVAL;
  	}
  
  	for (i = 0; i < ARRAY_SIZE(bt_mesh_cdb.nodes); i++) {
  		struct bt_mesh_cdb_node *node = &bt_mesh_cdb.nodes[i];
  
  		if (node->addr == BT_MESH_ADDR_UNASSIGNED) {
  			continue;
  		}
  
  		other_start = node->addr;
  		other_end = other_start + node->num_elem - 1;
  
  		if (!(addr_end < other_start || addr_start > other_end)) {
  			if (next) {
  				*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 uint16_t find_lowest_free_addr(uint8_t num_elem)
  {
  	uint16_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_cdb.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;
  }
  
  int bt_mesh_cdb_create(const uint8_t key[16])
  {
  	struct bt_mesh_cdb_subnet *sub;
  
  	if (atomic_test_and_set_bit(bt_mesh_cdb.flags,
  				    BT_MESH_CDB_VALID)) {
  		return -EALREADY;
  	}
  
  	sub = bt_mesh_cdb_subnet_alloc(BT_MESH_KEY_PRIMARY);
  	if (sub == NULL) {
  		return -ENOMEM;
  	}
  
  	memcpy(sub->keys[0].net_key, key, 16);
  	bt_mesh_cdb.iv_index = 0;
  
  	if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
  		bt_mesh_store_cdb();
  		bt_mesh_store_cdb_subnet(sub);
  	}
  
  	return 0;
  }
  
  void bt_mesh_cdb_clear(void)
  {
  	int i;
  
  	atomic_clear_bit(bt_mesh_cdb.flags, BT_MESH_CDB_VALID);
  
  	for (i = 0; i < ARRAY_SIZE(bt_mesh_cdb.nodes); ++i) {
  		if (bt_mesh_cdb.nodes[i].addr != BT_MESH_ADDR_UNASSIGNED) {
  			bt_mesh_cdb_node_del(&bt_mesh_cdb.nodes[i], true);
  		}
  	}
  
  	for (i = 0; i < ARRAY_SIZE(bt_mesh_cdb.subnets); ++i) {
  		if (bt_mesh_cdb.subnets[i].net_idx != BT_MESH_KEY_UNUSED) {
  			bt_mesh_cdb_subnet_del(&bt_mesh_cdb.subnets[i], true);
  		}
  	}
  
  	for (i = 0; i < ARRAY_SIZE(bt_mesh_cdb.app_keys); ++i) {
  		if (bt_mesh_cdb.app_keys[i].net_idx != BT_MESH_KEY_UNUSED) {
  			bt_mesh_cdb_app_key_del(&bt_mesh_cdb.app_keys[i], true);
  		}
  	}
  
  	if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
  		bt_mesh_store_cdb();
  	}
  }
  
  void bt_mesh_cdb_iv_update(uint32_t iv_index, bool iv_update)
  {
  	BT_DBG("Updating IV index to %d\n", iv_index);
  
  	bt_mesh_cdb.iv_index = iv_index;
  
  	atomic_set_bit_to(bt_mesh_cdb.flags, BT_MESH_CDB_IVU_IN_PROGRESS,
  			  iv_update);
  
  	if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
  		bt_mesh_store_cdb();
  	}
  }
  
  struct bt_mesh_cdb_subnet *bt_mesh_cdb_subnet_alloc(uint16_t net_idx)
  {
  	struct bt_mesh_cdb_subnet *sub;
  	int i;
  
  	if (bt_mesh_cdb_subnet_get(net_idx) != NULL) {
  		return NULL;
  	}
  
  	for (i = 0; i < ARRAY_SIZE(bt_mesh_cdb.subnets); ++i) {
  		sub = &bt_mesh_cdb.subnets[i];
  
  		if (sub->net_idx != BT_MESH_KEY_UNUSED) {
  			continue;
  		}
  
  		sub->net_idx = net_idx;
  
  		return sub;
  	}
  
  	return NULL;
  }
  
  void bt_mesh_cdb_subnet_del(struct bt_mesh_cdb_subnet *sub, bool store)
  {
  	BT_DBG("NetIdx 0x%03x store %u", sub->net_idx, store);
  
  	if (IS_ENABLED(CONFIG_BT_SETTINGS) && store) {
  		bt_mesh_clear_cdb_subnet(sub);
  	}
  
  	sub->net_idx = BT_MESH_KEY_UNUSED;
  	memset(sub->keys, 0, sizeof(sub->keys));
  }
  
  struct bt_mesh_cdb_subnet *bt_mesh_cdb_subnet_get(uint16_t net_idx)
  {
  	int i;
  
  	for (i = 0; i < ARRAY_SIZE(bt_mesh_cdb.subnets); ++i) {
  		if (bt_mesh_cdb.subnets[i].net_idx == net_idx) {
  			return &bt_mesh_cdb.subnets[i];
  		}
  	}
  
  	return NULL;
  }
  
  void bt_mesh_cdb_subnet_store(const struct bt_mesh_cdb_subnet *sub)
  {
  	bt_mesh_store_cdb_subnet(sub);
  }
  
  uint8_t bt_mesh_cdb_subnet_flags(const struct bt_mesh_cdb_subnet *sub)
  {
  	uint8_t flags = 0x00;
  
  	if (sub && sub->kr_flag) {
  		flags |= BT_MESH_NET_FLAG_KR;
  	}
  
  	if (atomic_test_bit(bt_mesh_cdb.flags, BT_MESH_CDB_IVU_IN_PROGRESS)) {
  		flags |= BT_MESH_NET_FLAG_IVU;
  	}
  
  	return flags;
  }
  
  struct bt_mesh_cdb_node *bt_mesh_cdb_node_alloc(const uint8_t uuid[16], uint16_t addr,
  						uint8_t num_elem, uint16_t net_idx)
  {
  	int i;
  
  	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) < 0) {
  		BT_DBG("Address range 0x%04x-0x%04x is not free", addr,
  		       addr + num_elem - 1);
  		return NULL;
  	}
  
  	for (i = 0; i < ARRAY_SIZE(bt_mesh_cdb.nodes); i++) {
  		struct bt_mesh_cdb_node *node = &bt_mesh_cdb.nodes[i];
  
  		if (node->addr == BT_MESH_ADDR_UNASSIGNED) {
  			memcpy(node->uuid, uuid, 16);
  			node->addr = addr;
  			node->num_elem = num_elem;
  			node->net_idx = net_idx;
  			atomic_set(node->flags, 0);
  			return node;
  		}
  	}
  
  	return NULL;
  }
  
  void bt_mesh_cdb_node_del(struct bt_mesh_cdb_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_cdb_node(node);
  	}
  
  	node->addr = BT_MESH_ADDR_UNASSIGNED;
  	memset(node->dev_key, 0, sizeof(node->dev_key));
  }
  
  struct bt_mesh_cdb_node *bt_mesh_cdb_node_get(uint16_t addr)
  {
  	int i;
  
  	for (i = 0; i < ARRAY_SIZE(bt_mesh_cdb.nodes); i++) {
  		struct bt_mesh_cdb_node *node = &bt_mesh_cdb.nodes[i];
  
  		if (addr >= node->addr &&
  		    addr <= node->addr + node->num_elem - 1) {
  			return node;
  		}
  	}
  
  	return NULL;
  }
  
  void bt_mesh_cdb_node_store(const struct bt_mesh_cdb_node *node)
  {
  	bt_mesh_store_cdb_node(node);
  }
  
  void bt_mesh_cdb_node_foreach(bt_mesh_cdb_node_func_t func, void *user_data)
  {
  	int i;
  
  	for (i = 0; i < ARRAY_SIZE(bt_mesh_cdb.nodes); ++i) {
  		if (bt_mesh_cdb.nodes[i].addr == BT_MESH_ADDR_UNASSIGNED) {
  			continue;
  		}
  
  		if (func(&bt_mesh_cdb.nodes[i], user_data) ==
  		    BT_MESH_CDB_ITER_STOP) {
  			break;
  		}
  	}
  }
  
  struct bt_mesh_cdb_app_key *bt_mesh_cdb_app_key_alloc(uint16_t net_idx,
  						      uint16_t app_idx)
  {
  	struct bt_mesh_cdb_app_key *key;
  	int i;
  
  	for (i = 0; i < ARRAY_SIZE(bt_mesh_cdb.app_keys); ++i) {
  		key = &bt_mesh_cdb.app_keys[i];
  
  		if (key->net_idx != BT_MESH_KEY_UNUSED) {
  			continue;
  		}
  
  		key->net_idx = net_idx;
  		key->app_idx = app_idx;
  
  		return key;
  	}
  
  	return NULL;
  }
  
  void bt_mesh_cdb_app_key_del(struct bt_mesh_cdb_app_key *key, bool store)
  {
  	BT_DBG("AppIdx 0x%03x store %u", key->app_idx, store);
  
  	if (IS_ENABLED(CONFIG_BT_SETTINGS) && store) {
  		bt_mesh_clear_cdb_app_key(key);
  	}
  
  	key->net_idx = BT_MESH_KEY_UNUSED;
  	memset(key->keys, 0, sizeof(key->keys));
  }
  
  struct bt_mesh_cdb_app_key *bt_mesh_cdb_app_key_get(uint16_t app_idx)
  {
  	int i;
  
  	for (i = 0; i < ARRAY_SIZE(bt_mesh_cdb.app_keys); i++) {
  		struct bt_mesh_cdb_app_key *key = &bt_mesh_cdb.app_keys[i];
  
  		if (key->net_idx != BT_MESH_KEY_UNUSED &&
  		    key->app_idx == app_idx) {
  			return key;
  		}
  	}
  
  	return NULL;
  }
  
  void bt_mesh_cdb_app_key_store(const struct bt_mesh_cdb_app_key *key)
  {
  	bt_mesh_store_cdb_app_key(key);
  }
  