diff --git a/nimble/host/mesh/include/mesh/atomic.h b/nimble/host/mesh/include/mesh/atomic.h new file mode 100644 index 000000000..2c7317948 --- /dev/null +++ b/nimble/host/mesh/include/mesh/atomic.h @@ -0,0 +1,409 @@ +/* atomic operations */ + +/* + * Copyright (c) 1997-2015, Wind River Systems, Inc. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __ATOMIC_H__ +#define __ATOMIC_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +typedef int atomic_t; +typedef atomic_t atomic_val_t; + +/** + * @defgroup atomic_apis Atomic Services APIs + * @ingroup kernel_apis + * @{ + */ + +/** + * @brief Atomic compare-and-set. + * + * This routine performs an atomic compare-and-set on @a target. If the current + * value of @a target equals @a old_value, @a target is set to @a new_value. + * If the current value of @a target does not equal @a old_value, @a target + * is left unchanged. + * + * @param target Address of atomic variable. + * @param old_value Original value to compare against. + * @param new_value New value to store. + * @return 1 if @a new_value is written, 0 otherwise. + */ +static inline int atomic_cas(atomic_t *target, atomic_val_t old_value, + atomic_val_t new_value) +{ + return __atomic_compare_exchange_n(target, &old_value, new_value, + 0, __ATOMIC_SEQ_CST, + __ATOMIC_SEQ_CST); +} + +/** + * + * @brief Atomic addition. + * + * This routine performs an atomic addition on @a target. + * + * @param target Address of atomic variable. + * @param value Value to add. + * + * @return Previous value of @a target. + */ +static inline atomic_val_t atomic_add(atomic_t *target, atomic_val_t value) +{ + return __atomic_fetch_add(target, value, __ATOMIC_SEQ_CST); +} + +/** + * + * @brief Atomic subtraction. + * + * This routine performs an atomic subtraction on @a target. + * + * @param target Address of atomic variable. + * @param value Value to subtract. + * + * @return Previous value of @a target. + */ + +static inline atomic_val_t atomic_sub(atomic_t *target, atomic_val_t value) +{ + return __atomic_fetch_sub(target, value, __ATOMIC_SEQ_CST); +} + +/** + * + * @brief Atomic increment. + * + * This routine performs an atomic increment by 1 on @a target. + * + * @param target Address of atomic variable. + * + * @return Previous value of @a target. + */ + +static inline atomic_val_t atomic_inc(atomic_t *target) +{ + return atomic_add(target, 1); +} + +/** + * + * @brief Atomic decrement. + * + * This routine performs an atomic decrement by 1 on @a target. + * + * @param target Address of atomic variable. + * + * @return Previous value of @a target. + */ + +static inline atomic_val_t atomic_dec(atomic_t *target) +{ + return atomic_sub(target, 1); +} + +/** + * + * @brief Atomic get. + * + * This routine performs an atomic read on @a target. + * + * @param target Address of atomic variable. + * + * @return Value of @a target. + */ + +static inline atomic_val_t atomic_get(const atomic_t *target) +{ + return __atomic_load_n(target, __ATOMIC_SEQ_CST); +} + +/** + * + * @brief Atomic get-and-set. + * + * This routine atomically sets @a target to @a value and returns + * the previous value of @a target. + * + * @param target Address of atomic variable. + * @param value Value to write to @a target. + * + * @return Previous value of @a target. + */ + +static inline atomic_val_t atomic_set(atomic_t *target, atomic_val_t value) +{ + /* This builtin, as described by Intel, is not a traditional + * test-and-set operation, but rather an atomic exchange operation. It + * writes value into *ptr, and returns the previous contents of *ptr. + */ + return __atomic_exchange_n(target, value, __ATOMIC_SEQ_CST); +} + +/** + * + * @brief Atomic clear. + * + * This routine atomically sets @a target to zero and returns its previous + * value. (Hence, it is equivalent to atomic_set(target, 0).) + * + * @param target Address of atomic variable. + * + * @return Previous value of @a target. + */ + +static inline atomic_val_t atomic_clear(atomic_t *target) +{ + return atomic_set(target, 0); +} + +/** + * + * @brief Atomic bitwise inclusive OR. + * + * This routine atomically sets @a target to the bitwise inclusive OR of + * @a target and @a value. + * + * @param target Address of atomic variable. + * @param value Value to OR. + * + * @return Previous value of @a target. + */ + +static inline atomic_val_t atomic_or(atomic_t *target, atomic_val_t value) +{ + return __atomic_fetch_or(target, value, __ATOMIC_SEQ_CST); +} + +/** + * + * @brief Atomic bitwise exclusive OR (XOR). + * + * This routine atomically sets @a target to the bitwise exclusive OR (XOR) of + * @a target and @a value. + * + * @param target Address of atomic variable. + * @param value Value to XOR + * + * @return Previous value of @a target. + */ + +static inline atomic_val_t atomic_xor(atomic_t *target, atomic_val_t value) +{ + return __atomic_fetch_xor(target, value, __ATOMIC_SEQ_CST); +} + +/** + * + * @brief Atomic bitwise AND. + * + * This routine atomically sets @a target to the bitwise AND of @a target + * and @a value. + * + * @param target Address of atomic variable. + * @param value Value to AND. + * + * @return Previous value of @a target. + */ + +static inline atomic_val_t atomic_and(atomic_t *target, atomic_val_t value) +{ + return __atomic_fetch_and(target, value, __ATOMIC_SEQ_CST); +} + +/** + * + * @brief Atomic bitwise NAND. + * + * This routine atomically sets @a target to the bitwise NAND of @a target + * and @a value. (This operation is equivalent to target = ~(target & value).) + * + * @param target Address of atomic variable. + * @param value Value to NAND. + * + * @return Previous value of @a target. + */ + +static inline atomic_val_t atomic_nand(atomic_t *target, atomic_val_t value) +{ + return __atomic_fetch_nand(target, value, __ATOMIC_SEQ_CST); +} + + /** + * @brief Initialize an atomic variable. + * + * This macro can be used to initialize an atomic variable. For example, + * @code atomic_t my_var = ATOMIC_INIT(75); @endcode + * + * @param i Value to assign to atomic variable. + */ +#define ATOMIC_INIT(i) (i) + + /** + * @cond INTERNAL_HIDDEN + */ + +#define ATOMIC_BITS (sizeof(atomic_val_t) * 8) +#define ATOMIC_MASK(bit) (1 << ((bit) & (ATOMIC_BITS - 1))) +#define ATOMIC_ELEM(addr, bit) ((addr) + ((bit) / ATOMIC_BITS)) + + /** + * INTERNAL_HIDDEN @endcond + */ + + /** + * @brief Define an array of atomic variables. + * + * This macro defines an array of atomic variables containing at least + * @a num_bits bits. + * + * @note + * If used from file scope, the bits of the array are initialized to zero; + * if used from within a function, the bits are left uninitialized. + * + * @param name Name of array of atomic variables. + * @param num_bits Number of bits needed. + */ +#define ATOMIC_DEFINE(name, num_bits) \ + atomic_t name[1 + ((num_bits) - 1) / ATOMIC_BITS] + + /** + * @brief Atomically test a bit. + * + * This routine tests whether bit number @a bit of @a target is set or not. + * The target may be a single atomic variable or an array of them. + * + * @param target Address of atomic variable or array. + * @param bit Bit number (starting from 0). + * + * @return 1 if the bit was set, 0 if it wasn't. + */ + static inline int + atomic_test_bit(const atomic_t *target, int bit) + { + atomic_val_t val = atomic_get(ATOMIC_ELEM(target, bit)); + + return (1 & (val >> (bit & (ATOMIC_BITS - 1)))); + } + + /** + * @brief Atomically test and clear a bit. + * + * Atomically clear bit number @a bit of @a target and return its old value. + * The target may be a single atomic variable or an array of them. + * + * @param target Address of atomic variable or array. + * @param bit Bit number (starting from 0). + * + * @return 1 if the bit was set, 0 if it wasn't. + */ + static inline int + atomic_test_and_clear_bit(atomic_t *target, int bit) + { + atomic_val_t mask = ATOMIC_MASK(bit); + atomic_val_t old; + + old = atomic_and(ATOMIC_ELEM(target, bit), ~mask); + + return (old & mask) != 0; + } + + /** + * @brief Atomically set a bit. + * + * Atomically set bit number @a bit of @a target and return its old value. + * The target may be a single atomic variable or an array of them. + * + * @param target Address of atomic variable or array. + * @param bit Bit number (starting from 0). + * + * @return 1 if the bit was set, 0 if it wasn't. + */ + static inline int + atomic_test_and_set_bit(atomic_t *target, int bit) + { + atomic_val_t mask = ATOMIC_MASK(bit); + atomic_val_t old; + + old = atomic_or(ATOMIC_ELEM(target, bit), mask); + + return (old & mask) != 0; + } + + /** + * @brief Atomically clear a bit. + * + * Atomically clear bit number @a bit of @a target. + * The target may be a single atomic variable or an array of them. + * + * @param target Address of atomic variable or array. + * @param bit Bit number (starting from 0). + * + * @return N/A + */ + static inline void + atomic_clear_bit(atomic_t *target, int bit) + { + atomic_val_t mask = ATOMIC_MASK(bit); + + atomic_and(ATOMIC_ELEM(target, bit), ~mask); + } + + /** + * @brief Atomically set a bit. + * + * Atomically set bit number @a bit of @a target. + * The target may be a single atomic variable or an array of them. + * + * @param target Address of atomic variable or array. + * @param bit Bit number (starting from 0). + * + * @return N/A + */ + static inline void + atomic_set_bit(atomic_t *target, int bit) + { + atomic_val_t mask = ATOMIC_MASK(bit); + + atomic_or(ATOMIC_ELEM(target, bit), mask); + } + +/** +* @brief Atomically set a bit to a given value. +* +* Atomically set bit number @a bit of @a target to value @a val. +* The target may be a single atomic variable or an array of them. +* +* @param target Address of atomic variable or array. +* @param bit Bit number (starting from 0). +* @param val true for 1, false for 0. +* +* @return N/A +*/ +static inline void atomic_set_bit_to(atomic_t *target, int bit, bool val) +{ + atomic_val_t mask = ATOMIC_MASK(bit); + + if (val) { + (void)atomic_or(ATOMIC_ELEM(target, bit), mask); + } else { + (void)atomic_and(ATOMIC_ELEM(target, bit), ~mask); + } +} + +/** + * @} + */ + +#ifdef __cplusplus +} +#endif + +#endif /* __ATOMIC_H__ */ diff --git a/nimble/host/mesh/include/mesh/cdb.h b/nimble/host/mesh/include/mesh/cdb.h new file mode 100644 index 000000000..f5be8c920 --- /dev/null +++ b/nimble/host/mesh/include/mesh/cdb.h @@ -0,0 +1,265 @@ +/* + * Copyright (c) 2019 Tobias Svehagen + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef _BLUETOOTH_MESH_CDB_H_ +#define _BLUETOOTH_MESH_CDB_H_ + +#if MYNEWT_VAL(BLE_MESH_CDB) +#define NODE_COUNT MYNEWT_VAL(BLE_MESH_CDB_NODE_COUNT) +#define SUBNET_COUNT MYNEWT_VAL(BLE_MESH_CDB_SUBNET_COUNT) +#define APP_KEY_COUNT MYNEWT_VAL(BLE_MESH_CDB_APP_KEY_COUNT) +#else +#define NODE_COUNT 0 +#define SUBNET_COUNT 0 +#define APP_KEY_COUNT 0 +#endif + +#include "atomic.h" + +enum { + BT_MESH_CDB_NODE_CONFIGURED, + BT_MESH_CDB_NODE_BLACKLISTED, + + BT_MESH_CDB_NODE_FLAG_COUNT +}; + +struct bt_mesh_cdb_node { + u8_t uuid[16]; + u16_t addr; + u16_t net_idx; + u8_t num_elem; + u8_t dev_key[16]; + + ATOMIC_DEFINE(flags, BT_MESH_CDB_NODE_FLAG_COUNT); +}; + +struct bt_mesh_cdb_subnet { + u16_t net_idx; + + bool kr_flag; + u8_t kr_phase; + + struct { + u8_t net_key[16]; + } keys[2]; +}; + +struct bt_mesh_cdb_app_key { + u16_t net_idx; + u16_t app_idx; + + struct { + u8_t app_key[16]; + } keys[2]; +}; + +enum { + BT_MESH_CDB_VALID, + BT_MESH_CDB_SUBNET_PENDING, + BT_MESH_CDB_KEYS_PENDING, + BT_MESH_CDB_NODES_PENDING, + BT_MESH_CDB_IVU_IN_PROGRESS, + + BT_MESH_CDB_FLAG_COUNT, +}; + +struct bt_mesh_cdb { + u32_t iv_index; + + ATOMIC_DEFINE(flags, BT_MESH_CDB_FLAG_COUNT); + + struct bt_mesh_cdb_node nodes[NODE_COUNT]; + struct bt_mesh_cdb_subnet subnets[SUBNET_COUNT]; + struct bt_mesh_cdb_app_key app_keys[APP_KEY_COUNT]; +}; + +extern struct bt_mesh_cdb bt_mesh_cdb; + +/** @brief Create the Mesh Configuration Database. + * + * Create and initialize the Mesh Configuration Database. A primary subnet, + * ie one with NetIdx 0, will be added and the provided key will be used as + * NetKey for that subnet. + * + * @param key The NetKey to be used for the primary subnet. + * + * @return 0 on success or negative error code on failure. + */ +int bt_mesh_cdb_create(const u8_t key[16]); + +/** @brief Clear the Mesh Configuration Database. + * + * Remove all nodes, subnets and app-keys stored in the database and mark + * the database as invalid. The data will be cleared from persistent storage + * if CONFIG_BT_SETTINGS is enabled. + */ +void bt_mesh_cdb_clear(void); + +/** @brief Set and store the IV Index and IV Update flag. + * + * The IV Index stored in the CDB will be the one used during provisioning + * of new nodes. This function is generally only used from inside the stack. + * + * This function will store the data to persistent storage if + * CONFIG_BT_SETTINGS is enabled. + * + * @param iv_index The new IV Index to use. + * @param iv_update True if there is an ongoing IV Update procedure. + */ +void bt_mesh_cdb_iv_update(u32_t iv_index, bool iv_update); + +/** @brief Allocate a node. + * + * Allocate a new node in the CDB. + * + * @param uuid UUID of the node. + * @param addr Address of the node's primary element. If 0, the lowest + * possible address available will be assigned to the node. + * @param num_elem Number of elements that the node has. + * @param net_idx NetIdx that the node was provisioned to. + * + * @return The new node or NULL if it cannot be allocated. + */ +struct bt_mesh_cdb_node *bt_mesh_cdb_node_alloc(const u8_t uuid[16], u16_t addr, + u8_t num_elem, u16_t net_idx); + +/** @brief Delete a node. + * + * Delete a node from the CDB. + * + * @param node The node to be deleted. + * @param store If true, the node will be cleared from persistent storage. + */ +void bt_mesh_cdb_node_del(struct bt_mesh_cdb_node *node, bool store); + +/** @brief Get a node by address. + * + * Try to find the node that has the provided address assigned to one of its + * elements. + * + * @param addr Address of the element to look for. + * + * @return The node that has an element with address addr or NULL if no such + * node exists. + */ +struct bt_mesh_cdb_node *bt_mesh_cdb_node_get(u16_t addr); + +/** @brief Store node to persistent storage. + * + * @param node Node to be stored. + */ +void bt_mesh_cdb_node_store(const struct bt_mesh_cdb_node *node); + +enum { + BT_MESH_CDB_ITER_STOP = 0, + BT_MESH_CDB_ITER_CONTINUE, +}; + +/** @typedef bt_mesh_cdb_node_func_t + * @brief Node iterator callback. + * + * @param node Node found. + * @param user_data Data given. + * + * @return BT_MESH_CDB_ITER_CONTINUE to continue to iterate through the nodes + * or BT_MESH_CDB_ITER_STOP to stop. + */ +typedef u8_t (*bt_mesh_cdb_node_func_t)(struct bt_mesh_cdb_node *node, + void *user_data); + +/** @brief Node iterator. + * + * Iterate nodes in the Mesh Configuration Database. The callback function + * will only be called for valid, ie allocated, nodes. + * + * @param func Callback function. + * @param user_data Data to pass to the callback. + */ +void bt_mesh_cdb_node_foreach(bt_mesh_cdb_node_func_t func, void *user_data); + +/** @brief Allocate a subnet. + * + * Allocate a new subnet in the CDB. + * + * @param net_idx NetIdx of the subnet. + * + * @return The new subnet or NULL if it cannot be allocated. + */ +struct bt_mesh_cdb_subnet *bt_mesh_cdb_subnet_alloc(u16_t net_idx); + +/** @brief Delete a subnet. + * + * Delete a subnet from the CDB. + * + * @param sub The subnet to be deleted. + * @param store If true, the subnet will be cleared from persistent storage. + */ +void bt_mesh_cdb_subnet_del(struct bt_mesh_cdb_subnet *sub, bool store); + +/** @brief Get a subnet by NetIdx + * + * Try to find the subnet with the specified NetIdx. + * + * @param net_idx NetIdx of the subnet to look for. + * + * @return The subnet with the specified NetIdx or NULL if no such subnet + * exists. + */ +struct bt_mesh_cdb_subnet *bt_mesh_cdb_subnet_get(u16_t net_idx); + +/** @brief Store subnet to persistent storage. + * + * @param sub Subnet to be stored. + */ +void bt_mesh_cdb_subnet_store(const struct bt_mesh_cdb_subnet *sub); + +/** @brief Get the flags for a subnet + * + * @param sub The subnet to get flags for. + * + * @return The flags for the subnet. + */ +u8_t bt_mesh_cdb_subnet_flags(const struct bt_mesh_cdb_subnet *sub); + + +/** @brief Allocate an application key. + * + * Allocate a new application key in the CDB. + * + * @param net_idx NetIdx of NetKey that the application key is bound to. + * @param app_idx AppIdx of the application key. + * + * @return The new application key or NULL if it cannot be allocated. + */ +struct bt_mesh_cdb_app_key *bt_mesh_cdb_app_key_alloc(u16_t net_idx, + u16_t app_idx); + +/** @brief Delete an application key. + * + * Delete an application key from the CDB. + * + * @param key The application key to be deleted. + * @param store If true, the key will be cleared from persistent storage. + */ +void bt_mesh_cdb_app_key_del(struct bt_mesh_cdb_app_key *key, bool store); + +/** @brief Get an application key by AppIdx + * + * Try to find the application key with the specified AppIdx. + * + * @param app_idx AppIdx of the application key to look for. + * + * @return The application key with the specified AppIdx or NULL if no such key + * exists. + */ +struct bt_mesh_cdb_app_key *bt_mesh_cdb_app_key_get(u16_t app_idx); + +/** @brief Store application key to persistent storage. + * + * @param key Application key to be stored. + */ +void bt_mesh_cdb_app_key_store(const struct bt_mesh_cdb_app_key *key); + +#endif /* ZEPHYR_INCLUDE_BLUETOOTH_MESH_CDB_H_ */ \ No newline at end of file diff --git a/nimble/host/mesh/include/mesh/glue.h b/nimble/host/mesh/include/mesh/glue.h index e37fcfbcb..a3894d0be 100644 --- a/nimble/host/mesh/include/mesh/glue.h +++ b/nimble/host/mesh/include/mesh/glue.h @@ -403,14 +403,15 @@ static inline unsigned int find_msb_set(u32_t op) #define CONFIG_BT_MAX_CONN MYNEWT_VAL(BLE_MAX_CONNECTIONS) #define CONFIG_BT_MESH_SEQ_STORE_RATE MYNEWT_VAL(BLE_MESH_SEQ_STORE_RATE) #define CONFIG_BT_MESH_RPL_STORE_TIMEOUT MYNEWT_VAL(BLE_MESH_RPL_STORE_TIMEOUT) -#define CONFIG_BT_MESH_APP_KEY_COUNT MYNEWT_VAL(BLE_MESH_APP_KEY_COUNT) -#define CONFIG_BT_MESH_SUBNET_COUNT MYNEWT_VAL(BLE_MESH_SUBNET_COUNT) +#define CONFIG_BT_MESH_APP_KEY_COUNT MYNEWT_VAL(BLE_MESH_CDB_APP_KEY_COUNT) +#define CONFIG_BT_MESH_SUBNET_COUNT MYNEWT_VAL(BLE_MESH_CDB_SUBNET_COUNT) #define CONFIG_BT_MESH_STORE_TIMEOUT MYNEWT_VAL(BLE_MESH_STORE_TIMEOUT) #define CONFIG_BT_MESH_IVU_DIVIDER MYNEWT_VAL(BLE_MESH_IVU_DIVIDER) #define CONFIG_BT_DEVICE_NAME MYNEWT_VAL(BLE_MESH_DEVICE_NAME) #define CONFIG_BT_MESH_TX_SEG_MAX MYNEWT_VAL(BLE_MESH_TX_SEG_MAX) #define CONFIG_BT_MESH_LABEL_COUNT MYNEWT_VAL(BLE_MESH_LABEL_COUNT) -#define CONFIG_BT_MESH_NODE_COUNT MYNEWT_VAL(BLE_MESH_NODE_COUNT) +#define CONFIG_BT_MESH_NODE_COUNT MYNEWT_VAL(BLE_MESH_CDB_NODE_COUNT) +#define CONFIG_BT_MESH_CDB BLE_MESH_CDB #define printk console_printf diff --git a/nimble/host/mesh/include/mesh/main.h b/nimble/host/mesh/include/mesh/main.h index 4a5bedba1..7966043a2 100644 --- a/nimble/host/mesh/include/mesh/main.h +++ b/nimble/host/mesh/include/mesh/main.h @@ -186,10 +186,12 @@ struct bt_mesh_prov { * the specified NetKeyIndex and primary element address. * * @param net_idx NetKeyIndex given during provisioning. + * @param uuid UUID of the added node * @param addr Primary element address. * @param num_elem Number of elements that this node has. */ - void (*node_added)(u16_t net_idx, u16_t addr, u8_t num_elem); + void (*node_added)(u16_t net_idx, u8_t uuid[16], u16_t addr, + u8_t num_elem); /** @brief Node has been reset. * diff --git a/nimble/host/mesh/include/mesh/mesh.h b/nimble/host/mesh/include/mesh/mesh.h index 9ba63ef0e..c67bf912d 100644 --- a/nimble/host/mesh/include/mesh/mesh.h +++ b/nimble/host/mesh/include/mesh/mesh.h @@ -22,5 +22,6 @@ #include "cfg_cli.h" #include "health_cli.h" #include "proxy.h" +#include "cdb.h" #endif /* __BT_MESH_H */ diff --git a/nimble/host/mesh/src/beacon.c b/nimble/host/mesh/src/beacon.c index 4f848c643..7764d86a0 100644 --- a/nimble/host/mesh/src/beacon.c +++ b/nimble/host/mesh/src/beacon.c @@ -21,6 +21,7 @@ #include "crypto.h" #include "beacon.h" #include "foundation.h" +#include "atomic.h" #define UNPROVISIONED_INTERVAL (K_SECONDS(5)) #define PROVISIONED_INTERVAL (K_SECONDS(10)) diff --git a/nimble/host/mesh/src/cdb.c b/nimble/host/mesh/src/cdb.c new file mode 100644 index 000000000..ac731bd88 --- /dev/null +++ b/nimble/host/mesh/src/cdb.c @@ -0,0 +1,385 @@ +/* + * Copyright (c) 2019 Tobias Svehagen + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define BT_DBG_ENABLED MYNEWT_VAL(BLE_MESH_DEBUG_CDB) +#define LOG_MODULE_NAME bt_mesh_cdb +#include "log/log.h" + +#include "mesh/mesh.h" +#include "net.h" +#include "settings.h" +#include "mesh_priv.h" +#include "mesh/glue.h" + +struct bt_mesh_cdb bt_mesh_cdb = { + .nodes = { + [0 ... (CONFIG_BT_MESH_NODE_COUNT - 1)] = { + .addr = BT_MESH_ADDR_UNASSIGNED, + } + }, + .subnets = { + [0 ... (CONFIG_BT_MESH_SUBNET_COUNT - 1)] = { + .net_idx = BT_MESH_KEY_UNUSED, + } + }, + .app_keys = { + [0 ... (CONFIG_BT_MESH_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(u16_t addr_start, u8_t num_elem, u16_t *next) +{ + u16_t addr_end = addr_start + num_elem - 1; + u16_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 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_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 u8_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(u32_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(u16_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(u16_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) +{ + if MYNEWT_VAL(BLE_MESH_SETTINGS) { + bt_mesh_store_cdb_subnet(sub); + } +} + +u8_t bt_mesh_cdb_subnet_flags(const struct bt_mesh_cdb_subnet *sub) +{ + u8_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 u8_t uuid[16], u16_t addr, + u8_t num_elem, u16_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(u16_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) +{ + if MYNEWT_VAL(BLE_MESH_SETTINGS) { + 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(u16_t net_idx, + u16_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_ADDR_UNASSIGNED; + memset(key->keys, 0, sizeof(key->keys)); +} + +struct bt_mesh_cdb_app_key *bt_mesh_cdb_app_key_get(u16_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) +{ + if MYNEWT_VAL(BLE_MESH_SETTINGS) { + bt_mesh_store_cdb_app_key(key); + } +} diff --git a/nimble/host/mesh/src/glue.c b/nimble/host/mesh/src/glue.c index 896f3d1a0..ebdb49bbd 100644 --- a/nimble/host/mesh/src/glue.c +++ b/nimble/host/mesh/src/glue.c @@ -867,4 +867,3 @@ char *settings_str_from_bytes(const void *vp, int vp_len, } #endif /* MYNEWT_VAL(BLE_MESH_SETTINGS) */ - diff --git a/nimble/host/mesh/src/mesh.c b/nimble/host/mesh/src/mesh.c index 160da2acc..eb0ff17ec 100644 --- a/nimble/host/mesh/src/mesh.c +++ b/nimble/host/mesh/src/mesh.c @@ -30,6 +30,7 @@ #include "mesh_priv.h" #include "settings.h" + u8_t g_mesh_addr_type; static struct ble_gap_event_listener mesh_event_listener; @@ -58,6 +59,52 @@ int bt_mesh_provision(const u8_t net_key[16], u16_t net_idx, pb_gatt_enabled = false; } + /* + * FIXME: + * Should net_key and iv_index be over-ridden? + */ + if (IS_ENABLED(BLE_MESH_CDB)) { + const struct bt_mesh_comp *comp; + const struct bt_mesh_prov *prov; + struct bt_mesh_cdb_node *node; + + if (!atomic_test_bit(bt_mesh_cdb.flags, + BT_MESH_CDB_VALID)) { + BT_ERR("No valid network"); + atomic_clear_bit(bt_mesh.flags, BT_MESH_VALID); + return -EINVAL; + } + + comp = bt_mesh_comp_get(); + if (comp == NULL) { + BT_ERR("Failed to get node composition"); + atomic_clear_bit(bt_mesh.flags, BT_MESH_VALID); + return -EINVAL; + } + + if (!bt_mesh_cdb_subnet_get(net_idx)) { + BT_ERR("No subnet with idx %d", net_idx); + atomic_clear_bit(bt_mesh.flags, BT_MESH_VALID); + return -ENOENT; + } + + prov = bt_mesh_prov_get(); + node = bt_mesh_cdb_node_alloc(prov->uuid, addr, + comp->elem_count, net_idx); + if (node == NULL) { + BT_ERR("Failed to allocate database node"); + atomic_clear_bit(bt_mesh.flags, BT_MESH_VALID); + return -ENOMEM; + } + + addr = node->addr; + iv_index = bt_mesh_cdb.iv_index; + memcpy(node->dev_key, dev_key, 16); + if (IS_ENABLED(CONFIG_BT_SETTINGS)) { + bt_mesh_store_cdb_node(node); + } + } + err = bt_mesh_net_create(net_idx, flags, net_key, iv_index); if (err) { atomic_clear_bit(bt_mesh.flags, BT_MESH_VALID); diff --git a/nimble/host/mesh/src/mesh_priv.h b/nimble/host/mesh/src/mesh_priv.h index de1b4e07f..03e61c877 100644 --- a/nimble/host/mesh/src/mesh_priv.h +++ b/nimble/host/mesh/src/mesh_priv.h @@ -8,6 +8,8 @@ #ifndef __MESH_PRIV_H #define __MESH_PRIV_H +#include + #define BT_MESH_KEY_PRIMARY 0x0000 #define BT_MESH_KEY_ANY 0xffff diff --git a/nimble/host/mesh/src/net.c b/nimble/host/mesh/src/net.c index bde847a4c..5a5a364dc 100644 --- a/nimble/host/mesh/src/net.c +++ b/nimble/host/mesh/src/net.c @@ -77,13 +77,6 @@ struct bt_mesh_net bt_mesh = { .net_idx = BT_MESH_KEY_UNUSED, } }, -#if MYNEWT_VAL(BLE_MESH_PROVISIONER) - .nodes = { - [0 ... (CONFIG_BT_MESH_NODE_COUNT - 1)] = { - .net_idx = BT_MESH_KEY_UNUSED, - } - }, -#endif }; static u32_t dup_cache[MYNEWT_VAL(BLE_MESH_MSG_CACHE_SIZE)]; @@ -700,6 +693,10 @@ do_update: } } + if (MYNEWT_VAL(BLE_MESH_CDB)) { + bt_mesh_cdb_iv_update(iv_index, iv_update); + } + if (IS_ENABLED(CONFIG_BT_SETTINGS)) { bt_mesh_store_iv(false); } diff --git a/nimble/host/mesh/src/net.h b/nimble/host/mesh/src/net.h index bcba65924..cef09bb2c 100644 --- a/nimble/host/mesh/src/net.h +++ b/nimble/host/mesh/src/net.h @@ -230,7 +230,6 @@ enum { BT_MESH_CFG_PENDING, BT_MESH_MOD_PENDING, BT_MESH_VA_PENDING, - BT_MESH_NODES_PENDING, /* Don't touch - intentionally last */ BT_MESH_FLAG_COUNT, @@ -263,10 +262,6 @@ struct bt_mesh_net { u8_t dev_key[16]; -#if MYNEWT_VAL(BLE_MESH_PROVISIONER) - struct bt_mesh_node nodes[MYNEWT_VAL(BLE_MESH_NODE_COUNT)]; -#endif - struct bt_mesh_app_key app_keys[MYNEWT_VAL(BLE_MESH_APP_KEY_COUNT)]; struct bt_mesh_subnet sub[MYNEWT_VAL(BLE_MESH_SUBNET_COUNT)]; diff --git a/nimble/host/mesh/src/nodes.c b/nimble/host/mesh/src/nodes.c deleted file mode 100644 index 127ef21e4..000000000 --- a/nimble/host/mesh/src/nodes.c +++ /dev/null @@ -1,161 +0,0 @@ -/* - * 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 diff --git a/nimble/host/mesh/src/nodes.h b/nimble/host/mesh/src/nodes.h deleted file mode 100644 index f86193d9e..000000000 --- a/nimble/host/mesh/src/nodes.h +++ /dev/null @@ -1,10 +0,0 @@ -/* - * Copyright (c) 2019 Tobias Svehagen - * - * SPDX-License-Identifier: Apache-2.0 - */ - -struct bt_mesh_node *bt_mesh_node_find(u16_t addr); -struct bt_mesh_node *bt_mesh_node_alloc(u16_t addr, u8_t num_elem, - u16_t net_idx); -void bt_mesh_node_del(struct bt_mesh_node *node, bool store); \ No newline at end of file diff --git a/nimble/host/mesh/src/prov.c b/nimble/host/mesh/src/prov.c index 36772957c..c2d2307fa 100644 --- a/nimble/host/mesh/src/prov.c +++ b/nimble/host/mesh/src/prov.c @@ -26,7 +26,6 @@ #include "prov.h" #include "testing.h" #include "settings.h" -#include "nodes.h" /* 3 transmissions, 20ms interval */ #define PROV_XMIT BT_MESH_TRANSMIT(2, 20) @@ -123,10 +122,11 @@ enum { #endif struct provisioner_link { - struct bt_mesh_node *node; + struct bt_mesh_cdb_node *node; u16_t addr; u16_t net_idx; u8_t attention_duration; + u8_t uuid[16]; }; struct prov_link { @@ -227,7 +227,7 @@ static int reset_state(void) if (IS_ENABLED(CONFIG_BT_MESH_PROVISIONER) && link.provisioner->node != NULL) { - bt_mesh_node_del(link.provisioner->node, false); + bt_mesh_cdb_node_del(link.provisioner->node, false); } #if (MYNEWT_VAL(BLE_MESH_PB_ADV)) @@ -699,9 +699,10 @@ static void prov_capabilities(const u8_t *data) return; } - link.provisioner->node = bt_mesh_node_alloc(link.provisioner->addr, - data[0], - link.provisioner->net_idx); + link.provisioner->node = + bt_mesh_cdb_node_alloc(link.provisioner->uuid, + link.provisioner->addr, data[0], + link.provisioner->net_idx); if (link.provisioner->node == NULL) { prov_send_fail_msg(PROV_ERR_RESOURCES); return; @@ -1142,7 +1143,7 @@ static void prov_input_complete(const u8_t *data) static void send_prov_data(void) { struct os_mbuf *pdu = PROV_BUF(34); - struct bt_mesh_subnet *sub; + struct bt_mesh_cdb_subnet *sub; u8_t session_key[16]; u8_t nonce[13]; int err; @@ -1175,7 +1176,7 @@ static void send_prov_data(void) BT_DBG("DevKey: %s", bt_hex(link.provisioner->node->dev_key, 16)); - sub = bt_mesh_subnet_get(link.provisioner->node->net_idx); + sub = bt_mesh_cdb_subnet_get(link.provisioner->node->net_idx); if (sub == NULL) { BT_ERR("No subnet with net_idx %u", link.provisioner->node->net_idx); @@ -1184,10 +1185,10 @@ static void send_prov_data(void) } prov_buf_init(pdu, PROV_DATA); - net_buf_simple_add_mem(pdu, sub->keys[sub->kr_flag].net, 16); + net_buf_simple_add_mem(pdu, sub->keys[sub->kr_flag].net_key, 16); net_buf_simple_add_be16(pdu, link.provisioner->node->net_idx); - net_buf_simple_add_u8(pdu, bt_mesh_net_flags(sub)); - net_buf_simple_add_be32(pdu, bt_mesh.iv_index); + net_buf_simple_add_u8(pdu, bt_mesh_cdb_subnet_flags(sub)); + net_buf_simple_add_be32(pdu, bt_mesh_cdb.iv_index); net_buf_simple_add_be16(pdu, link.provisioner->node->addr); net_buf_simple_add(pdu, 8); /* For MIC */ @@ -1220,7 +1221,7 @@ static void prov_complete(const u8_t *data) return; } - struct bt_mesh_node *node = link.provisioner->node; + struct bt_mesh_cdb_node *node = link.provisioner->node; #if MYNEWT_VAL(BLE_MESH_PB_ADV) u8_t reason = CLOSE_REASON_SUCCESS; #endif @@ -1230,7 +1231,7 @@ static void prov_complete(const u8_t *data) node->addr); if (IS_ENABLED(CONFIG_BT_SETTINGS)) { - bt_mesh_store_node(node); + bt_mesh_store_cdb_node(node); } link.provisioner->node = NULL; @@ -1241,7 +1242,10 @@ static void prov_complete(const u8_t *data) bearer_ctl_send(LINK_CLOSE, &reason, sizeof(reason)); #endif - bt_mesh_prov_node_added(node->net_idx, node->addr, node->num_elem); + if (prov->node_added) { + prov->node_added(node->net_idx, node->uuid, node->addr, + node->num_elem); + } /* * According to mesh profile spec (5.3.1.4.3), the close message should @@ -1849,6 +1853,7 @@ int bt_mesh_pb_adv_open(const u8_t uuid[16], u16_t net_idx, u16_t addr, bt_rand(&link.id, sizeof(link.id)); link.tx.id = 0x7F; + memcpy(link.provisioner->uuid, uuid, 16); link.provisioner->addr = addr; link.provisioner->net_idx = net_idx; link.provisioner->attention_duration = attention_duration; @@ -2005,13 +2010,6 @@ void bt_mesh_prov_complete(u16_t net_idx, u16_t addr) } } -void bt_mesh_prov_node_added(u16_t net_idx, u16_t addr, u8_t num_elem) -{ - if (prov->node_added) { - prov->node_added(net_idx, addr, num_elem); - } -} - void bt_mesh_prov_reset(void) { if (prov->reset) { diff --git a/nimble/host/mesh/src/settings.c b/nimble/host/mesh/src/settings.c index cb1d19ccb..83db392dd 100644 --- a/nimble/host/mesh/src/settings.c +++ b/nimble/host/mesh/src/settings.c @@ -9,6 +9,7 @@ #if MYNEWT_VAL(BLE_MESH_SETTINGS) +#include "mesh_priv.h" #include "mesh/mesh.h" #include "mesh/glue.h" #include "net.h" @@ -18,8 +19,6 @@ #include "foundation.h" #include "proxy.h" #include "settings.h" -#include "nodes.h" -#include "mesh_priv.h" #include "config/config.h" @@ -28,12 +27,15 @@ * bt_mesh_app_key and bt_mesh_subnet structs themselves, since once a key * gets deleted its struct becomes invalid and may be reused for other keys. */ -static struct key_update { +struct key_update { u16_t key_idx:12, /* AppKey or NetKey Index */ valid:1, /* 1 if this entry is valid, 0 if not */ app_key:1, /* 1 if this is an AppKey, 0 if a NetKey */ clear:1; /* 1 if key needs clearing, 0 if storing */ -} key_updates[CONFIG_BT_MESH_APP_KEY_COUNT + CONFIG_BT_MESH_SUBNET_COUNT]; +}; + +static struct key_update key_updates[CONFIG_BT_MESH_APP_KEY_COUNT + + CONFIG_BT_MESH_SUBNET_COUNT]; static struct k_delayed_work pending_store; @@ -113,11 +115,19 @@ struct va_val { u8_t uuid[16]; } __packed; +struct cdb_net_val { + u32_t iv_index; + bool iv_update; +} __packed; + /* Node storage information */ struct node_val { u16_t net_idx; - u8_t dev_key[16]; u8_t num_elem; + u8_t flags; +#define F_NODE_CONFIGURED 0x01 + u8_t uuid[16]; + u8_t dev_key[16]; } __packed; struct node_update { @@ -125,12 +135,48 @@ struct node_update { bool clear; }; -#if MYNEWT_VAL(BLE_MESH_PROVISIONER) -static struct node_update node_updates[CONFIG_BT_MESH_NODE_COUNT]; +#if MYNEWT_VAL(BLE_MESH_CDB) +static struct node_update cdb_node_updates[MYNEWT_VAL(BLE_MESH_CDB_NODE_COUNT)]; +static struct key_update cdb_key_updates[ + MYNEWT_VAL(BLE_MESH_CDB_SUBNET_COUNT) + + MYNEWT_VAL(BLE_MESH_CDB_APP_KEY_COUNT)]; #else -static struct node_update node_updates[0]; +static struct node_update cdb_node_updates[0]; +static struct key_update cdb_key_updates[0]; #endif +int settings_name_next(char *name, char **next) +{ + int rc = 0; + + if (next) { + *next = NULL; + } + + if (!name) { + return 0; + } + + /* name might come from flash directly, in flash the name would end + * with '=' or '\0' depending how storage is done. Flash reading is + * limited to what can be read + */ + while ((*name != '\0') && (*name != '=') && + (*name != '/')) { + rc++; + name++; + } + + if (*name == '/') { + if (next) { + *next = name + 1; + } + return rc; + } + + return rc; +} + /* We need this so we don't overwrite app-hardcoded values in case FCB * contains a history of changes but then has a NULL at the end. */ @@ -759,10 +805,33 @@ static int va_set(int argc, char **argv, char *val) } #endif -#if MYNEWT_VAL(BLE_MESH_PROVISIONER) -static int node_set(int argc, char **argv, char *str) +#if MYNEWT_VAL(BLE_MESH_CDB) +static int cdb_net_set(int argc, char *val) { - struct bt_mesh_node *node; + struct cdb_net_val net; + int len, err; + + len = sizeof(net); + err = settings_bytes_from_str(val, &net, &len); + if (err) { + BT_ERR("Failed to set \'cdb_net\'"); + return err; + } + + bt_mesh_cdb.iv_index = net.iv_index; + + if (net.iv_update) { + atomic_set_bit(bt_mesh_cdb.flags, BT_MESH_CDB_IVU_IN_PROGRESS); + } + + atomic_set_bit(bt_mesh_cdb.flags, BT_MESH_CDB_VALID); + + return 0; +} + +static int cdb_node_set(int argc, char *str) +{ + struct bt_mesh_cdb_node *node; struct node_val val; u16_t addr; int len, err; @@ -772,15 +841,16 @@ static int node_set(int argc, char **argv, char *str) return -ENOENT; } - addr = strtol(argv[0], NULL, 16); + addr = strtol(str, NULL, 16); + len = sizeof(str); - if (str == NULL) { + if (argc < 1) { BT_DBG("val (null)"); BT_DBG("Deleting node 0x%04x", addr); - node = bt_mesh_node_find(addr); + node = bt_mesh_cdb_node_get(addr); if (node) { - bt_mesh_node_del(node, false); + bt_mesh_cdb_node_del(node, false); } return 0; @@ -797,9 +867,10 @@ static int node_set(int argc, char **argv, char *str) return -EINVAL; } - node = bt_mesh_node_find(addr); + node = bt_mesh_cdb_node_get(addr); if (!node) { - node = bt_mesh_node_alloc(addr, val.num_elem, val.net_idx); + node = bt_mesh_cdb_node_alloc(val.uuid, addr, val.num_elem, + val.net_idx); } if (!node) { @@ -807,12 +878,163 @@ static int node_set(int argc, char **argv, char *str) return -ENOMEM; } - memcpy(node->dev_key, &val.dev_key, 16); + if (val.flags & F_NODE_CONFIGURED) { + atomic_set_bit(node->flags, BT_MESH_CDB_NODE_CONFIGURED); + } + + memcpy(node->uuid, val.uuid, 16); + memcpy(node->dev_key, val.dev_key, 16); BT_DBG("Node 0x%04x recovered from storage", addr); return 0; } + +static int cdb_subnet_set(int argc, char *name) +{ + struct bt_mesh_cdb_subnet *sub; + struct net_key_val key; + u16_t net_idx; + int len, len_rd, err; + + if (!name) { + BT_ERR("Insufficient number of arguments"); + return -ENOENT; + } + + len_rd = sizeof(sub); + net_idx = strtol(name, NULL, 16); + sub = bt_mesh_cdb_subnet_get(net_idx); + + if (len_rd == 0) { + BT_DBG("val (null)"); + if (!sub) { + BT_ERR("No subnet with NetKeyIndex 0x%03x", net_idx); + return -ENOENT; + } + + BT_DBG("Deleting NetKeyIndex 0x%03x", net_idx); + bt_mesh_cdb_subnet_del(sub, false); + return 0; + } + + len = sizeof(key); + err = settings_bytes_from_str(name, &key, &len); + if (err) { + BT_ERR("Failed to set \'net-key\'"); + return err; + } + + if (sub) { + BT_DBG("Updating existing NetKeyIndex 0x%03x", net_idx); + + sub->kr_flag = key.kr_flag; + sub->kr_phase = key.kr_phase; + memcpy(sub->keys[0].net_key, &key.val[0], 16); + memcpy(sub->keys[1].net_key, &key.val[1], 16); + + return 0; + } + + sub = bt_mesh_cdb_subnet_alloc(net_idx); + if (!sub) { + BT_ERR("No space to allocate a new subnet"); + return -ENOMEM; + } + + sub->kr_flag = key.kr_flag; + sub->kr_phase = key.kr_phase; + memcpy(sub->keys[0].net_key, &key.val[0], 16); + memcpy(sub->keys[1].net_key, &key.val[1], 16); + + BT_DBG("NetKeyIndex 0x%03x recovered from storage", net_idx); + + return 0; +} + +static int cdb_app_key_set(int argc, char *name) +{ + struct bt_mesh_cdb_app_key *app; + struct app_key_val key; + u16_t app_idx; + int len_rd, err; + + app_idx = strtol(name, NULL, 16); + len_rd = sizeof(key); + + if (len_rd == 0) { + BT_DBG("val (null)"); + BT_DBG("Deleting AppKeyIndex 0x%03x", app_idx); + + app = bt_mesh_cdb_app_key_get(app_idx); + if (app) { + bt_mesh_cdb_app_key_del(app, false); + } + + return 0; + } + + err = settings_bytes_from_str(name, &key, &len_rd); + if (err) { + BT_ERR("Failed to set \'app-key\'"); + return err; + } + + app = bt_mesh_cdb_app_key_get(app_idx); + if (!app) { + app = bt_mesh_cdb_app_key_alloc(key.net_idx, app_idx); + } + + if (!app) { + BT_ERR("No space for a new app key"); + return -ENOMEM; + } + + memcpy(app->keys[0].app_key, key.val[0], 16); + memcpy(app->keys[1].app_key, key.val[1], 16); + + BT_DBG("AppKeyIndex 0x%03x recovered from storage", app_idx); + + return 0; +} + +static int cdb_set(int argc, char **argv, char *name) +{ + int len; + char *next; + + if (argc < 1) { + BT_ERR("Insufficient number of arguments"); + return -ENOENT; + } + + if (!strcmp(name, "Net")) { + return cdb_net_set(1, name); + } + + + len = settings_name_next(name, &next); + + if (!next) { + BT_ERR("Insufficient number of arguments"); + return -ENOENT; + } + + if (!strncmp(name, "Node", len)) { + return cdb_node_set(1, next); + } + + if (!strncmp(name, "Subnet", len)) { + return cdb_subnet_set(1, next); + } + + if (!strncmp(name, "AppKey", len)) { + return cdb_app_key_set(1, next); + } + + BT_WARN("Unknown module key %s", name); + return -ENOENT; +} #endif const struct mesh_setting { @@ -832,8 +1054,8 @@ const struct mesh_setting { #if CONFIG_BT_MESH_LABEL_COUNT > 0 { "Va", va_set }, #endif -#if MYNEWT_VAL(BLE_MESH_PROVISIONER) - { "Node", node_set }, +#if MYNEWT_VAL(BLE_MESH_CDB) + { "cdb", cdb_set }, #endif }; @@ -979,8 +1201,7 @@ static int mesh_commit(void) #define GENERIC_PENDING_BITS (BIT(BT_MESH_KEYS_PENDING) | \ BIT(BT_MESH_HB_PUB_PENDING) | \ BIT(BT_MESH_CFG_PENDING) | \ - BIT(BT_MESH_MOD_PENDING) | \ - BIT(BT_MESH_NODES_PENDING)) + BIT(BT_MESH_MOD_PENDING)) static void schedule_store(int flag) { @@ -1440,19 +1661,64 @@ static void store_pending_keys(void) } } -static void store_node(struct bt_mesh_node *node) +static void clear_cdb(void) +{ + int err; + + err = settings_save_one("bt/mesh/cdb/Net", NULL); + if (err) { + BT_ERR("Failed to clear Network"); + } else { + BT_DBG("Cleared Network"); + } +} + +static void store_pending_cdb(void) +{ + char buf[BT_SETTINGS_SIZE(sizeof(struct cdb_net_val))]; + struct cdb_net_val net; + int err; + char *str; + + BT_DBG(""); + + net.iv_index = bt_mesh_cdb.iv_index; + net.iv_update = atomic_test_bit(bt_mesh_cdb.flags, + BT_MESH_CDB_IVU_IN_PROGRESS); + + str = settings_str_from_bytes(&net, sizeof(net), buf, sizeof(buf)); + if (!str) { + BT_ERR("Unable to encode Network as value"); + return; + } + err = settings_save_one("bt/mesh/cdb/Net", str); + if (err) { + BT_ERR("Failed to store Network value"); + } else { + BT_DBG("Stored Network value"); + } +} + +static void store_cdb_node(const struct bt_mesh_cdb_node *node) { char buf[BT_SETTINGS_SIZE(sizeof(struct node_val))]; struct node_val val; - char path[20]; + char path[30]; char *str; int err; val.net_idx = node->net_idx; val.num_elem = node->num_elem; + val.flags = 0; + + if (atomic_test_bit(node->flags, BT_MESH_CDB_NODE_CONFIGURED)) { + val.flags |= F_NODE_CONFIGURED; + } + + memcpy(val.uuid, node->uuid, 16); memcpy(val.dev_key, node->dev_key, 16); - snprintk(path, sizeof(path), "bt_mesh/Node/%x", node->addr); + snprintk(path, sizeof(path), "bt/mesh/cdb/Node/%x", node->addr); str = settings_str_from_bytes(&val, sizeof(val), buf, sizeof(buf)); if (!str) { @@ -1469,14 +1735,14 @@ static void store_node(struct bt_mesh_node *node) } } -static void clear_node(u16_t addr) +static void clear_cdb_node(u16_t addr) { - char path[20]; + char path[30]; int err; BT_DBG("Node 0x%04x", addr); - snprintk(path, sizeof(path), "bt_mesh/Node/%x", addr); + snprintk(path, sizeof(path), "bt/mesh/cdb/Node/%x", addr); err = settings_save_one(path, NULL); if (err) { BT_ERR("Failed to clear Node 0x%04x", addr); @@ -1485,25 +1751,27 @@ static void clear_node(u16_t addr) } } -static void store_pending_nodes(void) +static void store_pending_cdb_nodes(void) { int i; - for (i = 0; i < ARRAY_SIZE(node_updates); ++i) { - struct node_update *update = &node_updates[i]; + for (i = 0; i < ARRAY_SIZE(cdb_node_updates); ++i) { + struct node_update *update = &cdb_node_updates[i]; if (update->addr == BT_MESH_ADDR_UNASSIGNED) { continue; } - if (update->clear) { - clear_node(update->addr); - } else { - struct bt_mesh_node *node; + BT_DBG("addr: 0x%04x, clear: %d", update->addr, update->clear); - node = bt_mesh_node_find(update->addr); + if (update->clear) { + clear_cdb_node(update->addr); + } else { + struct bt_mesh_cdb_node *node; + + node = bt_mesh_cdb_node_get(update->addr); if (node) { - store_node(node); + store_cdb_node(node); } else { BT_WARN("Node 0x%04x not found", update->addr); } @@ -1513,8 +1781,139 @@ static void store_pending_nodes(void) } } -static struct node_update *node_update_find(u16_t addr, - struct node_update **free_slot) +static void store_cdb_subnet(const struct bt_mesh_cdb_subnet *sub) +{ + char buf[BT_SETTINGS_SIZE(sizeof(struct net_key_val))]; + struct net_key_val key; + char path[30]; + int err; + char *str; + + BT_DBG("NetKeyIndex 0x%03x NetKey %s", sub->net_idx, + bt_hex(sub->keys[0].net_key, 16)); + + memcpy(&key.val[0], sub->keys[0].net_key, 16); + memcpy(&key.val[1], sub->keys[1].net_key, 16); + key.kr_flag = sub->kr_flag; + key.kr_phase = sub->kr_phase; + + snprintk(path, sizeof(path), "bt/mesh/cdb/Subnet/%x", sub->net_idx); + + + str = settings_str_from_bytes(&key, sizeof(key), buf, sizeof(buf)); + if (!str) { + BT_ERR("Unable to encode Subnet as value"); + return; + } + err = settings_save_one(path, str); + if (err) { + BT_ERR("Failed to store Subnet value"); + } else { + BT_DBG("Stored Subnet value"); + } +} + +static void clear_cdb_subnet(u16_t net_idx) +{ + char path[30]; + int err; + + BT_DBG("NetKeyIndex 0x%03x", net_idx); + + snprintk(path, sizeof(path), "bt/mesh/cdb/Subnet/%x", net_idx); + err = settings_save_one(path, NULL); + if (err) { + BT_ERR("Failed to clear NetKeyIndex 0x%03x", net_idx); + } else { + BT_DBG("Cleared NetKeyIndex 0x%03x", net_idx); + } +} + +static void store_cdb_app_key(const struct bt_mesh_cdb_app_key *app) +{ + char buf[BT_SETTINGS_SIZE(sizeof(struct app_key_val))]; + struct app_key_val key; + char path[30]; + int err; + char *str; + + key.net_idx = app->net_idx; + key.updated = false; + memcpy(key.val[0], app->keys[0].app_key, 16); + memcpy(key.val[1], app->keys[1].app_key, 16); + + snprintk(path, sizeof(path), "bt/mesh/cdb/AppKey/%x", app->app_idx); + + str = settings_str_from_bytes(&key, sizeof(key), buf, sizeof(buf)); + err = settings_save_one(path, str); + if (err) { + BT_ERR("Failed to store AppKey"); + } else { + BT_DBG("Stored AppKey"); + } +} + +static void clear_cdb_app_key(u16_t app_idx) +{ + char path[30]; + int err; + + snprintk(path, sizeof(path), "bt/mesh/cdb/AppKey/%x", app_idx); + err = settings_save_one(path, NULL); + if (err) { + BT_ERR("Failed to clear AppKeyIndex 0x%03x", app_idx); + } else { + BT_DBG("Cleared AppKeyIndex 0x%03x", app_idx); + } +} + +static void store_pending_cdb_keys(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(cdb_key_updates); i++) { + struct key_update *update = &cdb_key_updates[i]; + + if (!update->valid) { + continue; + } + + if (update->clear) { + if (update->app_key) { + clear_cdb_app_key(update->key_idx); + } else { + clear_cdb_subnet(update->key_idx); + } + } else { + if (update->app_key) { + struct bt_mesh_cdb_app_key *key; + + key = bt_mesh_cdb_app_key_get(update->key_idx); + if (key) { + store_cdb_app_key(key); + } else { + BT_WARN("AppKeyIndex 0x%03x not found", + update->key_idx); + } + } else { + struct bt_mesh_cdb_subnet *sub; + + sub = bt_mesh_cdb_subnet_get(update->key_idx); + if (sub) { + store_cdb_subnet(sub); + } else { + BT_WARN("NetKeyIndex 0x%03x not found", + update->key_idx); + } + } + } + + update->valid = 0U; + } +} + +static struct node_update *cdb_node_update_find(u16_t addr, + struct node_update **free_slot) { struct node_update *match; int i; @@ -1522,8 +1921,8 @@ static struct node_update *node_update_find(u16_t addr, match = NULL; *free_slot = NULL; - for (i = 0; i < ARRAY_SIZE(node_updates); i++) { - struct node_update *update = &node_updates[i]; + for (i = 0; i < ARRAY_SIZE(cdb_node_updates); i++) { + struct node_update *update = &cdb_node_updates[i]; if (update->addr == BT_MESH_ADDR_UNASSIGNED) { *free_slot = update; @@ -1786,9 +2185,26 @@ static void store_pending(struct ble_npl_event *work) store_pending_va(); } - if (IS_ENABLED(CONFIG_BT_MESH_PROVISIONER) && - atomic_test_and_clear_bit(bt_mesh.flags, BT_MESH_NODES_PENDING)) { - store_pending_nodes(); + if (IS_ENABLED(CONFIG_BT_MESH_CDB)) { + if (atomic_test_and_clear_bit(bt_mesh_cdb.flags, + BT_MESH_CDB_SUBNET_PENDING)) { + if (atomic_test_bit(bt_mesh_cdb.flags, + BT_MESH_CDB_VALID)) { + store_pending_cdb(); + } else { + clear_cdb(); + } + } + + if (atomic_test_and_clear_bit(bt_mesh_cdb.flags, + BT_MESH_CDB_NODES_PENDING)) { + store_pending_cdb_nodes(); + } + + if (atomic_test_and_clear_bit(bt_mesh_cdb.flags, + BT_MESH_CDB_KEYS_PENDING)) { + store_pending_cdb_keys(); + } } } @@ -1977,51 +2393,196 @@ void bt_mesh_store_label(void) schedule_store(BT_MESH_VA_PENDING); } -void bt_mesh_store_node(struct bt_mesh_node *node) +static void schedule_cdb_store(int flag) { - struct node_update *update, *free_slot; - - BT_DBG("Node 0x%04x", node->addr); - - update = node_update_find(node->addr, &free_slot); - if (update) { - update->clear = false; - schedule_store(BT_MESH_NODES_PENDING); - return; - } - - if (!free_slot) { - store_node(node); - return; - } - - free_slot->addr = node->addr; - - schedule_store(BT_MESH_NODES_PENDING); + atomic_set_bit(bt_mesh_cdb.flags, flag); + k_delayed_work_submit(&pending_store, K_NO_WAIT); } -void bt_mesh_clear_node(struct bt_mesh_node *node) +void bt_mesh_store_cdb(void) +{ + schedule_cdb_store(BT_MESH_CDB_SUBNET_PENDING); +} + +void bt_mesh_store_cdb_node(const struct bt_mesh_cdb_node *node) { struct node_update *update, *free_slot; BT_DBG("Node 0x%04x", node->addr); - update = node_update_find(node->addr, &free_slot); + update = cdb_node_update_find(node->addr, &free_slot); if (update) { - update->clear = true; - schedule_store(BT_MESH_NODES_PENDING); + update->clear = false; + schedule_cdb_store(BT_MESH_CDB_NODES_PENDING); return; } if (!free_slot) { - clear_node(node->addr); + store_cdb_node(node); return; } - free_slot->clear = true; free_slot->addr = node->addr; - schedule_store(BT_MESH_NODES_PENDING); + schedule_cdb_store(BT_MESH_CDB_NODES_PENDING); +} + +void bt_mesh_clear_cdb_node(struct bt_mesh_cdb_node *node) +{ + struct node_update *update, *free_slot; + + BT_DBG("Node 0x%04x", node->addr); + + update = cdb_node_update_find(node->addr, &free_slot); + if (update) { + update->clear = true; + schedule_cdb_store(BT_MESH_CDB_NODES_PENDING); + return; + } + + if (!free_slot) { + clear_cdb_node(node->addr); + return; + } + + free_slot->addr = node->addr; + free_slot->clear = true; + + schedule_cdb_store(BT_MESH_CDB_NODES_PENDING); +} + +/* TODO: Could be shared with key_update_find? */ +static struct key_update *cdb_key_update_find(bool app_key, u16_t key_idx, + struct key_update **free_slot) +{ + struct key_update *match; + int i; + + match = NULL; + *free_slot = NULL; + + for (i = 0; i < ARRAY_SIZE(key_updates); i++) { + struct key_update *update = &cdb_key_updates[i]; + + if (!update->valid) { + *free_slot = update; + continue; + } + + if (update->app_key != app_key) { + continue; + } + + if (update->key_idx == key_idx) { + match = update; + } + } + + return match; +} + +void bt_mesh_store_cdb_subnet(const struct bt_mesh_cdb_subnet *sub) +{ + struct key_update *update, *free_slot; + + BT_DBG("NetKeyIndex 0x%03x", sub->net_idx); + + update = cdb_key_update_find(false, sub->net_idx, &free_slot); + if (update) { + update->clear = 0U; + schedule_cdb_store(BT_MESH_CDB_KEYS_PENDING); + return; + } + + if (!free_slot) { + store_cdb_subnet(sub); + return; + } + + free_slot->valid = 1U; + free_slot->key_idx = sub->net_idx; + free_slot->app_key = 0U; + free_slot->clear = 0U; + + schedule_cdb_store(BT_MESH_CDB_KEYS_PENDING); +} + +void bt_mesh_clear_cdb_subnet(struct bt_mesh_cdb_subnet *sub) +{ + struct key_update *update, *free_slot; + + BT_DBG("NetKeyIndex 0x%03x", sub->net_idx); + + update = cdb_key_update_find(false, sub->net_idx, &free_slot); + if (update) { + update->clear = 1U; + schedule_cdb_store(BT_MESH_CDB_KEYS_PENDING); + return; + } + + if (!free_slot) { + clear_cdb_subnet(sub->net_idx); + return; + } + + free_slot->valid = 1U; + free_slot->key_idx = sub->net_idx; + free_slot->app_key = 0U; + free_slot->clear = 1U; + + schedule_cdb_store(BT_MESH_CDB_KEYS_PENDING); +} + +void bt_mesh_store_cdb_app_key(const struct bt_mesh_cdb_app_key *key) +{ + struct key_update *update, *free_slot; + + BT_DBG("AppKeyIndex 0x%03x", key->app_idx); + + update = cdb_key_update_find(true, key->app_idx, &free_slot); + if (update) { + update->clear = 0U; + schedule_cdb_store(BT_MESH_CDB_KEYS_PENDING); + return; + } + + if (!free_slot) { + store_cdb_app_key(key); + return; + } + + free_slot->valid = 1U; + free_slot->key_idx = key->app_idx; + free_slot->app_key = 1U; + free_slot->clear = 0U; + + schedule_cdb_store(BT_MESH_CDB_KEYS_PENDING); +} + +void bt_mesh_clear_cdb_app_key(struct bt_mesh_cdb_app_key *key) +{ + struct key_update *update, *free_slot; + + BT_DBG("AppKeyIndex 0x%03x", key->app_idx); + + update = cdb_key_update_find(true, key->app_idx, &free_slot); + if (update) { + update->clear = 1U; + schedule_cdb_store(BT_MESH_CDB_KEYS_PENDING); + return; + } + + if (!free_slot) { + clear_cdb_app_key(key->app_idx); + return; + } + + free_slot->valid = 1U; + free_slot->key_idx = key->app_idx; + free_slot->app_key = 1U; + free_slot->clear = 1U; + + schedule_cdb_store(BT_MESH_CDB_KEYS_PENDING); } int bt_mesh_model_data_store(struct bt_mesh_model *mod, bool vnd, diff --git a/nimble/host/mesh/src/settings.h b/nimble/host/mesh/src/settings.h index c630814e5..2718cb5e3 100644 --- a/nimble/host/mesh/src/settings.h +++ b/nimble/host/mesh/src/settings.h @@ -16,12 +16,17 @@ void bt_mesh_store_mod_bind(struct bt_mesh_model *mod); void bt_mesh_store_mod_sub(struct bt_mesh_model *mod); void bt_mesh_store_mod_pub(struct bt_mesh_model *mod); void bt_mesh_store_label(void); -void bt_mesh_store_node(struct bt_mesh_node *node); +void bt_mesh_store_cdb(void); +void bt_mesh_store_cdb_node(const struct bt_mesh_cdb_node *node); +void bt_mesh_store_cdb_subnet(const struct bt_mesh_cdb_subnet *sub); +void bt_mesh_store_cdb_app_key(const struct bt_mesh_cdb_app_key *app); void bt_mesh_clear_net(void); void bt_mesh_clear_subnet(struct bt_mesh_subnet *sub); void bt_mesh_clear_app_key(struct bt_mesh_app_key *key); void bt_mesh_clear_rpl(void); -void bt_mesh_clear_node(struct bt_mesh_node *node); +void bt_mesh_clear_cdb_node(struct bt_mesh_cdb_node *node); +void bt_mesh_clear_cdb_subnet(struct bt_mesh_cdb_subnet *sub); +void bt_mesh_clear_cdb_app_key(struct bt_mesh_cdb_app_key *app); void bt_mesh_settings_init(void); diff --git a/nimble/host/mesh/src/shell.c b/nimble/host/mesh/src/shell.c index 91fbd9785..7afc9bc90 100644 --- a/nimble/host/mesh/src/shell.c +++ b/nimble/host/mesh/src/shell.c @@ -310,6 +310,53 @@ static u8_t hex2val(char c) } } +int char2hex(char c, uint8_t *x) +{ + if (c >= '0' && c <= '9') { + *x = c - '0'; + } else if (c >= 'a' && c <= 'f') { + *x = c - 'a' + 10; + } else if (c >= 'A' && c <= 'F') { + *x = c - 'A' + 10; + } else { + return -EINVAL; + } + + return 0; +} + +int hex2char(uint8_t x, char *c) +{ + if (x <= 9) { + *c = x + '0'; + } else if (x <= 15) { + *c = x - 10 + 'a'; + } else { + return -EINVAL; + } + + return 0; +} + +size_t bin2hex(const uint8_t *buf, size_t buflen, char *hex, size_t hexlen) +{ + if ((hexlen + 1) < buflen * 2) { + return 0; + } + + for (size_t i = 0; i < buflen; i++) { + if (hex2char(buf[i] >> 4, &hex[2 * i]) < 0) { + return 0; + } + if (hex2char(buf[i] & 0xf, &hex[2 * i + 1]) < 0) { + return 0; + } + } + + hex[2 * buflen] = '\0'; + return 2 * buflen; +} + static size_t hex2bin(const char *hex, u8_t *bin, size_t bin_len) { size_t len = 0; @@ -337,7 +384,8 @@ static void prov_complete(u16_t net_idx, u16_t addr) net.dst = addr; } -static void prov_node_added(u16_t net_idx, u16_t addr, u8_t num_elem) +static void prov_node_added(u16_t net_idx, u8_t uuid[16], u16_t addr, + u8_t num_elem) { printk("Node provisioned, net_idx 0x%04x address " "0x%04x elements %d", net_idx, addr, num_elem); @@ -1054,10 +1102,40 @@ static int cmd_beacon(int argc, char *argv[]) return 0; } +static void print_unprovisioned_beacon(u8_t uuid[16], + bt_mesh_prov_oob_info_t oob_info, + u32_t *uri_hash) +{ + char uuid_hex_str[32 + 1]; + + bin2hex(uuid, 16, uuid_hex_str, sizeof(uuid_hex_str)); + + printk("UUID %s, OOB Info 0x%04x, URI Hash 0x%lx", + uuid_hex_str, oob_info, + (uri_hash == NULL ? 0 : *uri_hash)); +} + +static int cmd_beacon_listen(int argc, char *argv[]) +{ + u8_t val = str2u8(argv[1]); + + if (val) { + prov.unprovisioned_beacon = print_unprovisioned_beacon; + } else { + prov.unprovisioned_beacon = NULL; + } + + return 0; +} + struct shell_cmd_help cmd_beacon_help = { NULL, "[val: off, on]", NULL }; +struct shell_cmd_help cmd_beacon_listen_help = { + NULL, "[val: off, on]", NULL +}; + static int cmd_ttl(int argc, char *argv[]) { u8_t ttl; @@ -2261,6 +2339,333 @@ struct shell_cmd_help cmd_del_fault_help = { NULL, "[Fault ID]", NULL }; +#if defined(CONFIG_BT_MESH_CDB) +struct shell_cmd_help cmd_cdb_create_help = { + NULL, "[NetKey]", NULL +}; +struct shell_cmd_help cmd_cdb_clear_help = { + NULL, NULL, NULL +}; +struct shell_cmd_help cmd_cdb_show_help = { + NULL, NULL, NULL +}; +struct shell_cmd_help cmd_cdb_node_add_help = { + NULL, " " + " [DevKey]", NULL +}; +struct shell_cmd_help cmd_cdb_node_del_help = { + NULL, "", NULL +}; +struct shell_cmd_help cmd_cdb_subnet_add_help = { + NULL, " []", NULL +}; +struct shell_cmd_help cmd_cdb_subnet_del_help = { + NULL, "", NULL +}; +struct shell_cmd_help cmd_cdb_app_key_add_help = { + NULL, " []", NULL +}; +struct shell_cmd_help cmd_cdb_app_key_del_help = { + NULL, "", NULL +}; + +static int cmd_cdb_create(int argc, char *argv[]) +{ + u8_t net_key[16]; + size_t len; + int err; + + if (argc < 2) { + bt_rand(net_key, 16); + } else { + len = hex2bin(argv[1], net_key, sizeof(net_key)); + memset(net_key + len, 0, sizeof(net_key) - len); + } + + err = bt_mesh_cdb_create(net_key); + if (err < 0) { + printk("Failed to create CDB (err %d)", err); + } + + return 0; +} + +static int cmd_cdb_clear(int argc, char *argv[]) +{ + bt_mesh_cdb_clear(); + + printk("Cleared CDB"); + + return 0; +} + +static void cdb_print_nodes(void) +{ + char key_hex_str[32 + 1], uuid_hex_str[32 + 1]; + struct bt_mesh_cdb_node *node; + int i, total = 0; + bool configured; + + printk("Address Elements Flags %-32s DevKey", "UUID"); + + for (i = 0; i < ARRAY_SIZE(bt_mesh_cdb.nodes); ++i) { + node = &bt_mesh_cdb.nodes[i]; + if (node->addr == BT_MESH_ADDR_UNASSIGNED) { + continue; + } + + configured = atomic_test_bit(node->flags, + BT_MESH_CDB_NODE_CONFIGURED); + + total++; + bin2hex(node->uuid, 16, uuid_hex_str, sizeof(uuid_hex_str)); + bin2hex(node->dev_key, 16, key_hex_str, sizeof(key_hex_str)); + printk("0x%04x %-8d %-5s %s %s", node->addr, + node->num_elem, configured ? "C" : "-", + uuid_hex_str, key_hex_str); + } + + printk("> Total nodes: %d", total); +} + +static void cdb_print_subnets(void) +{ + struct bt_mesh_cdb_subnet *subnet; + char key_hex_str[32 + 1]; + int i, total = 0; + + printk("NetIdx NetKey"); + + for (i = 0; i < ARRAY_SIZE(bt_mesh_cdb.subnets); ++i) { + subnet = &bt_mesh_cdb.subnets[i]; + if (subnet->net_idx == BT_MESH_KEY_UNUSED) { + continue; + } + + total++; + bin2hex(subnet->keys[0].net_key, 16, key_hex_str, + sizeof(key_hex_str)); + printk("0x%03x %s", subnet->net_idx, + key_hex_str); + } + + printk("> Total subnets: %d", total); +} + +static void cdb_print_app_keys(void) +{ + struct bt_mesh_cdb_app_key *app_key; + char key_hex_str[32 + 1]; + int i, total = 0; + + printk("NetIdx AppIdx AppKey"); + + for (i = 0; i < ARRAY_SIZE(bt_mesh_cdb.app_keys); ++i) { + app_key = &bt_mesh_cdb.app_keys[i]; + if (app_key->net_idx == BT_MESH_KEY_UNUSED) { + continue; + } + + total++; + bin2hex(app_key->keys[0].app_key, 16, key_hex_str, + sizeof(key_hex_str)); + printk("0x%03x 0x%03x %s", + app_key->net_idx, app_key->app_idx, key_hex_str); + } + + printk("> Total app-keys: %d", total); +} + +static int cmd_cdb_show(int argc, char *argv[]) +{ + if (!atomic_test_bit(bt_mesh_cdb.flags, BT_MESH_CDB_VALID)) { + printk("No valid networks"); + return 0; + } + + printk("Mesh Network Information"); + printk("========================"); + + cdb_print_nodes(); + printk("---"); + cdb_print_subnets(); + printk("---"); + cdb_print_app_keys(); + + return 0; +} + +static int cmd_cdb_node_add(int argc, char *argv[]) +{ + struct bt_mesh_cdb_node *node; + u8_t uuid[16], dev_key[16]; + u16_t addr, net_idx; + u8_t num_elem; + size_t len; + + len = hex2bin(argv[1], uuid, sizeof(uuid)); + memset(uuid + len, 0, sizeof(uuid) - len); + + addr = strtoul(argv[2], NULL, 0); + num_elem = strtoul(argv[3], NULL, 0); + net_idx = strtoul(argv[4], NULL, 0); + + if (argc < 6) { + bt_rand(dev_key, 16); + } else { + len = hex2bin(argv[5], dev_key, sizeof(dev_key)); + memset(dev_key + len, 0, sizeof(dev_key) - len); + } + + node = bt_mesh_cdb_node_alloc(uuid, addr, num_elem, net_idx); + if (node == NULL) { + printk("Failed to allocate node"); + return 0; + } + + memcpy(node->dev_key, dev_key, 16); + + if (IS_ENABLED(CONFIG_SETTINGS)) { + bt_mesh_store_cdb_node(node); + } + + printk("Added node 0x%04x", addr); + + return 0; +} + +static int cmd_cdb_node_del(int argc, char *argv[]) +{ + struct bt_mesh_cdb_node *node; + u16_t addr; + + addr = strtoul(argv[1], NULL, 0); + + node = bt_mesh_cdb_node_get(addr); + if (node == NULL) { + printk("No node with address 0x%04x", addr); + return 0; + } + + bt_mesh_cdb_node_del(node, true); + + printk("Deleted node 0x%04x", addr); + + return 0; +} + +static int cmd_cdb_subnet_add(int argc, + char *argv[]) +{ + struct bt_mesh_cdb_subnet *sub; + u8_t net_key[16]; + u16_t net_idx; + size_t len; + + net_idx = strtoul(argv[1], NULL, 0); + + if (argc < 3) { + bt_rand(net_key, 16); + } else { + len = hex2bin(argv[2], net_key, sizeof(net_key)); + memset(net_key + len, 0, sizeof(net_key) - len); + } + + sub = bt_mesh_cdb_subnet_alloc(net_idx); + if (sub == NULL) { + printk("Could not add subnet"); + return 0; + } + + memcpy(sub->keys[0].net_key, net_key, 16); + + if (IS_ENABLED(CONFIG_SETTINGS)) { + bt_mesh_store_cdb_subnet(sub); + } + + printk("Added Subnet 0x%03x", net_idx); + + return 0; +} + +static int cmd_cdb_subnet_del(int argc, + char *argv[]) +{ + struct bt_mesh_cdb_subnet *sub; + u16_t net_idx; + + net_idx = strtoul(argv[1], NULL, 0); + + sub = bt_mesh_cdb_subnet_get(net_idx); + if (sub == NULL) { + printk("No subnet with NetIdx 0x%03x", net_idx); + return 0; + } + + bt_mesh_cdb_subnet_del(sub, true); + + printk("Deleted subnet 0x%03x", net_idx); + + return 0; +} + +static int cmd_cdb_app_key_add(int argc, + char *argv[]) +{ + struct bt_mesh_cdb_app_key *key; + u16_t net_idx, app_idx; + u8_t app_key[16]; + size_t len; + + net_idx = strtoul(argv[1], NULL, 0); + app_idx = strtoul(argv[2], NULL, 0); + + if (argc < 4) { + bt_rand(app_key, 16); + } else { + len = hex2bin(argv[3], app_key, sizeof(app_key)); + memset(app_key + len, 0, sizeof(app_key) - len); + } + + key = bt_mesh_cdb_app_key_alloc(net_idx, app_idx); + if (key == NULL) { + printk("Could not add AppKey"); + return 0; + } + + memcpy(key->keys[0].app_key, app_key, 16); + + if (IS_ENABLED(CONFIG_SETTINGS)) { + bt_mesh_store_cdb_app_key(key); + } + + printk("Added AppKey 0x%03x", app_idx); + + return 0; +} + +static int cmd_cdb_app_key_del(int argc, + char *argv[]) +{ + struct bt_mesh_cdb_app_key *key; + u16_t app_idx; + + app_idx = strtoul(argv[1], NULL, 0); + + key = bt_mesh_cdb_app_key_get(app_idx); + if (key == NULL) { + printk("No AppKey 0x%03x", app_idx); + return 0; + } + + bt_mesh_cdb_app_key_del(key, true); + + printk("Deleted AppKey 0x%03x", app_idx); + + return 0; +} +#endif + #if MYNEWT_VAL(BLE_MESH_SHELL_MODELS) static int cmd_gen_onoff_get(int argc, char *argv[]) { @@ -2605,6 +3010,11 @@ static const struct shell_cmd mesh_commands[] = { .sc_cmd_func = cmd_beacon, .help = &cmd_beacon_help, }, + { + .sc_cmd = "beacon-listen", + .sc_cmd_func = cmd_beacon_listen, + .help = &cmd_beacon_listen_help, + }, { .sc_cmd = "ttl", .sc_cmd_func = cmd_ttl, @@ -2748,6 +3158,46 @@ static const struct shell_cmd mesh_commands[] = { .help = &cmd_del_fault_help, }, +#if MYNEWT_VAL(BLE_MESH_CDB) + { + .sc_cmd = "cdb-create", + .sc_cmd_func = cmd_cdb_create, + .help = &cmd_cdb_create_help, + },{ + .sc_cmd = "cdb-clear", + .sc_cmd_func = cmd_cdb_clear, + .help = &cmd_cdb_clear_help, + },{ + .sc_cmd = "cdb-show", + .sc_cmd_func = cmd_cdb_show, + .help = &cmd_cdb_show_help, + },{ + .sc_cmd = "cdb-node-add", + .sc_cmd_func = cmd_cdb_node_add, + .help = &cmd_cdb_node_add_help, + },{ + .sc_cmd = "cdb-node-del", + .sc_cmd_func = cmd_cdb_node_del, + .help = &cmd_cdb_node_del_help, + },{ + .sc_cmd = "cdb-subnet-add", + .sc_cmd_func = cmd_cdb_subnet_add, + .help = &cmd_cdb_subnet_add_help, + },{ + .sc_cmd = "cdb-subnet-del", + .sc_cmd_func = cmd_cdb_subnet_del, + .help = &cmd_cdb_subnet_del_help, + },{ + .sc_cmd = "cdb-app-key-add", + .sc_cmd_func = cmd_cdb_app_key_add, + .help = &cmd_cdb_app_key_add_help, + },{ + .sc_cmd = "cdb-app-key-add", + .sc_cmd_func = cmd_cdb_app_key_del, + .help = &cmd_cdb_app_key_del_help, + }, +#endif + #if MYNEWT_VAL(BLE_MESH_SHELL_MODELS) /* Generic Client Model Operations */ { diff --git a/nimble/host/mesh/src/transport.c b/nimble/host/mesh/src/transport.c index 8bf321d64..23411a8e1 100644 --- a/nimble/host/mesh/src/transport.c +++ b/nimble/host/mesh/src/transport.c @@ -25,7 +25,6 @@ #include "settings.h" #include "transport.h" #include "testing.h" -#include "nodes.h" /* The transport layer needs at least three buffers for itself to avoid * deadlocks. Ensure that there are a sufficient number of advertising @@ -643,7 +642,7 @@ static int sdu_recv(struct bt_mesh_net_rx *rx, u32_t seq, u8_t hdr, } if (IS_ENABLED(CONFIG_BT_MESH_PROVISIONER)) { - struct bt_mesh_node *node; + struct bt_mesh_cdb_node *node; /* * There is no way of knowing if we should use our @@ -651,7 +650,7 @@ static int sdu_recv(struct bt_mesh_net_rx *rx, u32_t seq, u8_t hdr, * message so we must try both. */ - node = bt_mesh_node_find(rx->ctx.addr); + node = bt_mesh_cdb_node_get(rx->ctx.addr); if (node == NULL) { BT_ERR("No node found for addr 0x%04x", rx->ctx.addr); @@ -1724,18 +1723,16 @@ int bt_mesh_app_key_get(const struct bt_mesh_subnet *subnet, u16_t app_idx, { struct bt_mesh_app_key *app_key; - if (app_idx == BT_MESH_KEY_DEV_LOCAL || - (app_idx == BT_MESH_KEY_DEV_REMOTE && - bt_mesh_elem_find(addr) != NULL)) { + if (app_idx == BT_MESH_KEY_DEV_LOCAL) { *aid = 0; *key = bt_mesh.dev_key; return 0; } else if (app_idx == BT_MESH_KEY_DEV_REMOTE) { - if (!IS_ENABLED(CONFIG_BT_MESH_PROVISIONER)) { + if (!IS_ENABLED(CONFIG_BT_MESH_CDB)) { return -EINVAL; } - struct bt_mesh_node *node = bt_mesh_node_find(addr); + struct bt_mesh_cdb_node *node = bt_mesh_cdb_node_get(addr); if (!node) { return -EINVAL; } diff --git a/nimble/host/mesh/syscfg.yml b/nimble/host/mesh/syscfg.yml index 98632232b..a61cbc54f 100644 --- a/nimble/host/mesh/syscfg.yml +++ b/nimble/host/mesh/syscfg.yml @@ -36,10 +36,32 @@ syscfg.defs: restrictions: - (BLE_MESH_PROV) - BLE_MESH_NODE_COUNT: + BLE_MESH_CDB: + description: > + Mesh Configuration Database [EXPERIMENTAL] + value: 1 + + BLE_MESH_CDB_NODE_COUNT: description: > This option specifies how many nodes each network can at most - save in the provisioning database. Range 1-4096 + save in the configuration database. + value: 1 + + BLE_MESH_CDB_SUBNET_COUNT: + description: > + This option specifies how many subnets that can at most be + saved in the configuration database. + value: 1 + + BLE_MESH_CDB_APP_KEY_COUNT: + description: > + This option specifies how many application keys that can at most + be saved in the configuration database. + value: 1 + + BLE_MESH_DEBUG_CDB: + description: + Use this option to enable configuration database debug logs. value: 1 BLE_MESH_PROXY: diff --git a/porting/examples/linux_blemesh/include/syscfg/syscfg.h b/porting/examples/linux_blemesh/include/syscfg/syscfg.h index 7d9a1f5c8..7880d3de6 100644 --- a/porting/examples/linux_blemesh/include/syscfg/syscfg.h +++ b/porting/examples/linux_blemesh/include/syscfg/syscfg.h @@ -1020,6 +1020,22 @@ #define MYNEWT_VAL_BLE_MESH_PROVISIONER (0) #endif +#ifndef MYNEWT_VAL_BLE_MESH_CDB +#define MYNEWT_VAL_BLE_MESH_CDB (1) +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_CDB_SUBNET_COUNT +#define MYNEWT_VAL_BLE_MESH_CDB_SUBNET_COUNT (1) +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_CDB_NODE_COUNT +#define MYNEWT_VAL_BLE_MESH_CDB_NODE_COUNT (1) +#endif + +#ifndef MYNEWT_VAL_BLE_MESH_CDB_APP_KEY_COUNT +#define MYNEWT_VAL_BLE_MESH_CDB_APP_KEY_COUNT (1) +#endif + #ifndef MYNEWT_VAL_BLE_MESH_PROV_LOG_LVL #define MYNEWT_VAL_BLE_MESH_PROV_LOG_LVL (1) #endif