summaryrefslogtreecommitdiff
path: root/src/libs/mynewt-nimble/nimble/host/mesh
diff options
context:
space:
mode:
Diffstat (limited to 'src/libs/mynewt-nimble/nimble/host/mesh')
-rw-r--r--src/libs/mynewt-nimble/nimble/host/mesh/include/mesh/access.h656
-rw-r--r--src/libs/mynewt-nimble/nimble/host/mesh/include/mesh/cfg_cli.h234
-rw-r--r--src/libs/mynewt-nimble/nimble/host/mesh/include/mesh/cfg_srv.h78
-rw-r--r--src/libs/mynewt-nimble/nimble/host/mesh/include/mesh/glue.h502
-rw-r--r--src/libs/mynewt-nimble/nimble/host/mesh/include/mesh/health_cli.h81
-rw-r--r--src/libs/mynewt-nimble/nimble/host/mesh/include/mesh/health_srv.h100
-rw-r--r--src/libs/mynewt-nimble/nimble/host/mesh/include/mesh/main.h441
-rw-r--r--src/libs/mynewt-nimble/nimble/host/mesh/include/mesh/mesh.h26
-rw-r--r--src/libs/mynewt-nimble/nimble/host/mesh/include/mesh/model_cli.h49
-rw-r--r--src/libs/mynewt-nimble/nimble/host/mesh/include/mesh/model_srv.h67
-rw-r--r--src/libs/mynewt-nimble/nimble/host/mesh/include/mesh/porting.h27
-rw-r--r--src/libs/mynewt-nimble/nimble/host/mesh/include/mesh/proxy.h43
-rw-r--r--src/libs/mynewt-nimble/nimble/host/mesh/include/mesh/slist.h468
-rw-r--r--src/libs/mynewt-nimble/nimble/host/mesh/include/mesh/testing.h105
-rw-r--r--src/libs/mynewt-nimble/nimble/host/mesh/pkg.yml49
-rw-r--r--src/libs/mynewt-nimble/nimble/host/mesh/src/access.c856
-rw-r--r--src/libs/mynewt-nimble/nimble/host/mesh/src/access.h67
-rw-r--r--src/libs/mynewt-nimble/nimble/host/mesh/src/adv.c439
-rw-r--r--src/libs/mynewt-nimble/nimble/host/mesh/src/adv.h79
-rw-r--r--src/libs/mynewt-nimble/nimble/host/mesh/src/atomic.h409
-rw-r--r--src/libs/mynewt-nimble/nimble/host/mesh/src/beacon.c441
-rw-r--r--src/libs/mynewt-nimble/nimble/host/mesh/src/beacon.h26
-rw-r--r--src/libs/mynewt-nimble/nimble/host/mesh/src/cfg_cli.c1498
-rw-r--r--src/libs/mynewt-nimble/nimble/host/mesh/src/cfg_srv.c3619
-rw-r--r--src/libs/mynewt-nimble/nimble/host/mesh/src/crypto.c911
-rw-r--r--src/libs/mynewt-nimble/nimble/host/mesh/src/crypto.h170
-rw-r--r--src/libs/mynewt-nimble/nimble/host/mesh/src/foundation.h171
-rw-r--r--src/libs/mynewt-nimble/nimble/host/mesh/src/friend.c1651
-rw-r--r--src/libs/mynewt-nimble/nimble/host/mesh/src/friend.h56
-rw-r--r--src/libs/mynewt-nimble/nimble/host/mesh/src/glue.c870
-rw-r--r--src/libs/mynewt-nimble/nimble/host/mesh/src/health_cli.c556
-rw-r--r--src/libs/mynewt-nimble/nimble/host/mesh/src/health_srv.c453
-rw-r--r--src/libs/mynewt-nimble/nimble/host/mesh/src/light_model.c58
-rw-r--r--src/libs/mynewt-nimble/nimble/host/mesh/src/light_model.h19
-rw-r--r--src/libs/mynewt-nimble/nimble/host/mesh/src/lpn.c1056
-rw-r--r--src/libs/mynewt-nimble/nimble/host/mesh/src/lpn.h68
-rw-r--r--src/libs/mynewt-nimble/nimble/host/mesh/src/mesh.c361
-rw-r--r--src/libs/mynewt-nimble/nimble/host/mesh/src/mesh_priv.h39
-rw-r--r--src/libs/mynewt-nimble/nimble/host/mesh/src/model_cli.c301
-rw-r--r--src/libs/mynewt-nimble/nimble/host/mesh/src/model_srv.c266
-rw-r--r--src/libs/mynewt-nimble/nimble/host/mesh/src/net.c1433
-rw-r--r--src/libs/mynewt-nimble/nimble/host/mesh/src/net.h412
-rw-r--r--src/libs/mynewt-nimble/nimble/host/mesh/src/nodes.c161
-rw-r--r--src/libs/mynewt-nimble/nimble/host/mesh/src/nodes.h10
-rw-r--r--src/libs/mynewt-nimble/nimble/host/mesh/src/prov.c2008
-rw-r--r--src/libs/mynewt-nimble/nimble/host/mesh/src/prov.h37
-rw-r--r--src/libs/mynewt-nimble/nimble/host/mesh/src/proxy.c1499
-rw-r--r--src/libs/mynewt-nimble/nimble/host/mesh/src/proxy.h45
-rw-r--r--src/libs/mynewt-nimble/nimble/host/mesh/src/settings.c2083
-rw-r--r--src/libs/mynewt-nimble/nimble/host/mesh/src/settings.h27
-rw-r--r--src/libs/mynewt-nimble/nimble/host/mesh/src/shell.c2819
-rw-r--r--src/libs/mynewt-nimble/nimble/host/mesh/src/shell.h6
-rw-r--r--src/libs/mynewt-nimble/nimble/host/mesh/src/testing.c200
-rw-r--r--src/libs/mynewt-nimble/nimble/host/mesh/src/testing.h23
-rw-r--r--src/libs/mynewt-nimble/nimble/host/mesh/src/transport.c1668
-rw-r--r--src/libs/mynewt-nimble/nimble/host/mesh/src/transport.h105
-rw-r--r--src/libs/mynewt-nimble/nimble/host/mesh/syscfg.yml661
57 files changed, 30563 insertions, 0 deletions
diff --git a/src/libs/mynewt-nimble/nimble/host/mesh/include/mesh/access.h b/src/libs/mynewt-nimble/nimble/host/mesh/include/mesh/access.h
new file mode 100644
index 00000000..1f99f412
--- /dev/null
+++ b/src/libs/mynewt-nimble/nimble/host/mesh/include/mesh/access.h
@@ -0,0 +1,656 @@
+/** @file
+ * @brief Bluetooth Mesh Access Layer APIs.
+ */
+
+/*
+ * Copyright (c) 2017 Intel Corporation
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#ifndef __BT_MESH_ACCESS_H
+#define __BT_MESH_ACCESS_H
+
+/**
+ * @brief Bluetooth Mesh Access Layer
+ * @defgroup bt_mesh_access Bluetooth Mesh Access Layer
+ * @ingroup bt_mesh
+ * @{
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define BT_MESH_ADDR_UNASSIGNED 0x0000
+#define BT_MESH_ADDR_ALL_NODES 0xffff
+#define BT_MESH_ADDR_PROXIES 0xfffc
+#define BT_MESH_ADDR_FRIENDS 0xfffd
+#define BT_MESH_ADDR_RELAYS 0xfffe
+
+#define BT_MESH_KEY_UNUSED 0xffff
+#define BT_MESH_KEY_DEV 0xfffe
+#define BT_MESH_KEY_DEV_LOCAL BT_MESH_KEY_DEV
+#define BT_MESH_KEY_DEV_REMOTE 0xfffd
+#define BT_MESH_KEY_DEV_ANY 0xfffc
+
+#define BT_MESH_IS_DEV_KEY(key) (key == BT_MESH_KEY_DEV_LOCAL || \
+ key == BT_MESH_KEY_DEV_REMOTE)
+
+/** Helper to define a mesh element within an array.
+ *
+ * In case the element has no SIG or Vendor models the helper
+ * macro BT_MESH_MODEL_NONE can be given instead.
+ *
+ * @param _loc Location Descriptor.
+ * @param _mods Array of models.
+ * @param _vnd_mods Array of vendor models.
+ */
+#define BT_MESH_ELEM(_loc, _mods, _vnd_mods) \
+{ \
+ .loc = (_loc), \
+ .model_count = ARRAY_SIZE(_mods), \
+ .models = (_mods), \
+ .vnd_model_count = ARRAY_SIZE(_vnd_mods), \
+ .vnd_models = (_vnd_mods), \
+}
+
+/** Abstraction that describes a Mesh Element */
+struct bt_mesh_elem {
+ /* Unicast Address. Set at runtime during provisioning. */
+ u16_t addr;
+
+ /* Location Descriptor (GATT Bluetooth Namespace Descriptors) */
+ const u16_t loc;
+
+ const u8_t model_count;
+ const u8_t vnd_model_count;
+
+ struct bt_mesh_model * const models;
+ struct bt_mesh_model * const vnd_models;
+};
+
+/* Foundation Models */
+#define BT_MESH_MODEL_ID_CFG_SRV 0x0000
+#define BT_MESH_MODEL_ID_CFG_CLI 0x0001
+#define BT_MESH_MODEL_ID_HEALTH_SRV 0x0002
+#define BT_MESH_MODEL_ID_HEALTH_CLI 0x0003
+
+/* Models from the Mesh Model Specification */
+#define BT_MESH_MODEL_ID_GEN_ONOFF_SRV 0x1000
+#define BT_MESH_MODEL_ID_GEN_ONOFF_CLI 0x1001
+#define BT_MESH_MODEL_ID_GEN_LEVEL_SRV 0x1002
+#define BT_MESH_MODEL_ID_GEN_LEVEL_CLI 0x1003
+#define BT_MESH_MODEL_ID_GEN_DEF_TRANS_TIME_SRV 0x1004
+#define BT_MESH_MODEL_ID_GEN_DEF_TRANS_TIME_CLI 0x1005
+#define BT_MESH_MODEL_ID_GEN_POWER_ONOFF_SRV 0x1006
+#define BT_MESH_MODEL_ID_GEN_POWER_ONOFF_SETUP_SRV 0x1007
+#define BT_MESH_MODEL_ID_GEN_POWER_ONOFF_CLI 0x1008
+#define BT_MESH_MODEL_ID_GEN_POWER_LEVEL_SRV 0x1009
+#define BT_MESH_MODEL_ID_GEN_POWER_LEVEL_SETUP_SRV 0x100a
+#define BT_MESH_MODEL_ID_GEN_POWER_LEVEL_CLI 0x100b
+#define BT_MESH_MODEL_ID_GEN_BATTERY_SRV 0x100c
+#define BT_MESH_MODEL_ID_GEN_BATTERY_CLI 0x100d
+#define BT_MESH_MODEL_ID_GEN_LOCATION_SRV 0x100e
+#define BT_MESH_MODEL_ID_GEN_LOCATION_SETUPSRV 0x100f
+#define BT_MESH_MODEL_ID_GEN_LOCATION_CLI 0x1010
+#define BT_MESH_MODEL_ID_GEN_ADMIN_PROP_SRV 0x1011
+#define BT_MESH_MODEL_ID_GEN_MANUFACTURER_PROP_SRV 0x1012
+#define BT_MESH_MODEL_ID_GEN_USER_PROP_SRV 0x1013
+#define BT_MESH_MODEL_ID_GEN_CLIENT_PROP_SRV 0x1014
+#define BT_MESH_MODEL_ID_GEN_PROP_CLI 0x1015
+#define BT_MESH_MODEL_ID_SENSOR_SRV 0x1100
+#define BT_MESH_MODEL_ID_SENSOR_SETUP_SRV 0x1101
+#define BT_MESH_MODEL_ID_SENSOR_CLI 0x1102
+#define BT_MESH_MODEL_ID_TIME_SRV 0x1200
+#define BT_MESH_MODEL_ID_TIME_SETUP_SRV 0x1201
+#define BT_MESH_MODEL_ID_TIME_CLI 0x1202
+#define BT_MESH_MODEL_ID_SCENE_SRV 0x1203
+#define BT_MESH_MODEL_ID_SCENE_SETUP_SRV 0x1204
+#define BT_MESH_MODEL_ID_SCENE_CLI 0x1205
+#define BT_MESH_MODEL_ID_SCHEDULER_SRV 0x1206
+#define BT_MESH_MODEL_ID_SCHEDULER_SETUP_SRV 0x1207
+#define BT_MESH_MODEL_ID_SCHEDULER_CLI 0x1208
+#define BT_MESH_MODEL_ID_LIGHT_LIGHTNESS_SRV 0x1300
+#define BT_MESH_MODEL_ID_LIGHT_LIGHTNESS_SETUP_SRV 0x1301
+#define BT_MESH_MODEL_ID_LIGHT_LIGHTNESS_CLI 0x1302
+#define BT_MESH_MODEL_ID_LIGHT_CTL_SRV 0x1303
+#define BT_MESH_MODEL_ID_LIGHT_CTL_SETUP_SRV 0x1304
+#define BT_MESH_MODEL_ID_LIGHT_CTL_CLI 0x1305
+#define BT_MESH_MODEL_ID_LIGHT_CTL_TEMP_SRV 0x1306
+#define BT_MESH_MODEL_ID_LIGHT_HSL_SRV 0x1307
+#define BT_MESH_MODEL_ID_LIGHT_HSL_SETUP_SRV 0x1308
+#define BT_MESH_MODEL_ID_LIGHT_HSL_CLI 0x1309
+#define BT_MESH_MODEL_ID_LIGHT_HSL_HUE_SRV 0x130a
+#define BT_MESH_MODEL_ID_LIGHT_HSL_SAT_SRV 0x130b
+#define BT_MESH_MODEL_ID_LIGHT_XYL_SRV 0x130c
+#define BT_MESH_MODEL_ID_LIGHT_XYL_SETUP_SRV 0x130d
+#define BT_MESH_MODEL_ID_LIGHT_XYL_CLI 0x130e
+#define BT_MESH_MODEL_ID_LIGHT_LC_SRV 0x130f
+#define BT_MESH_MODEL_ID_LIGHT_LC_SETUPSRV 0x1310
+#define BT_MESH_MODEL_ID_LIGHT_LC_CLI 0x1311
+
+/** Message sending context. */
+struct bt_mesh_msg_ctx {
+ /** NetKey Index of the subnet to send the message on. */
+ u16_t net_idx;
+
+ /** AppKey Index to encrypt the message with. */
+ u16_t app_idx;
+
+ /** Remote address. */
+ u16_t addr;
+
+ /** Destination address of a received message. Not used for sending. */
+ u16_t recv_dst;
+
+ /** RSSI of received packet. Not used for sending. */
+ s8_t recv_rssi;
+
+ /** Received TTL value. Not used for sending. */
+ u8_t recv_ttl;
+
+ /** Force sending reliably by using segment acknowledgement */
+ bool send_rel;
+
+ /** TTL, or BT_MESH_TTL_DEFAULT for default TTL. */
+ u8_t send_ttl;
+};
+
+struct bt_mesh_model_op {
+ /* OpCode encoded using the BT_MESH_MODEL_OP_* macros */
+ const u32_t opcode;
+
+ /* Minimum required message length */
+ const size_t min_len;
+
+ /* Message handler for the opcode */
+ void (*const func)(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf);
+};
+
+#define BT_MESH_MODEL_OP_1(b0) (b0)
+#define BT_MESH_MODEL_OP_2(b0, b1) (((b0) << 8) | (b1))
+#define BT_MESH_MODEL_OP_3(b0, cid) ((((b0) << 16) | 0xc00000) | (cid))
+
+#define BT_MESH_MODEL_OP_END { 0, 0, NULL }
+#define BT_MESH_MODEL_NO_OPS ((struct bt_mesh_model_op []) \
+ { BT_MESH_MODEL_OP_END })
+
+/** Helper to define an empty model array */
+#define BT_MESH_MODEL_NONE ((struct bt_mesh_model []){})
+
+/** Length of a short Mesh MIC. */
+#define BT_MESH_MIC_SHORT 4
+/** Length of a long Mesh MIC. */
+#define BT_MESH_MIC_LONG 8
+
+/** @def BT_MESH_MODEL_OP_LEN
+ *
+ * @brief Helper to determine the length of an opcode.
+ *
+ * @param _op Opcode.
+ */
+#define BT_MESH_MODEL_OP_LEN(_op) ((_op) <= 0xff ? 1 : (_op) <= 0xffff ? 2 : 3)
+
+/** @def BT_MESH_MODEL_BUF_LEN
+ *
+ * @brief Helper for model message buffer length.
+ *
+ * Returns the length of a Mesh model message buffer, including the opcode
+ * length and a short MIC.
+ *
+ * @param _op Opcode of the message.
+ * @param _payload_len Length of the model payload.
+ */
+#define BT_MESH_MODEL_BUF_LEN(_op, _payload_len) \
+ (BT_MESH_MODEL_OP_LEN(_op) + (_payload_len) + BT_MESH_MIC_SHORT)
+
+/** @def BT_MESH_MODEL_BUF_LEN_LONG_MIC
+ *
+ * @brief Helper for model message buffer length.
+ *
+ * Returns the length of a Mesh model message buffer, including the opcode
+ * length and a long MIC.
+ *
+ * @param _op Opcode of the message.
+ * @param _payload_len Length of the model payload.
+ */
+#define BT_MESH_MODEL_BUF_LEN_LONG_MIC(_op, _payload_len) \
+ (BT_MESH_MODEL_OP_LEN(_op) + (_payload_len) + BT_MESH_MIC_LONG)
+
+/** @def BT_MESH_MODEL_BUF_DEFINE
+ *
+ * @brief Define a Mesh model message buffer using @ref NET_BUF_SIMPLE.
+ *
+ * @param _op Opcode of the message.
+ * @param _payload_len Length of the model message payload.
+ */
+#define BT_MESH_MODEL_BUF(_op, _payload_len) \
+ NET_BUF_SIMPLE(BT_MESH_MODEL_BUF_LEN(_op, (_payload_len)))
+
+/** @def BT_MESH_MODEL_CB
+ *
+ * @brief Composition data SIG model entry with callback functions.
+ *
+ * @param _id Model ID.
+ * @param _op Array of model opcode handlers.
+ * @param _pub Model publish parameters.
+ * @param _user_data User data for the model.
+ * @param _cb Callback structure, or NULL to keep no callbacks.
+ */
+#define BT_MESH_MODEL_CB(_id, _op, _pub, _user_data, _cb) \
+{ \
+ .id = (_id), \
+ .op = _op, \
+ .keys = { [0 ... (CONFIG_BT_MESH_MODEL_KEY_COUNT - 1)] = \
+ BT_MESH_KEY_UNUSED }, \
+ .pub = _pub, \
+ .groups = { [0 ... (CONFIG_BT_MESH_MODEL_GROUP_COUNT - 1)] = \
+ BT_MESH_ADDR_UNASSIGNED }, \
+ .user_data = _user_data, \
+ .cb = _cb, \
+}
+
+/** @def BT_MESH_MODEL_VND_CB
+ *
+ * @brief Composition data vendor model entry with callback functions.
+ *
+ * @param _company Company ID.
+ * @param _id Model ID.
+ * @param _op Array of model opcode handlers.
+ * @param _pub Model publish parameters.
+ * @param _user_data User data for the model.
+ * @param _cb Callback structure, or NULL to keep no callbacks.
+ */
+#define BT_MESH_MODEL_VND_CB(_company, _id, _op, _pub, _user_data, _cb) \
+{ \
+ .vnd.company = (_company), \
+ .vnd.id = (_id), \
+ .op = _op, \
+ .pub = _pub, \
+ .keys = { [0 ... (CONFIG_BT_MESH_MODEL_KEY_COUNT - 1)] = \
+ BT_MESH_KEY_UNUSED }, \
+ .groups = { [0 ... (CONFIG_BT_MESH_MODEL_GROUP_COUNT - 1)] = \
+ BT_MESH_ADDR_UNASSIGNED }, \
+ .user_data = _user_data, \
+ .cb = _cb, \
+}
+
+
+/** @def BT_MESH_MODEL
+ *
+ * @brief Composition data SIG model entry.
+ *
+ * @param _id Model ID.
+ * @param _op Array of model opcode handlers.
+ * @param _pub Model publish parameters.
+ * @param _user_data User data for the model.
+ */
+#define BT_MESH_MODEL(_id, _op, _pub, _user_data) \
+ BT_MESH_MODEL_CB(_id, _op, _pub, _user_data, NULL)
+
+/** @def BT_MESH_MODEL_VND
+ *
+ * @brief Composition data vendor model entry.
+ *
+ * @param _company Company ID.
+ * @param _id Model ID.
+ * @param _op Array of model opcode handlers.
+ * @param _pub Model publish parameters.
+ * @param _user_data User data for the model.
+ */
+#define BT_MESH_MODEL_VND(_company, _id, _op, _pub, _user_data) \
+ BT_MESH_MODEL_VND_CB(_company, _id, _op, _pub, _user_data, NULL)
+
+/** @def BT_MESH_TRANSMIT
+ *
+ * @brief Encode transmission count & interval steps.
+ *
+ * @param count Number of retransmissions (first transmission is excluded).
+ * @param int_ms Interval steps in milliseconds. Must be greater than 0,
+ * less than or equal to 320, and a multiple of 10.
+ *
+ * @return Mesh transmit value that can be used e.g. for the default
+ * values of the configuration model data.
+ */
+#define BT_MESH_TRANSMIT(count, int_ms) ((count) | (((int_ms / 10) - 1) << 3))
+
+/** @def BT_MESH_TRANSMIT_COUNT
+ *
+ * @brief Decode transmit count from a transmit value.
+ *
+ * @param transmit Encoded transmit count & interval value.
+ *
+ * @return Transmission count (actual transmissions is N + 1).
+ */
+#define BT_MESH_TRANSMIT_COUNT(transmit) (((transmit) & (u8_t)BIT_MASK(3)))
+
+/** @def BT_MESH_TRANSMIT_INT
+ *
+ * @brief Decode transmit interval from a transmit value.
+ *
+ * @param transmit Encoded transmit count & interval value.
+ *
+ * @return Transmission interval in milliseconds.
+ */
+#define BT_MESH_TRANSMIT_INT(transmit) ((((transmit) >> 3) + 1) * 10)
+
+/** @def BT_MESH_PUB_TRANSMIT
+ *
+ * @brief Encode Publish Retransmit count & interval steps.
+ *
+ * @param count Number of retransmissions (first transmission is excluded).
+ * @param int_ms Interval steps in milliseconds. Must be greater than 0
+ * and a multiple of 50.
+ *
+ * @return Mesh transmit value that can be used e.g. for the default
+ * values of the configuration model data.
+ */
+#define BT_MESH_PUB_TRANSMIT(count, int_ms) BT_MESH_TRANSMIT(count, \
+ (int_ms) / 5)
+
+/** @def BT_MESH_PUB_TRANSMIT_COUNT
+ *
+ * @brief Decode Pubhlish Retransmit count from a given value.
+ *
+ * @param transmit Encoded Publish Retransmit count & interval value.
+ *
+ * @return Retransmission count (actual transmissions is N + 1).
+ */
+#define BT_MESH_PUB_TRANSMIT_COUNT(transmit) BT_MESH_TRANSMIT_COUNT(transmit)
+
+/** @def BT_MESH_PUB_TRANSMIT_INT
+ *
+ * @brief Decode Publish Retransmit interval from a given value.
+ *
+ * @param transmit Encoded Publish Retransmit count & interval value.
+ *
+ * @return Transmission interval in milliseconds.
+ */
+#define BT_MESH_PUB_TRANSMIT_INT(transmit) ((((transmit) >> 3) + 1) * 50)
+
+/** Model publication context. */
+struct bt_mesh_model_pub {
+ /** The model the context belongs to. Initialized by the stack. */
+ struct bt_mesh_model *mod;
+
+ u16_t addr; /**< Publish Address. */
+ u16_t key; /**< Publish AppKey Index. */
+
+ u8_t ttl; /**< Publish Time to Live. */
+ u8_t retransmit; /**< Retransmit Count & Interval Steps. */
+ u8_t period; /**< Publish Period. */
+ u8_t period_div:4, /**< Divisor for the Period. */
+ cred:1, /**< Friendship Credentials Flag. */
+ fast_period:1,/**< Use FastPeriodDivisor */
+ count:3; /**< Retransmissions left. */
+
+ u32_t period_start; /**< Start of the current period. */
+
+ /** @brief Publication buffer, containing the publication message.
+ *
+ * The application is expected to initialize this with
+ * a valid net_buf_simple pointer, with the help of e.g.
+ * the NET_BUF_SIMPLE() macro. The publication buffer must
+ * contain a valid publication message before calling the
+ * bt_mesh_model_publish() API or after the publication's
+ * @ref bt_mesh_model_pub.update callback has been called
+ * and returned success. The buffer must be created outside
+ * of function context, i.e. it must not be on the stack.
+ * This is most conveniently acheived by creating it inline
+ * when declaring the publication context:
+ *
+ * static struct bt_mesh_model_pub my_pub = {
+ * .msg = NET_BUF_SIMPLE(size),
+ * };
+ */
+ struct os_mbuf *msg;
+
+ /** @brief Callback for updating the publication buffer.
+ *
+ * When set to NULL, the model is assumed not to support
+ * periodic publishing. When set to non-NULL the callback
+ * will be called periodically and is expected to update
+ * @ref bt_mesh_model_pub.msg with a valid publication
+ * message.
+ *
+ * @param mod The Model the Publication Context belogs to.
+ *
+ * @return Zero on success or (negative) error code otherwise.
+ */
+ int (*update)(struct bt_mesh_model *mod);
+
+ /** Publish Period Timer. Only for stack-internal use. */
+ struct k_delayed_work timer;
+};
+
+/** Model callback functions. */
+struct bt_mesh_model_cb {
+ /** @brief Set value handler of user data tied to the model.
+ *
+ * @sa settings_handler::h_set
+ *
+ * @param model Model to set the persistent data of.
+ * @param val Data from the backend.
+ *
+ * @return 0 on success, error otherwise.
+ */
+ int (*const settings_set)(struct bt_mesh_model *model, char *val);
+
+ /** @brief Callback called when all settings have been loaded.
+ *
+ * This handler gets called after the settings have been loaded in
+ * full.
+ *
+ * @sa settings_handler::h_commit
+ *
+ * @param model Model this callback belongs to.
+ *
+ * @return 0 on success, error otherwise.
+ */
+ int (*const settings_commit)(struct bt_mesh_model *model);
+
+ /** @brief Model init callback.
+ *
+ * Called on every model instance during mesh initialization.
+ *
+ * @param model Model to be initialized.
+ *
+ * @return 0 on success, error otherwise.
+ */
+ int (*const init)(struct bt_mesh_model *model);
+
+ /** @brief Model reset callback.
+ *
+ * Called when the mesh node is reset. All model data is deleted on
+ * reset, and the model should clear its state.
+ *
+ * @param model Model this callback belongs to.
+ */
+ void (*const reset)(struct bt_mesh_model *model);
+};
+
+/** Abstraction that describes a Mesh Model instance */
+struct bt_mesh_model {
+ union {
+ const u16_t id;
+ struct {
+ u16_t company;
+ u16_t id;
+ } vnd;
+ };
+
+ /* Internal information, mainly for persistent storage */
+ u8_t elem_idx; /* Belongs to Nth element */
+ u8_t mod_idx; /* Is the Nth model in the element */
+ u16_t flags; /* Model flags for internal bookkeeping */
+
+ /* Model Publication */
+ struct bt_mesh_model_pub * const pub;
+
+ /* AppKey List */
+ u16_t keys[CONFIG_BT_MESH_MODEL_KEY_COUNT];
+
+ /* Subscription List (group or virtual addresses) */
+ u16_t groups[CONFIG_BT_MESH_MODEL_GROUP_COUNT];
+
+ const struct bt_mesh_model_op * const op;
+
+ /* Model callback structure. */
+ const struct bt_mesh_model_cb * const cb;
+
+#if MYNEWT_VAL(BLE_MESH_MODEL_EXTENSIONS)
+ /* Pointer to the next model in a model extension tree. */
+ struct bt_mesh_model *next;
+ /* Pointer to the first model this model extends. */
+ struct bt_mesh_model *extends;
+#endif
+ /* Model-specific user data */
+ void *user_data;
+};
+
+struct bt_mesh_send_cb {
+ void (*start)(u16_t duration, int err, void *cb_data);
+ void (*end)(int err, void *cb_data);
+};
+
+void bt_mesh_model_msg_init(struct os_mbuf *msg, u32_t opcode);
+
+/** Special TTL value to request using configured default TTL */
+#define BT_MESH_TTL_DEFAULT 0xff
+
+/** Maximum allowed TTL value */
+#define BT_MESH_TTL_MAX 0x7f
+
+/**
+ * @brief Send an Access Layer message.
+ *
+ * @param model Mesh (client) Model that the message belongs to.
+ * @param ctx Message context, includes keys, TTL, etc.
+ * @param msg Access Layer payload (the actual message to be sent).
+ * @param cb Optional "message sent" callback.
+ * @param cb_data User data to be passed to the callback.
+ *
+ * @return 0 on success, or (negative) error code on failure.
+ */
+int bt_mesh_model_send(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *msg,
+ const struct bt_mesh_send_cb *cb,
+ void *cb_data);
+
+/**
+ * @brief Send a model publication message.
+ *
+ * Before calling this function, the user needs to ensure that the model
+ * publication message (@ref bt_mesh_model_pub.msg) contains a valid
+ * message to be sent. Note that this API is only to be used for
+ * non-period publishing. For periodic publishing the app only needs
+ * to make sure that @ref bt_mesh_model_pub.msg contains a valid message
+ * whenever the @ref bt_mesh_model_pub.update callback is called.
+ *
+ * @param model Mesh (client) Model that's publishing the message.
+ *
+ * @return 0 on success, or (negative) error code on failure.
+ */
+int bt_mesh_model_publish(struct bt_mesh_model *model);
+
+/**
+ * @brief Get the element that a model belongs to.
+ *
+ * @param mod Mesh model.
+ *
+ * @return Pointer to the element that the given model belongs to.
+ */
+struct bt_mesh_elem *bt_mesh_model_elem(struct bt_mesh_model *mod);
+
+/** @brief Find a SIG model.
+ *
+ * @param elem Element to search for the model in.
+ * @param id Model ID of the model.
+ *
+ * @return A pointer to the Mesh model matching the given parameters, or NULL
+ * if no SIG model with the given ID exists in the given element.
+ */
+struct bt_mesh_model *bt_mesh_model_find(const struct bt_mesh_elem *elem,
+ u16_t id);
+
+/** @brief Find a vendor model.
+ *
+ * @param elem Element to search for the model in.
+ * @param company Company ID of the model.
+ * @param id Model ID of the model.
+ *
+ * @return A pointer to the Mesh model matching the given parameters, or NULL
+ * if no vendor model with the given ID exists in the given element.
+ */
+struct bt_mesh_model *bt_mesh_model_find_vnd(const struct bt_mesh_elem *elem,
+ u16_t company, u16_t id);
+
+/** @brief Get whether the model is in the primary element of the device.
+ *
+ * @param mod Mesh model.
+ *
+ * @return true if the model is on the primary element, false otherwise.
+ */
+static inline bool bt_mesh_model_in_primary(const struct bt_mesh_model *mod)
+{
+ return (mod->elem_idx == 0);
+}
+
+/** @brief Immediately store the model's user data in persistent storage.
+ *
+ * @param mod Mesh model.
+ * @param vnd This is a vendor model.
+ * @param data Model data to store, or NULL to delete any model data.
+ * @param data_len Length of the model data.
+ *
+ * @return 0 on success, or (negative) error code on failure.
+ */
+int bt_mesh_model_data_store(struct bt_mesh_model *mod, bool vnd,
+ const void *data, size_t data_len);
+
+/** @brief Let a model extend another.
+ *
+ * Mesh models may be extended to reuse their functionality, forming a more
+ * complex model. A Mesh model may extend any number of models, in any element.
+ * The extensions may also be nested, ie a model that extends another may itself
+ * be extended. Extensions may not be cyclical, and a model can only be extended
+ * by one other model.
+ *
+ * A set of models that extend each other form a model extension tree.
+ *
+ * All models in an extension tree share one subscription list per element. The
+ * access layer will utilize the combined subscription list of all models in an
+ * extension tree and element, giving the models extended subscription list
+ * capacity.
+ *
+ * @param[in] mod Mesh model.
+ * @param[in] base_mod The model being extended.
+ *
+ * @retval 0 Successfully extended the base_mod model.
+ * @retval -EALREADY The base_mod model is already extended.
+ */
+int bt_mesh_model_extend(struct bt_mesh_model *mod,
+ struct bt_mesh_model *base_mod);
+
+/** Node Composition */
+struct bt_mesh_comp {
+ u16_t cid;
+ u16_t pid;
+ u16_t vid;
+
+ size_t elem_count;
+ struct bt_mesh_elem *elem;
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+/**
+ * @}
+ */
+
+#endif /* __BT_MESH_ACCESS_H */
diff --git a/src/libs/mynewt-nimble/nimble/host/mesh/include/mesh/cfg_cli.h b/src/libs/mynewt-nimble/nimble/host/mesh/include/mesh/cfg_cli.h
new file mode 100644
index 00000000..7dc237be
--- /dev/null
+++ b/src/libs/mynewt-nimble/nimble/host/mesh/include/mesh/cfg_cli.h
@@ -0,0 +1,234 @@
+/** @file
+ * @brief Bluetooth Mesh Configuration Client Model APIs.
+ */
+
+/*
+ * Copyright (c) 2017 Intel Corporation
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#ifndef __BT_MESH_CFG_CLI_H
+#define __BT_MESH_CFG_CLI_H
+
+/**
+ * @brief Bluetooth Mesh
+ * @defgroup bt_mesh_cfg_cli Bluetooth Mesh Configuration Client Model
+ * @ingroup bt_mesh
+ * @{
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** Mesh Configuration Client Model Context */
+struct bt_mesh_cfg_cli {
+ struct bt_mesh_model *model;
+
+ struct k_sem op_sync;
+ u32_t op_pending;
+ void *op_param;
+};
+
+extern const struct bt_mesh_model_op bt_mesh_cfg_cli_op[];
+extern const struct bt_mesh_model_cb bt_mesh_cfg_cli_cb;
+
+#define BT_MESH_MODEL_CFG_CLI(cli_data) \
+ BT_MESH_MODEL_CB(BT_MESH_MODEL_ID_CFG_CLI, bt_mesh_cfg_cli_op, NULL, \
+ cli_data, &bt_mesh_cfg_cli_cb)
+
+int bt_mesh_cfg_comp_data_get(u16_t net_idx, u16_t addr, u8_t page,
+ u8_t *status, struct os_mbuf *comp);
+
+int bt_mesh_cfg_beacon_get(u16_t net_idx, u16_t addr, u8_t *status);
+
+int bt_mesh_cfg_beacon_set(u16_t net_idx, u16_t addr, u8_t val, u8_t *status);
+
+int bt_mesh_cfg_ttl_get(u16_t net_idx, u16_t addr, u8_t *ttl);
+
+int bt_mesh_cfg_ttl_set(u16_t net_idx, u16_t addr, u8_t val, u8_t *ttl);
+
+int bt_mesh_cfg_friend_get(u16_t net_idx, u16_t addr, u8_t *status);
+
+int bt_mesh_cfg_friend_set(u16_t net_idx, u16_t addr, u8_t val, u8_t *status);
+
+int bt_mesh_cfg_gatt_proxy_get(u16_t net_idx, u16_t addr, u8_t *status);
+
+int bt_mesh_cfg_gatt_proxy_set(u16_t net_idx, u16_t addr, u8_t val,
+ u8_t *status);
+
+int bt_mesh_cfg_relay_get(u16_t net_idx, u16_t addr, u8_t *status,
+ u8_t *transmit);
+
+int bt_mesh_cfg_relay_set(u16_t net_idx, u16_t addr, u8_t new_relay,
+ u8_t new_transmit, u8_t *status, u8_t *transmit);
+
+int bt_mesh_cfg_net_key_add(u16_t net_idx, u16_t addr, u16_t key_net_idx,
+ const u8_t net_key[16], u8_t *status);
+
+int bt_mesh_cfg_app_key_add(u16_t net_idx, u16_t addr, u16_t key_net_idx,
+ u16_t key_app_idx, const u8_t app_key[16],
+ u8_t *status);
+
+int bt_mesh_cfg_mod_app_bind(u16_t net_idx, u16_t addr, u16_t elem_addr,
+ u16_t mod_app_idx, u16_t mod_id, u8_t *status);
+
+int bt_mesh_cfg_mod_app_bind_vnd(u16_t net_idx, u16_t addr, u16_t elem_addr,
+ u16_t mod_app_idx, u16_t mod_id, u16_t cid,
+ u8_t *status);
+
+/** @def BT_MESH_PUB_PERIOD_100MS
+ *
+ * @brief Helper macro to encode model publication period in units of 100ms
+ *
+ * @param steps Number of 100ms steps.
+ *
+ * @return Encoded value that can be assigned to bt_mesh_cfg_mod_pub.period
+ */
+#define BT_MESH_PUB_PERIOD_100MS(steps) ((steps) & BIT_MASK(6))
+
+/** @def BT_MESH_PUB_PERIOD_SEC
+ *
+ * @brief Helper macro to encode model publication period in units of 1 second
+ *
+ * @param steps Number of 1 second steps.
+ *
+ * @return Encoded value that can be assigned to bt_mesh_cfg_mod_pub.period
+ */
+#define BT_MESH_PUB_PERIOD_SEC(steps) (((steps) & BIT_MASK(6)) | (1 << 6))
+
+/** @def BT_MESH_PUB_PERIOD_10SEC
+ *
+ * @brief Helper macro to encode model publication period in units of 10
+ * seconds
+ *
+ * @param steps Number of 10 second steps.
+ *
+ * @return Encoded value that can be assigned to bt_mesh_cfg_mod_pub.period
+ */
+#define BT_MESH_PUB_PERIOD_10SEC(steps) (((steps) & BIT_MASK(6)) | (2 << 6))
+
+/** @def BT_MESH_PUB_PERIOD_10MIN
+ *
+ * @brief Helper macro to encode model publication period in units of 10
+ * minutes
+ *
+ * @param steps Number of 10 minute steps.
+ *
+ * @return Encoded value that can be assigned to bt_mesh_cfg_mod_pub.period
+ */
+#define BT_MESH_PUB_PERIOD_10MIN(steps) (((steps) & BIT_MASK(6)) | (3 << 6))
+
+struct bt_mesh_cfg_mod_pub {
+ u16_t addr;
+ u16_t app_idx;
+ bool cred_flag;
+ u8_t ttl;
+ u8_t period;
+ u8_t transmit;
+};
+
+int bt_mesh_cfg_mod_pub_get(u16_t net_idx, u16_t addr, u16_t elem_addr,
+ u16_t mod_id, struct bt_mesh_cfg_mod_pub *pub,
+ u8_t *status);
+
+int bt_mesh_cfg_mod_pub_get_vnd(u16_t net_idx, u16_t addr, u16_t elem_addr,
+ u16_t mod_id, u16_t cid,
+ struct bt_mesh_cfg_mod_pub *pub, u8_t *status);
+
+int bt_mesh_cfg_mod_pub_set(u16_t net_idx, u16_t addr, u16_t elem_addr,
+ u16_t mod_id, struct bt_mesh_cfg_mod_pub *pub,
+ u8_t *status);
+
+int bt_mesh_cfg_mod_pub_set_vnd(u16_t net_idx, u16_t addr, u16_t elem_addr,
+ u16_t mod_id, u16_t cid,
+ struct bt_mesh_cfg_mod_pub *pub, u8_t *status);
+
+int bt_mesh_cfg_mod_sub_add(u16_t net_idx, u16_t addr, u16_t elem_addr,
+ u16_t sub_addr, u16_t mod_id, u8_t *status);
+
+int bt_mesh_cfg_mod_sub_add_vnd(u16_t net_idx, u16_t addr, u16_t elem_addr,
+ u16_t sub_addr, u16_t mod_id, u16_t cid,
+ u8_t *status);
+
+int bt_mesh_cfg_mod_sub_del(u16_t net_idx, u16_t addr, u16_t elem_addr,
+ u16_t sub_addr, u16_t mod_id, u8_t *status);
+
+int bt_mesh_cfg_mod_sub_del_vnd(u16_t net_idx, u16_t addr, u16_t elem_addr,
+ u16_t sub_addr, u16_t mod_id, u16_t cid,
+ u8_t *status);
+
+int bt_mesh_cfg_mod_sub_overwrite(u16_t net_idx, u16_t addr, u16_t elem_addr,
+ u16_t sub_addr, u16_t mod_id, u8_t *status);
+
+int bt_mesh_cfg_mod_sub_overwrite_vnd(u16_t net_idx, u16_t addr,
+ u16_t elem_addr, u16_t sub_addr,
+ u16_t mod_id, u16_t cid, u8_t *status);
+
+int bt_mesh_cfg_mod_sub_va_add(u16_t net_idx, u16_t addr, u16_t elem_addr,
+ const u8_t label[16], u16_t mod_id,
+ u16_t *virt_addr, u8_t *status);
+
+int bt_mesh_cfg_mod_sub_va_add_vnd(u16_t net_idx, u16_t addr, u16_t elem_addr,
+ const u8_t label[16], u16_t mod_id,
+ u16_t cid, u16_t *virt_addr, u8_t *status);
+
+int bt_mesh_cfg_mod_sub_va_del(u16_t net_idx, u16_t addr, u16_t elem_addr,
+ const u8_t label[16], u16_t mod_id,
+ u16_t *virt_addr, u8_t *status);
+
+int bt_mesh_cfg_mod_sub_va_del_vnd(u16_t net_idx, u16_t addr, u16_t elem_addr,
+ const u8_t label[16], u16_t mod_id,
+ u16_t cid, u16_t *virt_addr, u8_t *status);
+
+int bt_mesh_cfg_mod_sub_va_overwrite(u16_t net_idx, u16_t addr,
+ u16_t elem_addr, const u8_t label[16],
+ u16_t mod_id, u16_t *virt_addr,
+ u8_t *status);
+
+int bt_mesh_cfg_mod_sub_va_overwrite_vnd(u16_t net_idx, u16_t addr,
+ u16_t elem_addr, const u8_t label[16],
+ u16_t mod_id, u16_t cid,
+ u16_t *virt_addr, u8_t *status);
+
+struct bt_mesh_cfg_hb_sub {
+ u16_t src;
+ u16_t dst;
+ u8_t period;
+ u8_t count;
+ u8_t min;
+ u8_t max;
+};
+
+int bt_mesh_cfg_hb_sub_set(u16_t net_idx, u16_t addr,
+ struct bt_mesh_cfg_hb_sub *sub, u8_t *status);
+
+int bt_mesh_cfg_hb_sub_get(u16_t net_idx, u16_t addr,
+ struct bt_mesh_cfg_hb_sub *sub, u8_t *status);
+
+struct bt_mesh_cfg_hb_pub {
+ u16_t dst;
+ u8_t count;
+ u8_t period;
+ u8_t ttl;
+ u16_t feat;
+ u16_t net_idx;
+};
+
+int bt_mesh_cfg_hb_pub_set(u16_t net_idx, u16_t addr,
+ const struct bt_mesh_cfg_hb_pub *pub, u8_t *status);
+
+int bt_mesh_cfg_hb_pub_get(u16_t net_idx, u16_t addr,
+ struct bt_mesh_cfg_hb_pub *pub, u8_t *status);
+
+s32_t bt_mesh_cfg_cli_timeout_get(void);
+void bt_mesh_cfg_cli_timeout_set(s32_t timeout);
+
+#ifdef __cplusplus
+}
+#endif
+/**
+ * @}
+ */
+
+#endif /* __BT_MESH_CFG_CLI_H */
diff --git a/src/libs/mynewt-nimble/nimble/host/mesh/include/mesh/cfg_srv.h b/src/libs/mynewt-nimble/nimble/host/mesh/include/mesh/cfg_srv.h
new file mode 100644
index 00000000..14d8a295
--- /dev/null
+++ b/src/libs/mynewt-nimble/nimble/host/mesh/include/mesh/cfg_srv.h
@@ -0,0 +1,78 @@
+/** @file
+ * @brief Bluetooth Mesh Configuration Server Model APIs.
+ */
+
+/*
+ * Copyright (c) 2017 Intel Corporation
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#ifndef __BT_MESH_CFG_SRV_H
+#define __BT_MESH_CFG_SRV_H
+
+/**
+ * @brief Bluetooth Mesh
+ * @defgroup bt_mesh_cfg_srv Bluetooth Mesh Configuration Server Model
+ * @ingroup bt_mesh
+ * @{
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** Mesh Configuration Server Model Context */
+struct bt_mesh_cfg_srv {
+ struct bt_mesh_model *model;
+
+ u8_t net_transmit; /* Network Transmit state */
+ u8_t relay; /* Relay Mode state */
+ u8_t relay_retransmit; /* Relay Retransmit state */
+ u8_t beacon; /* Secure Network Beacon state */
+ u8_t gatt_proxy; /* GATT Proxy state */
+ u8_t frnd; /* Friend state */
+ u8_t default_ttl; /* Default TTL */
+
+ /* Heartbeat Publication */
+ struct bt_mesh_hb_pub {
+ struct k_delayed_work timer;
+
+ u16_t dst;
+ u16_t count;
+ u8_t period;
+ u8_t ttl;
+ u16_t feat;
+ u16_t net_idx;
+ } hb_pub;
+
+ /* Heartbeat Subscription */
+ struct bt_mesh_hb_sub {
+ s64_t expiry;
+
+ u16_t src;
+ u16_t dst;
+ u16_t count;
+ u8_t min_hops;
+ u8_t max_hops;
+
+ /* Optional subscription tracking function */
+ void (*func)(u8_t hops, u16_t feat);
+ } hb_sub;
+};
+
+extern const struct bt_mesh_model_op bt_mesh_cfg_srv_op[];
+extern const struct bt_mesh_model_cb bt_mesh_cfg_srv_cb;
+
+#define BT_MESH_MODEL_CFG_SRV(srv_data) \
+ BT_MESH_MODEL_CB(BT_MESH_MODEL_ID_CFG_SRV, bt_mesh_cfg_srv_op, NULL, \
+ srv_data, &bt_mesh_cfg_srv_cb)
+
+#ifdef __cplusplus
+}
+#endif
+
+/**
+ * @}
+ */
+
+#endif /* __BT_MESH_CFG_SRV_H */
diff --git a/src/libs/mynewt-nimble/nimble/host/mesh/include/mesh/glue.h b/src/libs/mynewt-nimble/nimble/host/mesh/include/mesh/glue.h
new file mode 100644
index 00000000..e37fcfbc
--- /dev/null
+++ b/src/libs/mynewt-nimble/nimble/host/mesh/include/mesh/glue.h
@@ -0,0 +1,502 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef _MESH_GLUE_
+#define _MESH_GLUE_
+
+#include <assert.h>
+#include <errno.h>
+
+#include "syscfg/syscfg.h"
+#include "logcfg/logcfg.h"
+#include "modlog/modlog.h"
+#include "nimble/nimble_npl.h"
+
+#include "os/os_mbuf.h"
+#include "os/queue.h"
+
+#include "nimble/ble.h"
+#include "host/ble_hs.h"
+#include "host/ble_uuid.h"
+#include "../src/ble_sm_priv.h"
+#include "../src/ble_hs_hci_priv.h"
+
+#include "tinycrypt/aes.h"
+#include "tinycrypt/constants.h"
+#include "tinycrypt/utils.h"
+#include "tinycrypt/cmac_mode.h"
+#include "tinycrypt/ecc_dh.h"
+
+#if MYNEWT_VAL(BLE_MESH_SETTINGS)
+#include "config/config.h"
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define u8_t uint8_t
+#define s8_t int8_t
+#define u16_t uint16_t
+#define s16_t int16_t
+#define u32_t uint32_t
+#define u64_t uint64_t
+#define s64_t int64_t
+#define s32_t int32_t
+
+/** @brief Helper to declare elements of bt_data arrays
+ *
+ * This macro is mainly for creating an array of struct bt_data
+ * elements which is then passed to bt_le_adv_start().
+ *
+ * @param _type Type of advertising data field
+ * @param _data Pointer to the data field payload
+ * @param _data_len Number of bytes behind the _data pointer
+ */
+#define BT_DATA(_type, _data, _data_len) \
+ { \
+ .type = (_type), \
+ .data_len = (_data_len), \
+ .data = (const u8_t *)(_data), \
+ }
+
+/** @brief Helper to declare elements of bt_data arrays
+ *
+ * This macro is mainly for creating an array of struct bt_data
+ * elements which is then passed to bt_le_adv_start().
+ *
+ * @param _type Type of advertising data field
+ * @param _bytes Variable number of single-byte parameters
+ */
+#define BT_DATA_BYTES(_type, _bytes...) \
+ BT_DATA(_type, ((u8_t []) { _bytes }), \
+ sizeof((u8_t []) { _bytes }))
+
+/* EIR/AD data type definitions */
+#define BT_DATA_FLAGS 0x01 /* AD flags */
+#define BT_DATA_UUID16_SOME 0x02 /* 16-bit UUID, more available */
+#define BT_DATA_UUID16_ALL 0x03 /* 16-bit UUID, all listed */
+#define BT_DATA_UUID32_SOME 0x04 /* 32-bit UUID, more available */
+#define BT_DATA_UUID32_ALL 0x05 /* 32-bit UUID, all listed */
+#define BT_DATA_UUID128_SOME 0x06 /* 128-bit UUID, more available */
+#define BT_DATA_UUID128_ALL 0x07 /* 128-bit UUID, all listed */
+#define BT_DATA_NAME_SHORTENED 0x08 /* Shortened name */
+#define BT_DATA_NAME_COMPLETE 0x09 /* Complete name */
+#define BT_DATA_TX_POWER 0x0a /* Tx Power */
+#define BT_DATA_SOLICIT16 0x14 /* Solicit UUIDs, 16-bit */
+#define BT_DATA_SOLICIT128 0x15 /* Solicit UUIDs, 128-bit */
+#define BT_DATA_SVC_DATA16 0x16 /* Service data, 16-bit UUID */
+#define BT_DATA_GAP_APPEARANCE 0x19 /* GAP appearance */
+#define BT_DATA_SOLICIT32 0x1f /* Solicit UUIDs, 32-bit */
+#define BT_DATA_SVC_DATA32 0x20 /* Service data, 32-bit UUID */
+#define BT_DATA_SVC_DATA128 0x21 /* Service data, 128-bit UUID */
+#define BT_DATA_URI 0x24 /* URI */
+#define BT_DATA_MESH_PROV 0x29 /* Mesh Provisioning PDU */
+#define BT_DATA_MESH_MESSAGE 0x2a /* Mesh Networking PDU */
+#define BT_DATA_MESH_BEACON 0x2b /* Mesh Beacon */
+
+#define BT_DATA_MANUFACTURER_DATA 0xff /* Manufacturer Specific Data */
+
+#define BT_LE_AD_LIMITED 0x01 /* Limited Discoverable */
+#define BT_LE_AD_GENERAL 0x02 /* General Discoverable */
+#define BT_LE_AD_NO_BREDR 0x04 /* BR/EDR not supported */
+
+#define sys_put_be16(a,b) put_be16(b, a)
+#define sys_put_le16(a,b) put_le16(b, a)
+#define sys_put_be32(a,b) put_be32(b, a)
+#define sys_get_be16(a) get_be16(a)
+#define sys_get_le16(a) get_le16(a)
+#define sys_get_be32(a) get_be32(a)
+#define sys_cpu_to_be16(a) htobe16(a)
+#define sys_cpu_to_be32(a) htobe32(a)
+#define sys_be32_to_cpu(a) be32toh(a)
+#define sys_be16_to_cpu(a) be16toh(a)
+#define sys_le16_to_cpu(a) le16toh(a)
+
+#ifndef ARRAY_SIZE
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
+#endif
+
+#define CODE_UNREACHABLE __builtin_unreachable()
+#define __ASSERT(code, str) \
+ do { \
+ if (!(code)) BT_ERR(str); \
+ assert(code); \
+ } while (0);
+
+#define __ASSERT_NO_MSG(test) __ASSERT(test, "")
+
+/* Mesh is designed to not use mbuf chains */
+#if BT_DBG_ENABLED
+#define ASSERT_NOT_CHAIN(om) assert(SLIST_NEXT(om, om_next) == NULL)
+#else
+#define ASSERT_NOT_CHAIN(om) (void)(om)
+#endif
+
+#define __packed __attribute__((__packed__))
+
+#define MSEC_PER_SEC (1000)
+#define K_MSEC(ms) (ms)
+#define K_SECONDS(s) K_MSEC((s) * MSEC_PER_SEC)
+#define K_MINUTES(m) K_SECONDS((m) * 60)
+#define K_HOURS(h) K_MINUTES((h) * 60)
+
+#ifndef BIT
+#define BIT(n) (1UL << (n))
+#endif
+
+#define BIT_MASK(n) (BIT(n) - 1)
+
+#define BT_GAP_ADV_FAST_INT_MIN_1 0x0030 /* 30 ms */
+#define BT_GAP_ADV_FAST_INT_MAX_1 0x0060 /* 60 ms */
+#define BT_GAP_ADV_FAST_INT_MIN_2 0x00a0 /* 100 ms */
+#define BT_GAP_ADV_FAST_INT_MAX_2 0x00f0 /* 150 ms */
+#define BT_GAP_ADV_SLOW_INT_MIN 0x0640 /* 1 s */
+#define BT_GAP_ADV_SLOW_INT_MAX 0x0780 /* 1.2 s */
+
+#ifndef MESH_LOG_MODULE
+#define MESH_LOG_MODULE BLE_MESH_LOG
+#endif
+
+#define CAT(a, ...) PRIMITIVE_CAT(a, __VA_ARGS__)
+#define PRIMITIVE_CAT(a, ...) a ## __VA_ARGS__
+
+#define BLE_MESH_LOG(lvl, ...) CAT(MESH_LOG_MODULE, CAT(_, lvl))(__VA_ARGS__)
+
+#define BT_DBG(fmt, ...) BLE_MESH_LOG(DEBUG, "%s: " fmt "\n", __func__, ## __VA_ARGS__);
+#define BT_INFO(fmt, ...) BLE_MESH_LOG(INFO, "%s: " fmt "\n", __func__, ## __VA_ARGS__);
+#define BT_WARN(fmt, ...) BLE_MESH_LOG(WARN, "%s: " fmt "\n", __func__, ## __VA_ARGS__);
+#define BT_ERR(fmt, ...) BLE_MESH_LOG(ERROR, "%s: " fmt "\n", __func__, ## __VA_ARGS__);
+#define BT_GATT_ERR(_att_err) (-(_att_err))
+
+typedef ble_addr_t bt_addr_le_t;
+
+#define k_fifo_init(queue) ble_npl_eventq_init(queue)
+#define net_buf_simple_tailroom(buf) OS_MBUF_TRAILINGSPACE(buf)
+#define net_buf_tailroom(buf) net_buf_simple_tailroom(buf)
+#define net_buf_headroom(buf) ((buf)->om_data - &(buf)->om_databuf[buf->om_pkthdr_len])
+#define net_buf_simple_headroom(buf) net_buf_headroom(buf)
+#define net_buf_simple_tail(buf) ((buf)->om_data + (buf)->om_len)
+
+struct net_buf_simple_state {
+ /** Offset of the data pointer from the beginning of the storage */
+ u16_t offset;
+ /** Length of data */
+ u16_t len;
+};
+
+static inline struct os_mbuf * NET_BUF_SIMPLE(uint16_t size)
+{
+ struct os_mbuf *buf;
+
+ buf = os_msys_get(size, 0);
+ assert(buf);
+
+ return buf;
+}
+
+#define K_NO_WAIT (0)
+#define K_FOREVER (-1)
+
+#if MYNEWT_VAL(BLE_EXT_ADV)
+#define BT_MESH_ADV_INST (MYNEWT_VAL(BLE_MULTI_ADV_INSTANCES))
+
+#if MYNEWT_VAL(BLE_MESH_PROXY)
+/* Note that BLE_MULTI_ADV_INSTANCES contains number of additional instances.
+ * Instance 0 is always there
+ */
+#if MYNEWT_VAL(BLE_MULTI_ADV_INSTANCES) < 1
+#error "Mesh needs at least BLE_MULTI_ADV_INSTANCES set to 1"
+#endif
+#define BT_MESH_ADV_GATT_INST (MYNEWT_VAL(BLE_MULTI_ADV_INSTANCES) - 1)
+#endif /* BLE_MESH_PROXY */
+#endif /* BLE_EXT_ADV */
+
+/* This is by purpose */
+static inline void net_buf_simple_init(struct os_mbuf *buf,
+ size_t reserve_head)
+{
+ /* This is called in Zephyr after init.
+ * Note in Mynewt case we don't care abour reserved head*/
+ buf->om_data = &buf->om_databuf[buf->om_pkthdr_len] + reserve_head;
+ buf->om_len = 0;
+}
+
+void net_buf_put(struct ble_npl_eventq *fifo, struct os_mbuf *buf);
+void * net_buf_ref(struct os_mbuf *om);
+void net_buf_unref(struct os_mbuf *om);
+uint16_t net_buf_simple_pull_le16(struct os_mbuf *om);
+uint16_t net_buf_simple_pull_be16(struct os_mbuf *om);
+uint32_t net_buf_simple_pull_be32(struct os_mbuf *om);
+uint32_t net_buf_simple_pull_le32(struct os_mbuf *om);
+uint8_t net_buf_simple_pull_u8(struct os_mbuf *om);
+void net_buf_simple_add_le16(struct os_mbuf *om, uint16_t val);
+void net_buf_simple_add_be16(struct os_mbuf *om, uint16_t val);
+void net_buf_simple_add_u8(struct os_mbuf *om, uint8_t val);
+void net_buf_simple_add_be32(struct os_mbuf *om, uint32_t val);
+void net_buf_simple_add_le32(struct os_mbuf *om, uint32_t val);
+void net_buf_add_zeros(struct os_mbuf *om, uint8_t len);
+void net_buf_simple_push_le16(struct os_mbuf *om, uint16_t val);
+void net_buf_simple_push_be16(struct os_mbuf *om, uint16_t val);
+void net_buf_simple_push_u8(struct os_mbuf *om, uint8_t val);
+void *net_buf_simple_pull(struct os_mbuf *om, uint8_t len);
+void *net_buf_simple_pull_mem(struct os_mbuf *om, uint8_t len);
+void *net_buf_simple_add(struct os_mbuf *om, uint8_t len);
+bool k_fifo_is_empty(struct ble_npl_eventq *q);
+void *net_buf_get(struct ble_npl_eventq *fifo,s32_t t);
+uint8_t *net_buf_simple_push(struct os_mbuf *om, uint8_t len);
+void net_buf_reserve(struct os_mbuf *om, size_t reserve);
+
+#define net_buf_add_mem(a,b,c) os_mbuf_append(a,b,c)
+#define net_buf_simple_add_mem(a,b,c) os_mbuf_append(a,b,c)
+#define net_buf_add_u8(a,b) net_buf_simple_add_u8(a,b)
+#define net_buf_add(a,b) net_buf_simple_add(a,b)
+
+#define net_buf_clone(a, b) os_mbuf_dup(a)
+#define net_buf_add_be32(a, b) net_buf_simple_add_be32(a, b)
+#define net_buf_add_be16(a, b) net_buf_simple_add_be16(a, b)
+#define net_buf_pull(a, b) net_buf_simple_pull(a, b)
+#define net_buf_pull_mem(a, b) net_buf_simple_pull_mem(a, b)
+#define net_buf_pull_u8(a) net_buf_simple_pull_u8(a)
+#define net_buf_pull_be16(a) net_buf_simple_pull_be16(a)
+#define net_buf_skip(a, b) net_buf_simple_pull_mem(a, b)
+
+#define BT_GATT_CCC_NOTIFY BLE_GATT_CHR_PROP_NOTIFY
+
+/** Description of different data types that can be encoded into
+ * advertising data. Used to form arrays that are passed to the
+ * bt_le_adv_start() function.
+ */
+struct bt_data {
+ u8_t type;
+ u8_t data_len;
+ const u8_t *data;
+};
+
+struct bt_pub_key_cb {
+ /** @brief Callback type for Public Key generation.
+ *
+ * Used to notify of the local public key or that the local key is not
+ * available (either because of a failure to read it or because it is
+ * being regenerated).
+ *
+ * @param key The local public key, or NULL in case of no key.
+ */
+ void (*func)(const u8_t key[64]);
+
+ struct bt_pub_key_cb *_next;
+};
+
+typedef void (*bt_dh_key_cb_t)(const u8_t key[32]);
+int bt_dh_key_gen(const u8_t remote_pk[64], bt_dh_key_cb_t cb);
+int bt_pub_key_gen(struct bt_pub_key_cb *new_cb);
+uint8_t *bt_pub_key_get(void);
+int bt_rand(void *buf, size_t len);
+const char * bt_hex(const void *buf, size_t len);
+int bt_encrypt_be(const uint8_t *key, const uint8_t *plaintext, uint8_t *enc_data);
+void bt_mesh_register_gatt(void);
+int bt_le_adv_start(const struct ble_gap_adv_params *param,
+ const struct bt_data *ad, size_t ad_len,
+ const struct bt_data *sd, size_t sd_len);
+int bt_le_adv_stop(bool proxy);
+
+struct k_delayed_work {
+ struct ble_npl_callout work;
+};
+
+void k_work_init(struct ble_npl_callout *work, ble_npl_event_fn handler);
+void k_delayed_work_init(struct k_delayed_work *w, ble_npl_event_fn *f);
+void k_delayed_work_cancel(struct k_delayed_work *w);
+void k_delayed_work_submit(struct k_delayed_work *w, uint32_t ms);
+int64_t k_uptime_get(void);
+u32_t k_uptime_get_32(void);
+void k_sleep(int32_t duration);
+void k_work_submit(struct ble_npl_callout *w);
+void k_work_add_arg(struct ble_npl_callout *w, void *arg);
+void k_delayed_work_add_arg(struct k_delayed_work *w, void *arg);
+uint32_t k_delayed_work_remaining_get(struct k_delayed_work *w);
+
+static inline void net_buf_simple_save(struct os_mbuf *buf,
+ struct net_buf_simple_state *state)
+{
+ state->offset = net_buf_simple_headroom(buf);
+ state->len = buf->om_len;
+}
+
+static inline void net_buf_simple_restore(struct os_mbuf *buf,
+ struct net_buf_simple_state *state)
+{
+ buf->om_data = &buf->om_databuf[buf->om_pkthdr_len] + state->offset;
+ buf->om_len = state->len;
+}
+
+static inline void sys_memcpy_swap(void *dst, const void *src, size_t length)
+{
+ __ASSERT(((src < dst && (src + length) <= dst) ||
+ (src > dst && (dst + length) <= src)),
+ "Source and destination buffers must not overlap");
+
+ src += length - 1;
+
+ for (; length > 0; length--) {
+ *((u8_t *)dst++) = *((u8_t *)src--);
+ }
+}
+
+#define popcount(x) __builtin_popcount(x)
+
+static inline unsigned int find_lsb_set(u32_t op)
+{
+ return __builtin_ffs(op);
+}
+
+static inline unsigned int find_msb_set(u32_t op)
+{
+ if (!op)
+ return 0;
+
+ return 32 - __builtin_clz(op);
+}
+
+#define CONFIG_BT_MESH_FRIEND BLE_MESH_FRIEND
+#define CONFIG_BT_MESH_GATT_PROXY BLE_MESH_GATT_PROXY
+#define CONFIG_BT_MESH_IV_UPDATE_TEST BLE_MESH_IV_UPDATE_TEST
+#define CONFIG_BT_MESH_LOW_POWER BLE_MESH_LOW_POWER
+#define CONFIG_BT_MESH_LPN_AUTO BLE_MESH_LPN_AUTO
+#define CONFIG_BT_MESH_LPN_ESTABLISHMENT BLE_MESH_LPN_ESTABLISHMENT
+#define CONFIG_BT_MESH_PB_ADV BLE_MESH_PB_ADV
+#define CONFIG_BT_MESH_PB_GATT BLE_MESH_PB_GATT
+#define CONFIG_BT_MESH_PROV BLE_MESH_PROV
+#define CONFIG_BT_MESH_PROXY BLE_MESH_PROXY
+#define CONFIG_BT_TESTING BLE_MESH_TESTING
+#define CONFIG_BT_SETTINGS BLE_MESH_SETTINGS
+#define CONFIG_SETTINGS BLE_MESH_SETTINGS
+#define CONFIG_BT_MESH_PROVISIONER BLE_MESH_PROVISIONER
+
+/* Above flags are used with IS_ENABLED macro */
+#define IS_ENABLED(config) MYNEWT_VAL(config)
+
+#define CONFIG_BT_MESH_LPN_GROUPS MYNEWT_VAL(BLE_MESH_LPN_GROUPS)
+#define CONFIG_BT_MESH_ADV_BUF_COUNT MYNEWT_VAL(BLE_MESH_ADV_BUF_COUNT)
+#define CONFIG_BT_MESH_FRIEND_QUEUE_SIZE MYNEWT_VAL(BLE_MESH_FRIEND_QUEUE_SIZE)
+#define CONFIG_BT_MESH_FRIEND_RECV_WIN MYNEWT_VAL(BLE_MESH_FRIEND_RECV_WIN)
+#define CONFIG_BT_MESH_LPN_POLL_TIMEOUT MYNEWT_VAL(BLE_MESH_LPN_POLL_TIMEOUT)
+#define CONFIG_BT_MESH_MODEL_GROUP_COUNT MYNEWT_VAL(BLE_MESH_MODEL_GROUP_COUNT)
+#define CONFIG_BT_MESH_MODEL_KEY_COUNT MYNEWT_VAL(BLE_MESH_MODEL_KEY_COUNT)
+#define CONFIG_BT_MESH_NODE_ID_TIMEOUT MYNEWT_VAL(BLE_MESH_NODE_ID_TIMEOUT)
+#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_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 printk console_printf
+
+#define CONTAINER_OF(ptr, type, field) \
+ ((type *)(((char *)(ptr)) - offsetof(type, field)))
+
+
+#define k_sem ble_npl_sem
+
+static inline void k_sem_init(struct k_sem *sem, unsigned int initial_count,
+ unsigned int limit)
+{
+ ble_npl_sem_init(sem, initial_count);
+}
+
+static inline int k_sem_take(struct k_sem *sem, s32_t timeout)
+{
+ uint32_t ticks;
+
+ ble_npl_time_ms_to_ticks(timeout, &ticks);
+ return - ble_npl_sem_pend(sem, ticks);
+}
+
+static inline void k_sem_give(struct k_sem *sem)
+{
+ ble_npl_sem_release(sem);
+}
+
+/* Helpers to access the storage array, since we don't have access to its
+ * type at this point anymore.
+ */
+
+#define BUF_SIZE(pool) (pool->omp_pool->mp_block_size)
+
+static inline int net_buf_id(struct os_mbuf *buf)
+{
+ struct os_mbuf_pool *pool = buf->om_omp;
+ u8_t *pool_start = (u8_t *)pool->omp_pool->mp_membuf_addr;
+ u8_t *buf_ptr = (u8_t *)buf;
+
+ return (buf_ptr - pool_start) / BUF_SIZE(pool);
+}
+
+/* XXX: We should not use os_mbuf_pkthdr chains to represent a list of
+ * packets, this is a hack. For now this is not an issue, because mesh
+ * does not use os_mbuf chains. We should change this in the future.
+ */
+STAILQ_HEAD(net_buf_slist_t, os_mbuf_pkthdr);
+
+void net_buf_slist_init(struct net_buf_slist_t *list);
+bool net_buf_slist_is_empty(struct net_buf_slist_t *list);
+struct os_mbuf *net_buf_slist_peek_head(struct net_buf_slist_t *list);
+struct os_mbuf *net_buf_slist_peek_next(struct os_mbuf *buf);
+struct os_mbuf *net_buf_slist_get(struct net_buf_slist_t *list);
+void net_buf_slist_put(struct net_buf_slist_t *list, struct os_mbuf *buf);
+void net_buf_slist_remove(struct net_buf_slist_t *list, struct os_mbuf *prev,
+ struct os_mbuf *cur);
+void net_buf_slist_merge_slist(struct net_buf_slist_t *list,
+ struct net_buf_slist_t *list_to_append);
+#define NET_BUF_SLIST_FOR_EACH_NODE(head, var) STAILQ_FOREACH(var, head, omp_next)
+
+#if MYNEWT_VAL(BLE_MESH_SETTINGS)
+
+#define settings_load conf_load
+int settings_bytes_from_str(char *val_str, void *vp, int *len);
+char *settings_str_from_bytes(const void *vp, int vp_len,
+ char *buf, int buf_len);
+
+#define snprintk snprintf
+#define BT_SETTINGS_SIZE(in_size) ((((((in_size) - 1) / 3) * 4) + 4) + 1)
+#define settings_save_one conf_save_one
+
+#else
+
+static inline int
+settings_load(void)
+{
+ return 0;
+}
+
+#endif /* MYNEWT_VAL(MYNEWT_VAL_BLE_MESH_SETTINGS) */
+
+#define BUILD_ASSERT(cond) _Static_assert(cond, "")
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _MESH_GLUE_ */
diff --git a/src/libs/mynewt-nimble/nimble/host/mesh/include/mesh/health_cli.h b/src/libs/mynewt-nimble/nimble/host/mesh/include/mesh/health_cli.h
new file mode 100644
index 00000000..8ab8d6d5
--- /dev/null
+++ b/src/libs/mynewt-nimble/nimble/host/mesh/include/mesh/health_cli.h
@@ -0,0 +1,81 @@
+/** @file
+ * @brief Bluetooth Mesh Health Client Model APIs.
+ */
+
+/*
+ * Copyright (c) 2017 Intel Corporation
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#ifndef __BT_MESH_HEALTH_CLI_H
+#define __BT_MESH_HEALTH_CLI_H
+
+/**
+ * @brief Bluetooth Mesh
+ * @defgroup bt_mesh_health_cli Bluetooth Mesh Health Client Model
+ * @ingroup bt_mesh
+ * @{
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** Mesh Health Client Model Context */
+struct bt_mesh_health_cli {
+ struct bt_mesh_model *model;
+
+ void (*current_status)(struct bt_mesh_health_cli *cli, u16_t addr,
+ u8_t test_id, u16_t cid, u8_t *faults,
+ size_t fault_count);
+
+ struct k_sem op_sync;
+ u32_t op_pending;
+ void *op_param;
+};
+
+extern const struct bt_mesh_model_op bt_mesh_health_cli_op[];
+extern const struct bt_mesh_model_cb bt_mesh_health_cli_cb;
+
+#define BT_MESH_MODEL_HEALTH_CLI(cli_data) \
+ BT_MESH_MODEL_CB(BT_MESH_MODEL_ID_HEALTH_CLI, bt_mesh_health_cli_op, \
+ NULL, cli_data, &bt_mesh_health_cli_cb)
+
+int bt_mesh_health_cli_set(struct bt_mesh_model *model);
+
+int bt_mesh_health_fault_get(u16_t net_idx, u16_t addr, u16_t app_idx,
+ u16_t cid, u8_t *test_id, u8_t *faults,
+ size_t *fault_count);
+
+int bt_mesh_health_fault_clear(u16_t net_idx, u16_t addr, u16_t app_idx,
+ u16_t cid, u8_t *test_id, u8_t *faults,
+ size_t *fault_count);
+
+int bt_mesh_health_fault_test(u16_t net_idx, u16_t addr, u16_t app_idx,
+ u16_t cid, u8_t test_id, u8_t *faults,
+ size_t *fault_count);
+
+int bt_mesh_health_period_get(u16_t net_idx, u16_t addr, u16_t app_idx,
+ u8_t *divisor);
+
+int bt_mesh_health_period_set(u16_t net_idx, u16_t addr, u16_t app_idx,
+ u8_t divisor, u8_t *updated_divisor);
+
+int bt_mesh_health_attention_get(u16_t net_idx, u16_t addr, u16_t app_idx,
+ u8_t *attention);
+
+int bt_mesh_health_attention_set(u16_t net_idx, u16_t addr, u16_t app_idx,
+ u8_t attention, u8_t *updated_attention);
+
+s32_t bt_mesh_health_cli_timeout_get(void);
+void bt_mesh_health_cli_timeout_set(s32_t timeout);
+
+#ifdef __cplusplus
+}
+#endif
+
+/**
+ * @}
+ */
+
+#endif /* __BT_MESH_HEALTH_CLI_H */
diff --git a/src/libs/mynewt-nimble/nimble/host/mesh/include/mesh/health_srv.h b/src/libs/mynewt-nimble/nimble/host/mesh/include/mesh/health_srv.h
new file mode 100644
index 00000000..83982376
--- /dev/null
+++ b/src/libs/mynewt-nimble/nimble/host/mesh/include/mesh/health_srv.h
@@ -0,0 +1,100 @@
+/** @file
+ * @brief Bluetooth Mesh Health Server Model APIs.
+ */
+
+/*
+ * Copyright (c) 2017 Intel Corporation
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#ifndef __BT_MESH_HEALTH_SRV_H
+#define __BT_MESH_HEALTH_SRV_H
+
+/**
+ * @brief Mesh Bluetooth Mesh Health Server Model
+ * @defgroup bt_mesh_health_srv
+ * @ingroup bt_mesh
+ * @{
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct bt_mesh_health_srv_cb {
+ /* Fetch current faults */
+ int (*fault_get_cur)(struct bt_mesh_model *model, u8_t *test_id,
+ u16_t *company_id, u8_t *faults,
+ u8_t *fault_count);
+
+ /* Fetch registered faults */
+ int (*fault_get_reg)(struct bt_mesh_model *model, u16_t company_id,
+ u8_t *test_id, u8_t *faults,
+ u8_t *fault_count);
+
+ /* Clear registered faults */
+ int (*fault_clear)(struct bt_mesh_model *model, u16_t company_id);
+
+ /* Run a specific test */
+ int (*fault_test)(struct bt_mesh_model *model, u8_t test_id,
+ u16_t company_id);
+
+ /* Attention on */
+ void (*attn_on)(struct bt_mesh_model *model);
+
+ /* Attention off */
+ void (*attn_off)(struct bt_mesh_model *model);
+};
+
+/** @def BT_MESH_HEALTH_FAULT_MSG
+ *
+ * A helper to define a health fault message.
+ *
+ * @param max_faults Maximum number of faults the element can have.
+ *
+ * @return a New net_buf_simple of the needed size.
+ */
+#define BT_MESH_HEALTH_FAULT_MSG(max_faults) \
+ NET_BUF_SIMPLE(1 + 3 + (max_faults))
+
+/** Mesh Health Server Model Context */
+struct bt_mesh_health_srv {
+ struct bt_mesh_model *model;
+
+ /* Optional callback struct */
+ const struct bt_mesh_health_srv_cb *cb;
+
+ /* Attention Timer state */
+ struct k_delayed_work attn_timer;
+};
+
+int bt_mesh_fault_update(struct bt_mesh_elem *elem);
+
+extern const struct bt_mesh_model_op bt_mesh_health_srv_op[];
+extern const struct bt_mesh_model_cb bt_mesh_health_srv_cb;
+
+/** @def BT_MESH_MODEL_HEALTH_SRV
+ *
+ * Define a new health server model. Note that this API needs to be
+ * repeated for each element that the application wants to have a
+ * health server model on. Each instance also needs a unique
+ * bt_mesh_health_srv and bt_mesh_model_pub context.
+ *
+ * @param srv Pointer to a unique struct bt_mesh_health_srv.
+ * @param pub Pointer to a unique struct bt_mesh_model_pub.
+ *
+ * @return New mesh model instance.
+ */
+#define BT_MESH_MODEL_HEALTH_SRV(srv, pub) \
+ BT_MESH_MODEL_CB(BT_MESH_MODEL_ID_HEALTH_SRV, bt_mesh_health_srv_op, \
+ pub, srv, &bt_mesh_health_srv_cb)
+
+#ifdef __cplusplus
+}
+#endif
+
+/**
+ * @}
+ */
+
+#endif /* __BT_MESH_HEALTH_SRV_H */
diff --git a/src/libs/mynewt-nimble/nimble/host/mesh/include/mesh/main.h b/src/libs/mynewt-nimble/nimble/host/mesh/include/mesh/main.h
new file mode 100644
index 00000000..4a5bedba
--- /dev/null
+++ b/src/libs/mynewt-nimble/nimble/host/mesh/include/mesh/main.h
@@ -0,0 +1,441 @@
+/** @file
+ * @brief Bluetooth Mesh Profile APIs.
+ */
+
+/*
+ * Copyright (c) 2017 Intel Corporation
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#ifndef __BT_MESH_MAIN_H
+#define __BT_MESH_MAIN_H
+
+/**
+ * @brief Bluetooth Mesh Provisioning
+ * @defgroup bt_mesh_prov Bluetooth Mesh Provisioning
+ * @ingroup bt_mesh
+ * @{
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef enum {
+ BT_MESH_NO_OUTPUT = 0,
+ BT_MESH_BLINK = BIT(0),
+ BT_MESH_BEEP = BIT(1),
+ BT_MESH_VIBRATE = BIT(2),
+ BT_MESH_DISPLAY_NUMBER = BIT(3),
+ BT_MESH_DISPLAY_STRING = BIT(4),
+} bt_mesh_output_action_t;
+
+typedef enum {
+ BT_MESH_NO_INPUT = 0,
+ BT_MESH_PUSH = BIT(0),
+ BT_MESH_TWIST = BIT(1),
+ BT_MESH_ENTER_NUMBER = BIT(2),
+ BT_MESH_ENTER_STRING = BIT(3),
+} bt_mesh_input_action_t;
+
+typedef enum {
+ BT_MESH_PROV_ADV = BIT(0),
+ BT_MESH_PROV_GATT = BIT(1),
+} bt_mesh_prov_bearer_t;
+
+typedef enum {
+ BT_MESH_PROV_OOB_OTHER = BIT(0),
+ BT_MESH_PROV_OOB_URI = BIT(1),
+ BT_MESH_PROV_OOB_2D_CODE = BIT(2),
+ BT_MESH_PROV_OOB_BAR_CODE = BIT(3),
+ BT_MESH_PROV_OOB_NFC = BIT(4),
+ BT_MESH_PROV_OOB_NUMBER = BIT(5),
+ BT_MESH_PROV_OOB_STRING = BIT(6),
+ /* 7 - 10 are reserved */
+ BT_MESH_PROV_OOB_ON_BOX = BIT(11),
+ BT_MESH_PROV_OOB_IN_BOX = BIT(12),
+ BT_MESH_PROV_OOB_ON_PAPER = BIT(13),
+ BT_MESH_PROV_OOB_IN_MANUAL = BIT(14),
+ BT_MESH_PROV_OOB_ON_DEV = BIT(15),
+} bt_mesh_prov_oob_info_t;
+
+/** Provisioning properties & capabilities. */
+struct bt_mesh_prov {
+ /** The UUID that's used when advertising as unprovisioned */
+ const u8_t *uuid;
+
+ /** Optional URI. This will be advertised separately from the
+ * unprovisioned beacon, however the unprovisioned beacon will
+ * contain a hash of it so the two can be associated by the
+ * provisioner.
+ */
+ const char *uri;
+
+ /** Out of Band information field. */
+ bt_mesh_prov_oob_info_t oob_info;
+
+ /** Static OOB value */
+ const u8_t *static_val;
+ /** Static OOB value length */
+ u8_t static_val_len;
+
+ /** Maximum size of Output OOB supported */
+ u8_t output_size;
+ /** Supported Output OOB Actions */
+ u16_t output_actions;
+
+ /* Maximum size of Input OOB supported */
+ u8_t input_size;
+ /** Supported Input OOB Actions */
+ u16_t input_actions;
+
+ /** @brief Output of a number is requested.
+ *
+ * This callback notifies the application that it should
+ * output the given number using the given action.
+ *
+ * @param act Action for outputting the number.
+ * @param num Number to be outputted.
+ *
+ * @return Zero on success or negative error code otherwise
+ */
+ int (*output_number)(bt_mesh_output_action_t act, u32_t num);
+
+ /** @brief Output of a string is requested.
+ *
+ * This callback notifies the application that it should
+ * display the given string to the user.
+ *
+ * @param str String to be displayed.
+ *
+ * @return Zero on success or negative error code otherwise
+ */
+ int (*output_string)(const char *str);
+
+ /** @brief Input is requested.
+ *
+ * This callback notifies the application that it should
+ * request input from the user using the given action. The
+ * requested input will either be a string or a number, and
+ * the application needs to consequently call the
+ * bt_mesh_input_string() or bt_mesh_input_number() functions
+ * once the data has been acquired from the user.
+ *
+ * @param act Action for inputting data.
+ * @param num Maximum size of the inputted data.
+ *
+ * @return Zero on success or negative error code otherwise
+ */
+ int (*input)(bt_mesh_input_action_t act, u8_t size);
+
+ /** @brief The other device finished their OOB input.
+ *
+ * This callback notifies the application that it should stop
+ * displaying its output OOB value, as the other party finished their
+ * OOB input.
+ */
+ void (*input_complete)(void);
+
+ /** @brief Unprovisioned beacon has been received.
+ *
+ * This callback notifies the application that an unprovisioned
+ * beacon has been received.
+ *
+ * @param uuid UUID
+ * @param oob_info OOB Information
+ * @param uri_hash Pointer to URI Hash value. NULL if no hash was
+ * present in the beacon.
+ */
+ void (*unprovisioned_beacon)(u8_t uuid[16],
+ bt_mesh_prov_oob_info_t oob_info,
+ u32_t *uri_hash);
+
+ /** @brief Provisioning link has been opened.
+ *
+ * This callback notifies the application that a provisioning
+ * link has been opened on the given provisioning bearer.
+ *
+ * @param bearer Provisioning bearer.
+ */
+ void (*link_open)(bt_mesh_prov_bearer_t bearer);
+
+ /** @brief Provisioning link has been closed.
+ *
+ * This callback notifies the application that a provisioning
+ * link has been closed on the given provisioning bearer.
+ *
+ * @param bearer Provisioning bearer.
+ */
+ void (*link_close)(bt_mesh_prov_bearer_t bearer);
+
+ /** @brief Provisioning is complete.
+ *
+ * This callback notifies the application that provisioning has
+ * been successfully completed, and that the local node has been
+ * assigned the specified NetKeyIndex and primary element address.
+ *
+ * @param net_idx NetKeyIndex given during provisioning.
+ * @param addr Primary element address.
+ */
+ void (*complete)(u16_t net_idx, u16_t addr);
+
+ /** @brief A new node has been added to the provisioning database.
+ *
+ * This callback notifies the application that provisioning has
+ * been successfully completed, and that a node has been assigned
+ * the specified NetKeyIndex and primary element address.
+ *
+ * @param net_idx NetKeyIndex given during provisioning.
+ * @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);
+
+ /** @brief Node has been reset.
+ *
+ * This callback notifies the application that the local node
+ * has been reset and needs to be reprovisioned. The node will
+ * not automatically advertise as unprovisioned, rather the
+ * bt_mesh_prov_enable() API needs to be called to enable
+ * unprovisioned advertising on one or more provisioning bearers.
+ */
+ void (*reset)(void);
+};
+
+/** @brief Provide provisioning input OOB string.
+ *
+ * This is intended to be called after the bt_mesh_prov input callback
+ * has been called with BT_MESH_ENTER_STRING as the action.
+ *
+ * @param str String.
+ *
+ * @return Zero on success or (negative) error code otherwise.
+ */
+int bt_mesh_input_string(const char *str);
+
+/** @brief Provide provisioning input OOB number.
+ *
+ * This is intended to be called after the bt_mesh_prov input callback
+ * has been called with BT_MESH_ENTER_NUMBER as the action.
+ *
+ * @param num Number.
+ *
+ * @return Zero on success or (negative) error code otherwise.
+ */
+int bt_mesh_input_number(u32_t num);
+
+/** @brief Enable specific provisioning bearers
+ *
+ * Enable one or more provisioning bearers.
+ *
+ * @param bearers Bit-wise or of provisioning bearers.
+ *
+ * @return Zero on success or (negative) error code otherwise.
+ */
+int bt_mesh_prov_enable(bt_mesh_prov_bearer_t bearers);
+
+/** @brief Disable specific provisioning bearers
+ *
+ * Disable one or more provisioning bearers.
+ *
+ * @param bearers Bit-wise or of provisioning bearers.
+ *
+ * @return Zero on success or (negative) error code otherwise.
+ */
+int bt_mesh_prov_disable(bt_mesh_prov_bearer_t bearers);
+
+/**
+ * @}
+ */
+
+/**
+ * @brief Bluetooth Mesh
+ * @defgroup bt_mesh Bluetooth Mesh
+ * @ingroup bluetooth
+ * @{
+ */
+
+/* Primary Network Key index */
+#define BT_MESH_NET_PRIMARY 0x000
+
+#define BT_MESH_RELAY_DISABLED 0x00
+#define BT_MESH_RELAY_ENABLED 0x01
+#define BT_MESH_RELAY_NOT_SUPPORTED 0x02
+
+#define BT_MESH_BEACON_DISABLED 0x00
+#define BT_MESH_BEACON_ENABLED 0x01
+
+#define BT_MESH_GATT_PROXY_DISABLED 0x00
+#define BT_MESH_GATT_PROXY_ENABLED 0x01
+#define BT_MESH_GATT_PROXY_NOT_SUPPORTED 0x02
+
+#define BT_MESH_FRIEND_DISABLED 0x00
+#define BT_MESH_FRIEND_ENABLED 0x01
+#define BT_MESH_FRIEND_NOT_SUPPORTED 0x02
+
+#define BT_MESH_NODE_IDENTITY_STOPPED 0x00
+#define BT_MESH_NODE_IDENTITY_RUNNING 0x01
+#define BT_MESH_NODE_IDENTITY_NOT_SUPPORTED 0x02
+
+/* Features */
+#define BT_MESH_FEAT_RELAY BIT(0)
+#define BT_MESH_FEAT_PROXY BIT(1)
+#define BT_MESH_FEAT_FRIEND BIT(2)
+#define BT_MESH_FEAT_LOW_POWER BIT(3)
+#define BT_MESH_FEAT_SUPPORTED (BT_MESH_FEAT_RELAY | \
+ BT_MESH_FEAT_PROXY | \
+ BT_MESH_FEAT_FRIEND | \
+ BT_MESH_FEAT_LOW_POWER)
+
+/** @brief Initialize Mesh support
+ *
+ * After calling this API, the node will not automatically advertise as
+ * unprovisioned, rather the bt_mesh_prov_enable() API needs to be called
+ * to enable unprovisioned advertising on one or more provisioning bearers.
+ *
+ * @param own_addr_type Node address type
+ * @param prov Node provisioning information.
+ * @param comp Node Composition.
+ *
+ * @return Zero on success or (negative) error code otherwise.
+ */
+int bt_mesh_init(u8_t own_addr_type,
+ const struct bt_mesh_prov *prov,
+ const struct bt_mesh_comp *comp);
+
+/** @brief Reset the state of the local Mesh node.
+ *
+ * Resets the state of the node, which means that it needs to be
+ * reprovisioned to become an active node in a Mesh network again.
+ *
+ * After calling this API, the node will not automatically advertise as
+ * unprovisioned, rather the bt_mesh_prov_enable() API needs to be called
+ * to enable unprovisioned advertising on one or more provisioning bearers.
+ *
+ */
+void bt_mesh_reset(void);
+
+/** @brief Suspend the Mesh network temporarily.
+ *
+ * This API can be used for power saving purposes, but the user should be
+ * aware that leaving the local node suspended for a long period of time
+ * may cause it to become permanently disconnected from the Mesh network.
+ * If at all possible, the Friendship feature should be used instead, to
+ * make the node into a Low Power Node.
+ *
+ * @return 0 on success, or (negative) error code on failure.
+ */
+int bt_mesh_suspend(void);
+
+/** @brief Resume a suspended Mesh network.
+ *
+ * This API resumes the local node, after it has been suspended using the
+ * bt_mesh_suspend() API.
+ *
+ * @return 0 on success, or (negative) error code on failure.
+ */
+int bt_mesh_resume(void);
+
+/** @brief Provision the local Mesh Node.
+ *
+ * This API should normally not be used directly by the application. The
+ * only exception is for testing purposes where manual provisioning is
+ * desired without an actual external provisioner.
+ *
+ * @param net_key Network Key
+ * @param net_idx Network Key Index
+ * @param flags Provisioning Flags
+ * @param iv_index IV Index
+ * @param addr Primary element address
+ * @param dev_key Device Key
+ *
+ * @return Zero on success or (negative) error code otherwise.
+ */
+int bt_mesh_provision(const u8_t net_key[16], u16_t net_idx,
+ u8_t flags, u32_t iv_index, u16_t addr,
+ const u8_t dev_key[16]);
+
+/** @brief Provision a Mesh Node using PB-ADV
+ *
+ * @param uuid UUID
+ * @param net_idx Network Key Index
+ * @param addr Address to assign to remote device. If addr is 0, the lowest
+ * available address will be chosen.
+ * @param attention_duration The attention duration to be send to remote device
+ *
+ * @return Zero on success or (negative) error code otherwise.
+ */
+int bt_mesh_provision_adv(const u8_t uuid[16], u16_t net_idx, u16_t addr,
+ u8_t attention_duration);
+
+/** @brief Check if the local node has been provisioned.
+ *
+ * This API can be used to check if the local node has been provisioned
+ * or not. It can e.g. be helpful to determine if there was a stored
+ * network in flash, i.e. if the network was restored after calling
+ * settings_load().
+ *
+ * @return True if the node is provisioned. False otherwise.
+ */
+bool bt_mesh_is_provisioned(void);
+
+/** @brief Toggle the IV Update test mode
+ *
+ * This API is only available if the IV Update test mode has been enabled
+ * in Kconfig. It is needed for passing most of the IV Update qualification
+ * test cases.
+ *
+ * @param enable true to enable IV Update test mode, false to disable it.
+ */
+void bt_mesh_iv_update_test(bool enable);
+
+/** @brief Toggle the IV Update state
+ *
+ * This API is only available if the IV Update test mode has been enabled
+ * in Kconfig. It is needed for passing most of the IV Update qualification
+ * test cases.
+ *
+ * @return true if IV Update In Progress state was entered, false otherwise.
+ */
+bool bt_mesh_iv_update(void);
+
+/** @brief Toggle the Low Power feature of the local device
+ *
+ * Enables or disables the Low Power feature of the local device. This is
+ * exposed as a run-time feature, since the device might want to change
+ * this e.g. based on being plugged into a stable power source or running
+ * from a battery power source.
+ *
+ * @param enable true to enable LPN functionality, false to disable it.
+ *
+ * @return Zero on success or (negative) error code otherwise.
+ */
+int bt_mesh_lpn_set(bool enable);
+
+/** @brief Send out a Friend Poll message.
+ *
+ * Send a Friend Poll message to the Friend of this node. If there is no
+ * established Friendship the function will return an error.
+ *
+ * @return Zero on success or (negative) error code otherwise.
+ */
+int bt_mesh_lpn_poll(void);
+
+/** @brief Register a callback for Friendship changes.
+ *
+ * Registers a callback that will be called whenever Friendship gets
+ * established or is lost.
+ *
+ * @param cb Function to call when the Friendship status changes.
+ */
+void bt_mesh_lpn_set_cb(void (*cb)(u16_t friend_addr, bool established));
+
+#ifdef __cplusplus
+}
+#endif
+
+/**
+ * @}
+ */
+
+#endif /* __BT_MESH_MAIN_H */
diff --git a/src/libs/mynewt-nimble/nimble/host/mesh/include/mesh/mesh.h b/src/libs/mynewt-nimble/nimble/host/mesh/include/mesh/mesh.h
new file mode 100644
index 00000000..9ba63ef0
--- /dev/null
+++ b/src/libs/mynewt-nimble/nimble/host/mesh/include/mesh/mesh.h
@@ -0,0 +1,26 @@
+/** @file
+ * @brief Bluetooth Mesh Profile APIs.
+ */
+
+/*
+ * Copyright (c) 2017 Intel Corporation
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#ifndef __BT_MESH_H
+#define __BT_MESH_H
+
+#include <stddef.h>
+#include "syscfg/syscfg.h"
+#include "os/os_mbuf.h"
+
+#include "glue.h"
+#include "access.h"
+#include "main.h"
+#include "cfg_srv.h"
+#include "health_srv.h"
+#include "cfg_cli.h"
+#include "health_cli.h"
+#include "proxy.h"
+
+#endif /* __BT_MESH_H */
diff --git a/src/libs/mynewt-nimble/nimble/host/mesh/include/mesh/model_cli.h b/src/libs/mynewt-nimble/nimble/host/mesh/include/mesh/model_cli.h
new file mode 100644
index 00000000..f2e77a47
--- /dev/null
+++ b/src/libs/mynewt-nimble/nimble/host/mesh/include/mesh/model_cli.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2017 Intel Corporation
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#ifndef __MODEL_CLI_H__
+#define __MODEL_CLI_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct bt_mesh_gen_model_cli {
+ struct bt_mesh_model *model;
+
+ struct k_sem op_sync;
+ u32_t op_pending;
+ void *op_param;
+};
+
+extern const struct bt_mesh_model_op gen_onoff_cli_op[];
+extern const struct bt_mesh_model_cb bt_mesh_gen_onoff_cli_cb;
+
+#define BT_MESH_MODEL_GEN_ONOFF_CLI(cli_data, pub) \
+ BT_MESH_MODEL_CB(BT_MESH_MODEL_ID_GEN_ONOFF_CLI, gen_onoff_cli_op, pub,\
+ cli_data, &bt_mesh_gen_onoff_cli_cb)
+
+extern const struct bt_mesh_model_op gen_level_cli_op[];
+extern const struct bt_mesh_model_cb bt_mesh_gen_level_cli_cb;
+
+#define BT_MESH_MODEL_GEN_LEVEL_CLI(cli_data, pub) \
+ BT_MESH_MODEL_CB(BT_MESH_MODEL_ID_GEN_LEVEL_CLI, gen_level_cli_op, pub,\
+ cli_data, &bt_mesh_gen_level_cli_cb)
+
+int bt_mesh_gen_onoff_get(u16_t net_idx, u16_t addr, u16_t app_idx,
+ u8_t *state);
+int bt_mesh_gen_onoff_set(u16_t net_idx, u16_t addr, u16_t app_idx,
+ u8_t val, u8_t *state);
+int bt_mesh_gen_level_get(u16_t net_idx, u16_t addr, u16_t app_idx,
+ s16_t *level);
+int bt_mesh_gen_level_set(u16_t net_idx, u16_t addr, u16_t app_idx,
+ s16_t val, s16_t *state);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __MODEL_CLI_H__ */
diff --git a/src/libs/mynewt-nimble/nimble/host/mesh/include/mesh/model_srv.h b/src/libs/mynewt-nimble/nimble/host/mesh/include/mesh/model_srv.h
new file mode 100644
index 00000000..e498ad34
--- /dev/null
+++ b/src/libs/mynewt-nimble/nimble/host/mesh/include/mesh/model_srv.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2017 Intel Corporation
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#ifndef __MODEL_SRV_H__
+#define __MODEL_SRV_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct bt_mesh_gen_onoff_srv {
+ struct bt_mesh_model *model;
+
+ int (*get)(struct bt_mesh_model *model, u8_t *state);
+ int (*set)(struct bt_mesh_model *model, u8_t state);
+};
+
+extern const struct bt_mesh_model_op gen_onoff_srv_op[];
+extern const struct bt_mesh_model_cb gen_onoff_srv_cb;
+
+#define BT_MESH_MODEL_GEN_ONOFF_SRV(srv, pub) \
+ BT_MESH_MODEL_CB(BT_MESH_MODEL_ID_GEN_ONOFF_SRV, \
+ gen_onoff_srv_op, pub, srv, &gen_onoff_srv_cb)
+
+struct bt_mesh_gen_level_srv {
+ struct bt_mesh_model *model;
+
+ int (*get)(struct bt_mesh_model *model, s16_t *level);
+ int (*set)(struct bt_mesh_model *model, s16_t level);
+};
+
+extern const struct bt_mesh_model_op gen_level_srv_op[];
+extern const struct bt_mesh_model_cb gen_level_srv_cb;
+
+#define BT_MESH_MODEL_GEN_LEVEL_SRV(srv, pub) \
+ BT_MESH_MODEL_CB(BT_MESH_MODEL_ID_GEN_LEVEL_SRV, \
+ gen_level_srv_op, pub, srv, &gen_level_srv_cb)
+
+struct bt_mesh_light_lightness_srv {
+ struct bt_mesh_model *model;
+
+ int (*get)(struct bt_mesh_model *model, s16_t *level);
+ int (*set)(struct bt_mesh_model *model, s16_t level);
+};
+
+extern const struct bt_mesh_model_op light_lightness_srv_op[];
+extern const struct bt_mesh_model_cb light_lightness_srv_cb;
+
+#define BT_MESH_MODEL_LIGHT_LIGHTNESS_SRV(srv, pub) \
+ BT_MESH_MODEL_CB(BT_MESH_MODEL_ID_LIGHT_LIGHTNESS_SRV, \
+ light_lightness_srv_op, pub, srv, &light_lightness_srv_cb)
+
+void bt_mesh_set_gen_onoff_srv_cb(int (*get)(struct bt_mesh_model *model, u8_t *state),
+ int (*set)(struct bt_mesh_model *model, u8_t state));
+void bt_mesh_set_gen_level_srv_cb(int (*get)(struct bt_mesh_model *model, s16_t *level),
+ int (*set)(struct bt_mesh_model *model, s16_t level));
+void bt_mesh_set_light_lightness_srv_cb(int (*get)(struct bt_mesh_model *model, s16_t *level),
+ int (*set)(struct bt_mesh_model *model, s16_t level));
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __MODEL_SRV_H__ */
diff --git a/src/libs/mynewt-nimble/nimble/host/mesh/include/mesh/porting.h b/src/libs/mynewt-nimble/nimble/host/mesh/include/mesh/porting.h
new file mode 100644
index 00000000..1667a8a0
--- /dev/null
+++ b/src/libs/mynewt-nimble/nimble/host/mesh/include/mesh/porting.h
@@ -0,0 +1,27 @@
+/** @file
+ * @brief Bluetooth Mesh Porting APIs.
+ */
+
+/*
+ * Copyright (c) 2017 Intel Corporation
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#ifndef __BT_MESH_PORTING_H
+#define __BT_MESH_PORTING_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void mesh_adv_thread(void *args);
+
+#ifdef __cplusplus
+}
+#endif
+
+/**
+ * @}
+ */
+
+#endif /* __BT_MESH_PORTING_H */
diff --git a/src/libs/mynewt-nimble/nimble/host/mesh/include/mesh/proxy.h b/src/libs/mynewt-nimble/nimble/host/mesh/include/mesh/proxy.h
new file mode 100644
index 00000000..63bbfa3e
--- /dev/null
+++ b/src/libs/mynewt-nimble/nimble/host/mesh/include/mesh/proxy.h
@@ -0,0 +1,43 @@
+/** @file
+ * @brief Bluetooth Mesh Proxy APIs.
+ */
+
+/*
+ * Copyright (c) 2017 Intel Corporation
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#ifndef __BT_MESH_PROXY_H
+#define __BT_MESH_PROXY_H
+
+/**
+ * @brief Bluetooth Mesh Proxy
+ * @defgroup bt_mesh_proxy Bluetooth Mesh Proxy
+ * @ingroup bt_mesh
+ * @{
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief Enable advertising with Node Identity.
+ *
+ * This API requires that GATT Proxy support has been enabled. Once called
+ * each subnet will start advertising using Node Identity for the next
+ * 60 seconds.
+ *
+ * @return 0 on success, or (negative) error code on failure.
+ */
+int bt_mesh_proxy_identity_enable(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+/**
+ * @}
+ */
+
+#endif /* __BT_MESH_PROXY_H */
diff --git a/src/libs/mynewt-nimble/nimble/host/mesh/include/mesh/slist.h b/src/libs/mynewt-nimble/nimble/host/mesh/include/mesh/slist.h
new file mode 100644
index 00000000..8a858f83
--- /dev/null
+++ b/src/libs/mynewt-nimble/nimble/host/mesh/include/mesh/slist.h
@@ -0,0 +1,468 @@
+/*
+ * Copyright (c) 2016 Intel Corporation
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+/**
+ * @file
+ *
+ * @brief Single-linked list implementation
+ *
+ * Single-linked list implementation using inline macros/functions.
+ * This API is not thread safe, and thus if a list is used across threads,
+ * calls to functions must be protected with synchronization primitives.
+ */
+
+#ifndef __SLIST_H__
+#define __SLIST_H__
+
+#include <stddef.h>
+#include <stdbool.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+struct _snode {
+ struct _snode *next;
+};
+
+typedef struct _snode sys_snode_t;
+
+struct _slist {
+ sys_snode_t *head;
+ sys_snode_t *tail;
+};
+
+typedef struct _slist sys_slist_t;
+
+/**
+ * @brief Provide the primitive to iterate on a list
+ * Note: the loop is unsafe and thus __sn should not be removed
+ *
+ * User _MUST_ add the loop statement curly braces enclosing its own code:
+ *
+ * SYS_SLIST_FOR_EACH_NODE(l, n) {
+ * <user code>
+ * }
+ *
+ * This and other SYS_SLIST_*() macros are not thread safe.
+ *
+ * @param __sl A pointer on a sys_slist_t to iterate on
+ * @param __sn A sys_snode_t pointer to peek each node of the list
+ */
+#define SYS_SLIST_FOR_EACH_NODE(__sl, __sn) \
+ for (__sn = sys_slist_peek_head(__sl); __sn; \
+ __sn = sys_slist_peek_next(__sn))
+
+/**
+ * @brief Provide the primitive to iterate on a list, from a node in the list
+ * Note: the loop is unsafe and thus __sn should not be removed
+ *
+ * User _MUST_ add the loop statement curly braces enclosing its own code:
+ *
+ * SYS_SLIST_ITERATE_FROM_NODE(l, n) {
+ * <user code>
+ * }
+ *
+ * Like SYS_SLIST_FOR_EACH_NODE(), but __dn already contains a node in the list
+ * where to start searching for the next entry from. If NULL, it starts from
+ * the head.
+ *
+ * This and other SYS_SLIST_*() macros are not thread safe.
+ *
+ * @param __sl A pointer on a sys_slist_t to iterate on
+ * @param __sn A sys_snode_t pointer to peek each node of the list
+ * it contains the starting node, or NULL to start from the head
+ */
+#define SYS_SLIST_ITERATE_FROM_NODE(__sl, __sn) \
+ for (__sn = __sn ? sys_slist_peek_next_no_check(__sn) \
+ : sys_slist_peek_head(__sl); \
+ __sn; \
+ __sn = sys_slist_peek_next(__sn))
+
+/**
+ * @brief Provide the primitive to safely iterate on a list
+ * Note: __sn can be removed, it will not break the loop.
+ *
+ * User _MUST_ add the loop statement curly braces enclosing its own code:
+ *
+ * SYS_SLIST_FOR_EACH_NODE_SAFE(l, n, s) {
+ * <user code>
+ * }
+ *
+ * This and other SYS_SLIST_*() macros are not thread safe.
+ *
+ * @param __sl A pointer on a sys_slist_t to iterate on
+ * @param __sn A sys_snode_t pointer to peek each node of the list
+ * @param __sns A sys_snode_t pointer for the loop to run safely
+ */
+#define SYS_SLIST_FOR_EACH_NODE_SAFE(__sl, __sn, __sns) \
+ for (__sn = sys_slist_peek_head(__sl), \
+ __sns = sys_slist_peek_next(__sn); \
+ __sn; __sn = __sns, \
+ __sns = sys_slist_peek_next(__sn))
+
+/*
+ * @brief Provide the primitive to resolve the container of a list node
+ * Note: it is safe to use with NULL pointer nodes
+ *
+ * @param __ln A pointer on a sys_node_t to get its container
+ * @param __cn Container struct type pointer
+ * @param __n The field name of sys_node_t within the container struct
+ */
+#define SYS_SLIST_CONTAINER(__ln, __cn, __n) \
+ ((__ln) ? CONTAINER_OF((__ln), __typeof__(*(__cn)), __n) : NULL)
+/*
+ * @brief Provide the primitive to peek container of the list head
+ *
+ * @param __sl A pointer on a sys_slist_t to peek
+ * @param __cn Container struct type pointer
+ * @param __n The field name of sys_node_t within the container struct
+ */
+#define SYS_SLIST_PEEK_HEAD_CONTAINER(__sl, __cn, __n) \
+ SYS_SLIST_CONTAINER(sys_slist_peek_head(__sl), __cn, __n)
+
+/*
+ * @brief Provide the primitive to peek container of the list tail
+ *
+ * @param __sl A pointer on a sys_slist_t to peek
+ * @param __cn Container struct type pointer
+ * @param __n The field name of sys_node_t within the container struct
+ */
+#define SYS_SLIST_PEEK_TAIL_CONTAINER(__sl, __cn, __n) \
+ SYS_SLIST_CONTAINER(sys_slist_peek_tail(__sl), __cn, __n)
+
+/*
+ * @brief Provide the primitive to peek the next container
+ *
+ * @param __cn Container struct type pointer
+ * @param __n The field name of sys_node_t within the container struct
+ */
+
+#define SYS_SLIST_PEEK_NEXT_CONTAINER(__cn, __n) \
+ ((__cn) ? SYS_SLIST_CONTAINER(sys_slist_peek_next(&((__cn)->__n)), \
+ __cn, __n) : NULL)
+
+/**
+ * @brief Provide the primitive to iterate on a list under a container
+ * Note: the loop is unsafe and thus __cn should not be detached
+ *
+ * User _MUST_ add the loop statement curly braces enclosing its own code:
+ *
+ * SYS_SLIST_FOR_EACH_CONTAINER(l, c, n) {
+ * <user code>
+ * }
+ *
+ * @param __sl A pointer on a sys_slist_t to iterate on
+ * @param __cn A pointer to peek each entry of the list
+ * @param __n The field name of sys_node_t within the container struct
+ */
+#define SYS_SLIST_FOR_EACH_CONTAINER(__sl, __cn, __n) \
+ for (__cn = SYS_SLIST_PEEK_HEAD_CONTAINER(__sl, __cn, __n); __cn; \
+ __cn = SYS_SLIST_PEEK_NEXT_CONTAINER(__cn, __n))
+
+/**
+ * @brief Provide the primitive to safely iterate on a list under a container
+ * Note: __cn can be detached, it will not break the loop.
+ *
+ * User _MUST_ add the loop statement curly braces enclosing its own code:
+ *
+ * SYS_SLIST_FOR_EACH_NODE_SAFE(l, c, cn, n) {
+ * <user code>
+ * }
+ *
+ * @param __sl A pointer on a sys_slist_t to iterate on
+ * @param __cn A pointer to peek each entry of the list
+ * @param __cns A pointer for the loop to run safely
+ * @param __n The field name of sys_node_t within the container struct
+ */
+#define SYS_SLIST_FOR_EACH_CONTAINER_SAFE(__sl, __cn, __cns, __n) \
+ for (__cn = SYS_SLIST_PEEK_HEAD_CONTAINER(__sl, __cn, __n), \
+ __cns = SYS_SLIST_PEEK_NEXT_CONTAINER(__cn, __n); __cn; \
+ __cn = __cns, __cns = SYS_SLIST_PEEK_NEXT_CONTAINER(__cn, __n))
+
+/**
+ * @brief Initialize a list
+ *
+ * @param list A pointer on the list to initialize
+ */
+static inline void sys_slist_init(sys_slist_t *list)
+{
+ list->head = NULL;
+ list->tail = NULL;
+}
+
+#define SYS_SLIST_STATIC_INIT(ptr_to_list) {NULL, NULL}
+
+/**
+ * @brief Test if the given list is empty
+ *
+ * @param list A pointer on the list to test
+ *
+ * @return a boolean, true if it's empty, false otherwise
+ */
+static inline bool sys_slist_is_empty(sys_slist_t *list)
+{
+ return (!list->head);
+}
+
+/**
+ * @brief Peek the first node from the list
+ *
+ * @param list A point on the list to peek the first node from
+ *
+ * @return A pointer on the first node of the list (or NULL if none)
+ */
+static inline sys_snode_t *sys_slist_peek_head(sys_slist_t *list)
+{
+ return list->head;
+}
+
+/**
+ * @brief Peek the last node from the list
+ *
+ * @param list A point on the list to peek the last node from
+ *
+ * @return A pointer on the last node of the list (or NULL if none)
+ */
+static inline sys_snode_t *sys_slist_peek_tail(sys_slist_t *list)
+{
+ return list->tail;
+}
+
+/**
+ * @brief Peek the next node from current node, node is not NULL
+ *
+ * Faster then sys_slist_peek_next() if node is known not to be NULL.
+ *
+ * @param node A pointer on the node where to peek the next node
+ *
+ * @return a pointer on the next node (or NULL if none)
+ */
+static inline sys_snode_t *sys_slist_peek_next_no_check(sys_snode_t *node)
+{
+ return node->next;
+}
+
+/**
+ * @brief Peek the next node from current node
+ *
+ * @param node A pointer on the node where to peek the next node
+ *
+ * @return a pointer on the next node (or NULL if none)
+ */
+static inline sys_snode_t *sys_slist_peek_next(sys_snode_t *node)
+{
+ return node ? sys_slist_peek_next_no_check(node) : NULL;
+}
+
+/**
+ * @brief Prepend a node to the given list
+ *
+ * This and other sys_slist_*() functions are not thread safe.
+ *
+ * @param list A pointer on the list to affect
+ * @param node A pointer on the node to prepend
+ */
+static inline void sys_slist_prepend(sys_slist_t *list,
+ sys_snode_t *node)
+{
+ node->next = list->head;
+ list->head = node;
+
+ if (!list->tail) {
+ list->tail = list->head;
+ }
+}
+
+/**
+ * @brief Append a node to the given list
+ *
+ * This and other sys_slist_*() functions are not thread safe.
+ *
+ * @param list A pointer on the list to affect
+ * @param node A pointer on the node to append
+ */
+static inline void sys_slist_append(sys_slist_t *list,
+ sys_snode_t *node)
+{
+ node->next = NULL;
+
+ if (!list->tail) {
+ list->tail = node;
+ list->head = node;
+ } else {
+ list->tail->next = node;
+ list->tail = node;
+ }
+}
+
+/**
+ * @brief Append a list to the given list
+ *
+ * Append a singly-linked, NULL-terminated list consisting of nodes containing
+ * the pointer to the next node as the first element of a node, to @a list.
+ * This and other sys_slist_*() functions are not thread safe.
+ *
+ * @param list A pointer on the list to affect
+ * @param head A pointer to the first element of the list to append
+ * @param tail A pointer to the last element of the list to append
+ */
+static inline void sys_slist_append_list(sys_slist_t *list,
+ void *head, void *tail)
+{
+ if (!list->tail) {
+ list->head = (sys_snode_t *)head;
+ list->tail = (sys_snode_t *)tail;
+ } else {
+ list->tail->next = (sys_snode_t *)head;
+ list->tail = (sys_snode_t *)tail;
+ }
+}
+
+/**
+ * @brief merge two slists, appending the second one to the first
+ *
+ * When the operation is completed, the appending list is empty.
+ * This and other sys_slist_*() functions are not thread safe.
+ *
+ * @param list A pointer on the list to affect
+ * @param list_to_append A pointer to the list to append.
+ */
+static inline void sys_slist_merge_slist(sys_slist_t *list,
+ sys_slist_t *list_to_append)
+{
+ sys_slist_append_list(list, list_to_append->head,
+ list_to_append->tail);
+ sys_slist_init(list_to_append);
+}
+
+/**
+ * @brief Insert a node to the given list
+ *
+ * This and other sys_slist_*() functions are not thread safe.
+ *
+ * @param list A pointer on the list to affect
+ * @param prev A pointer on the previous node
+ * @param node A pointer on the node to insert
+ */
+static inline void sys_slist_insert(sys_slist_t *list,
+ sys_snode_t *prev,
+ sys_snode_t *node)
+{
+ if (!prev) {
+ sys_slist_prepend(list, node);
+ } else if (!prev->next) {
+ sys_slist_append(list, node);
+ } else {
+ node->next = prev->next;
+ prev->next = node;
+ }
+}
+
+/**
+ * @brief Fetch and remove the first node of the given list
+ *
+ * List must be known to be non-empty.
+ * This and other sys_slist_*() functions are not thread safe.
+ *
+ * @param list A pointer on the list to affect
+ *
+ * @return A pointer to the first node of the list
+ */
+static inline sys_snode_t *sys_slist_get_not_empty(sys_slist_t *list)
+{
+ sys_snode_t *node = list->head;
+
+ list->head = node->next;
+ if (list->tail == node) {
+ list->tail = list->head;
+ }
+
+ return node;
+}
+
+/**
+ * @brief Fetch and remove the first node of the given list
+ *
+ * This and other sys_slist_*() functions are not thread safe.
+ *
+ * @param list A pointer on the list to affect
+ *
+ * @return A pointer to the first node of the list (or NULL if empty)
+ */
+static inline sys_snode_t *sys_slist_get(sys_slist_t *list)
+{
+ return sys_slist_is_empty(list) ? NULL : sys_slist_get_not_empty(list);
+}
+
+/**
+ * @brief Remove a node
+ *
+ * This and other sys_slist_*() functions are not thread safe.
+ *
+ * @param list A pointer on the list to affect
+ * @param prev_node A pointer on the previous node
+ * (can be NULL, which means the node is the list's head)
+ * @param node A pointer on the node to remove
+ */
+static inline void sys_slist_remove(sys_slist_t *list,
+ sys_snode_t *prev_node,
+ sys_snode_t *node)
+{
+ if (!prev_node) {
+ list->head = node->next;
+
+ /* Was node also the tail? */
+ if (list->tail == node) {
+ list->tail = list->head;
+ }
+ } else {
+ prev_node->next = node->next;
+
+ /* Was node the tail? */
+ if (list->tail == node) {
+ list->tail = prev_node;
+ }
+ }
+
+ node->next = NULL;
+}
+
+/**
+ * @brief Find and remove a node from a list
+ *
+ * This and other sys_slist_*() functions are not thread safe.
+ *
+ * @param list A pointer on the list to affect
+ * @param node A pointer on the node to remove from the list
+ *
+ * @return true if node was removed
+ */
+static inline bool sys_slist_find_and_remove(sys_slist_t *list,
+ sys_snode_t *node)
+{
+ sys_snode_t *prev = NULL;
+ sys_snode_t *test;
+
+ SYS_SLIST_FOR_EACH_NODE(list, test) {
+ if (test == node) {
+ sys_slist_remove(list, prev, node);
+ return true;
+ }
+
+ prev = test;
+ }
+
+ return false;
+}
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __SLIST_H__ */
diff --git a/src/libs/mynewt-nimble/nimble/host/mesh/include/mesh/testing.h b/src/libs/mynewt-nimble/nimble/host/mesh/include/mesh/testing.h
new file mode 100644
index 00000000..4c2b2a61
--- /dev/null
+++ b/src/libs/mynewt-nimble/nimble/host/mesh/include/mesh/testing.h
@@ -0,0 +1,105 @@
+/**
+ * @file testing.h
+ * @brief Internal API for Bluetooth testing.
+ */
+
+/*
+ * Copyright (c) 2017 Intel Corporation
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#ifndef __BT_TESTING_H
+#define __BT_TESTING_H
+
+#include "slist.h"
+#include "glue.h"
+#include "access.h"
+
+/**
+ * @brief Bluetooth testing
+ * @defgroup bt_test_cb Bluetooth testing callbacks
+ * @ingroup bluetooth
+ * @{
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** @brief Bluetooth Testing callbacks structure.
+ *
+ * Callback structure to be used for Bluetooth testing purposes.
+ * Allows access to Bluetooth stack internals, not exposed by public API.
+ */
+struct bt_test_cb {
+ void (*mesh_net_recv)(u8_t ttl, u8_t ctl, u16_t src, u16_t dst,
+ const void *payload, size_t payload_len);
+ void (*mesh_model_bound)(u16_t addr, struct bt_mesh_model *model,
+ u16_t key_idx);
+ void (*mesh_model_unbound)(u16_t addr, struct bt_mesh_model *model,
+ u16_t key_idx);
+ void (*mesh_prov_invalid_bearer)(u8_t opcode);
+ void (*mesh_trans_incomp_timer_exp)(void);
+
+ sys_snode_t node;
+};
+
+/** Register callbacks for Bluetooth testing purposes
+ *
+ * @param cb bt_test_cb callback structure
+ */
+void bt_test_cb_register(struct bt_test_cb *cb);
+
+/** Unregister callbacks for Bluetooth testing purposes
+ *
+ * @param cb bt_test_cb callback structure
+ */
+void bt_test_cb_unregister(struct bt_test_cb *cb);
+
+/** Send Friend Subscription List Add message.
+ *
+ * Used by Low Power node to send the group address for which messages are to
+ * be stored by Friend node.
+ *
+ * @param group Group address
+ *
+ * @return Zero on success or (negative) error code otherwise.
+ */
+int bt_test_mesh_lpn_group_add(u16_t group);
+
+/** Send Friend Subscription List Remove message.
+ *
+ * Used by Low Power node to remove the group addresses from Friend node
+ * subscription list. Messages sent to those addresses will not be stored
+ * by Friend node.
+ *
+ * @param groups Group addresses
+ * @param groups_count Group addresses count
+ *
+ * @return Zero on success or (negative) error code otherwise.
+ */
+int bt_test_mesh_lpn_group_remove(u16_t *groups, size_t groups_count);
+
+/** Clear replay protection list cache.
+ *
+ * @return Zero on success or (negative) error code otherwise.
+ */
+int bt_test_mesh_rpl_clear(void);
+
+u8_t mod_bind(struct bt_mesh_model *model, u16_t key_idx);
+u8_t mod_unbind(struct bt_mesh_model *model, u16_t key_idx, bool store);
+int cmd_mesh_init(int argc, char *argv[]);
+
+int bt_test_shell_init(void);
+int bt_test_bind_app_key_to_model(struct bt_mesh_model *model, u16_t key_idx, u16_t id);
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __BT_TESTING_H */
diff --git a/src/libs/mynewt-nimble/nimble/host/mesh/pkg.yml b/src/libs/mynewt-nimble/nimble/host/mesh/pkg.yml
new file mode 100644
index 00000000..44cc0c73
--- /dev/null
+++ b/src/libs/mynewt-nimble/nimble/host/mesh/pkg.yml
@@ -0,0 +1,49 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+pkg.name: nimble/host/mesh
+pkg.description: Bluetooth Mesh
+pkg.author: "Apache Mynewt <dev@mynewt.apache.org>"
+pkg.homepage: "http://mynewt.apache.org/"
+pkg.keywords:
+ - ble
+ - bluetooth
+ - mesh
+
+pkg.deps:
+ - "@apache-mynewt-core/kernel/os"
+ - "@apache-mynewt-core/util/mem"
+ - "@apache-mynewt-core/crypto/tinycrypt"
+ - nimble
+ - nimble/host
+
+pkg.deps.BLE_MESH_SHELL:
+ - "@apache-mynewt-core/sys/shell"
+
+pkg.deps.BLE_MESH_SETTINGS:
+ - "@apache-mynewt-core/encoding/base64"
+ - "@apache-mynewt-core/sys/config"
+
+pkg.req_apis:
+ - log
+ - stats
+
+pkg.init:
+ bt_mesh_register_gatt: 'MYNEWT_VAL(BLE_MESH_SYSINIT_STAGE)'
+ ble_mesh_shell_init: 'MYNEWT_VAL(BLE_MESH_SYSINIT_STAGE_SHELL)'
diff --git a/src/libs/mynewt-nimble/nimble/host/mesh/src/access.c b/src/libs/mynewt-nimble/nimble/host/mesh/src/access.c
new file mode 100644
index 00000000..ff8e9999
--- /dev/null
+++ b/src/libs/mynewt-nimble/nimble/host/mesh/src/access.c
@@ -0,0 +1,856 @@
+/* Bluetooth Mesh */
+
+/*
+ * Copyright (c) 2017 Intel Corporation
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include "syscfg/syscfg.h"
+#define MESH_LOG_MODULE BLE_MESH_ACCESS_LOG
+
+#include <errno.h>
+#include <os/os_mbuf.h>
+
+#include "mesh/mesh.h"
+
+#include "mesh_priv.h"
+#include "adv.h"
+#include "net.h"
+#include "lpn.h"
+#include "transport.h"
+#include "access.h"
+#include "foundation.h"
+#if MYNEWT_VAL(BLE_MESH_SHELL_MODELS)
+#include "mesh/model_cli.h"
+#endif
+
+static const struct bt_mesh_comp *dev_comp;
+static u16_t dev_primary_addr;
+
+void bt_mesh_model_foreach(void (*func)(struct bt_mesh_model *mod,
+ struct bt_mesh_elem *elem,
+ bool vnd, bool primary,
+ void *user_data),
+ void *user_data)
+{
+ int i, j;
+
+ for (i = 0; i < dev_comp->elem_count; i++) {
+ struct bt_mesh_elem *elem = &dev_comp->elem[i];
+
+ for (j = 0; j < elem->model_count; j++) {
+ struct bt_mesh_model *model = &elem->models[j];
+
+ func(model, elem, false, i == 0, user_data);
+ }
+
+ for (j = 0; j < elem->vnd_model_count; j++) {
+ struct bt_mesh_model *model = &elem->vnd_models[j];
+
+ func(model, elem, true, i == 0, user_data);
+ }
+ }
+}
+
+s32_t bt_mesh_model_pub_period_get(struct bt_mesh_model *mod)
+{
+ int period;
+
+ if (!mod->pub) {
+ return 0;
+ }
+
+ switch (mod->pub->period >> 6) {
+ case 0x00:
+ /* 1 step is 100 ms */
+ period = K_MSEC((mod->pub->period & BIT_MASK(6)) * 100);
+ break;
+ case 0x01:
+ /* 1 step is 1 second */
+ period = K_SECONDS(mod->pub->period & BIT_MASK(6));
+ break;
+ case 0x02:
+ /* 1 step is 10 seconds */
+ period = K_SECONDS((mod->pub->period & BIT_MASK(6)) * 10);
+ break;
+ case 0x03:
+ /* 1 step is 10 minutes */
+ period = K_MINUTES((mod->pub->period & BIT_MASK(6)) * 10);
+ break;
+ default:
+ CODE_UNREACHABLE;
+ }
+
+ if (mod->pub->fast_period) {
+ return period >> mod->pub->period_div;
+ } else {
+ return period;
+ }
+}
+
+static s32_t next_period(struct bt_mesh_model *mod)
+{
+ struct bt_mesh_model_pub *pub = mod->pub;
+ u32_t elapsed, period;
+
+ period = bt_mesh_model_pub_period_get(mod);
+ if (!period) {
+ return 0;
+ }
+
+ elapsed = k_uptime_get_32() - pub->period_start;
+
+ BT_DBG("Publishing took %ums", (unsigned) elapsed);
+
+ if (elapsed > period) {
+ BT_WARN("Publication sending took longer than the period");
+ /* Return smallest positive number since 0 means disabled */
+ return K_MSEC(1);
+ }
+
+ return period - elapsed;
+}
+
+static void publish_sent(int err, void *user_data)
+{
+ struct bt_mesh_model *mod = user_data;
+ s32_t delay;
+
+ BT_DBG("err %d", err);
+
+ if (mod->pub->count) {
+ delay = BT_MESH_PUB_TRANSMIT_INT(mod->pub->retransmit);
+ } else {
+ delay = next_period(mod);
+ }
+
+ if (delay) {
+ BT_DBG("Publishing next time in %dms", (int) delay);
+ k_delayed_work_submit(&mod->pub->timer, delay);
+ }
+}
+
+static void publish_start(u16_t duration, int err, void *user_data)
+{
+ struct bt_mesh_model *mod = user_data;
+ struct bt_mesh_model_pub *pub = mod->pub;
+
+ if (err) {
+ BT_ERR("Failed to publish: err %d", err);
+ return;
+ }
+
+ /* Initialize the timestamp for the beginning of a new period */
+ if (pub->count == BT_MESH_PUB_TRANSMIT_COUNT(pub->retransmit)) {
+ pub->period_start = k_uptime_get_32();
+ }
+}
+
+static const struct bt_mesh_send_cb pub_sent_cb = {
+ .start = publish_start,
+ .end = publish_sent,
+};
+
+static int publish_retransmit(struct bt_mesh_model *mod)
+{
+ struct os_mbuf *sdu = NET_BUF_SIMPLE(BT_MESH_TX_SDU_MAX);
+ struct bt_mesh_model_pub *pub = mod->pub;
+ struct bt_mesh_app_key *key;
+ struct bt_mesh_msg_ctx ctx = {
+ .addr = pub->addr,
+ .send_ttl = pub->ttl,
+ };
+ struct bt_mesh_net_tx tx = {
+ .ctx = &ctx,
+ .src = bt_mesh_model_elem(mod)->addr,
+ .xmit = bt_mesh_net_transmit_get(),
+ .friend_cred = pub->cred,
+ };
+ int err;
+
+ key = bt_mesh_app_key_find(pub->key);
+ if (!key) {
+ err = -EADDRNOTAVAIL;
+ goto done;
+ }
+
+ tx.sub = bt_mesh_subnet_get(key->net_idx);
+
+ ctx.net_idx = key->net_idx;
+ ctx.app_idx = key->app_idx;
+
+ net_buf_simple_init(sdu, 0);
+ net_buf_simple_add_mem(sdu, pub->msg->om_data, pub->msg->om_len);
+
+ pub->count--;
+
+ err = bt_mesh_trans_send(&tx, sdu, &pub_sent_cb, mod);
+
+done:
+ os_mbuf_free_chain(sdu);
+ return err;
+}
+
+static void mod_publish(struct ble_npl_event *work)
+{
+ struct bt_mesh_model_pub *pub = ble_npl_event_get_arg(work);
+ s32_t period_ms;
+ int err;
+
+ BT_DBG("");
+
+ period_ms = bt_mesh_model_pub_period_get(pub->mod);
+ BT_DBG("period %u ms", (unsigned) period_ms);
+
+ if (pub->count) {
+ err = publish_retransmit(pub->mod);
+ if (err) {
+ BT_ERR("Failed to retransmit (err %d)", err);
+
+ pub->count = 0;
+
+ /* Continue with normal publication */
+ if (period_ms) {
+ k_delayed_work_submit(&pub->timer, period_ms);
+ }
+ }
+
+ return;
+ }
+
+ if (!period_ms) {
+ return;
+ }
+
+ __ASSERT_NO_MSG(pub->update != NULL);
+
+ err = pub->update(pub->mod);
+ if (err) {
+ BT_ERR("Failed to update publication message");
+ return;
+ }
+
+ err = bt_mesh_model_publish(pub->mod);
+ if (err) {
+ BT_ERR("Publishing failed (err %d)", err);
+ }
+}
+
+struct bt_mesh_elem *bt_mesh_model_elem(struct bt_mesh_model *mod)
+{
+ return &dev_comp->elem[mod->elem_idx];
+}
+
+struct bt_mesh_model *bt_mesh_model_get(bool vnd, u8_t elem_idx, u8_t mod_idx)
+{
+ struct bt_mesh_elem *elem;
+
+ if (elem_idx >= dev_comp->elem_count) {
+ BT_ERR("Invalid element index %u", elem_idx);
+ return NULL;
+ }
+
+ elem = &dev_comp->elem[elem_idx];
+
+ if (vnd) {
+ if (mod_idx >= elem->vnd_model_count) {
+ BT_ERR("Invalid vendor model index %u", mod_idx);
+ return NULL;
+ }
+
+ return &elem->vnd_models[mod_idx];
+ } else {
+ if (mod_idx >= elem->model_count) {
+ BT_ERR("Invalid SIG model index %u", mod_idx);
+ return NULL;
+ }
+
+ return &elem->models[mod_idx];
+ }
+}
+
+static void mod_init(struct bt_mesh_model *mod, struct bt_mesh_elem *elem,
+ bool vnd, bool primary, void *user_data)
+{
+ int i;
+
+ if (mod->pub) {
+ mod->pub->mod = mod;
+ k_delayed_work_init(&mod->pub->timer, mod_publish);
+ k_delayed_work_add_arg(&mod->pub->timer, mod->pub);
+ }
+
+ for (i = 0; i < ARRAY_SIZE(mod->keys); i++) {
+ mod->keys[i] = BT_MESH_KEY_UNUSED;
+ }
+
+ mod->elem_idx = elem - dev_comp->elem;
+ if (vnd) {
+ mod->mod_idx = mod - elem->vnd_models;
+ } else {
+ mod->mod_idx = mod - elem->models;
+ }
+
+ if (mod->cb && mod->cb->init) {
+ mod->cb->init(mod);
+ }
+}
+
+int bt_mesh_comp_register(const struct bt_mesh_comp *comp)
+{
+ /* There must be at least one element */
+ if (!comp->elem_count) {
+ return -EINVAL;
+ }
+
+ dev_comp = comp;
+
+ bt_mesh_model_foreach(mod_init, NULL);
+
+ return 0;
+}
+
+void bt_mesh_comp_provision(u16_t addr)
+{
+ int i;
+
+ dev_primary_addr = addr;
+
+ BT_DBG("addr 0x%04x elem_count %zu", addr, dev_comp->elem_count);
+
+ for (i = 0; i < dev_comp->elem_count; i++) {
+ struct bt_mesh_elem *elem = &dev_comp->elem[i];
+
+ elem->addr = addr++;
+
+ BT_DBG("addr 0x%04x mod_count %u vnd_mod_count %u",
+ elem->addr, elem->model_count, elem->vnd_model_count);
+ }
+}
+
+void bt_mesh_comp_unprovision(void)
+{
+ BT_DBG("");
+
+ dev_primary_addr = BT_MESH_ADDR_UNASSIGNED;
+
+ bt_mesh_model_foreach(mod_init, NULL);
+}
+
+u16_t bt_mesh_primary_addr(void)
+{
+ return dev_primary_addr;
+}
+
+static u16_t *model_group_get(struct bt_mesh_model *mod, u16_t addr)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(mod->groups); i++) {
+ if (mod->groups[i] == addr) {
+ return &mod->groups[i];
+ }
+ }
+
+ return NULL;
+}
+
+struct find_group_visitor_ctx {
+ u16_t *entry;
+ struct bt_mesh_model *mod;
+ u16_t addr;
+};
+
+static enum bt_mesh_walk find_group_mod_visitor(struct bt_mesh_model *mod,
+ u32_t depth, void *user_data)
+{
+ struct find_group_visitor_ctx *ctx = user_data;
+
+ if (mod->elem_idx != ctx->mod->elem_idx) {
+ return BT_MESH_WALK_CONTINUE;
+ }
+
+ ctx->entry = model_group_get(mod, ctx->addr);
+ if (ctx->entry) {
+ ctx->mod = mod;
+ return BT_MESH_WALK_STOP;
+ }
+
+ return BT_MESH_WALK_CONTINUE;
+}
+
+u16_t *bt_mesh_model_find_group(struct bt_mesh_model **mod, u16_t addr)
+{
+ struct find_group_visitor_ctx ctx = {
+ .mod = *mod,
+ .entry = NULL,
+ .addr = addr,
+ };
+
+ bt_mesh_model_tree_walk(bt_mesh_model_root(*mod),
+ find_group_mod_visitor, &ctx);
+
+ *mod = ctx.mod;
+ return ctx.entry;
+}
+
+static struct bt_mesh_model *bt_mesh_elem_find_group(struct bt_mesh_elem *elem,
+ u16_t group_addr)
+{
+ struct bt_mesh_model *model;
+ u16_t *match;
+ int i;
+
+ for (i = 0; i < elem->model_count; i++) {
+ model = &elem->models[i];
+
+ match = model_group_get(model, group_addr);
+ if (match) {
+ return model;
+ }
+ }
+
+ for (i = 0; i < elem->vnd_model_count; i++) {
+ model = &elem->vnd_models[i];
+
+ match = model_group_get(model, group_addr);
+ if (match) {
+ return model;
+ }
+ }
+
+ return NULL;
+}
+
+struct bt_mesh_elem *bt_mesh_elem_find(u16_t addr)
+{
+ u16_t index;
+
+ if (BT_MESH_ADDR_IS_UNICAST(addr)) {
+ index = (addr - dev_comp->elem[0].addr);
+ if (index < dev_comp->elem_count) {
+ return &dev_comp->elem[index];
+ } else {
+ return NULL;
+ }
+ }
+
+ for (index = 0; index < dev_comp->elem_count; index++) {
+ struct bt_mesh_elem *elem = &dev_comp->elem[index];
+
+ if (bt_mesh_elem_find_group(elem, addr)) {
+ return elem;
+ }
+ }
+
+ return NULL;
+}
+
+u8_t bt_mesh_elem_count(void)
+{
+ return dev_comp->elem_count;
+}
+
+static bool model_has_key(struct bt_mesh_model *mod, u16_t key)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(mod->keys); i++) {
+ if (mod->keys[i] == key ||
+ (mod->keys[i] == BT_MESH_KEY_DEV_ANY &&
+ BT_MESH_IS_DEV_KEY(key))) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static bool model_has_dst(struct bt_mesh_model *mod, u16_t dst)
+{
+ if (BT_MESH_ADDR_IS_UNICAST(dst)) {
+ return (dev_comp->elem[mod->elem_idx].addr == dst);
+ } else if (BT_MESH_ADDR_IS_GROUP(dst) || BT_MESH_ADDR_IS_VIRTUAL(dst)) {
+ return bt_mesh_model_find_group(&mod, dst);
+ }
+
+ return (mod->elem_idx == 0 && bt_mesh_fixed_group_match(dst));
+}
+
+static const struct bt_mesh_model_op *find_op(struct bt_mesh_model *models,
+ u8_t model_count, u32_t opcode,
+ struct bt_mesh_model **model)
+{
+ u8_t i;
+
+ for (i = 0; i < model_count; i++) {
+ const struct bt_mesh_model_op *op;
+
+ *model = &models[i];
+
+ for (op = (*model)->op; op->func; op++) {
+ if (op->opcode == opcode) {
+ return op;
+ }
+ }
+ }
+
+ *model = NULL;
+ return NULL;
+}
+
+static int get_opcode(struct os_mbuf *buf, u32_t *opcode)
+{
+ switch (buf->om_data[0] >> 6) {
+ case 0x00:
+ case 0x01:
+ if (buf->om_data[0] == 0x7f) {
+ BT_ERR("Ignoring RFU OpCode");
+ return -EINVAL;
+ }
+
+ *opcode = net_buf_simple_pull_u8(buf);
+ return 0;
+ case 0x02:
+ if (buf->om_len < 2) {
+ BT_ERR("Too short payload for 2-octet OpCode");
+ return -EINVAL;
+ }
+
+ *opcode = net_buf_simple_pull_be16(buf);
+ return 0;
+ case 0x03:
+ if (buf->om_len < 3) {
+ BT_ERR("Too short payload for 3-octet OpCode");
+ return -EINVAL;
+ }
+
+ *opcode = net_buf_simple_pull_u8(buf) << 16;
+ *opcode |= net_buf_simple_pull_le16(buf);
+ return 0;
+ }
+
+ CODE_UNREACHABLE;
+}
+
+bool bt_mesh_fixed_group_match(u16_t addr)
+{
+ /* Check for fixed group addresses */
+ switch (addr) {
+ case BT_MESH_ADDR_ALL_NODES:
+ return true;
+ case BT_MESH_ADDR_PROXIES:
+ return (bt_mesh_gatt_proxy_get() == BT_MESH_GATT_PROXY_ENABLED);
+ case BT_MESH_ADDR_FRIENDS:
+ return (bt_mesh_friend_get() == BT_MESH_FRIEND_ENABLED);
+ case BT_MESH_ADDR_RELAYS:
+ return (bt_mesh_relay_get() == BT_MESH_RELAY_ENABLED);
+ default:
+ return false;
+ }
+}
+
+void bt_mesh_model_recv(struct bt_mesh_net_rx *rx, struct os_mbuf *buf)
+{
+ struct bt_mesh_model *models, *model;
+ const struct bt_mesh_model_op *op;
+ u32_t opcode;
+ u8_t count;
+ int i;
+
+ BT_DBG("app_idx 0x%04x src 0x%04x dst 0x%04x", rx->ctx.app_idx,
+ rx->ctx.addr, rx->ctx.recv_dst);
+ BT_DBG("len %u: %s", buf->om_len, bt_hex(buf->om_data, buf->om_len));
+
+ if (get_opcode(buf, &opcode) < 0) {
+ BT_WARN("Unable to decode OpCode");
+ return;
+ }
+
+ BT_DBG("OpCode 0x%08x", (unsigned) opcode);
+
+ for (i = 0; i < dev_comp->elem_count; i++) {
+ struct bt_mesh_elem *elem = &dev_comp->elem[i];
+ struct net_buf_simple_state state;
+
+ /* SIG models cannot contain 3-byte (vendor) OpCodes, and
+ * vendor models cannot contain SIG (1- or 2-byte) OpCodes, so
+ * we only need to do the lookup in one of the model lists.
+ */
+ if (BT_MESH_MODEL_OP_LEN(opcode) < 3) {
+ models = elem->models;
+ count = elem->model_count;
+ } else {
+ models = elem->vnd_models;
+ count = elem->vnd_model_count;
+ }
+
+ op = find_op(models, count, opcode, &model);
+ if (!op) {
+ BT_DBG("No OpCode 0x%08x for elem %d", opcode, i);
+ continue;
+ }
+
+ if (!model_has_key(model, rx->ctx.app_idx)) {
+ continue;
+ }
+
+ if (!model_has_dst(model, rx->ctx.recv_dst)) {
+ continue;
+ }
+
+ if (buf->om_len < op->min_len) {
+ BT_ERR("Too short message for OpCode 0x%08x", opcode);
+ continue;
+ }
+
+ /* The callback will likely parse the buffer, so
+ * store the parsing state in case multiple models
+ * receive the message.
+ */
+ net_buf_simple_save(buf, &state);
+ op->func(model, &rx->ctx, buf);
+ net_buf_simple_restore(buf, &state);
+ }
+}
+
+void bt_mesh_model_msg_init(struct os_mbuf *msg, u32_t opcode)
+{
+ net_buf_simple_init(msg, 0);
+
+ switch (BT_MESH_MODEL_OP_LEN(opcode)) {
+ case 1:
+ net_buf_simple_add_u8(msg, opcode);
+ break;
+ case 2:
+ net_buf_simple_add_be16(msg, opcode);
+ break;
+ case 3:
+ net_buf_simple_add_u8(msg, ((opcode >> 16) & 0xff));
+ net_buf_simple_add_le16(msg, opcode & 0xffff);
+ break;
+ default:
+ BT_WARN("Unknown opcode format");
+ break;
+ }
+}
+
+static int model_send(struct bt_mesh_model *model,
+ struct bt_mesh_net_tx *tx, bool implicit_bind,
+ struct os_mbuf *msg,
+ const struct bt_mesh_send_cb *cb, void *cb_data)
+{
+ BT_DBG("net_idx 0x%04x app_idx 0x%04x dst 0x%04x", tx->ctx->net_idx,
+ tx->ctx->app_idx, tx->ctx->addr);
+ BT_DBG("len %u: %s", msg->om_len, bt_hex(msg->om_data, msg->om_len));
+
+ if (!bt_mesh_is_provisioned()) {
+ BT_ERR("Local node is not yet provisioned");
+ return -EAGAIN;
+ }
+
+ if (net_buf_simple_tailroom(msg) < 4) {
+ BT_ERR("Not enough tailroom for TransMIC");
+ return -EINVAL;
+ }
+
+ if (msg->om_len > BT_MESH_TX_SDU_MAX - 4) {
+ BT_ERR("Too big message");
+ return -EMSGSIZE;
+ }
+
+ if (!implicit_bind && !model_has_key(model, tx->ctx->app_idx)) {
+ BT_ERR("Model not bound to AppKey 0x%04x", tx->ctx->app_idx);
+ return -EINVAL;
+ }
+
+ return bt_mesh_trans_send(tx, msg, cb, cb_data);
+}
+
+int bt_mesh_model_send(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *msg,
+ const struct bt_mesh_send_cb *cb, void *cb_data)
+{
+ struct bt_mesh_net_tx tx = {
+ .sub = bt_mesh_subnet_get(ctx->net_idx),
+ .ctx = ctx,
+ .src = bt_mesh_model_elem(model)->addr,
+ .xmit = bt_mesh_net_transmit_get(),
+ .friend_cred = 0,
+ };
+
+ return model_send(model, &tx, false, msg, cb, cb_data);
+}
+
+int bt_mesh_model_publish(struct bt_mesh_model *model)
+{
+ struct os_mbuf *sdu = NET_BUF_SIMPLE(BT_MESH_TX_SDU_MAX);
+ struct bt_mesh_model_pub *pub = model->pub;
+ struct bt_mesh_app_key *key;
+ struct bt_mesh_msg_ctx ctx = {
+ };
+ struct bt_mesh_net_tx tx = {
+ .ctx = &ctx,
+ .src = bt_mesh_model_elem(model)->addr,
+ .xmit = bt_mesh_net_transmit_get(),
+ };
+ int err;
+
+ BT_DBG("");
+
+ if (!pub) {
+ err = -ENOTSUP;
+ goto done;
+ }
+
+ if (pub->addr == BT_MESH_ADDR_UNASSIGNED) {
+ err = -EADDRNOTAVAIL;
+ goto done;
+ }
+
+ key = bt_mesh_app_key_find(pub->key);
+ if (!key) {
+ err = -EADDRNOTAVAIL;
+ goto done;
+ }
+
+ if (pub->msg->om_len + 4 > BT_MESH_TX_SDU_MAX) {
+ BT_ERR("Message does not fit maximum SDU size");
+ err = -EMSGSIZE;
+ goto done;
+ }
+
+ if (pub->count) {
+ BT_WARN("Clearing publish retransmit timer");
+ k_delayed_work_cancel(&pub->timer);
+ }
+
+ net_buf_simple_init(sdu, 0);
+ net_buf_simple_add_mem(sdu, pub->msg->om_data, pub->msg->om_len);
+
+ ctx.addr = pub->addr;
+ ctx.send_ttl = pub->ttl;
+ ctx.net_idx = key->net_idx;
+ ctx.app_idx = key->app_idx;
+
+ tx.friend_cred = pub->cred;
+ tx.sub = bt_mesh_subnet_get(ctx.net_idx),
+
+ pub->count = BT_MESH_PUB_TRANSMIT_COUNT(pub->retransmit);
+
+ BT_DBG("Publish Retransmit Count %u Interval %ums", pub->count,
+ BT_MESH_PUB_TRANSMIT_INT(pub->retransmit));
+
+ err = model_send(model, &tx, true, sdu, &pub_sent_cb, model);
+ if (err) {
+ /* Don't try retransmissions for this publish attempt */
+ pub->count = 0;
+ /* Make sure the publish timer gets reset */
+ publish_sent(err, model);
+ }
+
+done:
+ os_mbuf_free_chain(sdu);
+ return err;
+}
+
+struct bt_mesh_model *bt_mesh_model_find_vnd(const struct bt_mesh_elem *elem,
+ u16_t company, u16_t id)
+{
+ u8_t i;
+
+ for (i = 0; i < elem->vnd_model_count; i++) {
+ if (elem->vnd_models[i].vnd.company == company &&
+ elem->vnd_models[i].vnd.id == id) {
+ return &elem->vnd_models[i];
+ }
+ }
+
+ return NULL;
+}
+
+struct bt_mesh_model *bt_mesh_model_find(const struct bt_mesh_elem *elem,
+ u16_t id)
+{
+ u8_t i;
+
+ for (i = 0; i < elem->model_count; i++) {
+ if (elem->models[i].id == id) {
+ return &elem->models[i];
+ }
+ }
+
+ return NULL;
+}
+
+const struct bt_mesh_comp *bt_mesh_comp_get(void)
+{
+ return dev_comp;
+}
+
+struct bt_mesh_model *bt_mesh_model_root(struct bt_mesh_model *mod)
+{
+#ifdef CONFIG_BT_MESH_MODEL_EXTENSIONS
+ while (mod->next) {
+ mod = mod->next;
+ }
+#endif
+ return mod;
+}
+
+void bt_mesh_model_tree_walk(struct bt_mesh_model *root,
+ enum bt_mesh_walk (*cb)(struct bt_mesh_model *mod,
+ u32_t depth,
+ void *user_data),
+ void *user_data)
+{
+ struct bt_mesh_model *m = root;
+ u32_t depth = 0;
+
+ do {
+ if (cb(m, depth, user_data) == BT_MESH_WALK_STOP) {
+ return;
+ }
+#if MYNEWT_VAL(BLE_MESH_MODEL_EXTENSIONS)
+ if (m->extends) {
+ m = m->extends;
+ depth++;
+ } else if (m->flags & BT_MESH_MOD_NEXT_IS_PARENT) {
+ m = m->next->next;
+ depth--;
+ } else {
+ m = m->next;
+ }
+#endif
+ } while (m && m != root);
+}
+
+#if MYNEWT_VAL(BLE_MESH_MODEL_EXTENSIONS)
+int bt_mesh_model_extend(struct bt_mesh_model *mod,
+ struct bt_mesh_model *base_mod)
+{
+ /* Form a cyclical LCRS tree:
+ * The extends-pointer points to the first child, and the next-pointer
+ * points to the next sibling. The last sibling is marked by the
+ * BT_MESH_MOD_NEXT_IS_PARENT flag, and its next-pointer points back to
+ * the parent. This way, the whole tree is accessible from any node.
+ *
+ * We add children (extend them) by inserting them as the first child.
+ */
+ if (base_mod->next) {
+ return -EALREADY;
+ }
+
+ if (mod->extends) {
+ base_mod->next = mod->extends;
+ } else {
+ base_mod->next = mod;
+ base_mod->flags |= BT_MESH_MOD_NEXT_IS_PARENT;
+ }
+
+ mod->extends = base_mod;
+ return 0;
+}
+#endif
diff --git a/src/libs/mynewt-nimble/nimble/host/mesh/src/access.h b/src/libs/mynewt-nimble/nimble/host/mesh/src/access.h
new file mode 100644
index 00000000..48514983
--- /dev/null
+++ b/src/libs/mynewt-nimble/nimble/host/mesh/src/access.h
@@ -0,0 +1,67 @@
+/* Bluetooth Mesh */
+
+/*
+ * Copyright (c) 2017 Intel Corporation
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#ifndef __ACCESS_H__
+#define __ACCESS_H__
+
+#include "mesh/mesh.h"
+
+/* bt_mesh_model.flags */
+enum {
+ BT_MESH_MOD_BIND_PENDING = BIT(0),
+ BT_MESH_MOD_SUB_PENDING = BIT(1),
+ BT_MESH_MOD_PUB_PENDING = BIT(2),
+ BT_MESH_MOD_DATA_PRESENT = BIT(3),
+ BT_MESH_MOD_NEXT_IS_PARENT = BIT(4),
+};
+
+/* Tree walk return codes */
+enum bt_mesh_walk {
+ BT_MESH_WALK_STOP,
+ BT_MESH_WALK_CONTINUE,
+};
+
+void bt_mesh_elem_register(struct bt_mesh_elem *elem, u8_t count);
+
+u8_t bt_mesh_elem_count(void);
+
+/* Find local element based on unicast or group address */
+struct bt_mesh_elem *bt_mesh_elem_find(u16_t addr);
+
+struct bt_mesh_model *bt_mesh_model_root(struct bt_mesh_model *mod);
+void bt_mesh_model_tree_walk(struct bt_mesh_model *root,
+ enum bt_mesh_walk (*cb)(struct bt_mesh_model *mod,
+ u32_t depth,
+ void *user_data),
+ void *user_data);
+
+u16_t *bt_mesh_model_find_group(struct bt_mesh_model **mod, u16_t addr);
+
+bool bt_mesh_fixed_group_match(u16_t addr);
+
+void bt_mesh_model_foreach(void (*func)(struct bt_mesh_model *mod,
+ struct bt_mesh_elem *elem,
+ bool vnd, bool primary,
+ void *user_data),
+ void *user_data);
+
+s32_t bt_mesh_model_pub_period_get(struct bt_mesh_model *mod);
+
+void bt_mesh_comp_provision(u16_t addr);
+void bt_mesh_comp_unprovision(void);
+
+u16_t bt_mesh_primary_addr(void);
+
+const struct bt_mesh_comp *bt_mesh_comp_get(void);
+
+struct bt_mesh_model *bt_mesh_model_get(bool vnd, u8_t elem_idx, u8_t mod_idx);
+
+void bt_mesh_model_recv(struct bt_mesh_net_rx *rx, struct os_mbuf *buf);
+
+int bt_mesh_comp_register(const struct bt_mesh_comp *comp);
+#endif
diff --git a/src/libs/mynewt-nimble/nimble/host/mesh/src/adv.c b/src/libs/mynewt-nimble/nimble/host/mesh/src/adv.c
new file mode 100644
index 00000000..4bd51cc1
--- /dev/null
+++ b/src/libs/mynewt-nimble/nimble/host/mesh/src/adv.c
@@ -0,0 +1,439 @@
+/* Bluetooth Mesh */
+
+/*
+ * Copyright (c) 2018 Nordic Semiconductor ASA
+ * Copyright (c) 2017 Intel Corporation
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include "syscfg/syscfg.h"
+#define MESH_LOG_MODULE BLE_MESH_ADV_LOG
+
+#include "mesh/mesh.h"
+#include "host/ble_hs_adv.h"
+#include "host/ble_gap.h"
+#include "nimble/hci_common.h"
+#include "mesh/porting.h"
+
+#include "adv.h"
+#include "net.h"
+#include "foundation.h"
+#include "beacon.h"
+#include "prov.h"
+#include "proxy.h"
+
+/* Convert from ms to 0.625ms units */
+#define ADV_SCAN_UNIT(_ms) ((_ms) * 8 / 5)
+
+/* Window and Interval are equal for continuous scanning */
+#define MESH_SCAN_INTERVAL_MS 30
+#define MESH_SCAN_WINDOW_MS 30
+#define MESH_SCAN_INTERVAL ADV_SCAN_UNIT(MESH_SCAN_INTERVAL_MS)
+#define MESH_SCAN_WINDOW ADV_SCAN_UNIT(MESH_SCAN_WINDOW_MS)
+
+/* Pre-5.0 controllers enforce a minimum interval of 100ms
+ * whereas 5.0+ controllers can go down to 20ms.
+ */
+#define ADV_INT_DEFAULT_MS 100
+#define ADV_INT_FAST_MS 20
+
+static s32_t adv_int_min = ADV_INT_DEFAULT_MS;
+
+/* TinyCrypt PRNG consumes a lot of stack space, so we need to have
+ * an increased call stack whenever it's used.
+ */
+#if MYNEWT
+#define ADV_STACK_SIZE 768
+OS_TASK_STACK_DEFINE(g_blemesh_stack, ADV_STACK_SIZE);
+struct os_task adv_task;
+#endif
+
+static struct ble_npl_eventq adv_queue;
+extern u8_t g_mesh_addr_type;
+static int adv_initialized = false;
+
+static os_membuf_t adv_buf_mem[OS_MEMPOOL_SIZE(
+ MYNEWT_VAL(BLE_MESH_ADV_BUF_COUNT),
+ BT_MESH_ADV_DATA_SIZE + BT_MESH_MBUF_HEADER_SIZE)];
+
+struct os_mbuf_pool adv_os_mbuf_pool;
+static struct os_mempool adv_buf_mempool;
+
+static const u8_t adv_type[] = {
+ [BT_MESH_ADV_PROV] = BLE_HS_ADV_TYPE_MESH_PROV,
+ [BT_MESH_ADV_DATA] = BLE_HS_ADV_TYPE_MESH_MESSAGE,
+ [BT_MESH_ADV_BEACON] = BLE_HS_ADV_TYPE_MESH_BEACON,
+ [BT_MESH_ADV_URI] = BLE_HS_ADV_TYPE_URI,
+};
+
+
+static struct bt_mesh_adv adv_pool[CONFIG_BT_MESH_ADV_BUF_COUNT];
+
+static struct bt_mesh_adv *adv_alloc(int id)
+{
+ return &adv_pool[id];
+}
+
+static inline void adv_send_start(u16_t duration, int err,
+ const struct bt_mesh_send_cb *cb,
+ void *cb_data)
+{
+ if (cb && cb->start) {
+ cb->start(duration, err, cb_data);
+ }
+}
+
+static inline void adv_send_end(int err, const struct bt_mesh_send_cb *cb,
+ void *cb_data)
+{
+ if (cb && cb->end) {
+ cb->end(err, cb_data);
+ }
+}
+
+static inline void adv_send(struct os_mbuf *buf)
+{
+ const struct bt_mesh_send_cb *cb = BT_MESH_ADV(buf)->cb;
+ void *cb_data = BT_MESH_ADV(buf)->cb_data;
+ struct ble_gap_adv_params param = { 0 };
+ u16_t duration, adv_int;
+ struct bt_data ad;
+ int err;
+
+ adv_int = max(adv_int_min,
+ BT_MESH_TRANSMIT_INT(BT_MESH_ADV(buf)->xmit));
+#if MYNEWT_VAL(BLE_CONTROLLER)
+ duration = ((BT_MESH_TRANSMIT_COUNT(BT_MESH_ADV(buf)->xmit) + 1) *
+ (adv_int + 10));
+#else
+ duration = (MESH_SCAN_WINDOW_MS +
+ ((BT_MESH_TRANSMIT_COUNT(BT_MESH_ADV(buf)->xmit) + 1) *
+ (adv_int + 10)));
+#endif
+
+ BT_DBG("type %u om_len %u: %s", BT_MESH_ADV(buf)->type,
+ buf->om_len, bt_hex(buf->om_data, buf->om_len));
+ BT_DBG("count %u interval %ums duration %ums",
+ BT_MESH_TRANSMIT_COUNT(BT_MESH_ADV(buf)->xmit) + 1, adv_int,
+ duration);
+
+ ad.type = adv_type[BT_MESH_ADV(buf)->type];
+ ad.data_len = buf->om_len;
+ ad.data = buf->om_data;
+
+ param.itvl_min = ADV_SCAN_UNIT(adv_int);
+ param.itvl_max = param.itvl_min;
+ param.conn_mode = BLE_GAP_CONN_MODE_NON;
+
+ err = bt_le_adv_start(&param, &ad, 1, NULL, 0);
+ net_buf_unref(buf);
+ adv_send_start(duration, err, cb, cb_data);
+ if (err) {
+ BT_ERR("Advertising failed: err %d", err);
+ return;
+ }
+
+ BT_DBG("Advertising started. Sleeping %u ms", duration);
+
+ k_sleep(K_MSEC(duration));
+
+ err = bt_le_adv_stop(false);
+ adv_send_end(err, cb, cb_data);
+ if (err) {
+ BT_ERR("Stopping advertising failed: err %d", err);
+ return;
+ }
+
+ BT_DBG("Advertising stopped");
+}
+
+void
+mesh_adv_thread(void *args)
+{
+ static struct ble_npl_event *ev;
+ struct os_mbuf *buf;
+#if (MYNEWT_VAL(BLE_MESH_PROXY))
+ s32_t timeout;
+#endif
+
+ BT_DBG("started");
+
+ while (1) {
+#if (MYNEWT_VAL(BLE_MESH_PROXY))
+ ev = ble_npl_eventq_get(&adv_queue, 0);
+ while (!ev) {
+ timeout = bt_mesh_proxy_adv_start();
+ BT_DBG("Proxy Advertising up to %d ms", (int) timeout);
+
+ // FIXME: should we redefine K_SECONDS macro instead in glue?
+ if (timeout != K_FOREVER) {
+ timeout = ble_npl_time_ms_to_ticks32(timeout);
+ }
+
+ ev = ble_npl_eventq_get(&adv_queue, timeout);
+ bt_mesh_proxy_adv_stop();
+ }
+#else
+ ev = ble_npl_eventq_get(&adv_queue, BLE_NPL_TIME_FOREVER);
+#endif
+
+ if (!ev || !ble_npl_event_get_arg(ev)) {
+ continue;
+ }
+
+ buf = ble_npl_event_get_arg(ev);
+
+ /* busy == 0 means this was canceled */
+ if (BT_MESH_ADV(buf)->busy) {
+ BT_MESH_ADV(buf)->busy = 0;
+ adv_send(buf);
+ } else {
+ net_buf_unref(buf);
+ }
+
+ /* os_sched(NULL); */
+ }
+}
+
+void bt_mesh_adv_update(void)
+{
+ static struct ble_npl_event ev = { };
+
+ BT_DBG("");
+
+ ble_npl_eventq_put(&adv_queue, &ev);
+}
+
+struct os_mbuf *bt_mesh_adv_create_from_pool(struct os_mbuf_pool *pool,
+ bt_mesh_adv_alloc_t get_id,
+ enum bt_mesh_adv_type type,
+ u8_t xmit, s32_t timeout)
+{
+ struct bt_mesh_adv *adv;
+ struct os_mbuf *buf;
+
+ if (atomic_test_bit(bt_mesh.flags, BT_MESH_SUSPENDED)) {
+ BT_WARN("Refusing to allocate buffer while suspended");
+ return NULL;
+ }
+
+ buf = os_mbuf_get_pkthdr(pool, BT_MESH_ADV_USER_DATA_SIZE);
+ if (!buf) {
+ return NULL;
+ }
+
+ adv = get_id(net_buf_id(buf));
+ BT_MESH_ADV(buf) = adv;
+
+ memset(adv, 0, sizeof(*adv));
+
+ adv->type = type;
+ adv->xmit = xmit;
+
+ adv->ref_cnt = 1;
+ ble_npl_event_set_arg(&adv->ev, buf);
+
+ return buf;
+}
+
+struct os_mbuf *bt_mesh_adv_create(enum bt_mesh_adv_type type, u8_t xmit,
+ s32_t timeout)
+{
+ return bt_mesh_adv_create_from_pool(&adv_os_mbuf_pool, adv_alloc, type,
+ xmit, timeout);
+}
+
+void bt_mesh_adv_send(struct os_mbuf *buf, const struct bt_mesh_send_cb *cb,
+ void *cb_data)
+{
+ BT_DBG("buf %p, type 0x%02x len %u: %s", buf, BT_MESH_ADV(buf)->type, buf->om_len,
+ bt_hex(buf->om_data, buf->om_len));
+
+ BT_MESH_ADV(buf)->cb = cb;
+ BT_MESH_ADV(buf)->cb_data = cb_data;
+ BT_MESH_ADV(buf)->busy = 1;
+
+ net_buf_put(&adv_queue, net_buf_ref(buf));
+}
+
+static void bt_mesh_scan_cb(const bt_addr_le_t *addr, s8_t rssi,
+ u8_t adv_type, struct os_mbuf *buf)
+{
+ if (adv_type != BLE_HCI_ADV_TYPE_ADV_NONCONN_IND) {
+ return;
+ }
+
+#if BT_MESH_EXTENDED_DEBUG
+ BT_DBG("len %u: %s", buf->om_len, bt_hex(buf->om_data, buf->om_len));
+#endif
+
+ while (buf->om_len > 1) {
+ struct net_buf_simple_state state;
+ u8_t len, type;
+
+ len = net_buf_simple_pull_u8(buf);
+ /* Check for early termination */
+ if (len == 0) {
+ return;
+ }
+
+ if (len > buf->om_len) {
+ BT_WARN("AD malformed");
+ return;
+ }
+
+ net_buf_simple_save(buf, &state);
+
+ type = net_buf_simple_pull_u8(buf);
+
+ switch (type) {
+ case BLE_HS_ADV_TYPE_MESH_MESSAGE:
+ bt_mesh_net_recv(buf, rssi, BT_MESH_NET_IF_ADV);
+ break;
+#if MYNEWT_VAL(BLE_MESH_PB_ADV)
+ case BLE_HS_ADV_TYPE_MESH_PROV:
+ bt_mesh_pb_adv_recv(buf);
+ break;
+#endif
+ case BLE_HS_ADV_TYPE_MESH_BEACON:
+ bt_mesh_beacon_recv(buf);
+ break;
+ default:
+ break;
+ }
+
+ net_buf_simple_restore(buf, &state);
+ net_buf_simple_pull(buf, len);
+ }
+}
+
+void bt_mesh_adv_init(void)
+{
+ int rc;
+
+ /* Advertising should only be initialized once. Calling
+ * os_task init the second time will result in an assert. */
+ if (adv_initialized) {
+ return;
+ }
+
+ rc = os_mempool_init(&adv_buf_mempool, MYNEWT_VAL(BLE_MESH_ADV_BUF_COUNT),
+ BT_MESH_ADV_DATA_SIZE + BT_MESH_MBUF_HEADER_SIZE,
+ adv_buf_mem, "adv_buf_pool");
+ assert(rc == 0);
+
+ rc = os_mbuf_pool_init(&adv_os_mbuf_pool, &adv_buf_mempool,
+ BT_MESH_ADV_DATA_SIZE + BT_MESH_MBUF_HEADER_SIZE,
+ MYNEWT_VAL(BLE_MESH_ADV_BUF_COUNT));
+ assert(rc == 0);
+
+ ble_npl_eventq_init(&adv_queue);
+
+#if MYNEWT
+ os_task_init(&adv_task, "mesh_adv", mesh_adv_thread, NULL,
+ MYNEWT_VAL(BLE_MESH_ADV_TASK_PRIO), OS_WAIT_FOREVER,
+ g_blemesh_stack, ADV_STACK_SIZE);
+#endif
+
+ /* For BT5 controllers we can have fast advertising interval */
+ if (ble_hs_hci_get_hci_version() >= BLE_HCI_VER_BCS_5_0) {
+ adv_int_min = ADV_INT_FAST_MS;
+ }
+
+ adv_initialized = true;
+}
+
+int
+ble_adv_gap_mesh_cb(struct ble_gap_event *event, void *arg)
+{
+#if MYNEWT_VAL(BLE_EXT_ADV)
+ struct ble_gap_ext_disc_desc *ext_desc;
+#endif
+ struct ble_gap_disc_desc *desc;
+ struct os_mbuf *buf = NULL;
+
+#if BT_MESH_EXTENDED_DEBUG
+ BT_DBG("event->type %d", event->type);
+#endif
+
+ switch (event->type) {
+#if MYNEWT_VAL(BLE_EXT_ADV)
+ case BLE_GAP_EVENT_EXT_DISC:
+ ext_desc = &event->ext_disc;
+ buf = os_mbuf_get_pkthdr(&adv_os_mbuf_pool, 0);
+ if (!buf || os_mbuf_append(buf, ext_desc->data, ext_desc->length_data)) {
+ BT_ERR("Could not append data");
+ goto done;
+ }
+ bt_mesh_scan_cb(&ext_desc->addr, ext_desc->rssi,
+ ext_desc->legacy_event_type, buf);
+ break;
+#endif
+ case BLE_GAP_EVENT_DISC:
+ desc = &event->disc;
+ buf = os_mbuf_get_pkthdr(&adv_os_mbuf_pool, 0);
+ if (!buf || os_mbuf_append(buf, desc->data, desc->length_data)) {
+ BT_ERR("Could not append data");
+ goto done;
+ }
+
+ bt_mesh_scan_cb(&desc->addr, desc->rssi, desc->event_type, buf);
+ break;
+ default:
+ break;
+ }
+
+done:
+ if (buf) {
+ os_mbuf_free_chain(buf);
+ }
+
+ return 0;
+}
+
+int bt_mesh_scan_enable(void)
+{
+ int err;
+
+#if MYNEWT_VAL(BLE_EXT_ADV)
+ struct ble_gap_ext_disc_params uncoded_params =
+ { .itvl = MESH_SCAN_INTERVAL, .window = MESH_SCAN_WINDOW,
+ .passive = 1 };
+
+ BT_DBG("");
+
+ err = ble_gap_ext_disc(g_mesh_addr_type, 0, 0, 0, 0, 0,
+ &uncoded_params, NULL, NULL, NULL);
+#else
+ struct ble_gap_disc_params scan_param =
+ { .passive = 1, .filter_duplicates = 0, .itvl =
+ MESH_SCAN_INTERVAL, .window = MESH_SCAN_WINDOW };
+
+ BT_DBG("");
+
+ err = ble_gap_disc(g_mesh_addr_type, BLE_HS_FOREVER, &scan_param,
+ NULL, NULL);
+#endif
+ if (err && err != BLE_HS_EALREADY) {
+ BT_ERR("starting scan failed (err %d)", err);
+ return err;
+ }
+
+ return 0;
+}
+
+int bt_mesh_scan_disable(void)
+{
+ int err;
+
+ BT_DBG("");
+
+ err = ble_gap_disc_cancel();
+ if (err && err != BLE_HS_EALREADY) {
+ BT_ERR("stopping scan failed (err %d)", err);
+ return err;
+ }
+
+ return 0;
+}
diff --git a/src/libs/mynewt-nimble/nimble/host/mesh/src/adv.h b/src/libs/mynewt-nimble/nimble/host/mesh/src/adv.h
new file mode 100644
index 00000000..4d0f7d8b
--- /dev/null
+++ b/src/libs/mynewt-nimble/nimble/host/mesh/src/adv.h
@@ -0,0 +1,79 @@
+/* Bluetooth Mesh */
+
+/*
+ * Copyright (c) 2017 Intel Corporation
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#ifndef __ADV_H__
+#define __ADV_H__
+
+/* Maximum advertising data payload for a single data type */
+#include "mesh/mesh.h"
+
+#define BT_MESH_ADV(om) (*(struct bt_mesh_adv **) OS_MBUF_USRHDR(om))
+
+#define BT_MESH_ADV_DATA_SIZE 31
+
+/* The user data is a pointer (4 bytes) to struct bt_mesh_adv */
+#define BT_MESH_ADV_USER_DATA_SIZE (sizeof(struct bt_mesh_adv *))
+
+#define BT_MESH_MBUF_HEADER_SIZE (sizeof(struct os_mbuf_pkthdr) + \
+ BT_MESH_ADV_USER_DATA_SIZE +\
+ sizeof(struct os_mbuf))
+
+enum bt_mesh_adv_type
+{
+ BT_MESH_ADV_PROV,
+ BT_MESH_ADV_DATA,
+ BT_MESH_ADV_BEACON,
+ BT_MESH_ADV_URI,
+};
+
+typedef void (*bt_mesh_adv_func_t)(struct os_mbuf *buf, u16_t duration,
+ int err, void *user_data);
+
+struct bt_mesh_adv {
+ const struct bt_mesh_send_cb *cb;
+ void *cb_data;
+
+ u8_t type:2,
+ busy:1;
+ u8_t xmit;
+
+ /* For transport layer segment sending */
+ struct {
+ u8_t attempts;
+ } seg;
+
+ u8_t flags;
+
+ int ref_cnt;
+ struct ble_npl_event ev;
+};
+
+typedef struct bt_mesh_adv *(*bt_mesh_adv_alloc_t)(int id);
+
+/* xmit_count: Number of retransmissions, i.e. 0 == 1 transmission */
+struct os_mbuf *bt_mesh_adv_create(enum bt_mesh_adv_type type, u8_t xmit,
+ s32_t timeout);
+
+struct os_mbuf *bt_mesh_adv_create_from_pool(struct os_mbuf_pool *pool,
+ bt_mesh_adv_alloc_t get_id,
+ enum bt_mesh_adv_type type,
+ u8_t xmit, s32_t timeout);
+
+void bt_mesh_adv_send(struct os_mbuf *buf, const struct bt_mesh_send_cb *cb,
+ void *cb_data);
+
+void bt_mesh_adv_update(void);
+
+void bt_mesh_adv_init(void);
+
+int bt_mesh_scan_enable(void);
+
+int bt_mesh_scan_disable(void);
+
+int ble_adv_gap_mesh_cb(struct ble_gap_event *event, void *arg);
+#endif
diff --git a/src/libs/mynewt-nimble/nimble/host/mesh/src/atomic.h b/src/libs/mynewt-nimble/nimble/host/mesh/src/atomic.h
new file mode 100644
index 00000000..2c731794
--- /dev/null
+++ b/src/libs/mynewt-nimble/nimble/host/mesh/src/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/src/libs/mynewt-nimble/nimble/host/mesh/src/beacon.c b/src/libs/mynewt-nimble/nimble/host/mesh/src/beacon.c
new file mode 100644
index 00000000..cd540aa8
--- /dev/null
+++ b/src/libs/mynewt-nimble/nimble/host/mesh/src/beacon.c
@@ -0,0 +1,441 @@
+/* Bluetooth Mesh */
+
+/*
+ * Copyright (c) 2017 Intel Corporation
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include "syscfg/syscfg.h"
+#define MESH_LOG_MODULE BLE_MESH_BEACON_LOG
+
+#include <errno.h>
+#include <assert.h>
+#include "os/os_mbuf.h"
+#include "mesh/mesh.h"
+
+#include "adv.h"
+#include "mesh_priv.h"
+#include "net.h"
+#include "prov.h"
+#include "crypto.h"
+#include "beacon.h"
+#include "foundation.h"
+
+#define UNPROVISIONED_INTERVAL (K_SECONDS(5))
+#define PROVISIONED_INTERVAL (K_SECONDS(10))
+
+#define BEACON_TYPE_UNPROVISIONED 0x00
+#define BEACON_TYPE_SECURE 0x01
+
+/* 3 transmissions, 20ms interval */
+#define UNPROV_XMIT BT_MESH_TRANSMIT(2, 20)
+
+/* 1 transmission, 20ms interval */
+#define PROV_XMIT BT_MESH_TRANSMIT(0, 20)
+
+static struct k_delayed_work beacon_timer;
+
+static struct bt_mesh_subnet *cache_check(u8_t data[21])
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(bt_mesh.sub); i++) {
+ struct bt_mesh_subnet *sub = &bt_mesh.sub[i];
+
+ if (sub->net_idx == BT_MESH_KEY_UNUSED) {
+ continue;
+ }
+
+ if (!memcmp(sub->beacon_cache, data, 21)) {
+ return sub;
+ }
+ }
+
+ return NULL;
+}
+
+static void cache_add(u8_t data[21], struct bt_mesh_subnet *sub)
+{
+ memcpy(sub->beacon_cache, data, 21);
+}
+
+static void beacon_complete(int err, void *user_data)
+{
+ struct bt_mesh_subnet *sub = user_data;
+
+ BT_DBG("err %d", err);
+
+ sub->beacon_sent = k_uptime_get_32();
+}
+
+void bt_mesh_beacon_create(struct bt_mesh_subnet *sub,
+ struct os_mbuf *buf)
+{
+ u8_t flags = bt_mesh_net_flags(sub);
+ struct bt_mesh_subnet_keys *keys;
+
+ net_buf_simple_add_u8(buf, BEACON_TYPE_SECURE);
+
+ if (sub->kr_flag) {
+ keys = &sub->keys[1];
+ } else {
+ keys = &sub->keys[0];
+ }
+
+ net_buf_simple_add_u8(buf, flags);
+
+ /* Network ID */
+ net_buf_simple_add_mem(buf, keys->net_id, 8);
+
+ /* IV Index */
+ net_buf_simple_add_be32(buf, bt_mesh.iv_index);
+
+ net_buf_simple_add_mem(buf, sub->auth, 8);
+
+ BT_DBG("net_idx 0x%04x flags 0x%02x NetID %s", sub->net_idx,
+ flags, bt_hex(keys->net_id, 8));
+ BT_DBG("IV Index 0x%08x Auth %s", (unsigned) bt_mesh.iv_index,
+ bt_hex(sub->auth, 8));
+}
+
+/* If the interval has passed or is within 5 seconds from now send a beacon */
+#define BEACON_THRESHOLD(sub) (K_SECONDS(10 * ((sub)->beacons_last + 1)) - \
+ K_SECONDS(5))
+
+static int secure_beacon_send(void)
+{
+ static const struct bt_mesh_send_cb send_cb = {
+ .end = beacon_complete,
+ };
+ u32_t now = k_uptime_get_32();
+ int i;
+
+ BT_DBG("");
+
+ for (i = 0; i < ARRAY_SIZE(bt_mesh.sub); i++) {
+ struct bt_mesh_subnet *sub = &bt_mesh.sub[i];
+ struct os_mbuf *buf;
+ u32_t time_diff;
+
+ if (sub->net_idx == BT_MESH_KEY_UNUSED) {
+ continue;
+ }
+
+ time_diff = now - sub->beacon_sent;
+ if (time_diff < K_SECONDS(600) &&
+ time_diff < BEACON_THRESHOLD(sub)) {
+ continue;
+ }
+
+ buf = bt_mesh_adv_create(BT_MESH_ADV_BEACON, PROV_XMIT,
+ K_NO_WAIT);
+ if (!buf) {
+ BT_ERR("Unable to allocate beacon buffer");
+ return -ENOBUFS;
+ }
+
+ bt_mesh_beacon_create(sub, buf);
+
+ bt_mesh_adv_send(buf, &send_cb, sub);
+ net_buf_unref(buf);
+ }
+
+ return 0;
+}
+
+static int unprovisioned_beacon_send(void)
+{
+ const struct bt_mesh_prov *prov;
+ u8_t uri_hash[16] = { 0 };
+ struct os_mbuf *buf;
+ u16_t oob_info;
+
+ BT_DBG("unprovisioned_beacon_send");
+
+ buf = bt_mesh_adv_create(BT_MESH_ADV_BEACON, UNPROV_XMIT, K_NO_WAIT);
+ if (!buf) {
+ BT_ERR("Unable to allocate beacon buffer");
+ return -ENOBUFS;
+ }
+
+ prov = bt_mesh_prov_get();
+
+ net_buf_add_u8(buf, BEACON_TYPE_UNPROVISIONED);
+ net_buf_add_mem(buf, prov->uuid, 16);
+
+ if (prov->uri && bt_mesh_s1(prov->uri, uri_hash) == 0) {
+ oob_info = prov->oob_info | BT_MESH_PROV_OOB_URI;
+ } else {
+ oob_info = prov->oob_info;
+ }
+
+ net_buf_add_be16(buf, oob_info);
+ net_buf_add_mem(buf, uri_hash, 4);
+
+ bt_mesh_adv_send(buf, NULL, NULL);
+ net_buf_unref(buf);
+
+ if (prov->uri) {
+ size_t len;
+
+ buf = bt_mesh_adv_create(BT_MESH_ADV_URI, UNPROV_XMIT,
+ K_NO_WAIT);
+ if (!buf) {
+ BT_ERR("Unable to allocate URI buffer");
+ return -ENOBUFS;
+ }
+
+ len = strlen(prov->uri);
+ if (net_buf_tailroom(buf) < len) {
+ BT_WARN("Too long URI to fit advertising data");
+ } else {
+ net_buf_add_mem(buf, prov->uri, len);
+ bt_mesh_adv_send(buf, NULL, NULL);
+ }
+
+ net_buf_unref(buf);
+ }
+
+ return 0;
+}
+
+static void unprovisioned_beacon_recv(struct os_mbuf *buf)
+{
+#if MYNEWT_VAL(BLE_MESH_PB_ADV)
+ const struct bt_mesh_prov *prov;
+ u8_t *uuid;
+ u16_t oob_info;
+ u32_t uri_hash_val;
+ u32_t *uri_hash = NULL;
+
+ if (buf->om_len != 18 && buf->om_len != 22) {
+ BT_ERR("Invalid unprovisioned beacon length (%u)", buf->om_len);
+ return;
+ }
+
+ uuid = net_buf_simple_pull_mem(buf, 16);
+ oob_info = net_buf_simple_pull_be16(buf);
+
+ if (buf->om_len == 4) {
+ uri_hash_val = net_buf_simple_pull_be32(buf);
+ uri_hash = &uri_hash_val;
+ }
+
+ BT_DBG("uuid %s", bt_hex(uuid, 16));
+
+ prov = bt_mesh_prov_get();
+
+ if (prov->unprovisioned_beacon) {
+ prov->unprovisioned_beacon(uuid,
+ (bt_mesh_prov_oob_info_t)oob_info,
+ uri_hash);
+ }
+#endif
+}
+
+static void update_beacon_observation(void)
+{
+ static bool first_half;
+ int i;
+
+ /* Observation period is 20 seconds, whereas the beacon timer
+ * runs every 10 seconds. We process what's happened during the
+ * window only after the seconnd half.
+ */
+ first_half = !first_half;
+ if (first_half) {
+ return;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(bt_mesh.sub); i++) {
+ struct bt_mesh_subnet *sub = &bt_mesh.sub[i];
+
+ if (sub->net_idx == BT_MESH_KEY_UNUSED) {
+ continue;
+ }
+
+ sub->beacons_last = sub->beacons_cur;
+ sub->beacons_cur = 0;
+ }
+}
+
+static void beacon_send(struct ble_npl_event *work)
+{
+ /* Don't send anything if we have an active provisioning link */
+ if ((MYNEWT_VAL(BLE_MESH_PROV)) && bt_prov_active()) {
+ k_delayed_work_submit(&beacon_timer, UNPROVISIONED_INTERVAL);
+ return;
+ }
+
+ BT_DBG("");
+
+ if (bt_mesh_is_provisioned()) {
+ update_beacon_observation();
+ secure_beacon_send();
+
+ /* Only resubmit if beaconing is still enabled */
+ if (bt_mesh_beacon_get() == BT_MESH_BEACON_ENABLED ||
+ atomic_test_bit(bt_mesh.flags, BT_MESH_IVU_INITIATOR)) {
+ k_delayed_work_submit(&beacon_timer,
+ PROVISIONED_INTERVAL);
+ }
+ } else if (IS_ENABLED(CONFIG_BT_MESH_PB_ADV)) {
+ unprovisioned_beacon_send();
+ k_delayed_work_submit(&beacon_timer, UNPROVISIONED_INTERVAL);
+ }
+}
+
+static void secure_beacon_recv(struct os_mbuf *buf)
+{
+ u8_t *data, *net_id, *auth;
+ struct bt_mesh_subnet *sub;
+ u32_t iv_index;
+ bool new_key, kr_change, iv_change;
+ u8_t flags;
+
+ if (buf->om_len < 21) {
+ BT_ERR("Too short secure beacon (len %u)", buf->om_len);
+ return;
+ }
+
+ sub = cache_check(buf->om_data);
+ if (sub) {
+ /* We've seen this beacon before - just update the stats */
+ goto update_stats;
+ }
+
+ /* So we can add to the cache if auth matches */
+ data = buf->om_data;
+
+ flags = net_buf_simple_pull_u8(buf);
+ net_id = net_buf_simple_pull_mem(buf, 8);
+ iv_index = net_buf_simple_pull_be32(buf);
+ auth = buf->om_data;
+
+ BT_DBG("flags 0x%02x id %s iv_index 0x%08x",
+ flags, bt_hex(net_id, 8), (unsigned) iv_index);
+
+ sub = bt_mesh_subnet_find(net_id, flags, iv_index, auth, &new_key);
+ if (!sub) {
+ BT_DBG("No subnet that matched beacon");
+ return;
+ }
+
+ if (sub->kr_phase == BT_MESH_KR_PHASE_2 && !new_key) {
+ BT_WARN("Ignoring Phase 2 KR Update secured using old key");
+ return;
+ }
+
+ cache_add(data, sub);
+
+ /* If we have NetKey0 accept initiation only from it */
+ if (bt_mesh_subnet_get(BT_MESH_KEY_PRIMARY) &&
+ sub->net_idx != BT_MESH_KEY_PRIMARY) {
+ BT_WARN("Ignoring secure beacon on non-primary subnet");
+ goto update_stats;
+ }
+
+ BT_DBG("net_idx 0x%04x iv_index 0x%08x, current iv_index 0x%08x",
+ sub->net_idx, (unsigned) iv_index, (unsigned) bt_mesh.iv_index);
+
+ if (atomic_test_bit(bt_mesh.flags, BT_MESH_IVU_INITIATOR) &&
+ (atomic_test_bit(bt_mesh.flags, BT_MESH_IVU_IN_PROGRESS) ==
+ BT_MESH_IV_UPDATE(flags))) {
+ bt_mesh_beacon_ivu_initiator(false);
+ }
+
+ iv_change = bt_mesh_net_iv_update(iv_index, BT_MESH_IV_UPDATE(flags));
+
+ kr_change = bt_mesh_kr_update(sub, BT_MESH_KEY_REFRESH(flags), new_key);
+ if (kr_change) {
+ bt_mesh_net_beacon_update(sub);
+ }
+
+ if (iv_change) {
+ /* Update all subnets */
+ bt_mesh_net_sec_update(NULL);
+ } else if (kr_change) {
+ /* Key Refresh without IV Update only impacts one subnet */
+ bt_mesh_net_sec_update(sub);
+ }
+
+update_stats:
+ if (bt_mesh_beacon_get() == BT_MESH_BEACON_ENABLED &&
+ sub->beacons_cur < 0xff) {
+ sub->beacons_cur++;
+ }
+}
+
+void bt_mesh_beacon_recv(struct os_mbuf *buf)
+{
+ u8_t type;
+
+ BT_DBG("%u bytes: %s", buf->om_len, bt_hex(buf->om_data, buf->om_len));
+
+ if (buf->om_len < 1) {
+ BT_ERR("Too short beacon");
+ return;
+ }
+
+ type = net_buf_simple_pull_u8(buf);
+ switch (type) {
+ case BEACON_TYPE_UNPROVISIONED:
+ unprovisioned_beacon_recv(buf);
+ break;
+ case BEACON_TYPE_SECURE:
+ secure_beacon_recv(buf);
+ break;
+ default:
+ BT_WARN("Unknown beacon type 0x%02x", type);
+ break;
+ }
+}
+
+void bt_mesh_beacon_init(void)
+{
+ k_delayed_work_init(&beacon_timer, beacon_send);
+}
+
+void bt_mesh_beacon_ivu_initiator(bool enable)
+{
+ atomic_set_bit_to(bt_mesh.flags, BT_MESH_IVU_INITIATOR, enable);
+
+ if (enable) {
+ k_work_submit(&beacon_timer.work);
+ } else if (bt_mesh_beacon_get() == BT_MESH_BEACON_DISABLED) {
+ k_delayed_work_cancel(&beacon_timer);
+ }
+}
+
+void bt_mesh_beacon_enable(void)
+{
+ int i;
+
+ if (!bt_mesh_is_provisioned()) {
+ k_work_submit(&beacon_timer.work);
+ return;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(bt_mesh.sub); i++) {
+ struct bt_mesh_subnet *sub = &bt_mesh.sub[i];
+
+ if (sub->net_idx == BT_MESH_KEY_UNUSED) {
+ continue;
+ }
+
+ sub->beacons_last = 0;
+ sub->beacons_cur = 0;
+
+ bt_mesh_net_beacon_update(sub);
+ }
+
+ k_work_submit(&beacon_timer.work);
+}
+
+void bt_mesh_beacon_disable(void)
+{
+ if (!atomic_test_bit(bt_mesh.flags, BT_MESH_IVU_INITIATOR)) {
+ k_delayed_work_cancel(&beacon_timer);
+ }
+}
diff --git a/src/libs/mynewt-nimble/nimble/host/mesh/src/beacon.h b/src/libs/mynewt-nimble/nimble/host/mesh/src/beacon.h
new file mode 100644
index 00000000..ac4bfed8
--- /dev/null
+++ b/src/libs/mynewt-nimble/nimble/host/mesh/src/beacon.h
@@ -0,0 +1,26 @@
+/* Bluetooth Mesh */
+
+/*
+ * Copyright (c) 2017 Intel Corporation
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#ifndef __BEACON_H__
+#define __BEACON_H__
+
+#include "os/os_mbuf.h"
+
+void bt_mesh_beacon_enable(void);
+void bt_mesh_beacon_disable(void);
+
+void bt_mesh_beacon_ivu_initiator(bool enable);
+
+void bt_mesh_beacon_recv(struct os_mbuf *buf);
+
+void bt_mesh_beacon_create(struct bt_mesh_subnet *sub,
+ struct os_mbuf *buf);
+
+void bt_mesh_beacon_init(void);
+
+#endif
diff --git a/src/libs/mynewt-nimble/nimble/host/mesh/src/cfg_cli.c b/src/libs/mynewt-nimble/nimble/host/mesh/src/cfg_cli.c
new file mode 100644
index 00000000..2c2f6c3f
--- /dev/null
+++ b/src/libs/mynewt-nimble/nimble/host/mesh/src/cfg_cli.c
@@ -0,0 +1,1498 @@
+/* Bluetooth Mesh */
+
+/*
+ * Copyright (c) 2017 Intel Corporation
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include "syscfg/syscfg.h"
+#define MESH_LOG_MODULE BLE_MESH_MODEL_LOG
+#if MYNEWT_VAL(BLE_MESH_CFG_CLI)
+
+#include "mesh/mesh.h"
+
+#include <string.h>
+#include <errno.h>
+#include <stdbool.h>
+
+#include "net.h"
+#include "foundation.h"
+
+#define CID_NVAL 0xffff
+
+/* 2 byte dummy opcode for getting compile time buffer sizes. */
+#define DUMMY_2_BYTE_OP BT_MESH_MODEL_OP_2(0xff, 0xff)
+
+struct comp_data {
+ u8_t *status;
+ struct os_mbuf *comp;
+};
+
+static s32_t msg_timeout = K_SECONDS(5);
+
+static struct bt_mesh_cfg_cli *cli;
+
+static void comp_data_status(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ struct comp_data *param;
+ size_t to_copy;
+
+ BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s",
+ ctx->net_idx, ctx->app_idx, ctx->addr, buf->om_len,
+ bt_hex(buf->om_data, buf->om_len));
+
+ if (cli->op_pending != OP_DEV_COMP_DATA_STATUS) {
+ BT_WARN("Unexpected Composition Data Status");
+ return;
+ }
+
+ param = cli->op_param;
+
+ *(param->status) = net_buf_simple_pull_u8(buf);
+ to_copy = min(net_buf_simple_tailroom(param->comp), buf->om_len);
+ net_buf_simple_add_mem(param->comp, buf->om_data, to_copy);
+
+ k_sem_give(&cli->op_sync);
+}
+
+static void state_status_u8(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf*buf,
+ u32_t expect_status)
+{
+ u8_t *status;
+
+ BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s",
+ ctx->net_idx, ctx->app_idx, ctx->addr, buf->om_len,
+ bt_hex(buf->om_data, buf->om_len));
+
+ if (cli->op_pending != expect_status) {
+ BT_WARN("Unexpected Status (0x%08x != 0x%08x)",
+ (unsigned) cli->op_pending, (unsigned) expect_status);
+ return;
+ }
+
+ status = cli->op_param;
+ *status = net_buf_simple_pull_u8(buf);
+
+ k_sem_give(&cli->op_sync);
+}
+
+static void beacon_status(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ state_status_u8(model, ctx, buf, OP_BEACON_STATUS);
+}
+
+static void ttl_status(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf*buf)
+{
+ state_status_u8(model, ctx, buf, OP_DEFAULT_TTL_STATUS);
+}
+
+static void friend_status(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf*buf)
+{
+ state_status_u8(model, ctx, buf, OP_FRIEND_STATUS);
+}
+
+static void gatt_proxy_status(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf*buf)
+{
+ state_status_u8(model, ctx, buf, OP_GATT_PROXY_STATUS);
+}
+
+struct relay_param {
+ u8_t *status;
+ u8_t *transmit;
+};
+
+static void relay_status(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf*buf)
+{
+ struct relay_param *param;
+
+ BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s",
+ ctx->net_idx, ctx->app_idx, ctx->addr, buf->om_len,
+ bt_hex(buf->om_data, buf->om_len));
+
+ if (cli->op_pending != OP_RELAY_STATUS) {
+ BT_WARN("Unexpected Relay Status message");
+ return;
+ }
+
+ param = cli->op_param;
+ *param->status = net_buf_simple_pull_u8(buf);
+ *param->transmit = net_buf_simple_pull_u8(buf);
+
+ k_sem_give(&cli->op_sync);
+}
+
+struct net_key_param {
+ u8_t *status;
+ u16_t net_idx;
+};
+
+static void net_key_status(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ struct net_key_param *param;
+ u16_t net_idx, app_idx;
+ u8_t status;
+
+ BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s",
+ ctx->net_idx, ctx->app_idx, ctx->addr, buf->om_len,
+ bt_hex(buf->om_data, buf->om_len));
+
+ if (cli->op_pending != OP_NET_KEY_STATUS) {
+ BT_WARN("Unexpected Net Key Status message");
+ return;
+ }
+
+ status = net_buf_simple_pull_u8(buf);
+ key_idx_unpack(buf, &net_idx, &app_idx);
+
+ param = cli->op_param;
+ if (param->net_idx != net_idx) {
+ BT_WARN("Net Key Status key index does not match");
+ return;
+ }
+
+ if (param->status) {
+ *param->status = status;
+ }
+
+ k_sem_give(&cli->op_sync);
+}
+
+struct app_key_param {
+ u8_t *status;
+ u16_t net_idx;
+ u16_t app_idx;
+};
+
+static void app_key_status(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf*buf)
+{
+ struct app_key_param *param;
+ u16_t net_idx, app_idx;
+ u8_t status;
+
+ BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s",
+ ctx->net_idx, ctx->app_idx, ctx->addr, buf->om_len,
+ bt_hex(buf->om_data, buf->om_len));
+
+ if (cli->op_pending != OP_APP_KEY_STATUS) {
+ BT_WARN("Unexpected App Key Status message");
+ return;
+ }
+
+ status = net_buf_simple_pull_u8(buf);
+ key_idx_unpack(buf, &net_idx, &app_idx);
+
+ param = cli->op_param;
+ if (param->net_idx != net_idx || param->app_idx != app_idx) {
+ BT_WARN("App Key Status key indices did not match");
+ return;
+ }
+
+ if (param->status) {
+ *param->status = status;
+ }
+
+ k_sem_give(&cli->op_sync);
+}
+
+struct mod_app_param {
+ u8_t *status;
+ u16_t elem_addr;
+ u16_t mod_app_idx;
+ u16_t mod_id;
+ u16_t cid;
+};
+
+static void mod_app_status(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf*buf)
+{
+ u16_t elem_addr, mod_app_idx, mod_id, cid;
+ struct mod_app_param *param;
+ u8_t status;
+
+ BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s",
+ ctx->net_idx, ctx->app_idx, ctx->addr, buf->om_len,
+ bt_hex(buf->om_data, buf->om_len));
+
+ if (cli->op_pending != OP_MOD_APP_STATUS) {
+ BT_WARN("Unexpected Model App Status message");
+ return;
+ }
+
+ status = net_buf_simple_pull_u8(buf);
+ elem_addr = net_buf_simple_pull_le16(buf);
+ mod_app_idx = net_buf_simple_pull_le16(buf);
+
+ if (buf->om_len >= 4) {
+ cid = net_buf_simple_pull_le16(buf);
+ } else {
+ cid = CID_NVAL;
+ }
+
+ mod_id = net_buf_simple_pull_le16(buf);
+
+ param = cli->op_param;
+ if (param->elem_addr != elem_addr ||
+ param->mod_app_idx != mod_app_idx || param->mod_id != mod_id ||
+ param->cid != cid) {
+ BT_WARN("Model App Status parameters did not match");
+ return;
+ }
+
+ if (param->status) {
+ *param->status = status;
+ }
+
+ k_sem_give(&cli->op_sync);
+}
+
+struct mod_pub_param {
+ u16_t mod_id;
+ u16_t cid;
+ u16_t elem_addr;
+ u8_t *status;
+ struct bt_mesh_cfg_mod_pub *pub;
+};
+
+static void mod_pub_status(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf*buf)
+{
+ u16_t mod_id, cid, elem_addr;
+ struct mod_pub_param *param;
+ u8_t status;
+
+ BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s",
+ ctx->net_idx, ctx->app_idx, ctx->addr, buf->om_len,
+ bt_hex(buf->om_data, buf->om_len));
+
+ if (cli->op_pending != OP_MOD_PUB_STATUS) {
+ BT_WARN("Unexpected Model Pub Status message");
+ return;
+ }
+
+ param = cli->op_param;
+ if (param->cid != CID_NVAL) {
+ if (buf->om_len < 14) {
+ BT_WARN("Unexpected Mod Pub Status with SIG Model");
+ return;
+ }
+
+ cid = sys_get_le16(&buf->om_data[10]);
+ mod_id = sys_get_le16(&buf->om_data[12]);
+ } else {
+ if (buf->om_len > 12) {
+ BT_WARN("Unexpected Mod Pub Status with Vendor Model");
+ return;
+ }
+
+ cid = CID_NVAL;
+ mod_id = sys_get_le16(&buf->om_data[10]);
+ }
+
+ if (mod_id != param->mod_id || cid != param->cid) {
+ BT_WARN("Mod Pub Model ID or Company ID mismatch");
+ return;
+ }
+
+ status = net_buf_simple_pull_u8(buf);
+
+ elem_addr = net_buf_simple_pull_le16(buf);
+ if (elem_addr != param->elem_addr) {
+ BT_WARN("Model Pub Status for unexpected element (0x%04x)",
+ elem_addr);
+ return;
+ }
+
+ if (param->status) {
+ *param->status = status;
+ }
+
+ if (param->pub) {
+ param->pub->addr = net_buf_simple_pull_le16(buf);
+ param->pub->app_idx = net_buf_simple_pull_le16(buf);
+ param->pub->cred_flag = (param->pub->app_idx & BIT(12));
+ param->pub->app_idx &= BIT_MASK(12);
+ param->pub->ttl = net_buf_simple_pull_u8(buf);
+ param->pub->period = net_buf_simple_pull_u8(buf);
+ param->pub->transmit = net_buf_simple_pull_u8(buf);
+ }
+
+ k_sem_give(&cli->op_sync);
+}
+
+struct mod_sub_param {
+ u8_t *status;
+ u16_t elem_addr;
+ u16_t *sub_addr;
+ u16_t *expect_sub;
+ u16_t mod_id;
+ u16_t cid;
+};
+
+static void mod_sub_status(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf*buf)
+{
+ u16_t elem_addr, sub_addr, mod_id, cid;
+ struct mod_sub_param *param;
+ u8_t status;
+
+ BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s",
+ ctx->net_idx, ctx->app_idx, ctx->addr, buf->om_len,
+ bt_hex(buf->om_data, buf->om_len));
+
+ if (cli->op_pending != OP_MOD_SUB_STATUS) {
+ BT_WARN("Unexpected Model Subscription Status message");
+ return;
+ }
+
+ status = net_buf_simple_pull_u8(buf);
+ elem_addr = net_buf_simple_pull_le16(buf);
+ sub_addr = net_buf_simple_pull_le16(buf);
+
+ if (buf->om_len >= 4) {
+ cid = net_buf_simple_pull_le16(buf);
+ } else {
+ cid = CID_NVAL;
+ }
+
+ mod_id = net_buf_simple_pull_le16(buf);
+
+ param = cli->op_param;
+ if (param->elem_addr != elem_addr || param->mod_id != mod_id ||
+ (param->expect_sub && *param->expect_sub != sub_addr) ||
+ param->cid != cid) {
+ BT_WARN("Model Subscription Status parameters did not match");
+ return;
+ }
+
+ if (param->sub_addr) {
+ *param->sub_addr = sub_addr;
+ }
+
+ if (param->status) {
+ *param->status = status;
+ }
+
+ k_sem_give(&cli->op_sync);
+}
+
+struct hb_sub_param {
+ u8_t *status;
+ struct bt_mesh_cfg_hb_sub *sub;
+};
+
+static void hb_sub_status(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf*buf)
+{
+ struct hb_sub_param *param;
+
+ BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s",
+ ctx->net_idx, ctx->app_idx, ctx->addr, buf->om_len,
+ bt_hex(buf->om_data, buf->om_len));
+
+ if (cli->op_pending != OP_HEARTBEAT_SUB_STATUS) {
+ BT_WARN("Unexpected Heartbeat Subscription Status message");
+ return;
+ }
+
+ param = cli->op_param;
+
+ *param->status = net_buf_simple_pull_u8(buf);
+
+ param->sub->src = net_buf_simple_pull_le16(buf);
+ param->sub->dst = net_buf_simple_pull_le16(buf);
+ param->sub->period = net_buf_simple_pull_u8(buf);
+ param->sub->count = net_buf_simple_pull_u8(buf);
+ param->sub->min = net_buf_simple_pull_u8(buf);
+ param->sub->max = net_buf_simple_pull_u8(buf);
+
+ k_sem_give(&cli->op_sync);
+}
+
+struct hb_pub_param {
+ u8_t *status;
+ struct bt_mesh_cfg_hb_pub *pub;
+};
+
+static void hb_pub_status(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ struct hb_pub_param *param;
+
+ BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s",
+ ctx->net_idx, ctx->app_idx, ctx->addr, buf->om_len,
+ bt_hex(buf->om_data, buf->om_len));
+
+ if (cli->op_pending != OP_HEARTBEAT_PUB_STATUS) {
+ BT_WARN("Unexpected Heartbeat Publication Status message");
+ return;
+ }
+
+ param = cli->op_param;
+
+ *param->status = net_buf_simple_pull_u8(buf);
+
+ if (param->pub) {
+ param->pub->dst = net_buf_simple_pull_le16(buf);
+ param->pub->count = net_buf_simple_pull_u8(buf);
+ param->pub->period = net_buf_simple_pull_u8(buf);
+ param->pub->ttl = net_buf_simple_pull_u8(buf);
+ param->pub->feat = net_buf_simple_pull_u8(buf);
+ param->pub->net_idx = net_buf_simple_pull_u8(buf);
+ }
+
+ k_sem_give(&cli->op_sync);
+}
+
+const struct bt_mesh_model_op bt_mesh_cfg_cli_op[] = {
+ { OP_DEV_COMP_DATA_STATUS, 15, comp_data_status },
+ { OP_BEACON_STATUS, 1, beacon_status },
+ { OP_DEFAULT_TTL_STATUS, 1, ttl_status },
+ { OP_FRIEND_STATUS, 1, friend_status },
+ { OP_GATT_PROXY_STATUS, 1, gatt_proxy_status },
+ { OP_RELAY_STATUS, 2, relay_status },
+ { OP_NET_KEY_STATUS, 3, net_key_status },
+ { OP_APP_KEY_STATUS, 4, app_key_status },
+ { OP_MOD_APP_STATUS, 7, mod_app_status },
+ { OP_MOD_PUB_STATUS, 12, mod_pub_status },
+ { OP_MOD_SUB_STATUS, 7, mod_sub_status },
+ { OP_HEARTBEAT_SUB_STATUS, 9, hb_sub_status },
+ { OP_HEARTBEAT_PUB_STATUS, 10, hb_pub_status },
+ BT_MESH_MODEL_OP_END,
+};
+
+static int cfg_cli_init(struct bt_mesh_model *model)
+{
+ BT_DBG("");
+
+ if (!bt_mesh_model_in_primary(model)) {
+ BT_ERR("Configuration Client only allowed in primary element");
+ return -EINVAL;
+ }
+
+ if (!model->user_data) {
+ BT_ERR("No Configuration Client context provided");
+ return -EINVAL;
+ }
+
+ cli = model->user_data;
+ cli->model = model;
+
+ /*
+ * Configuration Model security is device-key based and both the local
+ * and remote keys are allowed to access this model.
+ */
+ model->keys[0] = BT_MESH_KEY_DEV_ANY;
+
+ k_sem_init(&cli->op_sync, 0, 1);
+
+ return 0;
+}
+
+const struct bt_mesh_model_cb bt_mesh_cfg_cli_cb = {
+ .init = cfg_cli_init,
+};
+
+static int cli_prepare(void *param, u32_t op)
+{
+ if (!cli) {
+ BT_ERR("No available Configuration Client context!");
+ return -EINVAL;
+ }
+
+ if (cli->op_pending) {
+ BT_WARN("Another synchronous operation pending");
+ return -EBUSY;
+ }
+
+ cli->op_param = param;
+ cli->op_pending = op;
+
+ return 0;
+}
+
+static void cli_reset(void)
+{
+ cli->op_pending = 0;
+ cli->op_param = NULL;
+}
+
+static int cli_wait(void)
+{
+ int err;
+
+ err = k_sem_take(&cli->op_sync, msg_timeout);
+
+ cli_reset();
+
+ return err;
+}
+
+int bt_mesh_cfg_comp_data_get(u16_t net_idx, u16_t addr, u8_t page,
+ u8_t *status, struct os_mbuf *comp)
+{
+ struct os_mbuf *msg = BT_MESH_MODEL_BUF(OP_DEV_COMP_DATA_GET, 1);
+ struct bt_mesh_msg_ctx ctx = {
+ .net_idx = net_idx,
+ .app_idx = BT_MESH_KEY_DEV_REMOTE,
+ .addr = addr,
+ .send_ttl = BT_MESH_TTL_DEFAULT,
+ };
+ struct comp_data param = {
+ .status = status,
+ .comp = comp,
+ };
+ int err;
+
+ err = cli_prepare(&param, OP_DEV_COMP_DATA_STATUS);
+ if (err) {
+ goto done;
+ }
+
+ bt_mesh_model_msg_init(msg, OP_DEV_COMP_DATA_GET);
+ net_buf_simple_add_u8(msg, page);
+
+ err = bt_mesh_model_send(cli->model, &ctx, msg, NULL, NULL);
+ if (err) {
+ BT_ERR("model_send() failed (err %d)", err);
+ cli_reset();
+ goto done;
+ }
+
+ err = cli_wait();
+done:
+ os_mbuf_free_chain(msg);
+ return err;
+}
+
+static int get_state_u8(u16_t net_idx, u16_t addr, u32_t op, u32_t rsp,
+ u8_t *val)
+{
+ struct os_mbuf *msg = BT_MESH_MODEL_BUF(DUMMY_2_BYTE_OP, 0);
+ struct bt_mesh_msg_ctx ctx = {
+ .net_idx = net_idx,
+ .app_idx = BT_MESH_KEY_DEV_REMOTE,
+ .addr = addr,
+ .send_ttl = BT_MESH_TTL_DEFAULT,
+ };
+ int err;
+
+ err = cli_prepare(val, rsp);
+ if (err) {
+ goto done;
+ }
+
+ bt_mesh_model_msg_init(msg, op);
+
+ err = bt_mesh_model_send(cli->model, &ctx, msg, NULL, NULL);
+ if (err) {
+ BT_ERR("model_send() failed (err %d)", err);
+ cli_reset();
+ goto done;
+ }
+
+ err = cli_wait();
+done:
+ os_mbuf_free_chain(msg);
+ return err;
+}
+
+static int set_state_u8(u16_t net_idx, u16_t addr, u32_t op, u32_t rsp,
+ u8_t new_val, u8_t *val)
+{
+ struct os_mbuf *msg = BT_MESH_MODEL_BUF(DUMMY_2_BYTE_OP, 1);
+ struct bt_mesh_msg_ctx ctx = {
+ .net_idx = net_idx,
+ .app_idx = BT_MESH_KEY_DEV_REMOTE,
+ .addr = addr,
+ .send_ttl = BT_MESH_TTL_DEFAULT,
+ };
+ int err;
+
+ err = cli_prepare(val, rsp);
+ if (err) {
+ goto done;
+ }
+
+ bt_mesh_model_msg_init(msg, op);
+ net_buf_simple_add_u8(msg, new_val);
+
+ err = bt_mesh_model_send(cli->model, &ctx, msg, NULL, NULL);
+ if (err) {
+ BT_ERR("model_send() failed (err %d)", err);
+ cli_reset();
+ goto done;
+ }
+
+ err = cli_wait();
+done:
+ os_mbuf_free_chain(msg);
+ return err;
+}
+
+int bt_mesh_cfg_beacon_get(u16_t net_idx, u16_t addr, u8_t *status)
+{
+ return get_state_u8(net_idx, addr, OP_BEACON_GET, OP_BEACON_STATUS,
+ status);
+}
+
+int bt_mesh_cfg_beacon_set(u16_t net_idx, u16_t addr, u8_t val, u8_t *status)
+{
+ return set_state_u8(net_idx, addr, OP_BEACON_SET, OP_BEACON_STATUS,
+ val, status);
+}
+
+int bt_mesh_cfg_ttl_get(u16_t net_idx, u16_t addr, u8_t *ttl)
+{
+ return get_state_u8(net_idx, addr, OP_DEFAULT_TTL_GET,
+ OP_DEFAULT_TTL_STATUS, ttl);
+}
+
+int bt_mesh_cfg_ttl_set(u16_t net_idx, u16_t addr, u8_t val, u8_t *ttl)
+{
+ return set_state_u8(net_idx, addr, OP_DEFAULT_TTL_SET,
+ OP_DEFAULT_TTL_STATUS, val, ttl);
+}
+
+int bt_mesh_cfg_friend_get(u16_t net_idx, u16_t addr, u8_t *status)
+{
+ return get_state_u8(net_idx, addr, OP_FRIEND_GET,
+ OP_FRIEND_STATUS, status);
+}
+
+int bt_mesh_cfg_friend_set(u16_t net_idx, u16_t addr, u8_t val, u8_t *status)
+{
+ return set_state_u8(net_idx, addr, OP_FRIEND_SET, OP_FRIEND_STATUS,
+ val, status);
+}
+
+int bt_mesh_cfg_gatt_proxy_get(u16_t net_idx, u16_t addr, u8_t *status)
+{
+ return get_state_u8(net_idx, addr, OP_GATT_PROXY_GET,
+ OP_GATT_PROXY_STATUS, status);
+}
+
+int bt_mesh_cfg_gatt_proxy_set(u16_t net_idx, u16_t addr, u8_t val,
+ u8_t *status)
+{
+ return set_state_u8(net_idx, addr, OP_GATT_PROXY_SET,
+ OP_GATT_PROXY_STATUS, val, status);
+}
+
+int bt_mesh_cfg_relay_get(u16_t net_idx, u16_t addr, u8_t *status,
+ u8_t *transmit)
+{
+ struct os_mbuf *msg = BT_MESH_MODEL_BUF(OP_RELAY_GET, 0);
+ struct bt_mesh_msg_ctx ctx = {
+ .net_idx = net_idx,
+ .app_idx = BT_MESH_KEY_DEV_REMOTE,
+ .addr = addr,
+ .send_ttl = BT_MESH_TTL_DEFAULT,
+ };
+ struct relay_param param = {
+ .status = status,
+ .transmit = transmit,
+ };
+ int err;
+
+ err = cli_prepare(&param, OP_RELAY_STATUS);
+ if (err) {
+ goto done;
+ }
+
+ bt_mesh_model_msg_init(msg, OP_RELAY_GET);
+
+ err = bt_mesh_model_send(cli->model, &ctx, msg, NULL, NULL);
+ if (err) {
+ BT_ERR("model_send() failed (err %d)", err);
+ cli_reset();
+ goto done;
+ }
+
+ err = cli_wait();
+done:
+ os_mbuf_free_chain(msg);
+ return err;
+}
+
+int bt_mesh_cfg_relay_set(u16_t net_idx, u16_t addr, u8_t new_relay,
+ u8_t new_transmit, u8_t *status, u8_t *transmit)
+{
+ struct os_mbuf *msg = BT_MESH_MODEL_BUF(OP_RELAY_SET, 2);
+ struct bt_mesh_msg_ctx ctx = {
+ .net_idx = net_idx,
+ .app_idx = BT_MESH_KEY_DEV_REMOTE,
+ .addr = addr,
+ .send_ttl = BT_MESH_TTL_DEFAULT,
+ };
+ struct relay_param param = {
+ .status = status,
+ .transmit = transmit,
+ };
+ int err;
+
+ err = cli_prepare(&param, OP_RELAY_STATUS);
+ if (err) {
+ goto done;
+ }
+
+ bt_mesh_model_msg_init(msg, OP_RELAY_SET);
+ net_buf_simple_add_u8(msg, new_relay);
+ net_buf_simple_add_u8(msg, new_transmit);
+
+ err = bt_mesh_model_send(cli->model, &ctx, msg, NULL, NULL);
+ if (err) {
+ BT_ERR("model_send() failed (err %d)", err);
+ cli_reset();
+ goto done;
+ }
+
+ err = cli_wait();
+done:
+ os_mbuf_free_chain(msg);
+ return err;
+}
+
+int bt_mesh_cfg_net_key_add(u16_t net_idx, u16_t addr, u16_t key_net_idx,
+ const u8_t net_key[16], u8_t *status)
+{
+ struct os_mbuf *msg = BT_MESH_MODEL_BUF(OP_NET_KEY_ADD, 18);
+ struct bt_mesh_msg_ctx ctx = {
+ .net_idx = net_idx,
+ .app_idx = BT_MESH_KEY_DEV_REMOTE,
+ .addr = addr,
+ .send_ttl = BT_MESH_TTL_DEFAULT,
+ };
+ struct net_key_param param = {
+ .status = status,
+ .net_idx = key_net_idx,
+ };
+ int err;
+
+ err = cli_prepare(&param, OP_NET_KEY_STATUS);
+ if (err) {
+ goto done;
+ }
+
+ bt_mesh_model_msg_init(msg, OP_NET_KEY_ADD);
+ net_buf_simple_add_le16(msg, key_net_idx);
+ net_buf_simple_add_mem(msg, net_key, 16);
+
+ err = bt_mesh_model_send(cli->model, &ctx, msg, NULL, NULL);
+ if (err) {
+ BT_ERR("model_send() failed (err %d)", err);
+ cli_reset();
+ goto done;
+ }
+
+ if (!status) {
+ cli_reset();
+ goto done;
+ }
+
+ err = cli_wait();
+done:
+ os_mbuf_free_chain(msg);
+ return err;
+}
+
+int bt_mesh_cfg_app_key_add(u16_t net_idx, u16_t addr, u16_t key_net_idx,
+ u16_t key_app_idx, const u8_t app_key[16],
+ u8_t *status)
+{
+ struct os_mbuf *msg = BT_MESH_MODEL_BUF(OP_APP_KEY_ADD, 19);
+ struct bt_mesh_msg_ctx ctx = {
+ .net_idx = net_idx,
+ .app_idx = BT_MESH_KEY_DEV_REMOTE,
+ .addr = addr,
+ .send_ttl = BT_MESH_TTL_DEFAULT,
+ };
+ struct app_key_param param = {
+ .status = status,
+ .net_idx = key_net_idx,
+ .app_idx = key_app_idx,
+ };
+ int err;
+
+ err = cli_prepare(&param, OP_APP_KEY_STATUS);
+ if (err) {
+ goto done;
+ }
+
+ bt_mesh_model_msg_init(msg, OP_APP_KEY_ADD);
+ key_idx_pack(msg, key_net_idx, key_app_idx);
+ net_buf_simple_add_mem(msg, app_key, 16);
+
+ err = bt_mesh_model_send(cli->model, &ctx, msg, NULL, NULL);
+ if (err) {
+ BT_ERR("model_send() failed (err %d)", err);
+ cli_reset();
+ goto done;
+ }
+
+ if (!status) {
+ cli_reset();
+ goto done;
+ }
+
+ err = cli_wait();
+done:
+ os_mbuf_free_chain(msg);
+ return err;
+}
+
+static int mod_app_bind(u16_t net_idx, u16_t addr, u16_t elem_addr,
+ u16_t mod_app_idx, u16_t mod_id, u16_t cid,
+ u8_t *status)
+{
+ struct os_mbuf *msg = BT_MESH_MODEL_BUF(OP_MOD_APP_BIND, 8);
+ struct bt_mesh_msg_ctx ctx = {
+ .net_idx = net_idx,
+ .app_idx = BT_MESH_KEY_DEV_REMOTE,
+ .addr = addr,
+ .send_ttl = BT_MESH_TTL_DEFAULT,
+ };
+ struct mod_app_param param = {
+ .status = status,
+ .elem_addr = elem_addr,
+ .mod_app_idx = mod_app_idx,
+ .mod_id = mod_id,
+ .cid = cid,
+ };
+ int err;
+
+ err = cli_prepare(&param, OP_MOD_APP_STATUS);
+ if (err) {
+ goto done;
+ }
+
+ bt_mesh_model_msg_init(msg, OP_MOD_APP_BIND);
+ net_buf_simple_add_le16(msg, elem_addr);
+ net_buf_simple_add_le16(msg, mod_app_idx);
+
+ if (cid != CID_NVAL) {
+ net_buf_simple_add_le16(msg, cid);
+ }
+
+ net_buf_simple_add_le16(msg, mod_id);
+
+ err = bt_mesh_model_send(cli->model, &ctx, msg, NULL, NULL);
+ if (err) {
+ BT_ERR("model_send() failed (err %d)", err);
+ cli_reset();
+ goto done;
+ }
+
+ if (!status) {
+ cli_reset();
+ goto done;
+ }
+
+ err = cli_wait();
+done:
+ os_mbuf_free_chain(msg);
+ return err;
+}
+
+int bt_mesh_cfg_mod_app_bind(u16_t net_idx, u16_t addr, u16_t elem_addr,
+ u16_t mod_app_idx, u16_t mod_id, u8_t *status)
+{
+ return mod_app_bind(net_idx, addr, elem_addr, mod_app_idx, mod_id,
+ CID_NVAL, status);
+}
+
+int bt_mesh_cfg_mod_app_bind_vnd(u16_t net_idx, u16_t addr, u16_t elem_addr,
+ u16_t mod_app_idx, u16_t mod_id, u16_t cid,
+ u8_t *status)
+{
+ if (cid == CID_NVAL) {
+ return -EINVAL;
+ }
+
+ return mod_app_bind(net_idx, addr, elem_addr, mod_app_idx, mod_id, cid,
+ status);
+}
+
+static int mod_sub(u32_t op, u16_t net_idx, u16_t addr, u16_t elem_addr,
+ u16_t sub_addr, u16_t mod_id, u16_t cid, u8_t *status)
+{
+ struct os_mbuf *msg = BT_MESH_MODEL_BUF(DUMMY_2_BYTE_OP, 8);
+ struct bt_mesh_msg_ctx ctx = {
+ .net_idx = net_idx,
+ .app_idx = BT_MESH_KEY_DEV_REMOTE,
+ .addr = addr,
+ .send_ttl = BT_MESH_TTL_DEFAULT,
+ };
+ struct mod_sub_param param = {
+ .status = status,
+ .elem_addr = elem_addr,
+ .expect_sub = &sub_addr,
+ .mod_id = mod_id,
+ .cid = cid,
+ };
+ int err;
+
+ err = cli_prepare(&param, OP_MOD_SUB_STATUS);
+ if (err) {
+ goto done;
+ }
+
+ bt_mesh_model_msg_init(msg, op);
+ net_buf_simple_add_le16(msg, elem_addr);
+ net_buf_simple_add_le16(msg, sub_addr);
+
+ if (cid != CID_NVAL) {
+ net_buf_simple_add_le16(msg, cid);
+ }
+
+ net_buf_simple_add_le16(msg, mod_id);
+
+ err = bt_mesh_model_send(cli->model, &ctx, msg, NULL, NULL);
+ if (err) {
+ BT_ERR("model_send() failed (err %d)", err);
+ cli_reset();
+ goto done;
+ }
+
+ if (!status) {
+ cli_reset();
+ goto done;
+ }
+
+ err = cli_wait();
+done:
+ os_mbuf_free_chain(msg);
+ return err;
+}
+
+int bt_mesh_cfg_mod_sub_add(u16_t net_idx, u16_t addr, u16_t elem_addr,
+ u16_t sub_addr, u16_t mod_id, u8_t *status)
+{
+ return mod_sub(OP_MOD_SUB_ADD, net_idx, addr, elem_addr, sub_addr,
+ mod_id, CID_NVAL, status);
+}
+
+int bt_mesh_cfg_mod_sub_add_vnd(u16_t net_idx, u16_t addr, u16_t elem_addr,
+ u16_t sub_addr, u16_t mod_id, u16_t cid,
+ u8_t *status)
+{
+ if (cid == CID_NVAL) {
+ return -EINVAL;
+ }
+
+ return mod_sub(OP_MOD_SUB_ADD, net_idx, addr, elem_addr, sub_addr,
+ mod_id, cid, status);
+}
+
+int bt_mesh_cfg_mod_sub_del(u16_t net_idx, u16_t addr, u16_t elem_addr,
+ u16_t sub_addr, u16_t mod_id, u8_t *status)
+{
+ return mod_sub(OP_MOD_SUB_DEL, net_idx, addr, elem_addr, sub_addr,
+ mod_id, CID_NVAL, status);
+}
+
+int bt_mesh_cfg_mod_sub_del_vnd(u16_t net_idx, u16_t addr, u16_t elem_addr,
+ u16_t sub_addr, u16_t mod_id, u16_t cid,
+ u8_t *status)
+{
+ if (cid == CID_NVAL) {
+ return -EINVAL;
+ }
+
+ return mod_sub(OP_MOD_SUB_DEL, net_idx, addr, elem_addr, sub_addr,
+ mod_id, cid, status);
+}
+
+int bt_mesh_cfg_mod_sub_overwrite(u16_t net_idx, u16_t addr, u16_t elem_addr,
+ u16_t sub_addr, u16_t mod_id, u8_t *status)
+{
+ return mod_sub(OP_MOD_SUB_OVERWRITE, net_idx, addr, elem_addr,
+ sub_addr, mod_id, CID_NVAL, status);
+}
+
+int bt_mesh_cfg_mod_sub_overwrite_vnd(u16_t net_idx, u16_t addr,
+ u16_t elem_addr, u16_t sub_addr,
+ u16_t mod_id, u16_t cid, u8_t *status)
+{
+ if (cid == CID_NVAL) {
+ return -EINVAL;
+ }
+
+ return mod_sub(OP_MOD_SUB_OVERWRITE, net_idx, addr, elem_addr,
+ sub_addr, mod_id, cid, status);
+}
+
+static int mod_sub_va(u32_t op, u16_t net_idx, u16_t addr, u16_t elem_addr,
+ const u8_t label[16], u16_t mod_id, u16_t cid,
+ u16_t *virt_addr, u8_t *status)
+{
+ struct os_mbuf *msg = BT_MESH_MODEL_BUF(DUMMY_2_BYTE_OP, 22);
+ struct bt_mesh_msg_ctx ctx = {
+ .net_idx = net_idx,
+ .app_idx = BT_MESH_KEY_DEV_REMOTE,
+ .addr = addr,
+ .send_ttl = BT_MESH_TTL_DEFAULT,
+ };
+ struct mod_sub_param param = {
+ .status = status,
+ .elem_addr = elem_addr,
+ .sub_addr = virt_addr,
+ .mod_id = mod_id,
+ .cid = cid,
+ };
+ int err;
+
+ err = cli_prepare(&param, OP_MOD_SUB_STATUS);
+ if (err) {
+ goto done;
+ }
+
+ BT_DBG("net_idx 0x%04x addr 0x%04x elem_addr 0x%04x label %s",
+ net_idx, addr, elem_addr, label);
+ BT_DBG("mod_id 0x%04x cid 0x%04x", mod_id, cid);
+
+ bt_mesh_model_msg_init(msg, op);
+ net_buf_simple_add_le16(msg, elem_addr);
+ net_buf_simple_add_mem(msg, label, 16);
+
+ if (cid != CID_NVAL) {
+ net_buf_simple_add_le16(msg, cid);
+ }
+
+ net_buf_simple_add_le16(msg, mod_id);
+
+ err = bt_mesh_model_send(cli->model, &ctx, msg, NULL, NULL);
+ if (err) {
+ BT_ERR("model_send() failed (err %d)", err);
+ cli_reset();
+ goto done;
+ }
+
+ if (!status) {
+ cli_reset();
+ goto done;
+ }
+
+ err = cli_wait();
+done:
+ os_mbuf_free_chain(msg);
+ return err;
+}
+
+int bt_mesh_cfg_mod_sub_va_add(u16_t net_idx, u16_t addr, u16_t elem_addr,
+ const u8_t label[16], u16_t mod_id,
+ u16_t *virt_addr, u8_t *status)
+{
+ return mod_sub_va(OP_MOD_SUB_VA_ADD, net_idx, addr, elem_addr, label,
+ mod_id, CID_NVAL, virt_addr, status);
+}
+
+int bt_mesh_cfg_mod_sub_va_add_vnd(u16_t net_idx, u16_t addr, u16_t elem_addr,
+ const u8_t label[16], u16_t mod_id,
+ u16_t cid, u16_t *virt_addr, u8_t *status)
+{
+ if (cid == CID_NVAL) {
+ return -EINVAL;
+ }
+
+ return mod_sub_va(OP_MOD_SUB_VA_ADD, net_idx, addr, elem_addr, label,
+ mod_id, cid, virt_addr, status);
+}
+
+int bt_mesh_cfg_mod_sub_va_del(u16_t net_idx, u16_t addr, u16_t elem_addr,
+ const u8_t label[16], u16_t mod_id,
+ u16_t *virt_addr, u8_t *status)
+{
+ return mod_sub_va(OP_MOD_SUB_VA_DEL, net_idx, addr, elem_addr, label,
+ mod_id, CID_NVAL, virt_addr, status);
+}
+
+int bt_mesh_cfg_mod_sub_va_del_vnd(u16_t net_idx, u16_t addr, u16_t elem_addr,
+ const u8_t label[16], u16_t mod_id,
+ u16_t cid, u16_t *virt_addr, u8_t *status)
+{
+ if (cid == CID_NVAL) {
+ return -EINVAL;
+ }
+
+ return mod_sub_va(OP_MOD_SUB_VA_DEL, net_idx, addr, elem_addr, label,
+ mod_id, cid, virt_addr, status);
+}
+
+int bt_mesh_cfg_mod_sub_va_overwrite(u16_t net_idx, u16_t addr,
+ u16_t elem_addr, const u8_t label[16],
+ u16_t mod_id, u16_t *virt_addr,
+ u8_t *status)
+{
+ return mod_sub_va(OP_MOD_SUB_VA_OVERWRITE, net_idx, addr, elem_addr,
+ label, mod_id, CID_NVAL, virt_addr, status);
+}
+
+int bt_mesh_cfg_mod_sub_va_overwrite_vnd(u16_t net_idx, u16_t addr,
+ u16_t elem_addr, const u8_t label[16],
+ u16_t mod_id, u16_t cid,
+ u16_t *virt_addr, u8_t *status)
+{
+ if (cid == CID_NVAL) {
+ return -EINVAL;
+ }
+
+ return mod_sub_va(OP_MOD_SUB_VA_OVERWRITE, net_idx, addr, elem_addr,
+ label, mod_id, cid, virt_addr, status);
+}
+
+static int mod_pub_get(u16_t net_idx, u16_t addr, u16_t elem_addr,
+ u16_t mod_id, u16_t cid,
+ struct bt_mesh_cfg_mod_pub *pub, u8_t *status)
+{
+ struct os_mbuf *msg = BT_MESH_MODEL_BUF(OP_MOD_PUB_GET, 6);
+ struct bt_mesh_msg_ctx ctx = {
+ .net_idx = net_idx,
+ .app_idx = BT_MESH_KEY_DEV_REMOTE,
+ .addr = addr,
+ .send_ttl = BT_MESH_TTL_DEFAULT,
+ };
+ struct mod_pub_param param = {
+ .mod_id = mod_id,
+ .cid = cid,
+ .elem_addr = elem_addr,
+ .status = status,
+ .pub = pub,
+ };
+ int err;
+
+ err = cli_prepare(&param, OP_MOD_PUB_STATUS);
+ if (err) {
+ goto done;
+ }
+
+ bt_mesh_model_msg_init(msg, OP_MOD_PUB_GET);
+
+ net_buf_simple_add_le16(msg, elem_addr);
+
+ if (cid != CID_NVAL) {
+ net_buf_simple_add_le16(msg, cid);
+ }
+
+ net_buf_simple_add_le16(msg, mod_id);
+
+ err = bt_mesh_model_send(cli->model, &ctx, msg, NULL, NULL);
+ if (err) {
+ BT_ERR("model_send() failed (err %d)", err);
+ cli_reset();
+ goto done;
+ }
+
+ if (!status) {
+ cli_reset();
+ goto done;
+ }
+
+ err = cli_wait();
+done:
+ os_mbuf_free_chain(msg);
+ return err;
+}
+
+int bt_mesh_cfg_mod_pub_get(u16_t net_idx, u16_t addr, u16_t elem_addr,
+ u16_t mod_id, struct bt_mesh_cfg_mod_pub *pub,
+ u8_t *status)
+{
+ return mod_pub_get(net_idx, addr, elem_addr, mod_id, CID_NVAL,
+ pub, status);
+}
+
+int bt_mesh_cfg_mod_pub_get_vnd(u16_t net_idx, u16_t addr, u16_t elem_addr,
+ u16_t mod_id, u16_t cid,
+ struct bt_mesh_cfg_mod_pub *pub, u8_t *status)
+{
+ if (cid == CID_NVAL) {
+ return -EINVAL;
+ }
+
+ return mod_pub_get(net_idx, addr, elem_addr, mod_id, cid, pub, status);
+}
+
+static int mod_pub_set(u16_t net_idx, u16_t addr, u16_t elem_addr,
+ u16_t mod_id, u16_t cid,
+ struct bt_mesh_cfg_mod_pub *pub, u8_t *status)
+{
+ struct os_mbuf *msg = BT_MESH_MODEL_BUF(OP_MOD_PUB_SET, 13);
+ struct bt_mesh_msg_ctx ctx = {
+ .net_idx = net_idx,
+ .app_idx = BT_MESH_KEY_DEV_REMOTE,
+ .addr = addr,
+ .send_ttl = BT_MESH_TTL_DEFAULT,
+ };
+ struct mod_pub_param param = {
+ .mod_id = mod_id,
+ .cid = cid,
+ .elem_addr = elem_addr,
+ .status = status,
+ .pub = pub,
+ };
+ int err;
+
+ err = cli_prepare(&param, OP_MOD_PUB_STATUS);
+ if (err) {
+ goto done;
+ }
+
+ bt_mesh_model_msg_init(msg, OP_MOD_PUB_SET);
+
+ net_buf_simple_add_le16(msg, elem_addr);
+ net_buf_simple_add_le16(msg, pub->addr);
+ net_buf_simple_add_le16(msg, (pub->app_idx | (pub->cred_flag << 12)));
+ net_buf_simple_add_u8(msg, pub->ttl);
+ net_buf_simple_add_u8(msg, pub->period);
+ net_buf_simple_add_u8(msg, pub->transmit);
+
+ if (cid != CID_NVAL) {
+ net_buf_simple_add_le16(msg, cid);
+ }
+
+ net_buf_simple_add_le16(msg, mod_id);
+
+ err = bt_mesh_model_send(cli->model, &ctx, msg, NULL, NULL);
+ if (err) {
+ BT_ERR("model_send() failed (err %d)", err);
+ cli_reset();
+ goto done;
+ }
+
+ if (!status) {
+ cli_reset();
+ goto done;
+ }
+
+ err = cli_wait();
+done:
+ os_mbuf_free_chain(msg);
+ return err;
+}
+
+int bt_mesh_cfg_mod_pub_set(u16_t net_idx, u16_t addr, u16_t elem_addr,
+ u16_t mod_id, struct bt_mesh_cfg_mod_pub *pub,
+ u8_t *status)
+{
+ return mod_pub_set(net_idx, addr, elem_addr, mod_id, CID_NVAL,
+ pub, status);
+}
+
+int bt_mesh_cfg_mod_pub_set_vnd(u16_t net_idx, u16_t addr, u16_t elem_addr,
+ u16_t mod_id, u16_t cid,
+ struct bt_mesh_cfg_mod_pub *pub, u8_t *status)
+{
+ if (cid == CID_NVAL) {
+ return -EINVAL;
+ }
+
+ return mod_pub_set(net_idx, addr, elem_addr, mod_id, cid, pub, status);
+}
+
+int bt_mesh_cfg_hb_sub_set(u16_t net_idx, u16_t addr,
+ struct bt_mesh_cfg_hb_sub *sub, u8_t *status)
+{
+ struct os_mbuf *msg = BT_MESH_MODEL_BUF(OP_HEARTBEAT_SUB_SET, 5);
+ struct bt_mesh_msg_ctx ctx = {
+ .net_idx = net_idx,
+ .app_idx = BT_MESH_KEY_DEV_REMOTE,
+ .addr = addr,
+ .send_ttl = BT_MESH_TTL_DEFAULT,
+ };
+ struct hb_sub_param param = {
+ .status = status,
+ .sub = sub,
+ };
+ int err;
+
+ err = cli_prepare(&param, OP_HEARTBEAT_SUB_STATUS);
+ if (err) {
+ goto done;
+ }
+
+ bt_mesh_model_msg_init(msg, OP_HEARTBEAT_SUB_SET);
+ net_buf_simple_add_le16(msg, sub->src);
+ net_buf_simple_add_le16(msg, sub->dst);
+ net_buf_simple_add_u8(msg, sub->period);
+
+ err = bt_mesh_model_send(cli->model, &ctx, msg, NULL, NULL);
+ if (err) {
+ BT_ERR("model_send() failed (err %d)", err);
+ cli_reset();
+ goto done;
+ }
+
+ if (!status) {
+ cli_reset();
+ goto done;
+ }
+
+ err = cli_wait();
+done:
+ os_mbuf_free_chain(msg);
+ return err;
+}
+
+int bt_mesh_cfg_hb_sub_get(u16_t net_idx, u16_t addr,
+ struct bt_mesh_cfg_hb_sub *sub, u8_t *status)
+{
+ struct os_mbuf *msg = BT_MESH_MODEL_BUF(OP_HEARTBEAT_SUB_GET, 0);
+ struct bt_mesh_msg_ctx ctx = {
+ .net_idx = net_idx,
+ .app_idx = BT_MESH_KEY_DEV_REMOTE,
+ .addr = addr,
+ .send_ttl = BT_MESH_TTL_DEFAULT,
+ };
+ struct hb_sub_param param = {
+ .status = status,
+ .sub = sub,
+ };
+ int err;
+
+ err = cli_prepare(&param, OP_HEARTBEAT_SUB_STATUS);
+ if (err) {
+ goto done;
+ }
+
+ bt_mesh_model_msg_init(msg, OP_HEARTBEAT_SUB_GET);
+
+ err = bt_mesh_model_send(cli->model, &ctx, msg, NULL, NULL);
+ if (err) {
+ BT_ERR("model_send() failed (err %d)", err);
+ cli_reset();
+ goto done;
+ }
+
+ if (!status) {
+ cli_reset();
+ goto done;
+ }
+
+ err = cli_wait();
+done:
+ os_mbuf_free_chain(msg);
+ return err;
+}
+
+int bt_mesh_cfg_hb_pub_set(u16_t net_idx, u16_t addr,
+ const struct bt_mesh_cfg_hb_pub *pub, u8_t *status)
+{
+ struct os_mbuf *msg = BT_MESH_MODEL_BUF(OP_HEARTBEAT_PUB_SET, 9);
+ struct bt_mesh_msg_ctx ctx = {
+ .net_idx = net_idx,
+ .app_idx = BT_MESH_KEY_DEV_REMOTE,
+ .addr = addr,
+ .send_ttl = BT_MESH_TTL_DEFAULT,
+ };
+ struct hb_pub_param param = {
+ .status = status,
+ };
+ int err;
+
+ err = cli_prepare(&param, OP_HEARTBEAT_PUB_STATUS);
+ if (err) {
+ goto done;
+ }
+
+ bt_mesh_model_msg_init(msg, OP_HEARTBEAT_PUB_SET);
+ net_buf_simple_add_le16(msg, pub->dst);
+ net_buf_simple_add_u8(msg, pub->count);
+ net_buf_simple_add_u8(msg, pub->period);
+ net_buf_simple_add_u8(msg, pub->ttl);
+ net_buf_simple_add_le16(msg, pub->feat);
+ net_buf_simple_add_le16(msg, pub->net_idx);
+
+ err = bt_mesh_model_send(cli->model, &ctx, msg, NULL, NULL);
+ if (err) {
+ BT_ERR("model_send() failed (err %d)", err);
+ cli_reset();
+ goto done;
+ }
+
+ if (!status) {
+ cli_reset();
+ goto done;
+ }
+
+ err = cli_wait();
+done:
+ os_mbuf_free_chain(msg);
+ return err;
+}
+
+int bt_mesh_cfg_hb_pub_get(u16_t net_idx, u16_t addr,
+ struct bt_mesh_cfg_hb_pub *pub, u8_t *status)
+{
+ struct os_mbuf *msg = BT_MESH_MODEL_BUF(OP_HEARTBEAT_PUB_GET, 0);
+ struct bt_mesh_msg_ctx ctx = {
+ .net_idx = net_idx,
+ .app_idx = BT_MESH_KEY_DEV,
+ .addr = addr,
+ .send_ttl = BT_MESH_TTL_DEFAULT,
+ };
+ struct hb_pub_param param = {
+ .status = status,
+ .pub = pub,
+ };
+ int err;
+
+ err = cli_prepare(&param, OP_HEARTBEAT_PUB_STATUS);
+ if (err) {
+ goto done;
+ }
+
+ bt_mesh_model_msg_init(msg, OP_HEARTBEAT_PUB_GET);
+
+ err = bt_mesh_model_send(cli->model, &ctx, msg, NULL, NULL);
+ if (err) {
+ BT_ERR("model_send() failed (err %d)", err);
+ cli_reset();
+ goto done;
+ }
+
+ if (!status) {
+ cli_reset();
+ goto done;
+ }
+
+ err = cli_wait();
+done:
+ os_mbuf_free_chain(msg);
+ return err;
+}
+
+s32_t bt_mesh_cfg_cli_timeout_get(void)
+{
+ return msg_timeout;
+}
+
+void bt_mesh_cfg_cli_timeout_set(s32_t timeout)
+{
+ msg_timeout = timeout;
+}
+
+#endif
diff --git a/src/libs/mynewt-nimble/nimble/host/mesh/src/cfg_srv.c b/src/libs/mynewt-nimble/nimble/host/mesh/src/cfg_srv.c
new file mode 100644
index 00000000..57aac90a
--- /dev/null
+++ b/src/libs/mynewt-nimble/nimble/host/mesh/src/cfg_srv.c
@@ -0,0 +1,3619 @@
+/* Bluetooth Mesh */
+
+/*
+ * Copyright (c) 2017 Intel Corporation
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include "syscfg/syscfg.h"
+#define MESH_LOG_MODULE BLE_MESH_MODEL_LOG
+
+#include <string.h>
+#include <errno.h>
+#include <stdbool.h>
+
+#include "mesh/mesh.h"
+
+#include "mesh_priv.h"
+#include "adv.h"
+#include "net.h"
+#include "lpn.h"
+#include "transport.h"
+#include "crypto.h"
+#include "access.h"
+#include "beacon.h"
+#include "proxy.h"
+#include "foundation.h"
+#include "friend.h"
+#include "testing.h"
+#include "settings.h"
+
+#define DEFAULT_TTL 7
+
+static struct bt_mesh_cfg_srv *conf;
+
+static struct label labels[CONFIG_BT_MESH_LABEL_COUNT];
+
+static int comp_add_elem(struct os_mbuf *buf, struct bt_mesh_elem *elem,
+ bool primary)
+{
+ struct bt_mesh_model *mod;
+ int i;
+
+ if (net_buf_simple_tailroom(buf) <
+ 4 + (elem->model_count * 2) + (elem->vnd_model_count * 2)) {
+ BT_ERR("Too large device composition");
+ return -E2BIG;
+ }
+
+ net_buf_simple_add_le16(buf, elem->loc);
+
+ net_buf_simple_add_u8(buf, elem->model_count);
+ net_buf_simple_add_u8(buf, elem->vnd_model_count);
+
+ for (i = 0; i < elem->model_count; i++) {
+ mod = &elem->models[i];
+ net_buf_simple_add_le16(buf, mod->id);
+ }
+
+ for (i = 0; i < elem->vnd_model_count; i++) {
+ mod = &elem->vnd_models[i];
+ net_buf_simple_add_le16(buf, mod->vnd.company);
+ net_buf_simple_add_le16(buf, mod->vnd.id);
+ }
+
+ return 0;
+}
+
+static int comp_get_page_0(struct os_mbuf *buf)
+{
+ u16_t feat = 0;
+ const struct bt_mesh_comp *comp;
+ int i;
+
+ comp = bt_mesh_comp_get();
+
+ if ((MYNEWT_VAL(BLE_MESH_RELAY))) {
+ feat |= BT_MESH_FEAT_RELAY;
+ }
+
+ if ((MYNEWT_VAL(BLE_MESH_GATT_PROXY))) {
+ feat |= BT_MESH_FEAT_PROXY;
+ }
+
+ if ((MYNEWT_VAL(BLE_MESH_FRIEND))) {
+ feat |= BT_MESH_FEAT_FRIEND;
+ }
+
+ if ((MYNEWT_VAL(BLE_MESH_LOW_POWER))) {
+ feat |= BT_MESH_FEAT_LOW_POWER;
+ }
+
+ net_buf_simple_add_le16(buf, comp->cid);
+ net_buf_simple_add_le16(buf, comp->pid);
+ net_buf_simple_add_le16(buf, comp->vid);
+ net_buf_simple_add_le16(buf, MYNEWT_VAL(BLE_MESH_CRPL));
+ net_buf_simple_add_le16(buf, feat);
+
+ for (i = 0; i < comp->elem_count; i++) {
+ int err;
+
+ err = comp_add_elem(buf, &comp->elem[i], i == 0);
+ if (err) {
+ return err;
+ }
+ }
+
+ return 0;
+}
+
+static void dev_comp_data_get(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ struct os_mbuf *sdu = NET_BUF_SIMPLE(BT_MESH_TX_SDU_MAX);
+ u8_t page;
+
+ BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s",
+ ctx->net_idx, ctx->app_idx, ctx->addr, buf->om_len,
+ bt_hex(buf->om_data, buf->om_len));
+
+ page = net_buf_simple_pull_u8(buf);
+ if (page != 0U) {
+ BT_DBG("Composition page %u not available", page);
+ page = 0U;
+ }
+
+ bt_mesh_model_msg_init(sdu, OP_DEV_COMP_DATA_STATUS);
+
+ net_buf_simple_add_u8(sdu, page);
+ if (comp_get_page_0(sdu) < 0) {
+ BT_ERR("Unable to get composition page 0");
+ goto done;
+ }
+
+ if (bt_mesh_model_send(model, ctx, sdu, NULL, NULL)) {
+ BT_ERR("Unable to send Device Composition Status response");
+ }
+
+done:
+ os_mbuf_free_chain(sdu);
+}
+
+static struct bt_mesh_model *get_model(struct bt_mesh_elem *elem,
+ struct os_mbuf *buf, bool *vnd)
+{
+ if (buf->om_len < 4) {
+ u16_t id;
+
+ id = net_buf_simple_pull_le16(buf);
+
+ BT_DBG("ID 0x%04x addr 0x%04x", id, elem->addr);
+
+ *vnd = false;
+
+ return bt_mesh_model_find(elem, id);
+ } else {
+ u16_t company, id;
+
+ company = net_buf_simple_pull_le16(buf);
+ id = net_buf_simple_pull_le16(buf);
+
+ BT_DBG("Company 0x%04x ID 0x%04x addr 0x%04x", company, id,
+ elem->addr);
+
+ *vnd = true;
+
+ return bt_mesh_model_find_vnd(elem, company, id);
+ }
+}
+
+static bool app_key_is_valid(u16_t app_idx)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(bt_mesh.app_keys); i++) {
+ struct bt_mesh_app_key *key = &bt_mesh.app_keys[i];
+
+ if (key->net_idx != BT_MESH_KEY_UNUSED &&
+ key->app_idx == app_idx) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static u8_t _mod_pub_set(struct bt_mesh_model *model, u16_t pub_addr,
+ u16_t app_idx, u8_t cred_flag, u8_t ttl, u8_t period,
+ u8_t retransmit, bool store)
+{
+ if (!model->pub) {
+ return STATUS_NVAL_PUB_PARAM;
+ }
+
+ if (!(MYNEWT_VAL(BLE_MESH_LOW_POWER)) && cred_flag) {
+ return STATUS_FEAT_NOT_SUPP;
+ }
+
+ if (!model->pub->update && period) {
+ return STATUS_NVAL_PUB_PARAM;
+ }
+
+ if (pub_addr == BT_MESH_ADDR_UNASSIGNED) {
+ if (model->pub->addr == BT_MESH_ADDR_UNASSIGNED) {
+ return STATUS_SUCCESS;
+ }
+
+ model->pub->addr = BT_MESH_ADDR_UNASSIGNED;
+ model->pub->key = 0;
+ model->pub->cred = 0;
+ model->pub->ttl = 0;
+ model->pub->period = 0;
+ model->pub->retransmit = 0;
+ model->pub->count = 0;
+
+ if (model->pub->update) {
+ k_delayed_work_cancel(&model->pub->timer);
+ }
+
+ if (IS_ENABLED(CONFIG_BT_SETTINGS) && store) {
+ bt_mesh_store_mod_pub(model);
+ }
+
+ return STATUS_SUCCESS;
+ }
+
+ if (!bt_mesh_app_key_find(app_idx)) {
+ return STATUS_INVALID_APPKEY;
+ }
+
+ model->pub->addr = pub_addr;
+ model->pub->key = app_idx;
+ model->pub->cred = cred_flag;
+ model->pub->ttl = ttl;
+ model->pub->period = period;
+ model->pub->retransmit = retransmit;
+
+ if (model->pub->update) {
+ s32_t period_ms;
+
+ period_ms = bt_mesh_model_pub_period_get(model);
+ BT_DBG("period %u ms", (unsigned) period_ms);
+
+ if (period_ms) {
+ k_delayed_work_submit(&model->pub->timer, period_ms);
+ } else {
+ k_delayed_work_cancel(&model->pub->timer);
+ }
+ }
+
+ if (IS_ENABLED(CONFIG_BT_SETTINGS) && store) {
+ bt_mesh_store_mod_pub(model);
+ }
+
+ return STATUS_SUCCESS;
+}
+
+u8_t mod_bind(struct bt_mesh_model *model, u16_t key_idx)
+{
+ int i;
+
+ BT_DBG("model %p key_idx 0x%03x", model, key_idx);
+
+ if (!app_key_is_valid(key_idx)) {
+ return STATUS_INVALID_APPKEY;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(model->keys); i++) {
+ /* Treat existing binding as success */
+ if (model->keys[i] == key_idx) {
+ return STATUS_SUCCESS;
+ }
+ }
+
+ for (i = 0; i < ARRAY_SIZE(model->keys); i++) {
+ if (model->keys[i] == BT_MESH_KEY_UNUSED) {
+ model->keys[i] = key_idx;
+
+ if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
+ bt_mesh_store_mod_bind(model);
+ }
+
+ return STATUS_SUCCESS;
+ }
+ }
+
+ return STATUS_INSUFF_RESOURCES;
+}
+
+u8_t mod_unbind(struct bt_mesh_model *model, u16_t key_idx, bool store)
+{
+ int i;
+
+ BT_DBG("model %p key_idx 0x%03x store %u", model, key_idx, store);
+
+ if (!app_key_is_valid(key_idx)) {
+ return STATUS_INVALID_APPKEY;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(model->keys); i++) {
+ if (model->keys[i] != key_idx) {
+ continue;
+ }
+
+ model->keys[i] = BT_MESH_KEY_UNUSED;
+
+ if (IS_ENABLED(CONFIG_BT_SETTINGS) && store) {
+ bt_mesh_store_mod_bind(model);
+ }
+
+ if (model->pub && model->pub->key == key_idx) {
+ _mod_pub_set(model, BT_MESH_ADDR_UNASSIGNED,
+ 0, 0, 0, 0, 0, store);
+ }
+ }
+
+ return STATUS_SUCCESS;
+}
+
+struct bt_mesh_app_key *bt_mesh_app_key_alloc(u16_t app_idx)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(bt_mesh.app_keys); i++) {
+ struct bt_mesh_app_key *key = &bt_mesh.app_keys[i];
+
+ if (key->net_idx == BT_MESH_KEY_UNUSED) {
+ return key;
+ }
+ }
+
+ return NULL;
+}
+
+static u8_t app_key_set(u16_t net_idx, u16_t app_idx, const u8_t val[16],
+ bool update)
+{
+ struct bt_mesh_app_keys *keys;
+ struct bt_mesh_app_key *key;
+ struct bt_mesh_subnet *sub;
+
+ BT_DBG("net_idx 0x%04x app_idx %04x update %u val %s",
+ net_idx, app_idx, update, bt_hex(val, 16));
+
+ sub = bt_mesh_subnet_get(net_idx);
+ if (!sub) {
+ return STATUS_INVALID_NETKEY;
+ }
+
+ key = bt_mesh_app_key_find(app_idx);
+ if (update) {
+ if (!key) {
+ return STATUS_INVALID_APPKEY;
+ }
+
+ if (key->net_idx != net_idx) {
+ return STATUS_INVALID_BINDING;
+ }
+
+ keys = &key->keys[1];
+
+ /* The AppKey Update message shall generate an error when node
+ * is in normal operation, Phase 2, or Phase 3 or in Phase 1
+ * when the AppKey Update message on a valid AppKeyIndex when
+ * the AppKey value is different.
+ */
+ if (sub->kr_phase != BT_MESH_KR_PHASE_1) {
+ return STATUS_CANNOT_UPDATE;
+ }
+
+ if (key->updated) {
+ if (memcmp(keys->val, val, 16)) {
+ return STATUS_CANNOT_UPDATE;
+ } else {
+ return STATUS_SUCCESS;
+ }
+ }
+
+ key->updated = true;
+ } else {
+ if (key) {
+ if (key->net_idx == net_idx &&
+ !memcmp(key->keys[0].val, val, 16)) {
+ return STATUS_SUCCESS;
+ }
+
+ if (key->net_idx == net_idx) {
+ return STATUS_IDX_ALREADY_STORED;
+ } else {
+ return STATUS_INVALID_NETKEY;
+ }
+ }
+
+ key = bt_mesh_app_key_alloc(app_idx);
+ if (!key) {
+ return STATUS_INSUFF_RESOURCES;
+ }
+
+ keys = &key->keys[0];
+ }
+
+ if (bt_mesh_app_id(val, &keys->id)) {
+ if (update) {
+ key->updated = false;
+ }
+
+ return STATUS_STORAGE_FAIL;
+ }
+
+ BT_DBG("app_idx 0x%04x AID 0x%02x", app_idx, keys->id);
+
+ key->net_idx = net_idx;
+ key->app_idx = app_idx;
+ memcpy(keys->val, val, 16);
+
+ if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
+ BT_DBG("Storing AppKey persistently");
+ bt_mesh_store_app_key(key);
+ }
+
+ return STATUS_SUCCESS;
+}
+
+static void app_key_add(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ struct os_mbuf *msg = BT_MESH_MODEL_BUF(OP_APP_KEY_STATUS, 4);
+ u16_t key_net_idx, key_app_idx;
+ u8_t status;
+
+ key_idx_unpack(buf, &key_net_idx, &key_app_idx);
+
+ BT_DBG("AppIdx 0x%04x NetIdx 0x%04x", key_app_idx, key_net_idx);
+
+ bt_mesh_model_msg_init(msg, OP_APP_KEY_STATUS);
+
+ status = app_key_set(key_net_idx, key_app_idx, buf->om_data, false);
+ BT_DBG("status 0x%02x", status);
+ net_buf_simple_add_u8(msg, status);
+
+ key_idx_pack(msg, key_net_idx, key_app_idx);
+
+ if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) {
+ BT_ERR("Unable to send App Key Status response");
+ }
+
+ os_mbuf_free_chain(msg);
+}
+
+static void app_key_update(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ struct os_mbuf *msg = BT_MESH_MODEL_BUF(OP_APP_KEY_STATUS, 4);
+ u16_t key_net_idx, key_app_idx;
+ u8_t status;
+
+ key_idx_unpack(buf, &key_net_idx, &key_app_idx);
+
+ BT_DBG("AppIdx 0x%04x NetIdx 0x%04x", key_app_idx, key_net_idx);
+
+ bt_mesh_model_msg_init(msg, OP_APP_KEY_STATUS);
+
+ status = app_key_set(key_net_idx, key_app_idx, buf->om_data, true);
+ BT_DBG("status 0x%02x", status);
+ net_buf_simple_add_u8(msg, status);
+
+ key_idx_pack(msg, key_net_idx, key_app_idx);
+
+ if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) {
+ BT_ERR("Unable to send App Key Status response");
+ }
+
+ os_mbuf_free_chain(msg);
+}
+
+struct unbind_data {
+ u16_t app_idx;
+ bool store;
+};
+
+static void _mod_unbind(struct bt_mesh_model *mod, struct bt_mesh_elem *elem,
+ bool vnd, bool primary, void *user_data)
+{
+ struct unbind_data *data = user_data;
+
+ mod_unbind(mod, data->app_idx, data->store);
+}
+
+void bt_mesh_app_key_del(struct bt_mesh_app_key *key, bool store)
+{
+ struct unbind_data data = { .app_idx = key->app_idx, .store = store };
+
+ BT_DBG("AppIdx 0x%03x store %u", key->app_idx, store);
+
+ bt_mesh_model_foreach(_mod_unbind, &data);
+
+ if (IS_ENABLED(CONFIG_BT_SETTINGS) && store) {
+ bt_mesh_clear_app_key(key);
+ }
+
+ key->net_idx = BT_MESH_KEY_UNUSED;
+ memset(key->keys, 0, sizeof(key->keys));
+}
+
+static void app_key_del(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ struct os_mbuf *msg = BT_MESH_MODEL_BUF(OP_APP_KEY_STATUS, 4);
+ u16_t key_net_idx, key_app_idx;
+ struct bt_mesh_app_key *key;
+ u8_t status;
+
+ key_idx_unpack(buf, &key_net_idx, &key_app_idx);
+
+ BT_DBG("AppIdx 0x%04x NetIdx 0x%04x", key_app_idx, key_net_idx);
+
+ if (!bt_mesh_subnet_get(key_net_idx)) {
+ status = STATUS_INVALID_NETKEY;
+ goto send_status;
+ }
+
+ key = bt_mesh_app_key_find(key_app_idx);
+ if (!key) {
+ /* Treat as success since the client might have missed a
+ * previous response and is resending the request.
+ */
+ status = STATUS_SUCCESS;
+ goto send_status;
+ }
+
+ if (key->net_idx != key_net_idx) {
+ status = STATUS_INVALID_BINDING;
+ goto send_status;
+ }
+
+ bt_mesh_app_key_del(key, true);
+ status = STATUS_SUCCESS;
+
+send_status:
+ bt_mesh_model_msg_init(msg, OP_APP_KEY_STATUS);
+
+ net_buf_simple_add_u8(msg, status);
+
+ key_idx_pack(msg, key_net_idx, key_app_idx);
+
+ if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) {
+ BT_ERR("Unable to send App Key Status response");
+ }
+
+ os_mbuf_free_chain(msg);
+}
+
+/* Index list length: 3 bytes for every pair and 2 bytes for an odd idx */
+#define IDX_LEN(num) (((num) / 2) * 3 + ((num) % 2) * 2)
+
+static void app_key_get(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ struct os_mbuf *msg =
+ BT_MESH_MODEL_BUF(OP_APP_KEY_LIST,
+ 3 + IDX_LEN(CONFIG_BT_MESH_APP_KEY_COUNT));
+ u16_t get_idx, i, prev;
+ u8_t status;
+
+ get_idx = net_buf_simple_pull_le16(buf);
+ if (get_idx > 0xfff) {
+ BT_ERR("Invalid NetKeyIndex 0x%04x", get_idx);
+ goto done;
+ }
+
+ BT_DBG("idx 0x%04x", get_idx);
+
+ bt_mesh_model_msg_init(msg, OP_APP_KEY_LIST);
+
+ if (!bt_mesh_subnet_get(get_idx)) {
+ status = STATUS_INVALID_NETKEY;
+ } else {
+ status = STATUS_SUCCESS;
+ }
+
+ net_buf_simple_add_u8(msg, status);
+ net_buf_simple_add_le16(msg, get_idx);
+
+ if (status != STATUS_SUCCESS) {
+ goto send_status;
+ }
+
+ prev = BT_MESH_KEY_UNUSED;
+ for (i = 0; i < ARRAY_SIZE(bt_mesh.app_keys); i++) {
+ struct bt_mesh_app_key *key = &bt_mesh.app_keys[i];
+
+ if (key->net_idx != get_idx) {
+ continue;
+ }
+
+ if (prev == BT_MESH_KEY_UNUSED) {
+ prev = key->app_idx;
+ continue;
+ }
+
+ key_idx_pack(msg, prev, key->app_idx);
+ prev = BT_MESH_KEY_UNUSED;
+ }
+
+ if (prev != BT_MESH_KEY_UNUSED) {
+ net_buf_simple_add_le16(msg, prev);
+ }
+
+send_status:
+ if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) {
+ BT_ERR("Unable to send AppKey List");
+ }
+
+done:
+ os_mbuf_free_chain(msg);
+}
+
+static void beacon_get(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ struct os_mbuf *msg = BT_MESH_MODEL_BUF(OP_BEACON_STATUS, 1);
+
+ BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s",
+ ctx->net_idx, ctx->app_idx, ctx->addr, buf->om_len,
+ bt_hex(buf->om_data, buf->om_len));
+
+ bt_mesh_model_msg_init(msg, OP_BEACON_STATUS);
+ net_buf_simple_add_u8(msg, bt_mesh_beacon_get());
+
+ if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) {
+ BT_ERR("Unable to send Config Beacon Status response");
+ }
+ os_mbuf_free_chain(msg);
+}
+
+static void beacon_set(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ struct os_mbuf *msg = BT_MESH_MODEL_BUF(OP_BEACON_STATUS, 1);
+ struct bt_mesh_cfg_srv *cfg = model->user_data;
+
+ BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s",
+ ctx->net_idx, ctx->app_idx, ctx->addr, buf->om_len,
+ bt_hex(buf->om_data, buf->om_len));
+
+ if (!cfg) {
+ BT_WARN("No Configuration Server context available");
+ } else if (buf->om_data[0] == 0x00 || buf->om_data[0] == 0x01) {
+ if (buf->om_data[0] != cfg->beacon) {
+ cfg->beacon = buf->om_data[0];
+
+ if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
+ bt_mesh_store_cfg();
+ }
+
+ if (cfg->beacon) {
+ bt_mesh_beacon_enable();
+ } else {
+ bt_mesh_beacon_disable();
+ }
+ }
+ } else {
+ BT_WARN("Invalid Config Beacon value 0x%02x", buf->om_data[0]);
+ goto done;
+ }
+
+ bt_mesh_model_msg_init(msg, OP_BEACON_STATUS);
+ net_buf_simple_add_u8(msg, bt_mesh_beacon_get());
+
+ if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) {
+ BT_ERR("Unable to send Config Beacon Status response");
+ }
+
+done:
+ os_mbuf_free_chain(msg);
+
+}
+
+static void default_ttl_get(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ struct os_mbuf *msg = BT_MESH_MODEL_BUF(OP_DEFAULT_TTL_STATUS, 1);
+
+ BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s",
+ ctx->net_idx, ctx->app_idx, ctx->addr, buf->om_len,
+ bt_hex(buf->om_data, buf->om_len));
+
+ bt_mesh_model_msg_init(msg, OP_DEFAULT_TTL_STATUS);
+ net_buf_simple_add_u8(msg, bt_mesh_default_ttl_get());
+
+ if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) {
+ BT_ERR("Unable to send Default TTL Status response");
+ }
+
+ os_mbuf_free_chain(msg);
+
+}
+
+static void default_ttl_set(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ struct os_mbuf *msg = BT_MESH_MODEL_BUF(OP_DEFAULT_TTL_STATUS, 1);
+ struct bt_mesh_cfg_srv *cfg = model->user_data;
+
+ BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s",
+ ctx->net_idx, ctx->app_idx, ctx->addr, buf->om_len,
+ bt_hex(buf->om_data, buf->om_len));
+
+ if (!cfg) {
+ BT_WARN("No Configuration Server context available");
+ } else if (buf->om_data[0] <= BT_MESH_TTL_MAX && buf->om_data[0] != 0x01) {
+ if (cfg->default_ttl != buf->om_data[0]) {
+ cfg->default_ttl = buf->om_data[0];
+
+ if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
+ bt_mesh_store_cfg();
+ }
+ }
+ } else {
+ BT_WARN("Prohibited Default TTL value 0x%02x", buf->om_data[0]);
+ goto done;
+ }
+
+ bt_mesh_model_msg_init(msg, OP_DEFAULT_TTL_STATUS);
+ net_buf_simple_add_u8(msg, bt_mesh_default_ttl_get());
+
+ if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) {
+ BT_ERR("Unable to send Default TTL Status response");
+ }
+
+done:
+ os_mbuf_free_chain(msg);
+}
+
+static void send_gatt_proxy_status(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx)
+{
+ struct os_mbuf *msg = BT_MESH_MODEL_BUF(OP_GATT_PROXY_STATUS, 1);
+
+ bt_mesh_model_msg_init(msg, OP_GATT_PROXY_STATUS);
+ net_buf_simple_add_u8(msg, bt_mesh_gatt_proxy_get());
+
+ if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) {
+ BT_ERR("Unable to send GATT Proxy Status");
+ }
+
+ os_mbuf_free_chain(msg);
+
+}
+
+static void gatt_proxy_get(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s",
+ ctx->net_idx, ctx->app_idx, ctx->addr, buf->om_len,
+ bt_hex(buf->om_data, buf->om_len));
+
+ send_gatt_proxy_status(model, ctx);
+}
+
+static void gatt_proxy_set(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ struct bt_mesh_cfg_srv *cfg = model->user_data;
+
+ BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s",
+ ctx->net_idx, ctx->app_idx, ctx->addr, buf->om_len,
+ bt_hex(buf->om_data, buf->om_len));
+
+ if (buf->om_data[0] != 0x00 && buf->om_data[0] != 0x01) {
+ BT_WARN("Invalid GATT Proxy value 0x%02x", buf->om_data[0]);
+ return;
+ }
+
+ if (!(MYNEWT_VAL(BLE_MESH_GATT_PROXY)) ||
+ bt_mesh_gatt_proxy_get() == BT_MESH_GATT_PROXY_NOT_SUPPORTED) {
+ goto send_status;
+ }
+
+ if (!cfg) {
+ BT_WARN("No Configuration Server context available");
+ goto send_status;
+ }
+
+ BT_DBG("GATT Proxy 0x%02x -> 0x%02x", cfg->gatt_proxy, buf->om_data[0]);
+
+ if (cfg->gatt_proxy == buf->om_data[0]) {
+ goto send_status;
+ }
+
+ cfg->gatt_proxy = buf->om_data[0];
+
+ if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
+ bt_mesh_store_cfg();
+ }
+
+ bt_mesh_adv_update();
+
+ if (cfg->hb_pub.feat & BT_MESH_FEAT_PROXY) {
+ bt_mesh_heartbeat_send();
+ }
+
+send_status:
+ send_gatt_proxy_status(model, ctx);
+}
+
+static void net_transmit_get(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ struct os_mbuf *msg = BT_MESH_MODEL_BUF(OP_NET_TRANSMIT_STATUS, 1);
+
+ BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s",
+ ctx->net_idx, ctx->app_idx, ctx->addr, buf->om_len,
+ bt_hex(buf->om_data, buf->om_len));
+
+ bt_mesh_model_msg_init(msg, OP_NET_TRANSMIT_STATUS);
+ net_buf_simple_add_u8(msg, bt_mesh_net_transmit_get());
+
+ if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) {
+ BT_ERR("Unable to send Config Network Transmit Status");
+ }
+
+ os_mbuf_free_chain(msg);
+
+}
+
+static void net_transmit_set(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ struct os_mbuf *msg = BT_MESH_MODEL_BUF(OP_NET_TRANSMIT_STATUS, 1);
+ struct bt_mesh_cfg_srv *cfg = model->user_data;
+
+ BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s",
+ ctx->net_idx, ctx->app_idx, ctx->addr, buf->om_len,
+ bt_hex(buf->om_data, buf->om_len));
+
+ BT_DBG("Transmit 0x%02x (count %u interval %ums)", buf->om_data[0],
+ BT_MESH_TRANSMIT_COUNT(buf->om_data[0]),
+ BT_MESH_TRANSMIT_INT(buf->om_data[0]));
+
+ if (!cfg) {
+ BT_WARN("No Configuration Server context available");
+ } else {
+ cfg->net_transmit = buf->om_data[0];
+
+ if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
+ bt_mesh_store_cfg();
+ }
+ }
+
+ bt_mesh_model_msg_init(msg, OP_NET_TRANSMIT_STATUS);
+ net_buf_simple_add_u8(msg, bt_mesh_net_transmit_get());
+
+ if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) {
+ BT_ERR("Unable to send Network Transmit Status");
+ }
+
+ os_mbuf_free_chain(msg);
+}
+
+static void relay_get(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ struct os_mbuf *msg = BT_MESH_MODEL_BUF(OP_RELAY_STATUS, 2);
+
+ BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s",
+ ctx->net_idx, ctx->app_idx, ctx->addr, buf->om_len,
+ bt_hex(buf->om_data, buf->om_len));
+
+ bt_mesh_model_msg_init(msg, OP_RELAY_STATUS);
+ net_buf_simple_add_u8(msg, bt_mesh_relay_get());
+ net_buf_simple_add_u8(msg, bt_mesh_relay_retransmit_get());
+
+ if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) {
+ BT_ERR("Unable to send Config Relay Status response");
+ }
+
+ os_mbuf_free_chain(msg);
+
+}
+
+static void relay_set(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ struct os_mbuf *msg = BT_MESH_MODEL_BUF(OP_RELAY_STATUS, 2);
+ struct bt_mesh_cfg_srv *cfg = model->user_data;
+
+ BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s",
+ ctx->net_idx, ctx->app_idx, ctx->addr, buf->om_len,
+ bt_hex(buf->om_data, buf->om_len));
+
+ if (!cfg) {
+ BT_WARN("No Configuration Server context available");
+ } else if (buf->om_data[0] == 0x00 || buf->om_data[0] == 0x01) {
+ bool change;
+
+ if (cfg->relay == BT_MESH_RELAY_NOT_SUPPORTED) {
+ change = false;
+ } else {
+ change = (cfg->relay != buf->om_data[0]);
+ cfg->relay = buf->om_data[0];
+ cfg->relay_retransmit = buf->om_data[1];
+
+ if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
+ bt_mesh_store_cfg();
+ }
+ }
+
+ BT_DBG("Relay 0x%02x (%s) xmit 0x%02x (count %u interval %u)",
+ cfg->relay, change ? "changed" : "not changed",
+ cfg->relay_retransmit,
+ BT_MESH_TRANSMIT_COUNT(cfg->relay_retransmit),
+ BT_MESH_TRANSMIT_INT(cfg->relay_retransmit));
+
+ if ((cfg->hb_pub.feat & BT_MESH_FEAT_RELAY) && change) {
+ bt_mesh_heartbeat_send();
+ }
+ } else {
+ BT_WARN("Invalid Relay value 0x%02x", buf->om_data[0]);
+ goto done;
+ }
+
+ bt_mesh_model_msg_init(msg, OP_RELAY_STATUS);
+ net_buf_simple_add_u8(msg, bt_mesh_relay_get());
+ net_buf_simple_add_u8(msg, bt_mesh_relay_retransmit_get());
+
+ if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) {
+ BT_ERR("Unable to send Relay Status response");
+ }
+
+done:
+ os_mbuf_free_chain(msg);
+
+}
+
+static void send_mod_pub_status(struct bt_mesh_model *cfg_mod,
+ struct bt_mesh_msg_ctx *ctx,
+ u16_t elem_addr, u16_t pub_addr,
+ bool vnd, struct bt_mesh_model *mod,
+ u8_t status, u8_t *mod_id)
+{
+ struct os_mbuf *msg = BT_MESH_MODEL_BUF(OP_MOD_PUB_STATUS, 14);
+
+ bt_mesh_model_msg_init(msg, OP_MOD_PUB_STATUS);
+
+ net_buf_simple_add_u8(msg, status);
+ net_buf_simple_add_le16(msg, elem_addr);
+
+ if (status != STATUS_SUCCESS) {
+ memset(net_buf_simple_add(msg, 7), 0, 7);
+ } else {
+ u16_t idx_cred;
+
+ net_buf_simple_add_le16(msg, pub_addr);
+
+ idx_cred = mod->pub->key | (u16_t)mod->pub->cred << 12;
+ net_buf_simple_add_le16(msg, idx_cred);
+ net_buf_simple_add_u8(msg, mod->pub->ttl);
+ net_buf_simple_add_u8(msg, mod->pub->period);
+ net_buf_simple_add_u8(msg, mod->pub->retransmit);
+ }
+
+ if (vnd) {
+ memcpy(net_buf_simple_add(msg, 4), mod_id, 4);
+ } else {
+ memcpy(net_buf_simple_add(msg, 2), mod_id, 2);
+ }
+
+ if (bt_mesh_model_send(cfg_mod, ctx, msg, NULL, NULL)) {
+ BT_ERR("Unable to send Model Publication Status");
+ }
+
+ os_mbuf_free_chain(msg);
+}
+
+static void mod_pub_get(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ u16_t elem_addr, pub_addr = 0;
+ struct bt_mesh_model *mod;
+ struct bt_mesh_elem *elem;
+ u8_t *mod_id, status;
+ bool vnd;
+
+ elem_addr = net_buf_simple_pull_le16(buf);
+ if (!BT_MESH_ADDR_IS_UNICAST(elem_addr)) {
+ BT_WARN("Prohibited element address");
+ return;
+ }
+
+ mod_id = buf->om_data;
+
+ BT_DBG("elem_addr 0x%04x", elem_addr);
+
+ elem = bt_mesh_elem_find(elem_addr);
+ if (!elem) {
+ mod = NULL;
+ vnd = (buf->om_len == 4);
+ status = STATUS_INVALID_ADDRESS;
+ goto send_status;
+ }
+
+ mod = get_model(elem, buf, &vnd);
+ if (!mod) {
+ status = STATUS_INVALID_MODEL;
+ goto send_status;
+ }
+
+ if (!mod->pub) {
+ status = STATUS_NVAL_PUB_PARAM;
+ goto send_status;
+ }
+
+ pub_addr = mod->pub->addr;
+ status = STATUS_SUCCESS;
+
+send_status:
+ send_mod_pub_status(model, ctx, elem_addr, pub_addr, vnd, mod,
+ status, mod_id);
+}
+
+static void mod_pub_set(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ u8_t retransmit, status, pub_ttl, pub_period, cred_flag;
+ u16_t elem_addr, pub_addr, pub_app_idx;
+ struct bt_mesh_model *mod;
+ struct bt_mesh_elem *elem;
+ u8_t *mod_id;
+ bool vnd;
+
+ elem_addr = net_buf_simple_pull_le16(buf);
+ if (!BT_MESH_ADDR_IS_UNICAST(elem_addr)) {
+ BT_WARN("Prohibited element address");
+ return;
+ }
+
+ pub_addr = net_buf_simple_pull_le16(buf);
+ pub_app_idx = net_buf_simple_pull_le16(buf);
+ cred_flag = ((pub_app_idx >> 12) & BIT_MASK(1));
+ pub_app_idx &= BIT_MASK(12);
+
+ pub_ttl = net_buf_simple_pull_u8(buf);
+ if (pub_ttl > BT_MESH_TTL_MAX && pub_ttl != BT_MESH_TTL_DEFAULT) {
+ BT_ERR("Invalid TTL value 0x%02x", pub_ttl);
+ return;
+ }
+
+ pub_period = net_buf_simple_pull_u8(buf);
+ retransmit = net_buf_simple_pull_u8(buf);
+ mod_id = buf->om_data;
+
+ BT_DBG("elem_addr 0x%04x pub_addr 0x%04x cred_flag %u",
+ elem_addr, pub_addr, cred_flag);
+ BT_DBG("pub_app_idx 0x%03x, pub_ttl %u pub_period 0x%02x",
+ pub_app_idx, pub_ttl, pub_period);
+ BT_DBG("retransmit 0x%02x (count %u interval %ums)", retransmit,
+ BT_MESH_PUB_TRANSMIT_COUNT(retransmit),
+ BT_MESH_PUB_TRANSMIT_INT(retransmit));
+
+ elem = bt_mesh_elem_find(elem_addr);
+ if (!elem) {
+ mod = NULL;
+ vnd = (buf->om_len == 4);
+ status = STATUS_INVALID_ADDRESS;
+ goto send_status;
+ }
+
+ mod = get_model(elem, buf, &vnd);
+ if (!mod) {
+ status = STATUS_INVALID_MODEL;
+ goto send_status;
+ }
+
+ status = _mod_pub_set(mod, pub_addr, pub_app_idx, cred_flag, pub_ttl,
+ pub_period, retransmit, true);
+
+send_status:
+ send_mod_pub_status(model, ctx, elem_addr, pub_addr, vnd, mod,
+ status, mod_id);
+}
+
+struct label *get_label(u16_t index)
+{
+ if (index >= ARRAY_SIZE(labels)) {
+ return NULL;
+ }
+
+ return &labels[index];
+}
+
+#if CONFIG_BT_MESH_LABEL_COUNT > 0
+static inline void va_store(struct label *store)
+{
+ atomic_set_bit(store->flags, BT_MESH_VA_CHANGED);
+ if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
+ bt_mesh_store_label();
+ }
+}
+
+static struct label *va_find(const u8_t *label_uuid,
+ struct label **free_slot)
+{
+ struct label *match = NULL;
+ int i;
+
+ if (free_slot != NULL) {
+ *free_slot = NULL;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(labels); i++) {
+ if (labels[i].ref == 0) {
+ if (free_slot != NULL) {
+ *free_slot = &labels[i];
+ }
+ continue;
+ }
+
+ if (!memcmp(labels[i].uuid, label_uuid, 16)) {
+ match = &labels[i];
+ }
+ }
+
+ return match;
+}
+
+static u8_t va_add(u8_t *label_uuid, u16_t *addr)
+{
+ struct label *update, *free_slot = NULL;
+
+ update = va_find(label_uuid, &free_slot);
+ if (update) {
+ update->ref++;
+ va_store(update);
+ return 0;
+ }
+
+ if (!free_slot) {
+ return STATUS_INSUFF_RESOURCES;
+ }
+
+ if (bt_mesh_virtual_addr(label_uuid, addr) < 0) {
+ return STATUS_UNSPECIFIED;
+ }
+
+ free_slot->ref = 1;
+ free_slot->addr = *addr;
+ memcpy(free_slot->uuid, label_uuid, 16);
+ va_store(free_slot);
+
+ return STATUS_SUCCESS;
+}
+
+static u8_t va_del(u8_t *label_uuid, u16_t *addr)
+{
+ struct label *update;
+
+ update = va_find(label_uuid, NULL);
+ if (update) {
+ update->ref--;
+
+ if (addr) {
+ *addr = update->addr;
+ }
+
+ va_store(update);
+ }
+
+ if (addr) {
+ *addr = BT_MESH_ADDR_UNASSIGNED;
+ }
+
+ return STATUS_CANNOT_REMOVE;
+}
+
+static size_t mod_sub_list_clear(struct bt_mesh_model *mod)
+{
+ u8_t *label_uuid;
+ size_t clear_count;
+ int i;
+
+ /* Unref stored labels related to this model */
+ for (i = 0, clear_count = 0; i < ARRAY_SIZE(mod->groups); i++) {
+ if (!BT_MESH_ADDR_IS_VIRTUAL(mod->groups[i])) {
+ if (mod->groups[i] != BT_MESH_ADDR_UNASSIGNED) {
+ mod->groups[i] = BT_MESH_ADDR_UNASSIGNED;
+ clear_count++;
+ }
+
+ continue;
+ }
+
+ label_uuid = bt_mesh_label_uuid_get(mod->groups[i]);
+
+ mod->groups[i] = BT_MESH_ADDR_UNASSIGNED;
+ clear_count++;
+
+ if (label_uuid) {
+ va_del(label_uuid, NULL);
+ } else {
+ BT_ERR("Label UUID not found");
+ }
+ }
+
+ return clear_count;
+}
+
+static void mod_pub_va_set(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ u8_t retransmit, status, pub_ttl, pub_period, cred_flag;
+ u16_t elem_addr, pub_addr, pub_app_idx;
+ struct bt_mesh_model *mod;
+ struct bt_mesh_elem *elem;
+ u8_t *label_uuid;
+ u8_t *mod_id;
+ bool vnd;
+
+ elem_addr = net_buf_simple_pull_le16(buf);
+ if (!BT_MESH_ADDR_IS_UNICAST(elem_addr)) {
+ BT_WARN("Prohibited element address");
+ return;
+ }
+
+ label_uuid = net_buf_simple_pull_mem(buf, 16);
+ pub_app_idx = net_buf_simple_pull_le16(buf);
+ cred_flag = ((pub_app_idx >> 12) & BIT_MASK(1));
+ pub_app_idx &= BIT_MASK(12);
+ pub_ttl = net_buf_simple_pull_u8(buf);
+ if (pub_ttl > BT_MESH_TTL_MAX && pub_ttl != BT_MESH_TTL_DEFAULT) {
+ BT_ERR("Invalid TTL value 0x%02x", pub_ttl);
+ return;
+ }
+
+ pub_period = net_buf_simple_pull_u8(buf);
+ retransmit = net_buf_simple_pull_u8(buf);
+ mod_id = buf->om_data;
+
+ BT_DBG("elem_addr 0x%04x cred_flag %u", elem_addr, cred_flag);
+ BT_DBG("pub_app_idx 0x%03x, pub_ttl %u pub_period 0x%02x",
+ pub_app_idx, pub_ttl, pub_period);
+ BT_DBG("retransmit 0x%02x (count %u interval %ums)", retransmit,
+ BT_MESH_PUB_TRANSMIT_COUNT(retransmit),
+ BT_MESH_PUB_TRANSMIT_INT(retransmit));
+
+ elem = bt_mesh_elem_find(elem_addr);
+ if (!elem) {
+ mod = NULL;
+ vnd = (buf->om_len == 4);
+ pub_addr = 0;
+ status = STATUS_INVALID_ADDRESS;
+ goto send_status;
+ }
+
+ mod = get_model(elem, buf, &vnd);
+ if (!mod) {
+ pub_addr = 0;
+ status = STATUS_INVALID_MODEL;
+ goto send_status;
+ }
+
+ status = va_add(label_uuid, &pub_addr);
+ if (status == STATUS_SUCCESS) {
+ status = _mod_pub_set(mod, pub_addr, pub_app_idx, cred_flag,
+ pub_ttl, pub_period, retransmit, true);
+ }
+
+send_status:
+ send_mod_pub_status(model, ctx, elem_addr, pub_addr, vnd, mod,
+ status, mod_id);
+}
+#else
+static size_t mod_sub_list_clear(struct bt_mesh_model *mod)
+{
+ size_t clear_count;
+ int i;
+
+ /* Unref stored labels related to this model */
+ for (i = 0, clear_count = 0; i < ARRAY_SIZE(mod->groups); i++) {
+ if (mod->groups[i] != BT_MESH_ADDR_UNASSIGNED) {
+ mod->groups[i] = BT_MESH_ADDR_UNASSIGNED;
+ clear_count++;
+ }
+ }
+
+ return clear_count;
+}
+
+static void mod_pub_va_set(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ u8_t *mod_id, status;
+ struct bt_mesh_model *mod;
+ struct bt_mesh_elem *elem;
+ u16_t elem_addr, pub_addr = 0;
+ bool vnd;
+
+ elem_addr = net_buf_simple_pull_le16(buf);
+ if (!BT_MESH_ADDR_IS_UNICAST(elem_addr)) {
+ BT_WARN("Prohibited element address");
+ return;
+ }
+
+ net_buf_simple_pull(buf, 16);
+ mod_id = net_buf_simple_pull(buf, 4);
+
+ BT_DBG("elem_addr 0x%04x", elem_addr);
+
+ elem = bt_mesh_elem_find(elem_addr);
+ if (!elem) {
+ mod = NULL;
+ vnd = (buf->om_len == 4);
+ status = STATUS_INVALID_ADDRESS;
+ goto send_status;
+ }
+
+ mod = get_model(elem, buf, &vnd);
+ if (!mod) {
+ status = STATUS_INVALID_MODEL;
+ goto send_status;
+ }
+
+ if (!mod->pub) {
+ status = STATUS_NVAL_PUB_PARAM;
+ goto send_status;
+ }
+
+ pub_addr = mod->pub->addr;
+ status = STATUS_INSUFF_RESOURCES;
+
+send_status:
+ send_mod_pub_status(model, ctx, elem_addr, pub_addr, vnd, mod,
+ status, mod_id);
+}
+#endif /* MYNEWT_VAL(BLE_MESH_LABEL_COUNT) > 0 */
+
+static void send_mod_sub_status(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx, u8_t status,
+ u16_t elem_addr, u16_t sub_addr, u8_t *mod_id,
+ bool vnd)
+{
+ struct os_mbuf *msg = BT_MESH_MODEL_BUF(OP_MOD_SUB_STATUS, 9);
+
+ BT_DBG("status 0x%02x elem_addr 0x%04x sub_addr 0x%04x", status,
+ elem_addr, sub_addr);
+
+ bt_mesh_model_msg_init(msg, OP_MOD_SUB_STATUS);
+
+ net_buf_simple_add_u8(msg, status);
+ net_buf_simple_add_le16(msg, elem_addr);
+ net_buf_simple_add_le16(msg, sub_addr);
+
+ if (vnd) {
+ memcpy(net_buf_simple_add(msg, 4), mod_id, 4);
+ } else {
+ memcpy(net_buf_simple_add(msg, 2), mod_id, 2);
+ }
+
+ if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) {
+ BT_ERR("Unable to send Model Subscription Status");
+ }
+
+ os_mbuf_free_chain(msg);
+}
+
+static void mod_sub_add(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ u16_t elem_addr, sub_addr;
+ struct bt_mesh_model *mod;
+ struct bt_mesh_elem *elem;
+ u8_t *mod_id;
+ u8_t status;
+ u16_t *entry;
+ bool vnd;
+
+ elem_addr = net_buf_simple_pull_le16(buf);
+ if (!BT_MESH_ADDR_IS_UNICAST(elem_addr)) {
+ BT_WARN("Prohibited element address");
+ return;
+ }
+
+ sub_addr = net_buf_simple_pull_le16(buf);
+
+ BT_DBG("elem_addr 0x%04x, sub_addr 0x%04x", elem_addr, sub_addr);
+
+ mod_id = buf->om_data;
+
+ elem = bt_mesh_elem_find(elem_addr);
+ if (!elem) {
+ mod = NULL;
+ vnd = (buf->om_len == 4);
+ status = STATUS_INVALID_ADDRESS;
+ goto send_status;
+ }
+
+ mod = get_model(elem, buf, &vnd);
+ if (!mod) {
+ status = STATUS_INVALID_MODEL;
+ goto send_status;
+ }
+
+ if (!BT_MESH_ADDR_IS_GROUP(sub_addr)) {
+ status = STATUS_INVALID_ADDRESS;
+ goto send_status;
+ }
+
+ if (bt_mesh_model_find_group(&mod, sub_addr)) {
+ /* Tried to add existing subscription */
+ BT_DBG("found existing subscription");
+ status = STATUS_SUCCESS;
+ goto send_status;
+ }
+
+ entry = bt_mesh_model_find_group(&mod, BT_MESH_ADDR_UNASSIGNED);
+ if (!entry) {
+ status = STATUS_INSUFF_RESOURCES;
+ goto send_status;
+ }
+
+ *entry = sub_addr;
+ status = STATUS_SUCCESS;
+
+ if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
+ bt_mesh_store_mod_sub(mod);
+ }
+
+ if (IS_ENABLED(CONFIG_BT_MESH_LOW_POWER)) {
+ bt_mesh_lpn_group_add(sub_addr);
+ }
+
+
+send_status:
+ send_mod_sub_status(model, ctx, status, elem_addr, sub_addr,
+ mod_id, vnd);
+}
+
+static void mod_sub_del(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ u16_t elem_addr, sub_addr;
+ struct bt_mesh_model *mod;
+ struct bt_mesh_elem *elem;
+ u8_t *mod_id;
+ u16_t *match;
+ u8_t status;
+ bool vnd;
+
+ elem_addr = net_buf_simple_pull_le16(buf);
+ if (!BT_MESH_ADDR_IS_UNICAST(elem_addr)) {
+ BT_WARN("Prohibited element address");
+ return;
+ }
+
+ sub_addr = net_buf_simple_pull_le16(buf);
+
+ BT_DBG("elem_addr 0x%04x sub_addr 0x%04x", elem_addr, sub_addr);
+
+ mod_id = buf->om_data;
+
+ elem = bt_mesh_elem_find(elem_addr);
+ if (!elem) {
+ mod = NULL;
+ vnd = (buf->om_len == 4);
+ status = STATUS_INVALID_ADDRESS;
+ goto send_status;
+ }
+
+ mod = get_model(elem, buf, &vnd);
+ if (!mod) {
+ status = STATUS_INVALID_MODEL;
+ goto send_status;
+ }
+
+ if (!BT_MESH_ADDR_IS_GROUP(sub_addr)) {
+ status = STATUS_INVALID_ADDRESS;
+ goto send_status;
+ }
+
+ /* An attempt to remove a non-existing address shall be treated
+ * as a success.
+ */
+ status = STATUS_SUCCESS;
+
+ if ((MYNEWT_VAL(BLE_MESH_LOW_POWER))) {
+ bt_mesh_lpn_group_del(&sub_addr, 1);
+ }
+
+ match = bt_mesh_model_find_group(&mod, sub_addr);
+ if (match) {
+ *match = BT_MESH_ADDR_UNASSIGNED;
+
+ if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
+ bt_mesh_store_mod_sub(mod);
+ }
+ }
+
+send_status:
+ send_mod_sub_status(model, ctx, status, elem_addr, sub_addr,
+ mod_id, vnd);
+}
+
+static enum bt_mesh_walk mod_sub_clear_visitor(struct bt_mesh_model *mod,
+ u32_t depth, void *user_data)
+{
+ if (IS_ENABLED(CONFIG_BT_MESH_LOW_POWER)) {
+ bt_mesh_lpn_group_del(mod->groups, ARRAY_SIZE(mod->groups));
+ }
+
+ mod_sub_list_clear(mod);
+
+ return BT_MESH_WALK_CONTINUE;
+}
+
+static void mod_sub_overwrite(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ u16_t elem_addr, sub_addr;
+ struct bt_mesh_model *mod;
+ struct bt_mesh_elem *elem;
+ u8_t *mod_id;
+ u8_t status;
+ bool vnd;
+
+ elem_addr = net_buf_simple_pull_le16(buf);
+ if (!BT_MESH_ADDR_IS_UNICAST(elem_addr)) {
+ BT_WARN("Prohibited element address");
+ return;
+ }
+
+ sub_addr = net_buf_simple_pull_le16(buf);
+
+ BT_DBG("elem_addr 0x%04x sub_addr 0x%04x", elem_addr, sub_addr);
+
+ mod_id = buf->om_data;
+
+ elem = bt_mesh_elem_find(elem_addr);
+ if (!elem) {
+ mod = NULL;
+ vnd = (buf->om_len == 4);
+ status = STATUS_INVALID_ADDRESS;
+ goto send_status;
+ }
+
+ mod = get_model(elem, buf, &vnd);
+ if (!mod) {
+ status = STATUS_INVALID_MODEL;
+ goto send_status;
+ }
+
+ if (!BT_MESH_ADDR_IS_GROUP(sub_addr)) {
+ status = STATUS_INVALID_ADDRESS;
+ goto send_status;
+ }
+
+ if (ARRAY_SIZE(mod->groups) > 0) {
+ bt_mesh_model_tree_walk(bt_mesh_model_root(mod),
+ mod_sub_clear_visitor, NULL);
+
+ mod->groups[0] = sub_addr;
+ status = STATUS_SUCCESS;
+
+ if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
+ bt_mesh_store_mod_sub(mod);
+ }
+
+ if (IS_ENABLED(CONFIG_BT_MESH_LOW_POWER)) {
+ bt_mesh_lpn_group_add(sub_addr);
+ }
+ } else {
+ status = STATUS_INSUFF_RESOURCES;
+ }
+
+
+send_status:
+ send_mod_sub_status(model, ctx, status, elem_addr, sub_addr,
+ mod_id, vnd);
+}
+
+static void mod_sub_del_all(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ struct bt_mesh_model *mod;
+ struct bt_mesh_elem *elem;
+ u16_t elem_addr;
+ u8_t *mod_id;
+ u8_t status;
+ bool vnd;
+
+ elem_addr = net_buf_simple_pull_le16(buf);
+ if (!BT_MESH_ADDR_IS_UNICAST(elem_addr)) {
+ BT_WARN("Prohibited element address");
+ return;
+ }
+
+ BT_DBG("elem_addr 0x%04x", elem_addr);
+
+ mod_id = buf->om_data;
+
+ elem = bt_mesh_elem_find(elem_addr);
+ if (!elem) {
+ mod = NULL;
+ vnd = (buf->om_len == 4);
+ status = STATUS_INVALID_ADDRESS;
+ goto send_status;
+ }
+
+ mod = get_model(elem, buf, &vnd);
+ if (!mod) {
+ status = STATUS_INVALID_MODEL;
+ goto send_status;
+ }
+
+ bt_mesh_model_tree_walk(bt_mesh_model_root(mod), mod_sub_clear_visitor,
+ NULL);
+
+ if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
+ bt_mesh_store_mod_sub(mod);
+ }
+
+ status = STATUS_SUCCESS;
+
+send_status:
+ send_mod_sub_status(model, ctx, status, elem_addr,
+ BT_MESH_ADDR_UNASSIGNED, mod_id, vnd);
+}
+
+struct mod_sub_list_ctx {
+ u16_t elem_idx;
+ struct os_mbuf *msg;
+};
+
+static enum bt_mesh_walk mod_sub_list_visitor(struct bt_mesh_model *mod,
+ u32_t depth, void *ctx)
+{
+ struct mod_sub_list_ctx *visit = ctx;
+ int count = 0;
+ int i;
+
+ if (mod->elem_idx != visit->elem_idx) {
+ return BT_MESH_WALK_CONTINUE;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(mod->groups); i++) {
+ if (mod->groups[i] == BT_MESH_ADDR_UNASSIGNED) {
+ continue;
+ }
+
+ if (net_buf_simple_tailroom(visit->msg) <
+ 2 + BT_MESH_MIC_SHORT) {
+ BT_WARN("No room for all groups");
+ return BT_MESH_WALK_STOP;
+ }
+
+ net_buf_simple_add_le16(visit->msg, mod->groups[i]);
+ count++;
+ }
+
+ BT_DBG("sublist: model %u:%x: %u groups", mod->elem_idx, mod->id,
+ count);
+
+ return BT_MESH_WALK_CONTINUE;
+}
+
+static void mod_sub_get(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ struct os_mbuf *msg = NET_BUF_SIMPLE(BT_MESH_TX_SDU_MAX);
+ struct mod_sub_list_ctx visit_ctx;
+ struct bt_mesh_model *mod;
+ struct bt_mesh_elem *elem;
+ u16_t addr, id;
+
+ addr = net_buf_simple_pull_le16(buf);
+ if (!BT_MESH_ADDR_IS_UNICAST(addr)) {
+ BT_WARN("Prohibited element address");
+ goto done;
+ }
+
+ id = net_buf_simple_pull_le16(buf);
+
+ BT_DBG("addr 0x%04x id 0x%04x", addr, id);
+
+ bt_mesh_model_msg_init(msg, OP_MOD_SUB_LIST);
+
+ elem = bt_mesh_elem_find(addr);
+ if (!elem) {
+ net_buf_simple_add_u8(msg, STATUS_INVALID_ADDRESS);
+ net_buf_simple_add_le16(msg, addr);
+ net_buf_simple_add_le16(msg, id);
+ goto send_list;
+ }
+
+ mod = bt_mesh_model_find(elem, id);
+ if (!mod) {
+ net_buf_simple_add_u8(msg, STATUS_INVALID_MODEL);
+ net_buf_simple_add_le16(msg, addr);
+ net_buf_simple_add_le16(msg, id);
+ goto send_list;
+ }
+
+ net_buf_simple_add_u8(msg, STATUS_SUCCESS);
+
+ net_buf_simple_add_le16(msg, addr);
+ net_buf_simple_add_le16(msg, id);
+
+ visit_ctx.msg = msg;
+ visit_ctx.elem_idx = mod->elem_idx;
+ bt_mesh_model_tree_walk(bt_mesh_model_root(mod), mod_sub_list_visitor,
+ &visit_ctx);
+
+send_list:
+ if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) {
+ BT_ERR("Unable to send Model Subscription List");
+ }
+
+done:
+ os_mbuf_free_chain(msg);
+
+}
+
+static void mod_sub_get_vnd(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ struct os_mbuf *msg = NET_BUF_SIMPLE(BT_MESH_TX_SDU_MAX);
+ struct bt_mesh_model *mod;
+ struct bt_mesh_elem *elem;
+ u16_t company, addr, id;
+
+ addr = net_buf_simple_pull_le16(buf);
+ if (!BT_MESH_ADDR_IS_UNICAST(addr)) {
+ BT_WARN("Prohibited element address");
+ goto done;
+ }
+
+ company = net_buf_simple_pull_le16(buf);
+ id = net_buf_simple_pull_le16(buf);
+
+ BT_DBG("addr 0x%04x company 0x%04x id 0x%04x", addr, company, id);
+
+ bt_mesh_model_msg_init(msg, OP_MOD_SUB_LIST_VND);
+
+ elem = bt_mesh_elem_find(addr);
+ if (!elem) {
+ net_buf_simple_add_u8(msg, STATUS_INVALID_ADDRESS);
+ net_buf_simple_add_le16(msg, addr);
+ net_buf_simple_add_le16(msg, company);
+ net_buf_simple_add_le16(msg, id);
+ goto send_list;
+ }
+
+ mod = bt_mesh_model_find_vnd(elem, company, id);
+ if (!mod) {
+ net_buf_simple_add_u8(msg, STATUS_INVALID_MODEL);
+ net_buf_simple_add_le16(msg, addr);
+ net_buf_simple_add_le16(msg, company);
+ net_buf_simple_add_le16(msg, id);
+ goto send_list;
+ }
+
+ net_buf_simple_add_u8(msg, STATUS_SUCCESS);
+
+ net_buf_simple_add_le16(msg, addr);
+ net_buf_simple_add_le16(msg, company);
+ net_buf_simple_add_le16(msg, id);
+
+ bt_mesh_model_tree_walk(bt_mesh_model_root(mod), mod_sub_list_visitor,
+ msg);
+
+send_list:
+ if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) {
+ BT_ERR("Unable to send Vendor Model Subscription List");
+ }
+
+done:
+ os_mbuf_free_chain(msg);
+
+}
+
+#if MYNEWT_VAL(BLE_MESH_LABEL_COUNT) > 0
+static void mod_sub_va_add(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ u16_t elem_addr, sub_addr;
+ struct bt_mesh_model *mod;
+ struct bt_mesh_elem *elem;
+ u8_t *label_uuid;
+ u8_t *mod_id;
+ u16_t *entry;
+ u8_t status;
+ bool vnd;
+
+ elem_addr = net_buf_simple_pull_le16(buf);
+ if (!BT_MESH_ADDR_IS_UNICAST(elem_addr)) {
+ BT_WARN("Prohibited element address");
+ return;
+ }
+
+ label_uuid = net_buf_simple_pull_mem(buf, 16);
+
+ BT_DBG("elem_addr 0x%04x", elem_addr);
+
+ mod_id = buf->om_data;
+ elem = bt_mesh_elem_find(elem_addr);
+ if (!elem) {
+ mod = NULL;
+ vnd = (buf->om_len == 4);
+ sub_addr = BT_MESH_ADDR_UNASSIGNED;
+ status = STATUS_INVALID_ADDRESS;
+ goto send_status;
+ }
+
+ mod = get_model(elem, buf, &vnd);
+ if (!mod) {
+ sub_addr = BT_MESH_ADDR_UNASSIGNED;
+ status = STATUS_INVALID_MODEL;
+ goto send_status;
+ }
+
+ status = va_add(label_uuid, &sub_addr);
+ if (status != STATUS_SUCCESS) {
+ goto send_status;
+ }
+
+ if (bt_mesh_model_find_group(&mod, sub_addr)) {
+ /* Tried to add existing subscription */
+ status = STATUS_SUCCESS;
+ goto send_status;
+ }
+
+
+ entry = bt_mesh_model_find_group(&mod, BT_MESH_ADDR_UNASSIGNED);
+ if (!entry) {
+ status = STATUS_INSUFF_RESOURCES;
+ goto send_status;
+ }
+
+ *entry = sub_addr;
+
+ if (IS_ENABLED(CONFIG_BT_MESH_LOW_POWER)) {
+ bt_mesh_lpn_group_add(sub_addr);
+ }
+
+ if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
+ bt_mesh_store_mod_sub(mod);
+ }
+
+ status = STATUS_SUCCESS;
+
+send_status:
+ send_mod_sub_status(model, ctx, status, elem_addr, sub_addr,
+ mod_id, vnd);
+}
+
+static void mod_sub_va_del(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ u16_t elem_addr, sub_addr;
+ struct bt_mesh_model *mod;
+ struct bt_mesh_elem *elem;
+ u8_t *label_uuid;
+ u8_t *mod_id;
+ u16_t *match;
+ u8_t status;
+ bool vnd;
+
+ elem_addr = net_buf_simple_pull_le16(buf);
+ if (!BT_MESH_ADDR_IS_UNICAST(elem_addr)) {
+ BT_WARN("Prohibited element address");
+ return;
+ }
+
+ label_uuid = net_buf_simple_pull_mem(buf, 16);
+
+ BT_DBG("elem_addr 0x%04x", elem_addr);
+
+ mod_id = buf->om_data;
+
+ elem = bt_mesh_elem_find(elem_addr);
+ if (!elem) {
+ mod = NULL;
+ vnd = (buf->om_len == 4);
+ sub_addr = BT_MESH_ADDR_UNASSIGNED;
+ status = STATUS_INVALID_ADDRESS;
+ goto send_status;
+ }
+
+ mod = get_model(elem, buf, &vnd);
+ if (!mod) {
+ sub_addr = BT_MESH_ADDR_UNASSIGNED;
+ status = STATUS_INVALID_MODEL;
+ goto send_status;
+ }
+
+ status = va_del(label_uuid, &sub_addr);
+ if (sub_addr == BT_MESH_ADDR_UNASSIGNED) {
+ goto send_status;
+ }
+
+ if (IS_ENABLED(CONFIG_BT_MESH_LOW_POWER)) {
+ bt_mesh_lpn_group_del(&sub_addr, 1);
+ }
+
+ match = bt_mesh_model_find_group(&mod, sub_addr);
+ if (match) {
+ *match = BT_MESH_ADDR_UNASSIGNED;
+
+ if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
+ bt_mesh_store_mod_sub(mod);
+ }
+
+ status = STATUS_SUCCESS;
+ } else {
+ status = STATUS_CANNOT_REMOVE;
+ }
+
+send_status:
+ send_mod_sub_status(model, ctx, status, elem_addr, sub_addr,
+ mod_id, vnd);
+}
+
+static void mod_sub_va_overwrite(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ u16_t elem_addr, sub_addr = BT_MESH_ADDR_UNASSIGNED;
+ struct bt_mesh_model *mod;
+ struct bt_mesh_elem *elem;
+ u8_t *label_uuid;
+ u8_t *mod_id;
+ u8_t status;
+ bool vnd;
+
+ elem_addr = net_buf_simple_pull_le16(buf);
+ if (!BT_MESH_ADDR_IS_UNICAST(elem_addr)) {
+ BT_WARN("Prohibited element address");
+ return;
+ }
+
+ label_uuid = net_buf_simple_pull_mem(buf, 16);
+
+ BT_DBG("elem_addr 0x%04x", elem_addr);
+
+ mod_id = buf->om_data;
+
+ elem = bt_mesh_elem_find(elem_addr);
+ if (!elem) {
+ mod = NULL;
+ vnd = (buf->om_len == 4);
+ status = STATUS_INVALID_ADDRESS;
+ goto send_status;
+ }
+
+ mod = get_model(elem, buf, &vnd);
+ if (!mod) {
+ status = STATUS_INVALID_MODEL;
+ goto send_status;
+ }
+
+ if (ARRAY_SIZE(mod->groups) > 0) {
+ bt_mesh_model_tree_walk(bt_mesh_model_root(mod),
+ mod_sub_clear_visitor, NULL);
+
+ status = va_add(label_uuid, &sub_addr);
+ if (status == STATUS_SUCCESS) {
+ mod->groups[0] = sub_addr;
+
+ if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
+ bt_mesh_store_mod_sub(mod);
+ }
+
+ if (IS_ENABLED(CONFIG_BT_MESH_LOW_POWER)) {
+ bt_mesh_lpn_group_add(sub_addr);
+ }
+ }
+ } else {
+ status = STATUS_INSUFF_RESOURCES;
+ }
+
+send_status:
+ send_mod_sub_status(model, ctx, status, elem_addr, sub_addr,
+ mod_id, vnd);
+}
+#else
+static void mod_sub_va_add(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ struct bt_mesh_model *mod;
+ struct bt_mesh_elem *elem;
+ u16_t elem_addr;
+ u8_t *mod_id;
+ u8_t status;
+ bool vnd;
+
+ elem_addr = net_buf_simple_pull_le16(buf);
+ if (!BT_MESH_ADDR_IS_UNICAST(elem_addr)) {
+ BT_WARN("Prohibited element address");
+ return;
+ }
+
+ net_buf_simple_pull(buf, 16);
+
+ mod_id = buf->om_data;
+
+ elem = bt_mesh_elem_find(elem_addr);
+ if (!elem) {
+ mod = NULL;
+ vnd = (buf->om_len == 4);
+ status = STATUS_INVALID_ADDRESS;
+ goto send_status;
+ }
+
+ mod = get_model(elem, buf, &vnd);
+ if (!mod) {
+ status = STATUS_INVALID_MODEL;
+ goto send_status;
+ }
+
+ status = STATUS_INSUFF_RESOURCES;
+
+send_status:
+ send_mod_sub_status(model, ctx, status, elem_addr,
+ BT_MESH_ADDR_UNASSIGNED, mod_id, vnd);
+}
+
+static void mod_sub_va_del(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ struct bt_mesh_elem *elem;
+ u16_t elem_addr;
+ u8_t *mod_id;
+ u8_t status;
+ bool vnd;
+
+ elem_addr = net_buf_simple_pull_le16(buf);
+ if (!BT_MESH_ADDR_IS_UNICAST(elem_addr)) {
+ BT_WARN("Prohibited element address");
+ return;
+ }
+
+ net_buf_simple_pull(buf, 16);
+
+ mod_id = buf->om_data;
+
+ elem = bt_mesh_elem_find(elem_addr);
+ if (!elem) {
+ vnd = (buf->om_len == 4);
+ status = STATUS_INVALID_ADDRESS;
+ goto send_status;
+ }
+
+ if (!get_model(elem, buf, &vnd)) {
+ status = STATUS_INVALID_MODEL;
+ goto send_status;
+ }
+
+ status = STATUS_INSUFF_RESOURCES;
+
+send_status:
+ send_mod_sub_status(model, ctx, status, elem_addr,
+ BT_MESH_ADDR_UNASSIGNED, mod_id, vnd);
+}
+
+static void mod_sub_va_overwrite(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ struct bt_mesh_elem *elem;
+ u16_t elem_addr;
+ u8_t *mod_id;
+ u8_t status;
+ bool vnd;
+
+ elem_addr = net_buf_simple_pull_le16(buf);
+ if (!BT_MESH_ADDR_IS_UNICAST(elem_addr)) {
+ BT_WARN("Prohibited element address");
+ return;
+ }
+
+ net_buf_simple_pull(buf, 18);
+
+ mod_id = buf->om_data;
+
+ elem = bt_mesh_elem_find(elem_addr);
+ if (!elem) {
+ vnd = (buf->om_len == 4);
+ status = STATUS_INVALID_ADDRESS;
+ goto send_status;
+ }
+
+ if (!get_model(elem, buf, &vnd)) {
+ status = STATUS_INVALID_MODEL;
+ goto send_status;
+ }
+
+ status = STATUS_INSUFF_RESOURCES;
+
+send_status:
+ send_mod_sub_status(model, ctx, status, elem_addr,
+ BT_MESH_ADDR_UNASSIGNED, mod_id, vnd);
+}
+#endif /* MYNEWT_VAL(BLE_MESH_LABEL_COUNT) > 0 */
+
+static void send_net_key_status(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ u16_t idx, u8_t status)
+{
+ struct os_mbuf *msg = BT_MESH_MODEL_BUF(OP_NET_KEY_STATUS, 3);
+
+ bt_mesh_model_msg_init(msg, OP_NET_KEY_STATUS);
+
+ net_buf_simple_add_u8(msg, status);
+ net_buf_simple_add_le16(msg, idx);
+
+ if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) {
+ BT_ERR("Unable to send NetKey Status");
+ }
+
+ os_mbuf_free_chain(msg);
+}
+
+static void net_key_add(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ struct bt_mesh_subnet *sub;
+ u16_t idx;
+ int err;
+
+ idx = net_buf_simple_pull_le16(buf);
+ if (idx > 0xfff) {
+ BT_ERR("Invalid NetKeyIndex 0x%04x", idx);
+ return;
+ }
+
+ BT_DBG("idx 0x%04x", idx);
+
+ sub = bt_mesh_subnet_get(idx);
+ if (!sub) {
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(bt_mesh.sub); i++) {
+ if (bt_mesh.sub[i].net_idx == BT_MESH_KEY_UNUSED) {
+ sub = &bt_mesh.sub[i];
+ break;
+ }
+ }
+
+ if (!sub) {
+ send_net_key_status(model, ctx, idx,
+ STATUS_INSUFF_RESOURCES);
+ return;
+ }
+ }
+
+ /* Check for already existing subnet */
+ if (sub->net_idx == idx) {
+ u8_t status;
+
+ if (memcmp(buf->om_data, sub->keys[0].net, 16)) {
+ status = STATUS_IDX_ALREADY_STORED;
+ } else {
+ status = STATUS_SUCCESS;
+ }
+
+ send_net_key_status(model, ctx, idx, status);
+ return;
+ }
+
+ err = bt_mesh_net_keys_create(&sub->keys[0], buf->om_data);
+ if (err) {
+ send_net_key_status(model, ctx, idx, STATUS_UNSPECIFIED);
+ return;
+ }
+
+ sub->net_idx = idx;
+
+ if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
+ BT_DBG("Storing NetKey persistently");
+ bt_mesh_store_subnet(sub);
+ }
+
+ /* Make sure we have valid beacon data to be sent */
+ bt_mesh_net_beacon_update(sub);
+
+ if ((MYNEWT_VAL(BLE_MESH_GATT_PROXY))) {
+ sub->node_id = BT_MESH_NODE_IDENTITY_STOPPED;
+ bt_mesh_proxy_beacon_send(sub);
+ bt_mesh_adv_update();
+ } else {
+ sub->node_id = BT_MESH_NODE_IDENTITY_NOT_SUPPORTED;
+ }
+
+ send_net_key_status(model, ctx, idx, STATUS_SUCCESS);
+}
+
+static void net_key_update(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ struct bt_mesh_subnet *sub;
+ u16_t idx;
+ int err;
+
+ idx = net_buf_simple_pull_le16(buf);
+ if (idx > 0xfff) {
+ BT_ERR("Invalid NetKeyIndex 0x%04x", idx);
+ return;
+ }
+
+ BT_DBG("idx 0x%04x", idx);
+
+ sub = bt_mesh_subnet_get(idx);
+ if (!sub) {
+ send_net_key_status(model, ctx, idx, STATUS_INVALID_NETKEY);
+ return;
+ }
+
+ /* The node shall successfully process a NetKey Update message on a
+ * valid NetKeyIndex when the NetKey value is different and the Key
+ * Refresh procedure has not been started, or when the NetKey value is
+ * the same in Phase 1. The NetKey Update message shall generate an
+ * error when the node is in Phase 2, or Phase 3.
+ */
+ switch (sub->kr_phase) {
+ case BT_MESH_KR_NORMAL:
+ if (!memcmp(buf->om_data, sub->keys[0].net, 16)) {
+ return;
+ }
+ break;
+ case BT_MESH_KR_PHASE_1:
+ if (!memcmp(buf->om_data, sub->keys[1].net, 16)) {
+ send_net_key_status(model, ctx, idx, STATUS_SUCCESS);
+ return;
+ }
+ /* fall through */
+ case BT_MESH_KR_PHASE_2:
+ case BT_MESH_KR_PHASE_3:
+ send_net_key_status(model, ctx, idx, STATUS_CANNOT_UPDATE);
+ return;
+ }
+
+ err = bt_mesh_net_keys_create(&sub->keys[1], buf->om_data);
+ if (!err && ((MYNEWT_VAL(BLE_MESH_LOW_POWER)) ||
+ (MYNEWT_VAL(BLE_MESH_FRIEND)))) {
+ err = friend_cred_update(sub);
+ }
+
+ if (err) {
+ send_net_key_status(model, ctx, idx, STATUS_UNSPECIFIED);
+ return;
+ }
+
+ sub->kr_phase = BT_MESH_KR_PHASE_1;
+
+ if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
+ BT_DBG("Storing NetKey persistently");
+ bt_mesh_store_subnet(sub);
+ }
+
+ bt_mesh_net_beacon_update(sub);
+
+ send_net_key_status(model, ctx, idx, STATUS_SUCCESS);
+}
+
+static void hb_pub_disable(struct bt_mesh_cfg_srv *cfg)
+{
+ BT_DBG("");
+
+ cfg->hb_pub.dst = BT_MESH_ADDR_UNASSIGNED;
+ cfg->hb_pub.count = 0;
+ cfg->hb_pub.ttl = 0;
+ cfg->hb_pub.period = 0;
+
+ k_delayed_work_cancel(&cfg->hb_pub.timer);
+}
+
+static void net_key_del(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ struct bt_mesh_subnet *sub;
+ u16_t del_idx;
+ u8_t status;
+
+ del_idx = net_buf_simple_pull_le16(buf);
+ if (del_idx > 0xfff) {
+ BT_ERR("Invalid NetKeyIndex 0x%04x", del_idx);
+ return;
+ }
+
+ BT_DBG("idx 0x%04x", del_idx);
+
+ sub = bt_mesh_subnet_get(del_idx);
+ if (!sub) {
+ /* This could be a retry of a previous attempt that had its
+ * response lost, so pretend that it was a success.
+ */
+ status = STATUS_SUCCESS;
+ goto send_status;
+ }
+
+ /* The key that the message was encrypted with cannot be removed.
+ * The NetKey List must contain a minimum of one NetKey.
+ */
+ if (ctx->net_idx == del_idx) {
+ status = STATUS_CANNOT_REMOVE;
+ goto send_status;
+ }
+
+ bt_mesh_subnet_del(sub, true);
+ status = STATUS_SUCCESS;
+
+send_status:
+ send_net_key_status(model, ctx, del_idx, status);
+}
+
+static void net_key_get(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ struct os_mbuf *msg =
+ BT_MESH_MODEL_BUF(OP_NET_KEY_LIST,
+ IDX_LEN(CONFIG_BT_MESH_SUBNET_COUNT));
+ u16_t prev, i;
+
+ bt_mesh_model_msg_init(msg, OP_NET_KEY_LIST);
+
+ prev = BT_MESH_KEY_UNUSED;
+ for (i = 0; i < ARRAY_SIZE(bt_mesh.sub); i++) {
+ struct bt_mesh_subnet *sub = &bt_mesh.sub[i];
+
+ if (sub->net_idx == BT_MESH_KEY_UNUSED) {
+ continue;
+ }
+
+ if (prev == BT_MESH_KEY_UNUSED) {
+ prev = sub->net_idx;
+ continue;
+ }
+
+ key_idx_pack(msg, prev, sub->net_idx);
+ prev = BT_MESH_KEY_UNUSED;
+ }
+
+ if (prev != BT_MESH_KEY_UNUSED) {
+ net_buf_simple_add_le16(msg, prev);
+ }
+
+ if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) {
+ BT_ERR("Unable to send NetKey List");
+ }
+
+ os_mbuf_free_chain(msg);
+}
+
+static void node_identity_get(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ struct os_mbuf *msg = BT_MESH_MODEL_BUF(OP_NODE_IDENTITY_STATUS, 4);
+ struct bt_mesh_subnet *sub;
+ u8_t node_id;
+ u16_t idx;
+
+ BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s",
+ ctx->net_idx, ctx->app_idx, ctx->addr, buf->om_len,
+ bt_hex(buf->om_data, buf->om_len));
+
+ idx = net_buf_simple_pull_le16(buf);
+ if (idx > 0xfff) {
+ BT_ERR("Invalid NetKeyIndex 0x%04x", idx);
+ goto done;
+ }
+
+ bt_mesh_model_msg_init(msg, OP_NODE_IDENTITY_STATUS);
+
+ sub = bt_mesh_subnet_get(idx);
+ if (!sub) {
+ net_buf_simple_add_u8(msg, STATUS_INVALID_NETKEY);
+ node_id = 0x00;
+ } else {
+ net_buf_simple_add_u8(msg, STATUS_SUCCESS);
+ node_id = sub->node_id;
+ }
+
+ net_buf_simple_add_le16(msg, idx);
+ net_buf_simple_add_u8(msg, node_id);
+
+ if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) {
+ BT_ERR("Unable to send Node Identity Status");
+ }
+
+done:
+ os_mbuf_free_chain(msg);
+}
+
+static void node_identity_set(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ struct os_mbuf *msg = BT_MESH_MODEL_BUF(OP_NODE_IDENTITY_STATUS, 4);
+ struct bt_mesh_subnet *sub;
+ u8_t node_id;
+ u16_t idx;
+
+ BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s",
+ ctx->net_idx, ctx->app_idx, ctx->addr, buf->om_len,
+ bt_hex(buf->om_data, buf->om_len));
+
+ idx = net_buf_simple_pull_le16(buf);
+ if (idx > 0xfff) {
+ BT_WARN("Invalid NetKeyIndex 0x%04x", idx);
+ goto done;
+ }
+
+ node_id = net_buf_simple_pull_u8(buf);
+ if (node_id != 0x00 && node_id != 0x01) {
+ BT_WARN("Invalid Node ID value 0x%02x", node_id);
+ goto done;
+ }
+
+ bt_mesh_model_msg_init(msg, OP_NODE_IDENTITY_STATUS);
+
+ sub = bt_mesh_subnet_get(idx);
+ if (!sub) {
+ net_buf_simple_add_u8(msg, STATUS_INVALID_NETKEY);
+ net_buf_simple_add_le16(msg, idx);
+ net_buf_simple_add_u8(msg, node_id);
+ } else {
+ net_buf_simple_add_u8(msg, STATUS_SUCCESS);
+ net_buf_simple_add_le16(msg, idx);
+
+ if (MYNEWT_VAL(BLE_MESH_GATT_PROXY)) {
+ if (node_id) {
+ bt_mesh_proxy_identity_start(sub);
+ } else {
+ bt_mesh_proxy_identity_stop(sub);
+ }
+ bt_mesh_adv_update();
+ }
+
+ net_buf_simple_add_u8(msg, sub->node_id);
+ }
+
+ if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) {
+ BT_ERR("Unable to send Node Identity Status");
+ }
+
+done:
+ os_mbuf_free_chain(msg);
+
+}
+
+static void create_mod_app_status(struct os_mbuf *msg,
+ struct bt_mesh_model *mod, bool vnd,
+ u16_t elem_addr, u16_t app_idx,
+ u8_t status, u8_t *mod_id)
+{
+ bt_mesh_model_msg_init(msg, OP_MOD_APP_STATUS);
+
+ net_buf_simple_add_u8(msg, status);
+ net_buf_simple_add_le16(msg, elem_addr);
+ net_buf_simple_add_le16(msg, app_idx);
+
+ if (vnd) {
+ memcpy(net_buf_simple_add(msg, 4), mod_id, 4);
+ } else {
+ memcpy(net_buf_simple_add(msg, 2), mod_id, 2);
+ }
+}
+
+static void mod_app_bind(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ struct os_mbuf *msg = BT_MESH_MODEL_BUF(OP_MOD_APP_STATUS, 9);
+ u16_t elem_addr, key_app_idx;
+ struct bt_mesh_model *mod;
+ struct bt_mesh_elem *elem;
+ u8_t *mod_id, status;
+ bool vnd;
+
+ elem_addr = net_buf_simple_pull_le16(buf);
+ if (!BT_MESH_ADDR_IS_UNICAST(elem_addr)) {
+ BT_WARN("Prohibited element address");
+ goto done;
+ }
+
+ key_app_idx = net_buf_simple_pull_le16(buf);
+ mod_id = buf->om_data;
+
+ elem = bt_mesh_elem_find(elem_addr);
+ if (!elem) {
+ mod = NULL;
+ vnd = (buf->om_len == 4);
+ status = STATUS_INVALID_ADDRESS;
+ goto send_status;
+ }
+
+ mod = get_model(elem, buf, &vnd);
+ if (!mod) {
+ status = STATUS_INVALID_MODEL;
+ goto send_status;
+ }
+
+ /* Configuration Server only allows device key based access */
+ if (model == mod) {
+ BT_ERR("Client tried to bind AppKey to Configuration Model");
+ status = STATUS_CANNOT_BIND;
+ goto send_status;
+ }
+
+ status = mod_bind(mod, key_app_idx);
+
+ if (IS_ENABLED(CONFIG_BT_TESTING) && status == STATUS_SUCCESS) {
+ bt_test_mesh_model_bound(ctx->addr, mod, key_app_idx);
+ }
+
+send_status:
+ BT_DBG("status 0x%02x", status);
+ create_mod_app_status(msg, mod, vnd, elem_addr, key_app_idx, status,
+ mod_id);
+
+ if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) {
+ BT_ERR("Unable to send Model App Bind Status response");
+ }
+
+done:
+ os_mbuf_free_chain(msg);
+
+}
+
+static void mod_app_unbind(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ struct os_mbuf *msg = BT_MESH_MODEL_BUF(OP_MOD_APP_STATUS, 9);
+ u16_t elem_addr, key_app_idx;
+ struct bt_mesh_model *mod;
+ struct bt_mesh_elem *elem;
+ u8_t *mod_id, status;
+ bool vnd;
+
+ elem_addr = net_buf_simple_pull_le16(buf);
+ if (!BT_MESH_ADDR_IS_UNICAST(elem_addr)) {
+ BT_WARN("Prohibited element address");
+ goto done;
+ }
+
+ key_app_idx = net_buf_simple_pull_le16(buf);
+ mod_id = buf->om_data;
+
+ elem = bt_mesh_elem_find(elem_addr);
+ if (!elem) {
+ mod = NULL;
+ vnd = (buf->om_len == 4);
+ status = STATUS_INVALID_ADDRESS;
+ goto send_status;
+ }
+
+ mod = get_model(elem, buf, &vnd);
+ if (!mod) {
+ status = STATUS_INVALID_MODEL;
+ goto send_status;
+ }
+
+ status = mod_unbind(mod, key_app_idx, true);
+
+ if (IS_ENABLED(CONFIG_BT_TESTING) && status == STATUS_SUCCESS) {
+ bt_test_mesh_model_unbound(ctx->addr, mod, key_app_idx);
+ }
+
+send_status:
+ BT_DBG("status 0x%02x", status);
+ create_mod_app_status(msg, mod, vnd, elem_addr, key_app_idx, status,
+ mod_id);
+
+ if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) {
+ BT_ERR("Unable to send Model App Unbind Status response");
+ }
+
+done:
+ os_mbuf_free_chain(msg);
+}
+
+#define KEY_LIST_LEN (MYNEWT_VAL(BLE_MESH_MODEL_KEY_COUNT) * 2)
+
+static void mod_app_get(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ struct os_mbuf *msg = NET_BUF_SIMPLE(max(BT_MESH_MODEL_BUF_LEN(OP_VND_MOD_APP_LIST,
+ 9 + KEY_LIST_LEN),
+ BT_MESH_MODEL_BUF_LEN(OP_SIG_MOD_APP_LIST,
+ 9 + KEY_LIST_LEN)));
+
+ struct bt_mesh_model *mod;
+ struct bt_mesh_elem *elem;
+ u8_t *mod_id, status;
+ u16_t elem_addr;
+ bool vnd;
+
+ elem_addr = net_buf_simple_pull_le16(buf);
+ if (!BT_MESH_ADDR_IS_UNICAST(elem_addr)) {
+ BT_WARN("Prohibited element address");
+ goto done;
+ }
+
+ mod_id = buf->om_data;
+
+ BT_DBG("elem_addr 0x%04x", elem_addr);
+
+ elem = bt_mesh_elem_find(elem_addr);
+ if (!elem) {
+ mod = NULL;
+ vnd = (buf->om_len == 4);
+ status = STATUS_INVALID_ADDRESS;
+ goto send_list;
+ }
+
+ mod = get_model(elem, buf, &vnd);
+ if (!mod) {
+ status = STATUS_INVALID_MODEL;
+ goto send_list;
+ }
+
+ status = STATUS_SUCCESS;
+
+send_list:
+ if (vnd) {
+ bt_mesh_model_msg_init(msg, OP_VND_MOD_APP_LIST);
+ } else {
+ bt_mesh_model_msg_init(msg, OP_SIG_MOD_APP_LIST);
+ }
+
+ net_buf_simple_add_u8(msg, status);
+ net_buf_simple_add_le16(msg, elem_addr);
+
+ if (vnd) {
+ net_buf_simple_add_mem(msg, mod_id, 4);
+ } else {
+ net_buf_simple_add_mem(msg, mod_id, 2);
+ }
+
+ if (mod) {
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(mod->keys); i++) {
+ if (mod->keys[i] != BT_MESH_KEY_UNUSED) {
+ net_buf_simple_add_le16(msg, mod->keys[i]);
+ }
+ }
+ }
+
+ if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) {
+ BT_ERR("Unable to send Model Application List message");
+ }
+
+done:
+ os_mbuf_free_chain(msg);
+}
+
+static void node_reset(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ struct os_mbuf *msg = BT_MESH_MODEL_BUF(OP_NODE_RESET_STATUS, 0);
+
+ BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s",
+ ctx->net_idx, ctx->app_idx, ctx->addr, buf->om_len,
+ bt_hex(buf->om_data, buf->om_len));
+
+
+ bt_mesh_model_msg_init(msg, OP_NODE_RESET_STATUS);
+
+ /* Send the response first since we wont have any keys left to
+ * send it later.
+ */
+ if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) {
+ BT_ERR("Unable to send Node Reset Status");
+ }
+
+ bt_mesh_reset();
+ os_mbuf_free_chain(msg);
+}
+
+static void send_friend_status(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx)
+{
+ struct os_mbuf *msg = BT_MESH_MODEL_BUF(OP_FRIEND_STATUS, 1);
+ struct bt_mesh_cfg_srv *cfg = model->user_data;
+
+ bt_mesh_model_msg_init(msg, OP_FRIEND_STATUS);
+ net_buf_simple_add_u8(msg, cfg->frnd);
+
+ if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) {
+ BT_ERR("Unable to send Friend Status");
+ }
+ os_mbuf_free_chain(msg);
+}
+
+static void friend_get(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s",
+ ctx->net_idx, ctx->app_idx, ctx->addr, buf->om_len,
+ bt_hex(buf->om_data, buf->om_len));
+
+ send_friend_status(model, ctx);
+}
+
+static void friend_set(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ struct bt_mesh_cfg_srv *cfg = model->user_data;
+
+ BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s",
+ ctx->net_idx, ctx->app_idx, ctx->addr, buf->om_len,
+ bt_hex(buf->om_data, buf->om_len));
+
+ if (buf->om_data[0] != 0x00 && buf->om_data[0] != 0x01) {
+ BT_WARN("Invalid Friend value 0x%02x", buf->om_data[0]);
+ return;
+ }
+
+ if (!cfg) {
+ BT_WARN("No Configuration Server context available");
+ goto send_status;
+ }
+
+ BT_DBG("Friend 0x%02x -> 0x%02x", cfg->frnd, buf->om_data[0]);
+
+ if (cfg->frnd == buf->om_data[0]) {
+ goto send_status;
+ }
+
+ if (MYNEWT_VAL(BLE_MESH_FRIEND)) {
+ cfg->frnd = buf->om_data[0];
+
+ if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
+ bt_mesh_store_cfg();
+ }
+
+ if (cfg->frnd == BT_MESH_FRIEND_DISABLED) {
+ bt_mesh_friend_clear_net_idx(BT_MESH_KEY_ANY);
+ }
+ }
+
+ if (cfg->hb_pub.feat & BT_MESH_FEAT_FRIEND) {
+ bt_mesh_heartbeat_send();
+ }
+
+send_status:
+ send_friend_status(model, ctx);
+}
+
+static void lpn_timeout_get(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ struct os_mbuf *msg = BT_MESH_MODEL_BUF(OP_LPN_TIMEOUT_STATUS, 5);
+ struct bt_mesh_friend *frnd;
+ u16_t lpn_addr;
+ s32_t timeout;
+
+ lpn_addr = net_buf_simple_pull_le16(buf);
+
+ BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x lpn_addr 0x%02x",
+ ctx->net_idx, ctx->app_idx, ctx->addr, lpn_addr);
+
+ /* check if it's the address of the Low Power Node? */
+ if (!BT_MESH_ADDR_IS_UNICAST(lpn_addr)) {
+ BT_WARN("Invalid LPNAddress; ignoring msg");
+ goto done;
+ }
+
+ bt_mesh_model_msg_init(msg, OP_LPN_TIMEOUT_STATUS);
+ net_buf_simple_add_le16(msg, lpn_addr);
+
+ if (!IS_ENABLED(CONFIG_BT_MESH_FRIEND)) {
+ timeout = 0;
+ goto send_rsp;
+ }
+
+ frnd = bt_mesh_friend_find(BT_MESH_KEY_ANY, lpn_addr, true, true);
+ if (!frnd) {
+ timeout = 0;
+ goto send_rsp;
+ }
+
+ timeout = k_delayed_work_remaining_get(&frnd->timer) / 100;
+
+send_rsp:
+ net_buf_simple_add_u8(msg, timeout);
+ net_buf_simple_add_u8(msg, timeout >> 8);
+ net_buf_simple_add_u8(msg, timeout >> 16);
+
+ if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) {
+ BT_ERR("Unable to send LPN PollTimeout Status");
+ }
+
+done:
+ os_mbuf_free_chain(msg);
+}
+
+static void send_krp_status(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ u16_t idx, u8_t phase, u8_t status)
+{
+ struct os_mbuf *msg = BT_MESH_MODEL_BUF(OP_KRP_STATUS, 4);
+
+ bt_mesh_model_msg_init(msg, OP_KRP_STATUS);
+
+ net_buf_simple_add_u8(msg, status);
+ net_buf_simple_add_le16(msg, idx);
+ net_buf_simple_add_u8(msg, phase);
+
+ if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) {
+ BT_ERR("Unable to send Key Refresh State Status");
+ }
+
+ os_mbuf_free_chain(msg);
+}
+
+static void krp_get(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ struct bt_mesh_subnet *sub;
+ u16_t idx;
+
+ idx = net_buf_simple_pull_le16(buf);
+ if (idx > 0xfff) {
+ BT_ERR("Invalid NetKeyIndex 0x%04x", idx);
+ return;
+ }
+
+ BT_DBG("idx 0x%04x", idx);
+
+ sub = bt_mesh_subnet_get(idx);
+ if (!sub) {
+ send_krp_status(model, ctx, idx, 0x00, STATUS_INVALID_NETKEY);
+ } else {
+ send_krp_status(model, ctx, idx, sub->kr_phase,
+ STATUS_SUCCESS);
+ }
+}
+
+static void krp_set(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ struct bt_mesh_subnet *sub;
+ u8_t phase;
+ u16_t idx;
+
+ idx = net_buf_simple_pull_le16(buf);
+ phase = net_buf_simple_pull_u8(buf);
+
+ if (idx > 0xfff) {
+ BT_ERR("Invalid NetKeyIndex 0x%04x", idx);
+ return;
+ }
+
+ BT_DBG("idx 0x%04x transition 0x%02x", idx, phase);
+
+ sub = bt_mesh_subnet_get(idx);
+ if (!sub) {
+ send_krp_status(model, ctx, idx, 0x00, STATUS_INVALID_NETKEY);
+ return;
+ }
+
+ BT_DBG("%u -> %u", sub->kr_phase, phase);
+
+ if (phase < BT_MESH_KR_PHASE_2 || phase > BT_MESH_KR_PHASE_3 ||
+ (sub->kr_phase == BT_MESH_KR_NORMAL &&
+ phase == BT_MESH_KR_PHASE_2)) {
+ BT_WARN("Prohibited transition %u -> %u", sub->kr_phase, phase);
+ return;
+ }
+
+ if (sub->kr_phase == BT_MESH_KR_PHASE_1 &&
+ phase == BT_MESH_KR_PHASE_2) {
+ sub->kr_phase = BT_MESH_KR_PHASE_2;
+ sub->kr_flag = 1;
+ bt_mesh_net_beacon_update(sub);
+ } else if ((sub->kr_phase == BT_MESH_KR_PHASE_1 ||
+ sub->kr_phase == BT_MESH_KR_PHASE_2) &&
+ phase == BT_MESH_KR_PHASE_3) {
+ bt_mesh_net_revoke_keys(sub);
+ if ((MYNEWT_VAL(BLE_MESH_LOW_POWER)) ||
+ (MYNEWT_VAL(BLE_MESH_FRIEND))) {
+ friend_cred_refresh(ctx->net_idx);
+ }
+ sub->kr_phase = BT_MESH_KR_NORMAL;
+ sub->kr_flag = 0;
+ bt_mesh_net_beacon_update(sub);
+ }
+
+ send_krp_status(model, ctx, idx, sub->kr_phase, STATUS_SUCCESS);
+}
+
+static u8_t hb_log(u16_t val)
+{
+ if (!val) {
+ return 0x00;
+ } else if (val == 0xffff) {
+ return 0xff;
+ } else {
+ return 32 - __builtin_clz(val);
+ }
+}
+
+static u8_t hb_pub_count_log(u16_t val)
+{
+ if (!val) {
+ return 0x00;
+ } else if (val == 0x01) {
+ return 0x01;
+ } else if (val == 0xffff) {
+ return 0xff;
+ } else {
+ return 32 - __builtin_clz(val - 1) + 1;
+ }
+}
+
+static u16_t hb_pwr2(u8_t val, u8_t sub)
+{
+ if (!val) {
+ return 0x0000;
+ } else if (val == 0xff || val == 0x11) {
+ return 0xffff;
+ } else {
+ return (1 << (val - sub));
+ }
+}
+
+struct hb_pub_param {
+ u16_t dst;
+ u8_t count_log;
+ u8_t period_log;
+ u8_t ttl;
+ u16_t feat;
+ u16_t net_idx;
+} __packed;
+
+static void hb_pub_send_status(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx, u8_t status,
+ struct hb_pub_param *orig_msg)
+{
+ struct os_mbuf *msg = BT_MESH_MODEL_BUF(OP_HEARTBEAT_PUB_STATUS, 10);
+ struct bt_mesh_cfg_srv *cfg = model->user_data;
+
+ BT_DBG("src 0x%04x status 0x%02x", ctx->addr, status);
+
+ bt_mesh_model_msg_init(msg, OP_HEARTBEAT_PUB_STATUS);
+
+ net_buf_simple_add_u8(msg, status);
+
+ if (orig_msg) {
+ memcpy(net_buf_simple_add(msg, sizeof(*orig_msg)), orig_msg,
+ sizeof(*orig_msg));
+ goto send;
+ }
+
+ net_buf_simple_add_le16(msg, cfg->hb_pub.dst);
+ net_buf_simple_add_u8(msg, hb_pub_count_log(cfg->hb_pub.count));
+ net_buf_simple_add_u8(msg, cfg->hb_pub.period);
+ net_buf_simple_add_u8(msg, cfg->hb_pub.ttl);
+ net_buf_simple_add_le16(msg, cfg->hb_pub.feat);
+ net_buf_simple_add_le16(msg, cfg->hb_pub.net_idx);
+
+send:
+ if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) {
+ BT_ERR("Unable to send Heartbeat Publication Status");
+ }
+
+ os_mbuf_free_chain(msg);
+}
+
+static void heartbeat_pub_get(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ BT_DBG("src 0x%04x", ctx->addr);
+
+ hb_pub_send_status(model, ctx, STATUS_SUCCESS, NULL);
+}
+
+static void heartbeat_pub_set(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ struct hb_pub_param *param = (void *)buf->om_data;
+ struct bt_mesh_cfg_srv *cfg = model->user_data;
+ u16_t dst, feat, idx;
+ u8_t status;
+
+ BT_DBG("src 0x%04x", ctx->addr);
+
+ dst = sys_le16_to_cpu(param->dst);
+ /* All other address types but virtual are valid */
+ if (BT_MESH_ADDR_IS_VIRTUAL(dst)) {
+ status = STATUS_INVALID_ADDRESS;
+ goto failed;
+ }
+
+ if (param->count_log > 0x11 && param->count_log != 0xff) {
+ status = STATUS_CANNOT_SET;
+ goto failed;
+ }
+
+ if (param->period_log > 0x10) {
+ status = STATUS_CANNOT_SET;
+ goto failed;
+ }
+
+ if (param->ttl > BT_MESH_TTL_MAX && param->ttl != BT_MESH_TTL_DEFAULT) {
+ BT_ERR("Invalid TTL value 0x%02x", param->ttl);
+ return;
+ }
+
+ feat = sys_le16_to_cpu(param->feat);
+
+ idx = sys_le16_to_cpu(param->net_idx);
+ if (idx > 0xfff) {
+ BT_ERR("Invalid NetKeyIndex 0x%04x", idx);
+ return;
+ }
+
+ if (!bt_mesh_subnet_get(idx)) {
+ status = STATUS_INVALID_NETKEY;
+ goto failed;
+ }
+
+ cfg->hb_pub.dst = dst;
+ cfg->hb_pub.period = param->period_log;
+ cfg->hb_pub.feat = feat & BT_MESH_FEAT_SUPPORTED;
+ cfg->hb_pub.net_idx = idx;
+
+ if (dst == BT_MESH_ADDR_UNASSIGNED) {
+ hb_pub_disable(cfg);
+ } else {
+ /* 2^(n-1) */
+ cfg->hb_pub.count = hb_pwr2(param->count_log, 1);
+ cfg->hb_pub.ttl = param->ttl;
+
+ BT_DBG("period %u ms", hb_pwr2(param->period_log, 1) * 1000);
+
+ /* The first Heartbeat message shall be published as soon
+ * as possible after the Heartbeat Publication Period state
+ * has been configured for periodic publishing.
+ */
+ if (param->period_log && param->count_log) {
+ k_work_submit(&cfg->hb_pub.timer.work);
+ } else {
+ k_delayed_work_cancel(&cfg->hb_pub.timer);
+ }
+ }
+
+ if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
+ bt_mesh_store_hb_pub();
+ }
+
+ hb_pub_send_status(model, ctx, STATUS_SUCCESS, NULL);
+
+ return;
+
+failed:
+ hb_pub_send_status(model, ctx, status, param);
+}
+
+static void hb_sub_send_status(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx, u8_t status)
+{
+ struct os_mbuf *msg = BT_MESH_MODEL_BUF(OP_HEARTBEAT_SUB_STATUS, 9);
+ struct bt_mesh_cfg_srv *cfg = model->user_data;
+ u16_t period;
+ s64_t uptime;
+
+ BT_DBG("src 0x%04x status 0x%02x", ctx->addr, status);
+
+ uptime = k_uptime_get();
+ if (uptime > cfg->hb_sub.expiry) {
+ period = 0;
+ } else {
+ period = (cfg->hb_sub.expiry - uptime) / 1000;
+ }
+
+ bt_mesh_model_msg_init(msg, OP_HEARTBEAT_SUB_STATUS);
+
+ net_buf_simple_add_u8(msg, status);
+
+ net_buf_simple_add_le16(msg, cfg->hb_sub.src);
+ net_buf_simple_add_le16(msg, cfg->hb_sub.dst);
+
+ net_buf_simple_add_u8(msg, hb_log(period));
+ net_buf_simple_add_u8(msg, hb_log(cfg->hb_sub.count));
+ net_buf_simple_add_u8(msg, cfg->hb_sub.min_hops);
+ net_buf_simple_add_u8(msg, cfg->hb_sub.max_hops);
+
+
+ if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) {
+ BT_ERR("Unable to send Heartbeat Subscription Status");
+ }
+
+ os_mbuf_free_chain(msg);
+}
+
+static void heartbeat_sub_get(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ BT_DBG("src 0x%04x", ctx->addr);
+
+ hb_sub_send_status(model, ctx, STATUS_SUCCESS);
+}
+
+static void heartbeat_sub_set(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ struct bt_mesh_cfg_srv *cfg = model->user_data;
+ u16_t sub_src, sub_dst;
+ u8_t sub_period;
+ s32_t period_ms;
+
+ BT_DBG("src 0x%04x", ctx->addr);
+
+ sub_src = net_buf_simple_pull_le16(buf);
+ sub_dst = net_buf_simple_pull_le16(buf);
+ sub_period = net_buf_simple_pull_u8(buf);
+
+ BT_DBG("sub_src 0x%04x sub_dst 0x%04x period 0x%02x",
+ sub_src, sub_dst, sub_period);
+
+ if (sub_src != BT_MESH_ADDR_UNASSIGNED &&
+ !BT_MESH_ADDR_IS_UNICAST(sub_src)) {
+ BT_WARN("Prohibited source address");
+ return;
+ }
+
+ if (BT_MESH_ADDR_IS_VIRTUAL(sub_dst) || BT_MESH_ADDR_IS_RFU(sub_dst) ||
+ (BT_MESH_ADDR_IS_UNICAST(sub_dst) &&
+ sub_dst != bt_mesh_primary_addr())) {
+ BT_WARN("Prohibited destination address");
+ return;
+ }
+
+ if (sub_period > 0x11) {
+ BT_WARN("Prohibited subscription period 0x%02x", sub_period);
+ return;
+ }
+
+ if (sub_src == BT_MESH_ADDR_UNASSIGNED ||
+ sub_dst == BT_MESH_ADDR_UNASSIGNED ||
+ sub_period == 0x00) {
+ /* Only an explicit address change to unassigned should
+ * trigger clearing of the values according to
+ * MESH/NODE/CFG/HBS/BV-02-C.
+ */
+ if (sub_src == BT_MESH_ADDR_UNASSIGNED ||
+ sub_dst == BT_MESH_ADDR_UNASSIGNED) {
+ cfg->hb_sub.src = BT_MESH_ADDR_UNASSIGNED;
+ cfg->hb_sub.dst = BT_MESH_ADDR_UNASSIGNED;
+ cfg->hb_sub.min_hops = BT_MESH_TTL_MAX;
+ cfg->hb_sub.max_hops = 0;
+ cfg->hb_sub.count = 0;
+ }
+
+ period_ms = 0;
+ } else {
+ cfg->hb_sub.src = sub_src;
+ cfg->hb_sub.dst = sub_dst;
+ cfg->hb_sub.min_hops = BT_MESH_TTL_MAX;
+ cfg->hb_sub.max_hops = 0;
+ cfg->hb_sub.count = 0;
+ period_ms = hb_pwr2(sub_period, 1) * 1000;
+ }
+
+ /* Let the transport layer know it needs to handle this address */
+ bt_mesh_set_hb_sub_dst(cfg->hb_sub.dst);
+
+ BT_DBG("period_ms %u", (unsigned) period_ms);
+
+ if (period_ms) {
+ cfg->hb_sub.expiry = k_uptime_get() + period_ms;
+ } else {
+ cfg->hb_sub.expiry = 0;
+ }
+
+ hb_sub_send_status(model, ctx, STATUS_SUCCESS);
+
+ /* MESH/NODE/CFG/HBS/BV-01-C expects the MinHops to be 0x7f after
+ * disabling subscription, but 0x00 for subsequent Get requests.
+ */
+ if (!period_ms) {
+ cfg->hb_sub.min_hops = 0;
+ }
+}
+
+const struct bt_mesh_model_op bt_mesh_cfg_srv_op[] = {
+ { OP_DEV_COMP_DATA_GET, 1, dev_comp_data_get },
+ { OP_APP_KEY_ADD, 19, app_key_add },
+ { OP_APP_KEY_UPDATE, 19, app_key_update },
+ { OP_APP_KEY_DEL, 3, app_key_del },
+ { OP_APP_KEY_GET, 2, app_key_get },
+ { OP_BEACON_GET, 0, beacon_get },
+ { OP_BEACON_SET, 1, beacon_set },
+ { OP_DEFAULT_TTL_GET, 0, default_ttl_get },
+ { OP_DEFAULT_TTL_SET, 1, default_ttl_set },
+ { OP_GATT_PROXY_GET, 0, gatt_proxy_get },
+ { OP_GATT_PROXY_SET, 1, gatt_proxy_set },
+ { OP_NET_TRANSMIT_GET, 0, net_transmit_get },
+ { OP_NET_TRANSMIT_SET, 1, net_transmit_set },
+ { OP_RELAY_GET, 0, relay_get },
+ { OP_RELAY_SET, 2, relay_set },
+ { OP_MOD_PUB_GET, 4, mod_pub_get },
+ { OP_MOD_PUB_SET, 11, mod_pub_set },
+ { OP_MOD_PUB_VA_SET, 24, mod_pub_va_set },
+ { OP_MOD_SUB_ADD, 6, mod_sub_add },
+ { OP_MOD_SUB_VA_ADD, 20, mod_sub_va_add },
+ { OP_MOD_SUB_DEL, 6, mod_sub_del },
+ { OP_MOD_SUB_VA_DEL, 20, mod_sub_va_del },
+ { OP_MOD_SUB_OVERWRITE, 6, mod_sub_overwrite },
+ { OP_MOD_SUB_VA_OVERWRITE, 20, mod_sub_va_overwrite },
+ { OP_MOD_SUB_DEL_ALL, 4, mod_sub_del_all },
+ { OP_MOD_SUB_GET, 4, mod_sub_get },
+ { OP_MOD_SUB_GET_VND, 6, mod_sub_get_vnd },
+ { OP_NET_KEY_ADD, 18, net_key_add },
+ { OP_NET_KEY_UPDATE, 18, net_key_update },
+ { OP_NET_KEY_DEL, 2, net_key_del },
+ { OP_NET_KEY_GET, 0, net_key_get },
+ { OP_NODE_IDENTITY_GET, 2, node_identity_get },
+ { OP_NODE_IDENTITY_SET, 3, node_identity_set },
+ { OP_MOD_APP_BIND, 6, mod_app_bind },
+ { OP_MOD_APP_UNBIND, 6, mod_app_unbind },
+ { OP_SIG_MOD_APP_GET, 4, mod_app_get },
+ { OP_VND_MOD_APP_GET, 6, mod_app_get },
+ { OP_NODE_RESET, 0, node_reset },
+ { OP_FRIEND_GET, 0, friend_get },
+ { OP_FRIEND_SET, 1, friend_set },
+ { OP_LPN_TIMEOUT_GET, 2, lpn_timeout_get },
+ { OP_KRP_GET, 2, krp_get },
+ { OP_KRP_SET, 3, krp_set },
+ { OP_HEARTBEAT_PUB_GET, 0, heartbeat_pub_get },
+ { OP_HEARTBEAT_PUB_SET, 9, heartbeat_pub_set },
+ { OP_HEARTBEAT_SUB_GET, 0, heartbeat_sub_get },
+ { OP_HEARTBEAT_SUB_SET, 5, heartbeat_sub_set },
+ BT_MESH_MODEL_OP_END,
+};
+
+static void hb_publish(struct ble_npl_event *work)
+{
+ struct bt_mesh_cfg_srv *cfg = ble_npl_event_get_arg(work);
+ struct bt_mesh_subnet *sub;
+ u16_t period_ms;
+
+ BT_DBG("hb_pub.count: %u", cfg->hb_pub.count);
+
+ sub = bt_mesh_subnet_get(cfg->hb_pub.net_idx);
+ if (!sub) {
+ BT_ERR("No matching subnet for idx 0x%02x",
+ cfg->hb_pub.net_idx);
+ cfg->hb_pub.dst = BT_MESH_ADDR_UNASSIGNED;
+ return;
+ }
+
+ if (cfg->hb_pub.count == 0) {
+ return;
+ }
+
+ period_ms = hb_pwr2(cfg->hb_pub.period, 1) * 1000;
+ if (period_ms && cfg->hb_pub.count > 1) {
+ k_delayed_work_submit(&cfg->hb_pub.timer, period_ms);
+ }
+
+ bt_mesh_heartbeat_send();
+
+ if (cfg->hb_pub.count != 0xffff) {
+ cfg->hb_pub.count--;
+ }
+}
+
+static bool conf_is_valid(struct bt_mesh_cfg_srv *cfg)
+{
+ if (cfg->relay > 0x02) {
+ return false;
+ }
+
+ if (cfg->beacon > 0x01) {
+ return false;
+ }
+
+ if (cfg->default_ttl > BT_MESH_TTL_MAX) {
+ return false;
+ }
+
+ return true;
+}
+
+static int cfg_srv_init(struct bt_mesh_model *model)
+{
+ struct bt_mesh_cfg_srv *cfg = model->user_data;
+
+ BT_DBG("");
+
+ if (!bt_mesh_model_in_primary(model)) {
+ BT_ERR("Configuration Server only allowed in primary element");
+ return -EINVAL;
+ }
+
+ if (!cfg) {
+ BT_ERR("No Configuration Server context provided");
+ return -EINVAL;
+ }
+
+ if (!conf_is_valid(cfg)) {
+ BT_ERR("Invalid values in configuration");
+ return -EINVAL;
+ }
+
+ /*
+ * Configuration Model security is device-key based and only the local
+ * device-key is allowed to access this model.
+ */
+ model->keys[0] = BT_MESH_KEY_DEV_LOCAL;
+
+ if (!(MYNEWT_VAL(BLE_MESH_RELAY))) {
+ cfg->relay = BT_MESH_RELAY_NOT_SUPPORTED;
+ }
+
+ if (!(MYNEWT_VAL(BLE_MESH_FRIEND))) {
+ cfg->frnd = BT_MESH_FRIEND_NOT_SUPPORTED;
+ }
+
+ if (!(MYNEWT_VAL(BLE_MESH_GATT_PROXY))) {
+ cfg->gatt_proxy = BT_MESH_GATT_PROXY_NOT_SUPPORTED;
+ }
+
+ k_delayed_work_init(&cfg->hb_pub.timer, hb_publish);
+ k_delayed_work_add_arg(&cfg->hb_pub.timer, cfg);
+ cfg->hb_pub.net_idx = BT_MESH_KEY_UNUSED;
+ cfg->hb_sub.expiry = 0;
+
+ cfg->model = model;
+
+ conf = cfg;
+
+ return 0;
+}
+
+const struct bt_mesh_model_cb bt_mesh_cfg_srv_cb = {
+ .init = cfg_srv_init,
+};
+
+static void mod_reset(struct bt_mesh_model *mod, struct bt_mesh_elem *elem,
+ bool vnd, bool primary, void *user_data)
+{
+ size_t clear_count;
+
+ /* Clear model state that isn't otherwise cleared. E.g. AppKey
+ * binding and model publication is cleared as a consequence
+ * of removing all app keys, however model subscription and user data
+ * clearing must be taken care of here.
+ */
+
+ clear_count = mod_sub_list_clear(mod);
+
+ if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
+ if (clear_count) {
+ bt_mesh_store_mod_sub(mod);
+ }
+
+ bt_mesh_model_data_store(mod, vnd, NULL, 0);
+ }
+
+ if (mod->cb && mod->cb->reset) {
+ mod->cb->reset(mod);
+ }
+}
+
+void bt_mesh_cfg_reset(void)
+{
+ struct bt_mesh_cfg_srv *cfg = conf;
+ int i;
+
+ BT_DBG("");
+
+ if (!cfg) {
+ return;
+ }
+
+ bt_mesh_set_hb_sub_dst(BT_MESH_ADDR_UNASSIGNED);
+
+ cfg->hb_sub.src = BT_MESH_ADDR_UNASSIGNED;
+ cfg->hb_sub.dst = BT_MESH_ADDR_UNASSIGNED;
+ cfg->hb_sub.expiry = 0;
+
+ /* Delete all net keys, which also takes care of all app keys which
+ * are associated with each net key.
+ */
+ for (i = 0; i < ARRAY_SIZE(bt_mesh.sub); i++) {
+ struct bt_mesh_subnet *sub = &bt_mesh.sub[i];
+
+ if (sub->net_idx != BT_MESH_KEY_UNUSED) {
+ bt_mesh_subnet_del(sub, true);
+ }
+ }
+
+ bt_mesh_model_foreach(mod_reset, NULL);
+
+ memset(labels, 0, sizeof(labels));
+}
+
+void bt_mesh_heartbeat(u16_t src, u16_t dst, u8_t hops, u16_t feat)
+{
+ struct bt_mesh_cfg_srv *cfg = conf;
+
+ if (!cfg) {
+ BT_WARN("No configuaration server context available");
+ return;
+ }
+
+ if (src != cfg->hb_sub.src || dst != cfg->hb_sub.dst) {
+ BT_WARN("No subscription for received heartbeat");
+ return;
+ }
+
+ if (k_uptime_get() > cfg->hb_sub.expiry) {
+ BT_WARN("Heartbeat subscription period expired");
+ return;
+ }
+
+ cfg->hb_sub.min_hops = min(cfg->hb_sub.min_hops, hops);
+ cfg->hb_sub.max_hops = max(cfg->hb_sub.max_hops, hops);
+
+ if (cfg->hb_sub.count < 0xffff) {
+ cfg->hb_sub.count++;
+ }
+
+ BT_DBG("src 0x%04x dst 0x%04x hops %u min %u max %u count %u", src,
+ dst, hops, cfg->hb_sub.min_hops, cfg->hb_sub.max_hops,
+ cfg->hb_sub.count);
+
+ if (cfg->hb_sub.func) {
+ cfg->hb_sub.func(hops, feat);
+ }
+}
+
+u8_t bt_mesh_net_transmit_get(void)
+{
+ if (conf) {
+ return conf->net_transmit;
+ }
+
+ return 0;
+}
+
+u8_t bt_mesh_relay_get(void)
+{
+ if (conf) {
+ return conf->relay;
+ }
+
+ return BT_MESH_RELAY_NOT_SUPPORTED;
+}
+
+u8_t bt_mesh_friend_get(void)
+{
+ BT_DBG("conf %p conf->frnd 0x%02x", conf, conf->frnd);
+
+ if (conf) {
+ return conf->frnd;
+ }
+
+ return BT_MESH_FRIEND_NOT_SUPPORTED;
+}
+
+u8_t bt_mesh_relay_retransmit_get(void)
+{
+ if (conf) {
+ return conf->relay_retransmit;
+ }
+
+ return 0;
+}
+
+u8_t bt_mesh_beacon_get(void)
+{
+ if (conf) {
+ return conf->beacon;
+ }
+
+ return BT_MESH_BEACON_DISABLED;
+}
+
+u8_t bt_mesh_gatt_proxy_get(void)
+{
+ if (conf) {
+ return conf->gatt_proxy;
+ }
+
+ return BT_MESH_GATT_PROXY_NOT_SUPPORTED;
+}
+
+u8_t bt_mesh_default_ttl_get(void)
+{
+ if (conf) {
+ return conf->default_ttl;
+ }
+
+ return DEFAULT_TTL;
+}
+
+u8_t *bt_mesh_label_uuid_get(u16_t addr)
+{
+ int i;
+
+ BT_DBG("addr 0x%04x", addr);
+
+ for (i = 0; i < ARRAY_SIZE(labels); i++) {
+ if (labels[i].addr == addr) {
+ BT_DBG("Found Label UUID for 0x%04x: %s", addr,
+ bt_hex(labels[i].uuid, 16));
+ return labels[i].uuid;
+ }
+ }
+
+ BT_WARN("No matching Label UUID for 0x%04x", addr);
+
+ return NULL;
+}
+
+struct bt_mesh_hb_pub *bt_mesh_hb_pub_get(void)
+{
+ if (!conf) {
+ return NULL;
+ }
+
+ return &conf->hb_pub;
+}
+
+void bt_mesh_hb_pub_disable(void)
+{
+ if (conf) {
+ hb_pub_disable(conf);
+ }
+}
+
+struct bt_mesh_cfg_srv *bt_mesh_cfg_get(void)
+{
+ return conf;
+}
+
+void bt_mesh_subnet_del(struct bt_mesh_subnet *sub, bool store)
+{
+ int i;
+
+ BT_DBG("NetIdx 0x%03x store %u", sub->net_idx, store);
+
+ if (conf && conf->hb_pub.net_idx == sub->net_idx) {
+ hb_pub_disable(conf);
+
+ if (IS_ENABLED(CONFIG_BT_SETTINGS) && store) {
+ bt_mesh_store_hb_pub();
+ }
+ }
+
+ /* Delete any app keys bound to this NetKey index */
+ for (i = 0; i < ARRAY_SIZE(bt_mesh.app_keys); i++) {
+ struct bt_mesh_app_key *key = &bt_mesh.app_keys[i];
+
+ if (key->net_idx == sub->net_idx) {
+ bt_mesh_app_key_del(key, store);
+ }
+ }
+
+ if (IS_ENABLED(CONFIG_BT_MESH_FRIEND)) {
+ bt_mesh_friend_clear_net_idx(sub->net_idx);
+ }
+
+ if (IS_ENABLED(CONFIG_BT_SETTINGS) && store) {
+ bt_mesh_clear_subnet(sub);
+ }
+
+ memset(sub, 0, sizeof(*sub));
+ sub->net_idx = BT_MESH_KEY_UNUSED;
+}
diff --git a/src/libs/mynewt-nimble/nimble/host/mesh/src/crypto.c b/src/libs/mynewt-nimble/nimble/host/mesh/src/crypto.c
new file mode 100644
index 00000000..b6a0ba21
--- /dev/null
+++ b/src/libs/mynewt-nimble/nimble/host/mesh/src/crypto.c
@@ -0,0 +1,911 @@
+/* Bluetooth Mesh */
+
+/*
+ * Copyright (c) 2017 Intel Corporation
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include "syscfg/syscfg.h"
+#define MESH_LOG_MODULE BLE_MESH_CRYPTO_LOG
+
+#include <string.h>
+#include <stdbool.h>
+#include <errno.h>
+
+#include <tinycrypt/constants.h>
+#include <tinycrypt/utils.h>
+#include <tinycrypt/aes.h>
+#include <tinycrypt/cmac_mode.h>
+#include <tinycrypt/ccm_mode.h>
+
+#include "crypto.h"
+
+#define NET_MIC_LEN(pdu) (((pdu)[1] & 0x80) ? 8 : 4)
+#define APP_MIC_LEN(aszmic) ((aszmic) ? 8 : 4)
+
+int bt_mesh_aes_cmac(const u8_t key[16], struct bt_mesh_sg *sg,
+ size_t sg_len, u8_t mac[16])
+{
+ struct tc_aes_key_sched_struct sched;
+ struct tc_cmac_struct state;
+
+ if (tc_cmac_setup(&state, key, &sched) == TC_CRYPTO_FAIL) {
+ return -EIO;
+ }
+
+ for (; sg_len; sg_len--, sg++) {
+ if (tc_cmac_update(&state, sg->data,
+ sg->len) == TC_CRYPTO_FAIL) {
+ return -EIO;
+ }
+ }
+
+ if (tc_cmac_final(mac, &state) == TC_CRYPTO_FAIL) {
+ return -EIO;
+ }
+
+ return 0;
+}
+
+int bt_mesh_k1(const u8_t *ikm, size_t ikm_len, const u8_t salt[16],
+ const char *info, u8_t okm[16])
+{
+ int err;
+
+ err = bt_mesh_aes_cmac_one(salt, ikm, ikm_len, okm);
+ if (err < 0) {
+ return err;
+ }
+
+ return bt_mesh_aes_cmac_one(okm, info, strlen(info), okm);
+}
+
+int bt_mesh_k2(const u8_t n[16], const u8_t *p, size_t p_len,
+ u8_t net_id[1], u8_t enc_key[16], u8_t priv_key[16])
+{
+ struct bt_mesh_sg sg[3];
+ u8_t salt[16];
+ u8_t out[16];
+ u8_t t[16];
+ u8_t pad;
+ int err;
+
+ BT_DBG("n %s", bt_hex(n, 16));
+ BT_DBG("p %s", bt_hex(p, p_len));
+
+ err = bt_mesh_s1("smk2", salt);
+ if (err) {
+ return err;
+ }
+
+ err = bt_mesh_aes_cmac_one(salt, n, 16, t);
+ if (err) {
+ return err;
+ }
+
+ pad = 0x01;
+
+ sg[0].data = NULL;
+ sg[0].len = 0;
+ sg[1].data = p;
+ sg[1].len = p_len;
+ sg[2].data = &pad;
+ sg[2].len = sizeof(pad);
+
+ err = bt_mesh_aes_cmac(t, sg, ARRAY_SIZE(sg), out);
+ if (err) {
+ return err;
+ }
+
+ net_id[0] = out[15] & 0x7f;
+
+ sg[0].data = out;
+ sg[0].len = sizeof(out);
+ pad = 0x02;
+
+ err = bt_mesh_aes_cmac(t, sg, ARRAY_SIZE(sg), out);
+ if (err) {
+ return err;
+ }
+
+ memcpy(enc_key, out, 16);
+
+ pad = 0x03;
+
+ err = bt_mesh_aes_cmac(t, sg, ARRAY_SIZE(sg), out);
+ if (err) {
+ return err;
+ }
+
+ memcpy(priv_key, out, 16);
+
+ BT_DBG("NID 0x%02x enc_key %s", net_id[0], bt_hex(enc_key, 16));
+ BT_DBG("priv_key %s", bt_hex(priv_key, 16));
+
+ return 0;
+}
+
+int bt_mesh_k3(const u8_t n[16], u8_t out[8])
+{
+ u8_t id64[] = { 'i', 'd', '6', '4', 0x01 };
+ u8_t tmp[16];
+ u8_t t[16];
+ int err;
+
+ err = bt_mesh_s1("smk3", tmp);
+ if (err) {
+ return err;
+ }
+
+ err = bt_mesh_aes_cmac_one(tmp, n, 16, t);
+ if (err) {
+ return err;
+ }
+
+ err = bt_mesh_aes_cmac_one(t, id64, sizeof(id64), tmp);
+ if (err) {
+ return err;
+ }
+
+ memcpy(out, tmp + 8, 8);
+
+ return 0;
+}
+
+int bt_mesh_k4(const u8_t n[16], u8_t out[1])
+{
+ u8_t id6[] = { 'i', 'd', '6', 0x01 };
+ u8_t tmp[16];
+ u8_t t[16];
+ int err;
+
+ err = bt_mesh_s1("smk4", tmp);
+ if (err) {
+ return err;
+ }
+
+ err = bt_mesh_aes_cmac_one(tmp, n, 16, t);
+ if (err) {
+ return err;
+ }
+
+ err = bt_mesh_aes_cmac_one(t, id6, sizeof(id6), tmp);
+ if (err) {
+ return err;
+ }
+
+ out[0] = tmp[15] & BIT_MASK(6);
+
+ return 0;
+}
+
+int bt_mesh_id128(const u8_t n[16], const char *s, u8_t out[16])
+{
+ const char *id128 = "id128\x01";
+ u8_t salt[16];
+ int err;
+
+ err = bt_mesh_s1(s, salt);
+ if (err) {
+ return err;
+ }
+
+ return bt_mesh_k1(n, 16, salt, id128, out);
+}
+
+static int bt_mesh_ccm_decrypt(const u8_t key[16], u8_t nonce[13],
+ const u8_t *enc_msg, size_t msg_len,
+ const u8_t *aad, size_t aad_len,
+ u8_t *out_msg, size_t mic_size)
+{
+ u8_t msg[16], pmsg[16], cmic[16], cmsg[16], Xn[16], mic[16];
+ u16_t last_blk, blk_cnt;
+ size_t i, j;
+ int err;
+
+ if (msg_len < 1 || aad_len >= 0xff00) {
+ return -EINVAL;
+ }
+
+ /* C_mic = e(AppKey, 0x01 || nonce || 0x0000) */
+ pmsg[0] = 0x01;
+ memcpy(pmsg + 1, nonce, 13);
+ sys_put_be16(0x0000, pmsg + 14);
+
+ err = bt_encrypt_be(key, pmsg, cmic);
+ if (err) {
+ return err;
+ }
+
+ /* X_0 = e(AppKey, 0x09 || nonce || length) */
+ if (mic_size == sizeof(u64_t)) {
+ pmsg[0] = 0x19 | (aad_len ? 0x40 : 0x00);
+ } else {
+ pmsg[0] = 0x09 | (aad_len ? 0x40 : 0x00);
+ }
+
+ memcpy(pmsg + 1, nonce, 13);
+ sys_put_be16(msg_len, pmsg + 14);
+
+ err = bt_encrypt_be(key, pmsg, Xn);
+ if (err) {
+ return err;
+ }
+
+ /* If AAD is being used to authenticate, include it here */
+ if (aad_len) {
+ sys_put_be16(aad_len, pmsg);
+
+ for (i = 0; i < sizeof(u16_t); i++) {
+ pmsg[i] = Xn[i] ^ pmsg[i];
+ }
+
+ j = 0;
+ aad_len += sizeof(u16_t);
+ while (aad_len > 16) {
+ do {
+ pmsg[i] = Xn[i] ^ aad[j];
+ i++, j++;
+ } while (i < 16);
+
+ aad_len -= 16;
+ i = 0;
+
+ err = bt_encrypt_be(key, pmsg, Xn);
+ if (err) {
+ return err;
+ }
+ }
+
+ for (; i < aad_len; i++, j++) {
+ pmsg[i] = Xn[i] ^ aad[j];
+ }
+
+ for (i = aad_len; i < 16; i++) {
+ pmsg[i] = Xn[i];
+ }
+
+ err = bt_encrypt_be(key, pmsg, Xn);
+ if (err) {
+ return err;
+ }
+ }
+
+ last_blk = msg_len % 16;
+ blk_cnt = (msg_len + 15) / 16;
+ if (!last_blk) {
+ last_blk = 16;
+ }
+
+ for (j = 0; j < blk_cnt; j++) {
+ if (j + 1 == blk_cnt) {
+ /* C_1 = e(AppKey, 0x01 || nonce || 0x0001) */
+ pmsg[0] = 0x01;
+ memcpy(pmsg + 1, nonce, 13);
+ sys_put_be16(j + 1, pmsg + 14);
+
+ err = bt_encrypt_be(key, pmsg, cmsg);
+ if (err) {
+ return err;
+ }
+
+ /* Encrypted = Payload[0-15] ^ C_1 */
+ for (i = 0; i < last_blk; i++) {
+ msg[i] = enc_msg[(j * 16) + i] ^ cmsg[i];
+ }
+
+ memcpy(out_msg + (j * 16), msg, last_blk);
+
+ /* X_1 = e(AppKey, X_0 ^ Payload[0-15]) */
+ for (i = 0; i < last_blk; i++) {
+ pmsg[i] = Xn[i] ^ msg[i];
+ }
+
+ for (i = last_blk; i < 16; i++) {
+ pmsg[i] = Xn[i] ^ 0x00;
+ }
+
+ err = bt_encrypt_be(key, pmsg, Xn);
+ if (err) {
+ return err;
+ }
+
+ /* MIC = C_mic ^ X_1 */
+ for (i = 0; i < sizeof(mic); i++) {
+ mic[i] = cmic[i] ^ Xn[i];
+ }
+ } else {
+ /* C_1 = e(AppKey, 0x01 || nonce || 0x0001) */
+ pmsg[0] = 0x01;
+ memcpy(pmsg + 1, nonce, 13);
+ sys_put_be16(j + 1, pmsg + 14);
+
+ err = bt_encrypt_be(key, pmsg, cmsg);
+ if (err) {
+ return err;
+ }
+
+ /* Encrypted = Payload[0-15] ^ C_1 */
+ for (i = 0; i < 16; i++) {
+ msg[i] = enc_msg[(j * 16) + i] ^ cmsg[i];
+ }
+
+ memcpy(out_msg + (j * 16), msg, 16);
+
+ /* X_1 = e(AppKey, X_0 ^ Payload[0-15]) */
+ for (i = 0; i < 16; i++) {
+ pmsg[i] = Xn[i] ^ msg[i];
+ }
+
+ err = bt_encrypt_be(key, pmsg, Xn);
+ if (err) {
+ return err;
+ }
+ }
+ }
+
+ if (memcmp(mic, enc_msg + msg_len, mic_size)) {
+ return -EBADMSG;
+ }
+
+ return 0;
+}
+
+static int bt_mesh_ccm_encrypt(const u8_t key[16], u8_t nonce[13],
+ const u8_t *msg, size_t msg_len,
+ const u8_t *aad, size_t aad_len,
+ u8_t *out_msg, size_t mic_size)
+{
+ u8_t pmsg[16], cmic[16], cmsg[16], mic[16], Xn[16];
+ u16_t blk_cnt, last_blk;
+ size_t i, j;
+ int err;
+
+ BT_DBG("key %s", bt_hex(key, 16));
+ BT_DBG("nonce %s", bt_hex(nonce, 13));
+ BT_DBG("msg (len %zu) %s", msg_len, bt_hex(msg, msg_len));
+ BT_DBG("aad_len %zu mic_size %zu", aad_len, mic_size);
+
+ /* Unsupported AAD size */
+ if (aad_len >= 0xff00) {
+ return -EINVAL;
+ }
+
+ /* C_mic = e(AppKey, 0x01 || nonce || 0x0000) */
+ pmsg[0] = 0x01;
+ memcpy(pmsg + 1, nonce, 13);
+ sys_put_be16(0x0000, pmsg + 14);
+
+ err = bt_encrypt_be(key, pmsg, cmic);
+ if (err) {
+ return err;
+ }
+
+ /* X_0 = e(AppKey, 0x09 || nonce || length) */
+ if (mic_size == sizeof(u64_t)) {
+ pmsg[0] = 0x19 | (aad_len ? 0x40 : 0x00);
+ } else {
+ pmsg[0] = 0x09 | (aad_len ? 0x40 : 0x00);
+ }
+
+ memcpy(pmsg + 1, nonce, 13);
+ sys_put_be16(msg_len, pmsg + 14);
+
+ err = bt_encrypt_be(key, pmsg, Xn);
+ if (err) {
+ return err;
+ }
+
+ /* If AAD is being used to authenticate, include it here */
+ if (aad_len) {
+ sys_put_be16(aad_len, pmsg);
+
+ for (i = 0; i < sizeof(u16_t); i++) {
+ pmsg[i] = Xn[i] ^ pmsg[i];
+ }
+
+ j = 0;
+ aad_len += sizeof(u16_t);
+ while (aad_len > 16) {
+ do {
+ pmsg[i] = Xn[i] ^ aad[j];
+ i++, j++;
+ } while (i < 16);
+
+ aad_len -= 16;
+ i = 0;
+
+ err = bt_encrypt_be(key, pmsg, Xn);
+ if (err) {
+ return err;
+ }
+ }
+
+ for (; i < aad_len; i++, j++) {
+ pmsg[i] = Xn[i] ^ aad[j];
+ }
+
+ for (i = aad_len; i < 16; i++) {
+ pmsg[i] = Xn[i];
+ }
+
+ err = bt_encrypt_be(key, pmsg, Xn);
+ if (err) {
+ return err;
+ }
+ }
+
+ last_blk = msg_len % 16;
+ blk_cnt = (msg_len + 15) / 16;
+ if (!last_blk) {
+ last_blk = 16;
+ }
+
+ for (j = 0; j < blk_cnt; j++) {
+ if (j + 1 == blk_cnt) {
+ /* X_1 = e(AppKey, X_0 ^ Payload[0-15]) */
+ for (i = 0; i < last_blk; i++) {
+ pmsg[i] = Xn[i] ^ msg[(j * 16) + i];
+ }
+ for (i = last_blk; i < 16; i++) {
+ pmsg[i] = Xn[i] ^ 0x00;
+ }
+
+ err = bt_encrypt_be(key, pmsg, Xn);
+ if (err) {
+ return err;
+ }
+
+ /* MIC = C_mic ^ X_1 */
+ for (i = 0; i < sizeof(mic); i++) {
+ mic[i] = cmic[i] ^ Xn[i];
+ }
+
+ /* C_1 = e(AppKey, 0x01 || nonce || 0x0001) */
+ pmsg[0] = 0x01;
+ memcpy(pmsg + 1, nonce, 13);
+ sys_put_be16(j + 1, pmsg + 14);
+
+ err = bt_encrypt_be(key, pmsg, cmsg);
+ if (err) {
+ return err;
+ }
+
+ /* Encrypted = Payload[0-15] ^ C_1 */
+ for (i = 0; i < last_blk; i++) {
+ out_msg[(j * 16) + i] =
+ msg[(j * 16) + i] ^ cmsg[i];
+ }
+ } else {
+ /* X_1 = e(AppKey, X_0 ^ Payload[0-15]) */
+ for (i = 0; i < 16; i++) {
+ pmsg[i] = Xn[i] ^ msg[(j * 16) + i];
+ }
+
+ err = bt_encrypt_be(key, pmsg, Xn);
+ if (err) {
+ return err;
+ }
+
+ /* C_1 = e(AppKey, 0x01 || nonce || 0x0001) */
+ pmsg[0] = 0x01;
+ memcpy(pmsg + 1, nonce, 13);
+ sys_put_be16(j + 1, pmsg + 14);
+
+ err = bt_encrypt_be(key, pmsg, cmsg);
+ if (err) {
+ return err;
+ }
+
+ /* Encrypted = Payload[0-15] ^ C_N */
+ for (i = 0; i < 16; i++) {
+ out_msg[(j * 16) + i] =
+ msg[(j * 16) + i] ^ cmsg[i];
+ }
+
+ }
+ }
+
+ memcpy(out_msg + msg_len, mic, mic_size);
+
+ return 0;
+}
+
+static void create_proxy_nonce(u8_t nonce[13], const u8_t *pdu,
+ u32_t iv_index)
+{
+ /* Nonce Type */
+ nonce[0] = 0x03;
+
+ /* Pad */
+ nonce[1] = 0x00;
+
+ /* Sequence Number */
+ nonce[2] = pdu[2];
+ nonce[3] = pdu[3];
+ nonce[4] = pdu[4];
+
+ /* Source Address */
+ nonce[5] = pdu[5];
+ nonce[6] = pdu[6];
+
+ /* Pad */
+ nonce[7] = 0;
+ nonce[8] = 0;
+
+ /* IV Index */
+ sys_put_be32(iv_index, &nonce[9]);
+}
+
+static void create_net_nonce(u8_t nonce[13], const u8_t *pdu,
+ u32_t iv_index)
+{
+ /* Nonce Type */
+ nonce[0] = 0x00;
+
+ /* FRND + TTL */
+ nonce[1] = pdu[1];
+
+ /* Sequence Number */
+ nonce[2] = pdu[2];
+ nonce[3] = pdu[3];
+ nonce[4] = pdu[4];
+
+ /* Source Address */
+ nonce[5] = pdu[5];
+ nonce[6] = pdu[6];
+
+ /* Pad */
+ nonce[7] = 0;
+ nonce[8] = 0;
+
+ /* IV Index */
+ sys_put_be32(iv_index, &nonce[9]);
+}
+
+int bt_mesh_net_obfuscate(u8_t *pdu, u32_t iv_index,
+ const u8_t privacy_key[16])
+{
+ u8_t priv_rand[16] = { 0x00, 0x00, 0x00, 0x00, 0x00, };
+ u8_t tmp[16];
+ int err, i;
+
+ BT_DBG("IVIndex %u, PrivacyKey %s", (unsigned) iv_index,
+ bt_hex(privacy_key, 16));
+
+ sys_put_be32(iv_index, &priv_rand[5]);
+ memcpy(&priv_rand[9], &pdu[7], 7);
+
+ BT_DBG("PrivacyRandom %s", bt_hex(priv_rand, 16));
+
+ err = bt_encrypt_be(privacy_key, priv_rand, tmp);
+ if (err) {
+ return err;
+ }
+
+ for (i = 0; i < 6; i++) {
+ pdu[1 + i] ^= tmp[i];
+ }
+
+ return 0;
+}
+
+int bt_mesh_net_encrypt(const u8_t key[16], struct os_mbuf *buf,
+ u32_t iv_index, bool proxy)
+{
+ u8_t mic_len = NET_MIC_LEN(buf->om_data);
+ u8_t nonce[13];
+ int err;
+
+ BT_DBG("IVIndex %u EncKey %s mic_len %u", (unsigned) iv_index,
+ bt_hex(key, 16), mic_len);
+ BT_DBG("PDU (len %u) %s", buf->om_len, bt_hex(buf->om_data, buf->om_len));
+
+ if (IS_ENABLED(CONFIG_BT_MESH_PROXY) && proxy) {
+ create_proxy_nonce(nonce, buf->om_data, iv_index);
+ } else {
+ create_net_nonce(nonce, buf->om_data, iv_index);
+ }
+
+ BT_DBG("Nonce %s", bt_hex(nonce, 13));
+
+ err = bt_mesh_ccm_encrypt(key, nonce, &buf->om_data[7], buf->om_len - 7,
+ NULL, 0, &buf->om_data[7], mic_len);
+ if (!err) {
+ net_buf_simple_add(buf, mic_len);
+ }
+
+ return err;
+}
+
+int bt_mesh_net_decrypt(const u8_t key[16], struct os_mbuf *buf,
+ u32_t iv_index, bool proxy)
+{
+ u8_t mic_len = NET_MIC_LEN(buf->om_data);
+ u8_t nonce[13];
+
+ BT_DBG("PDU (%u bytes) %s", buf->om_len, bt_hex(buf->om_data, buf->om_len));
+ BT_DBG("iv_index %u, key %s mic_len %u", (unsigned) iv_index,
+ bt_hex(key, 16), mic_len);
+
+ if (IS_ENABLED(CONFIG_BT_MESH_PROXY) && proxy) {
+ create_proxy_nonce(nonce, buf->om_data, iv_index);
+ } else {
+ create_net_nonce(nonce, buf->om_data, iv_index);
+ }
+
+ BT_DBG("Nonce %s", bt_hex(nonce, 13));
+
+ buf->om_len -= mic_len;
+
+ return bt_mesh_ccm_decrypt(key, nonce, &buf->om_data[7], buf->om_len - 7,
+ NULL, 0, &buf->om_data[7], mic_len);
+}
+
+static void create_app_nonce(u8_t nonce[13], bool dev_key, u8_t aszmic,
+ u16_t src, u16_t dst, u32_t seq_num,
+ u32_t iv_index)
+{
+ if (dev_key) {
+ nonce[0] = 0x02;
+ } else {
+ nonce[0] = 0x01;
+ }
+
+ sys_put_be32((seq_num | ((u32_t)aszmic << 31)), &nonce[1]);
+
+ sys_put_be16(src, &nonce[5]);
+ sys_put_be16(dst, &nonce[7]);
+
+ sys_put_be32(iv_index, &nonce[9]);
+}
+
+static int mesh_app_encrypt(const u8_t key[16], bool dev_key, u8_t aszmic,
+ struct os_mbuf *buf, const u8_t *ad,
+ u16_t src, u16_t dst, u32_t seq_num, u32_t iv_index)
+{
+ u8_t nonce[13];
+
+ BT_DBG("AppKey %s", bt_hex(key, 16));
+ BT_DBG("dev_key %u src 0x%04x dst 0x%04x", dev_key, src, dst);
+ BT_DBG("seq_num 0x%08x iv_index 0x%08x", (unsigned) seq_num,
+ (unsigned) iv_index);
+ BT_DBG("Clear: %s", bt_hex(buf->om_data, buf->om_len));
+
+ create_app_nonce(nonce, dev_key, aszmic, src, dst, seq_num, iv_index);
+
+ BT_DBG("Nonce %s", bt_hex(nonce, 13));
+
+ return bt_mesh_ccm_encrypt(key, nonce, buf->om_data, buf->om_len, ad,
+ ad ? 16 : 0, buf->om_data,
+ APP_MIC_LEN(aszmic));
+}
+
+int bt_mesh_app_encrypt_in_place(const u8_t key[16], bool dev_key, u8_t aszmic,
+ struct os_mbuf *buf, const u8_t *ad, u16_t src,
+ u16_t dst, u32_t seq_num, u32_t iv_index)
+{
+ int err;
+
+ err = mesh_app_encrypt(key, dev_key, aszmic, buf, ad, src, dst,
+ seq_num, iv_index);
+ if (!err) {
+ BT_DBG("Encr: %s", bt_hex(buf->om_data, buf->om_len));
+ }
+
+ return err;
+}
+
+int bt_mesh_app_encrypt(const u8_t key[16], bool dev_key, u8_t aszmic,
+ struct os_mbuf *buf, const u8_t *ad,
+ u16_t src, u16_t dst, u32_t seq_num, u32_t iv_index)
+{
+ int err;
+
+ err = mesh_app_encrypt(key, dev_key, aszmic, buf, ad, src, dst,
+ seq_num, iv_index);
+
+ if (!err) {
+ net_buf_simple_add(buf, APP_MIC_LEN(aszmic));
+ BT_DBG("Encr: %s", bt_hex(buf->om_data, buf->om_len));
+ }
+
+ return err;
+}
+
+static int mesh_app_decrypt(const u8_t key[16], bool dev_key, u8_t aszmic,
+ struct os_mbuf *buf, struct os_mbuf *out,
+ const u8_t *ad, u16_t src, u16_t dst,
+ u32_t seq_num, u32_t iv_index)
+{
+ u8_t nonce[13];
+
+ BT_DBG("EncData (len %u) %s", buf->om_len,
+ bt_hex(buf->om_data, buf->om_len));
+
+ create_app_nonce(nonce, dev_key, aszmic, src, dst, seq_num, iv_index);
+
+ BT_DBG("AppKey %s", bt_hex(key, 16));
+ BT_DBG("Nonce %s", bt_hex(nonce, 13));
+
+ return bt_mesh_ccm_decrypt(key, nonce, buf->om_data, buf->om_len, ad,
+ ad ? 16 : 0, out->om_data,
+ APP_MIC_LEN(aszmic));
+}
+
+int bt_mesh_app_decrypt_in_place(const u8_t key[16], bool dev_key, u8_t aszmic,
+ struct os_mbuf *buf, const u8_t *ad, u16_t src,
+ u16_t dst, u32_t seq_num, u32_t iv_index)
+{
+ return mesh_app_decrypt(key, dev_key, aszmic, buf, buf,
+ ad, src, dst, seq_num, iv_index);
+}
+
+int bt_mesh_app_decrypt(const u8_t key[16], bool dev_key, u8_t aszmic,
+ struct os_mbuf *buf, struct os_mbuf *out,
+ const u8_t *ad, u16_t src, u16_t dst, u32_t seq_num,
+ u32_t iv_index)
+{
+ int err;
+
+ err = mesh_app_decrypt(key, dev_key, aszmic, buf, out,
+ ad, src, dst, seq_num, iv_index);
+ if (!err) {
+ net_buf_simple_add(out, buf->om_len);
+ }
+
+ return err;
+}
+
+/* reversed, 8-bit, poly=0x07 */
+static const u8_t crc_table[256] = {
+ 0x00, 0x91, 0xe3, 0x72, 0x07, 0x96, 0xe4, 0x75,
+ 0x0e, 0x9f, 0xed, 0x7c, 0x09, 0x98, 0xea, 0x7b,
+ 0x1c, 0x8d, 0xff, 0x6e, 0x1b, 0x8a, 0xf8, 0x69,
+ 0x12, 0x83, 0xf1, 0x60, 0x15, 0x84, 0xf6, 0x67,
+
+ 0x38, 0xa9, 0xdb, 0x4a, 0x3f, 0xae, 0xdc, 0x4d,
+ 0x36, 0xa7, 0xd5, 0x44, 0x31, 0xa0, 0xd2, 0x43,
+ 0x24, 0xb5, 0xc7, 0x56, 0x23, 0xb2, 0xc0, 0x51,
+ 0x2a, 0xbb, 0xc9, 0x58, 0x2d, 0xbc, 0xce, 0x5f,
+
+ 0x70, 0xe1, 0x93, 0x02, 0x77, 0xe6, 0x94, 0x05,
+ 0x7e, 0xef, 0x9d, 0x0c, 0x79, 0xe8, 0x9a, 0x0b,
+ 0x6c, 0xfd, 0x8f, 0x1e, 0x6b, 0xfa, 0x88, 0x19,
+ 0x62, 0xf3, 0x81, 0x10, 0x65, 0xf4, 0x86, 0x17,
+
+ 0x48, 0xd9, 0xab, 0x3a, 0x4f, 0xde, 0xac, 0x3d,
+ 0x46, 0xd7, 0xa5, 0x34, 0x41, 0xd0, 0xa2, 0x33,
+ 0x54, 0xc5, 0xb7, 0x26, 0x53, 0xc2, 0xb0, 0x21,
+ 0x5a, 0xcb, 0xb9, 0x28, 0x5d, 0xcc, 0xbe, 0x2f,
+
+ 0xe0, 0x71, 0x03, 0x92, 0xe7, 0x76, 0x04, 0x95,
+ 0xee, 0x7f, 0x0d, 0x9c, 0xe9, 0x78, 0x0a, 0x9b,
+ 0xfc, 0x6d, 0x1f, 0x8e, 0xfb, 0x6a, 0x18, 0x89,
+ 0xf2, 0x63, 0x11, 0x80, 0xf5, 0x64, 0x16, 0x87,
+
+ 0xd8, 0x49, 0x3b, 0xaa, 0xdf, 0x4e, 0x3c, 0xad,
+ 0xd6, 0x47, 0x35, 0xa4, 0xd1, 0x40, 0x32, 0xa3,
+ 0xc4, 0x55, 0x27, 0xb6, 0xc3, 0x52, 0x20, 0xb1,
+ 0xca, 0x5b, 0x29, 0xb8, 0xcd, 0x5c, 0x2e, 0xbf,
+
+ 0x90, 0x01, 0x73, 0xe2, 0x97, 0x06, 0x74, 0xe5,
+ 0x9e, 0x0f, 0x7d, 0xec, 0x99, 0x08, 0x7a, 0xeb,
+ 0x8c, 0x1d, 0x6f, 0xfe, 0x8b, 0x1a, 0x68, 0xf9,
+ 0x82, 0x13, 0x61, 0xf0, 0x85, 0x14, 0x66, 0xf7,
+
+ 0xa8, 0x39, 0x4b, 0xda, 0xaf, 0x3e, 0x4c, 0xdd,
+ 0xa6, 0x37, 0x45, 0xd4, 0xa1, 0x30, 0x42, 0xd3,
+ 0xb4, 0x25, 0x57, 0xc6, 0xb3, 0x22, 0x50, 0xc1,
+ 0xba, 0x2b, 0x59, 0xc8, 0xbd, 0x2c, 0x5e, 0xcf
+};
+
+u8_t bt_mesh_fcs_calc(const u8_t *data, u8_t data_len)
+{
+ u8_t fcs = 0xff;
+
+ while (data_len--) {
+ fcs = crc_table[fcs ^ *data++];
+ }
+
+ BT_DBG("fcs 0x%02x", 0xff - fcs);
+
+ return 0xff - fcs;
+}
+
+bool bt_mesh_fcs_check(struct os_mbuf *buf, u8_t received_fcs)
+{
+ const u8_t *data = buf->om_data;
+ u16_t data_len = buf->om_len;
+ u8_t fcs = 0xff;
+
+ while (data_len--) {
+ fcs = crc_table[fcs ^ *data++];
+ }
+
+ return crc_table[fcs ^ received_fcs] == 0xcf;
+}
+
+int bt_mesh_virtual_addr(const u8_t virtual_label[16], u16_t *addr)
+{
+ u8_t salt[16];
+ u8_t tmp[16];
+ int err;
+
+ err = bt_mesh_s1("vtad", salt);
+ if (err) {
+ return err;
+ }
+
+ err = bt_mesh_aes_cmac_one(salt, virtual_label, 16, tmp);
+ if (err) {
+ return err;
+ }
+
+ *addr = (sys_get_be16(&tmp[14]) & 0x3fff) | 0x8000;
+
+ return 0;
+}
+
+int bt_mesh_prov_conf_salt(const u8_t conf_inputs[145], u8_t salt[16])
+{
+ const u8_t conf_salt_key[16] = { 0 };
+
+ return bt_mesh_aes_cmac_one(conf_salt_key, conf_inputs, 145, salt);
+}
+
+int bt_mesh_prov_conf_key(const u8_t dhkey[32], const u8_t conf_salt[16],
+ u8_t conf_key[16])
+{
+ return bt_mesh_k1(dhkey, 32, conf_salt, "prck", conf_key);
+}
+
+int bt_mesh_prov_conf(const u8_t conf_key[16], const u8_t rand[16],
+ const u8_t auth[16], u8_t conf[16])
+{
+ struct bt_mesh_sg sg[] = { { rand, 16 }, { auth, 16 } };
+
+ BT_DBG("ConfirmationKey %s", bt_hex(conf_key, 16));
+ BT_DBG("RandomDevice %s", bt_hex(rand, 16));
+ BT_DBG("AuthValue %s", bt_hex(auth, 16));
+
+ return bt_mesh_aes_cmac(conf_key, sg, ARRAY_SIZE(sg), conf);
+}
+
+int bt_mesh_prov_decrypt(const u8_t key[16], u8_t nonce[13],
+ const u8_t data[25 + 8], u8_t out[25])
+{
+ return bt_mesh_ccm_decrypt(key, nonce, data, 25, NULL, 0, out, 8);
+}
+
+int bt_mesh_prov_encrypt(const u8_t key[16], u8_t nonce[13],
+ const u8_t data[25], u8_t out[25 + 8])
+{
+ return bt_mesh_ccm_encrypt(key, nonce, data, 25, NULL, 0, out, 8);
+}
+
+int bt_mesh_beacon_auth(const u8_t beacon_key[16], u8_t flags,
+ const u8_t net_id[8], u32_t iv_index,
+ u8_t auth[8])
+{
+ u8_t msg[13], tmp[16];
+ int err;
+
+ BT_DBG("BeaconKey %s", bt_hex(beacon_key, 16));
+ BT_DBG("NetId %s", bt_hex(net_id, 8));
+ BT_DBG("IV Index 0x%08x", (unsigned) iv_index);
+
+ msg[0] = flags;
+ memcpy(&msg[1], net_id, 8);
+ sys_put_be32(iv_index, &msg[9]);
+
+ BT_DBG("BeaconMsg %s", bt_hex(msg, sizeof(msg)));
+
+ err = bt_mesh_aes_cmac_one(beacon_key, msg, sizeof(msg), tmp);
+ if (!err) {
+ memcpy(auth, tmp, 8);
+ }
+
+ return err;
+}
diff --git a/src/libs/mynewt-nimble/nimble/host/mesh/src/crypto.h b/src/libs/mynewt-nimble/nimble/host/mesh/src/crypto.h
new file mode 100644
index 00000000..745cf324
--- /dev/null
+++ b/src/libs/mynewt-nimble/nimble/host/mesh/src/crypto.h
@@ -0,0 +1,170 @@
+/* Bluetooth Mesh */
+
+/*
+ * Copyright (c) 2017 Intel Corporation
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#ifndef __CRYPTO_H__
+#define __CRYPTO_H__
+
+#include "mesh/mesh.h"
+
+struct bt_mesh_sg {
+ const void *data;
+ size_t len;
+};
+
+int bt_mesh_aes_cmac(const u8_t key[16], struct bt_mesh_sg *sg,
+ size_t sg_len, u8_t mac[16]);
+
+static inline int bt_mesh_aes_cmac_one(const u8_t key[16], const void *m,
+ size_t len, u8_t mac[16])
+{
+ struct bt_mesh_sg sg = { m, len };
+
+ return bt_mesh_aes_cmac(key, &sg, 1, mac);
+}
+
+static inline bool bt_mesh_s1(const char *m, u8_t salt[16])
+{
+ const u8_t zero[16] = { 0 };
+
+ return bt_mesh_aes_cmac_one(zero, m, strlen(m), salt);
+}
+
+int bt_mesh_k1(const u8_t *ikm, size_t ikm_len, const u8_t salt[16],
+ const char *info, u8_t okm[16]);
+
+#define bt_mesh_k1_str(ikm, ikm_len, salt_str, info, okm) \
+({ \
+ const u8_t salt[16] = salt_str; \
+ bt_mesh_k1(ikm, ikm_len, salt, info, okm); \
+})
+
+int bt_mesh_k2(const u8_t n[16], const u8_t *p, size_t p_len,
+ u8_t net_id[1], u8_t enc_key[16], u8_t priv_key[16]);
+
+int bt_mesh_k3(const u8_t n[16], u8_t out[8]);
+
+int bt_mesh_k4(const u8_t n[16], u8_t out[1]);
+
+int bt_mesh_id128(const u8_t n[16], const char *s, u8_t out[16]);
+
+static inline int bt_mesh_id_resolving_key(const u8_t net_key[16],
+ u8_t resolving_key[16])
+{
+ return bt_mesh_k1_str(net_key, 16, "smbt", "smbi", resolving_key);
+}
+
+static inline int bt_mesh_identity_key(const u8_t net_key[16],
+ u8_t identity_key[16])
+{
+ return bt_mesh_id128(net_key, "nkik", identity_key);
+}
+
+static inline int bt_mesh_beacon_key(const u8_t net_key[16],
+ u8_t beacon_key[16])
+{
+ return bt_mesh_id128(net_key, "nkbk", beacon_key);
+}
+
+int bt_mesh_beacon_auth(const u8_t beacon_key[16], u8_t flags,
+ const u8_t net_id[16], u32_t iv_index,
+ u8_t auth[8]);
+
+static inline int bt_mesh_app_id(const u8_t app_key[16], u8_t app_id[1])
+{
+ return bt_mesh_k4(app_key, app_id);
+}
+
+static inline int bt_mesh_session_key(const u8_t dhkey[32],
+ const u8_t prov_salt[16],
+ u8_t session_key[16])
+{
+ return bt_mesh_k1(dhkey, 32, prov_salt, "prsk", session_key);
+}
+
+static inline int bt_mesh_prov_nonce(const u8_t dhkey[32],
+ const u8_t prov_salt[16],
+ u8_t nonce[13])
+{
+ u8_t tmp[16];
+ int err;
+
+ err = bt_mesh_k1(dhkey, 32, prov_salt, "prsn", tmp);
+ if (!err) {
+ memcpy(nonce, tmp + 3, 13);
+ }
+
+ return err;
+}
+
+static inline int bt_mesh_dev_key(const u8_t dhkey[32],
+ const u8_t prov_salt[16],
+ u8_t dev_key[16])
+{
+ return bt_mesh_k1(dhkey, 32, prov_salt, "prdk", dev_key);
+}
+
+static inline int bt_mesh_prov_salt(const u8_t conf_salt[16],
+ const u8_t prov_rand[16],
+ const u8_t dev_rand[16],
+ u8_t prov_salt[16])
+{
+ const u8_t prov_salt_key[16] = { 0 };
+ struct bt_mesh_sg sg[] = {
+ { conf_salt, 16 },
+ { prov_rand, 16 },
+ { dev_rand, 16 },
+ };
+
+ return bt_mesh_aes_cmac(prov_salt_key, sg, ARRAY_SIZE(sg), prov_salt);
+}
+
+int bt_mesh_net_obfuscate(u8_t *pdu, u32_t iv_index,
+ const u8_t privacy_key[16]);
+
+int bt_mesh_net_encrypt(const u8_t key[16], struct os_mbuf *buf,
+ u32_t iv_index, bool proxy);
+
+int bt_mesh_net_decrypt(const u8_t key[16], struct os_mbuf *buf,
+ u32_t iv_index, bool proxy);
+
+int bt_mesh_app_encrypt_in_place(const u8_t key[16], bool dev_key, u8_t aszmic,
+ struct os_mbuf*buf, const u8_t *ad, u16_t src,
+ u16_t dst, u32_t seq_num, u32_t iv_index);
+
+int bt_mesh_app_encrypt(const u8_t key[16], bool dev_key, u8_t aszmic,
+ struct os_mbuf*buf, const u8_t *ad,
+ u16_t src, u16_t dst, u32_t seq_num, u32_t iv_index);
+
+int bt_mesh_app_decrypt_in_place(const u8_t key[16], bool dev_key, u8_t aszmic,
+ struct os_mbuf *buf, const u8_t *ad, u16_t src,
+ u16_t dst, u32_t seq_num, u32_t iv_index);
+
+int bt_mesh_app_decrypt(const u8_t key[16], bool dev_key, u8_t aszmic,
+ struct os_mbuf*buf, struct os_mbuf*out,
+ const u8_t *ad, u16_t src, u16_t dst, u32_t seq_num,
+ u32_t iv_index);
+
+u8_t bt_mesh_fcs_calc(const u8_t *data, u8_t data_len);
+
+bool bt_mesh_fcs_check(struct os_mbuf *buf, u8_t received_fcs);
+
+int bt_mesh_virtual_addr(const u8_t virtual_label[16], u16_t *addr);
+
+int bt_mesh_prov_conf_salt(const u8_t conf_inputs[145], u8_t salt[16]);
+
+int bt_mesh_prov_conf_key(const u8_t dhkey[32], const u8_t conf_salt[16],
+ u8_t conf_key[16]);
+
+int bt_mesh_prov_conf(const u8_t conf_key[16], const u8_t rand[16],
+ const u8_t auth[16], u8_t conf[16]);
+
+int bt_mesh_prov_decrypt(const u8_t key[16], u8_t nonce[13],
+ const u8_t data[25 + 8], u8_t out[25]);
+
+int bt_mesh_prov_encrypt(const u8_t key[16], u8_t nonce[13],
+ const u8_t data[25], u8_t out[25 + 8]);
+#endif
diff --git a/src/libs/mynewt-nimble/nimble/host/mesh/src/foundation.h b/src/libs/mynewt-nimble/nimble/host/mesh/src/foundation.h
new file mode 100644
index 00000000..ee615ae9
--- /dev/null
+++ b/src/libs/mynewt-nimble/nimble/host/mesh/src/foundation.h
@@ -0,0 +1,171 @@
+/* Bluetooth Mesh */
+
+/*
+ * Copyright (c) 2017 Intel Corporation
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#ifndef __FUNDATION_H__
+#define __FUNDATION_H__
+
+#define OP_APP_KEY_ADD BT_MESH_MODEL_OP_1(0x00)
+#define OP_APP_KEY_UPDATE BT_MESH_MODEL_OP_1(0x01)
+#define OP_DEV_COMP_DATA_STATUS BT_MESH_MODEL_OP_1(0x02)
+#define OP_MOD_PUB_SET BT_MESH_MODEL_OP_1(0x03)
+#define OP_HEALTH_CURRENT_STATUS BT_MESH_MODEL_OP_1(0x04)
+#define OP_HEALTH_FAULT_STATUS BT_MESH_MODEL_OP_1(0x05)
+#define OP_HEARTBEAT_PUB_STATUS BT_MESH_MODEL_OP_1(0x06)
+#define OP_APP_KEY_DEL BT_MESH_MODEL_OP_2(0x80, 0x00)
+#define OP_APP_KEY_GET BT_MESH_MODEL_OP_2(0x80, 0x01)
+#define OP_APP_KEY_LIST BT_MESH_MODEL_OP_2(0x80, 0x02)
+#define OP_APP_KEY_STATUS BT_MESH_MODEL_OP_2(0x80, 0x03)
+#define OP_ATTENTION_GET BT_MESH_MODEL_OP_2(0x80, 0x04)
+#define OP_ATTENTION_SET BT_MESH_MODEL_OP_2(0x80, 0x05)
+#define OP_ATTENTION_SET_UNREL BT_MESH_MODEL_OP_2(0x80, 0x06)
+#define OP_ATTENTION_STATUS BT_MESH_MODEL_OP_2(0x80, 0x07)
+#define OP_DEV_COMP_DATA_GET BT_MESH_MODEL_OP_2(0x80, 0x08)
+#define OP_BEACON_GET BT_MESH_MODEL_OP_2(0x80, 0x09)
+#define OP_BEACON_SET BT_MESH_MODEL_OP_2(0x80, 0x0a)
+#define OP_BEACON_STATUS BT_MESH_MODEL_OP_2(0x80, 0x0b)
+#define OP_DEFAULT_TTL_GET BT_MESH_MODEL_OP_2(0x80, 0x0c)
+#define OP_DEFAULT_TTL_SET BT_MESH_MODEL_OP_2(0x80, 0x0d)
+#define OP_DEFAULT_TTL_STATUS BT_MESH_MODEL_OP_2(0x80, 0x0e)
+#define OP_FRIEND_GET BT_MESH_MODEL_OP_2(0x80, 0x0f)
+#define OP_FRIEND_SET BT_MESH_MODEL_OP_2(0x80, 0x10)
+#define OP_FRIEND_STATUS BT_MESH_MODEL_OP_2(0x80, 0x11)
+#define OP_GATT_PROXY_GET BT_MESH_MODEL_OP_2(0x80, 0x12)
+#define OP_GATT_PROXY_SET BT_MESH_MODEL_OP_2(0x80, 0x13)
+#define OP_GATT_PROXY_STATUS BT_MESH_MODEL_OP_2(0x80, 0x14)
+#define OP_KRP_GET BT_MESH_MODEL_OP_2(0x80, 0x15)
+#define OP_KRP_SET BT_MESH_MODEL_OP_2(0x80, 0x16)
+#define OP_KRP_STATUS BT_MESH_MODEL_OP_2(0x80, 0x17)
+#define OP_MOD_PUB_GET BT_MESH_MODEL_OP_2(0x80, 0x18)
+#define OP_MOD_PUB_STATUS BT_MESH_MODEL_OP_2(0x80, 0x19)
+#define OP_MOD_PUB_VA_SET BT_MESH_MODEL_OP_2(0x80, 0x1a)
+#define OP_MOD_SUB_ADD BT_MESH_MODEL_OP_2(0x80, 0x1b)
+#define OP_MOD_SUB_DEL BT_MESH_MODEL_OP_2(0x80, 0x1c)
+#define OP_MOD_SUB_DEL_ALL BT_MESH_MODEL_OP_2(0x80, 0x1d)
+#define OP_MOD_SUB_OVERWRITE BT_MESH_MODEL_OP_2(0x80, 0x1e)
+#define OP_MOD_SUB_STATUS BT_MESH_MODEL_OP_2(0x80, 0x1f)
+#define OP_MOD_SUB_VA_ADD BT_MESH_MODEL_OP_2(0x80, 0x20)
+#define OP_MOD_SUB_VA_DEL BT_MESH_MODEL_OP_2(0x80, 0x21)
+#define OP_MOD_SUB_VA_OVERWRITE BT_MESH_MODEL_OP_2(0x80, 0x22)
+#define OP_NET_TRANSMIT_GET BT_MESH_MODEL_OP_2(0x80, 0x23)
+#define OP_NET_TRANSMIT_SET BT_MESH_MODEL_OP_2(0x80, 0x24)
+#define OP_NET_TRANSMIT_STATUS BT_MESH_MODEL_OP_2(0x80, 0x25)
+#define OP_RELAY_GET BT_MESH_MODEL_OP_2(0x80, 0x26)
+#define OP_RELAY_SET BT_MESH_MODEL_OP_2(0x80, 0x27)
+#define OP_RELAY_STATUS BT_MESH_MODEL_OP_2(0x80, 0x28)
+#define OP_MOD_SUB_GET BT_MESH_MODEL_OP_2(0x80, 0x29)
+#define OP_MOD_SUB_LIST BT_MESH_MODEL_OP_2(0x80, 0x2a)
+#define OP_MOD_SUB_GET_VND BT_MESH_MODEL_OP_2(0x80, 0x2b)
+#define OP_MOD_SUB_LIST_VND BT_MESH_MODEL_OP_2(0x80, 0x2c)
+#define OP_LPN_TIMEOUT_GET BT_MESH_MODEL_OP_2(0x80, 0x2d)
+#define OP_LPN_TIMEOUT_STATUS BT_MESH_MODEL_OP_2(0x80, 0x2e)
+#define OP_HEALTH_FAULT_CLEAR BT_MESH_MODEL_OP_2(0x80, 0x2f)
+#define OP_HEALTH_FAULT_CLEAR_UNREL BT_MESH_MODEL_OP_2(0x80, 0x30)
+#define OP_HEALTH_FAULT_GET BT_MESH_MODEL_OP_2(0x80, 0x31)
+#define OP_HEALTH_FAULT_TEST BT_MESH_MODEL_OP_2(0x80, 0x32)
+#define OP_HEALTH_FAULT_TEST_UNREL BT_MESH_MODEL_OP_2(0x80, 0x33)
+#define OP_HEALTH_PERIOD_GET BT_MESH_MODEL_OP_2(0x80, 0x34)
+#define OP_HEALTH_PERIOD_SET BT_MESH_MODEL_OP_2(0x80, 0x35)
+#define OP_HEALTH_PERIOD_SET_UNREL BT_MESH_MODEL_OP_2(0x80, 0x36)
+#define OP_HEALTH_PERIOD_STATUS BT_MESH_MODEL_OP_2(0x80, 0x37)
+#define OP_HEARTBEAT_PUB_GET BT_MESH_MODEL_OP_2(0x80, 0x38)
+#define OP_HEARTBEAT_PUB_SET BT_MESH_MODEL_OP_2(0x80, 0x39)
+#define OP_HEARTBEAT_SUB_GET BT_MESH_MODEL_OP_2(0x80, 0x3a)
+#define OP_HEARTBEAT_SUB_SET BT_MESH_MODEL_OP_2(0x80, 0x3b)
+#define OP_HEARTBEAT_SUB_STATUS BT_MESH_MODEL_OP_2(0x80, 0x3c)
+#define OP_MOD_APP_BIND BT_MESH_MODEL_OP_2(0x80, 0x3d)
+#define OP_MOD_APP_STATUS BT_MESH_MODEL_OP_2(0x80, 0x3e)
+#define OP_MOD_APP_UNBIND BT_MESH_MODEL_OP_2(0x80, 0x3f)
+#define OP_NET_KEY_ADD BT_MESH_MODEL_OP_2(0x80, 0x40)
+#define OP_NET_KEY_DEL BT_MESH_MODEL_OP_2(0x80, 0x41)
+#define OP_NET_KEY_GET BT_MESH_MODEL_OP_2(0x80, 0x42)
+#define OP_NET_KEY_LIST BT_MESH_MODEL_OP_2(0x80, 0x43)
+#define OP_NET_KEY_STATUS BT_MESH_MODEL_OP_2(0x80, 0x44)
+#define OP_NET_KEY_UPDATE BT_MESH_MODEL_OP_2(0x80, 0x45)
+#define OP_NODE_IDENTITY_GET BT_MESH_MODEL_OP_2(0x80, 0x46)
+#define OP_NODE_IDENTITY_SET BT_MESH_MODEL_OP_2(0x80, 0x47)
+#define OP_NODE_IDENTITY_STATUS BT_MESH_MODEL_OP_2(0x80, 0x48)
+#define OP_NODE_RESET BT_MESH_MODEL_OP_2(0x80, 0x49)
+#define OP_NODE_RESET_STATUS BT_MESH_MODEL_OP_2(0x80, 0x4a)
+#define OP_SIG_MOD_APP_GET BT_MESH_MODEL_OP_2(0x80, 0x4b)
+#define OP_SIG_MOD_APP_LIST BT_MESH_MODEL_OP_2(0x80, 0x4c)
+#define OP_VND_MOD_APP_GET BT_MESH_MODEL_OP_2(0x80, 0x4d)
+#define OP_VND_MOD_APP_LIST BT_MESH_MODEL_OP_2(0x80, 0x4e)
+
+#define STATUS_SUCCESS 0x00
+#define STATUS_INVALID_ADDRESS 0x01
+#define STATUS_INVALID_MODEL 0x02
+#define STATUS_INVALID_APPKEY 0x03
+#define STATUS_INVALID_NETKEY 0x04
+#define STATUS_INSUFF_RESOURCES 0x05
+#define STATUS_IDX_ALREADY_STORED 0x06
+#define STATUS_NVAL_PUB_PARAM 0x07
+#define STATUS_NOT_SUB_MOD 0x08
+#define STATUS_STORAGE_FAIL 0x09
+#define STATUS_FEAT_NOT_SUPP 0x0a
+#define STATUS_CANNOT_UPDATE 0x0b
+#define STATUS_CANNOT_REMOVE 0x0c
+#define STATUS_CANNOT_BIND 0x0d
+#define STATUS_TEMP_STATE_CHG_FAIL 0x0e
+#define STATUS_CANNOT_SET 0x0f
+#define STATUS_UNSPECIFIED 0x10
+#define STATUS_INVALID_BINDING 0x11
+
+enum {
+ BT_MESH_VA_CHANGED, /* Label information changed */
+};
+
+struct label {
+ u16_t ref;
+ u16_t addr;
+ u8_t uuid[16];
+ atomic_t flags[1];
+};
+
+void bt_mesh_cfg_reset(void);
+
+void bt_mesh_heartbeat(u16_t src, u16_t dst, u8_t hops, u16_t feat);
+
+void bt_mesh_attention(struct bt_mesh_model *model, u8_t time);
+
+struct label *get_label(u16_t index);
+
+u8_t *bt_mesh_label_uuid_get(u16_t addr);
+
+struct bt_mesh_hb_pub *bt_mesh_hb_pub_get(void);
+void bt_mesh_hb_pub_disable(void);
+struct bt_mesh_cfg_srv *bt_mesh_cfg_get(void);
+
+u8_t bt_mesh_net_transmit_get(void);
+u8_t bt_mesh_relay_get(void);
+u8_t bt_mesh_friend_get(void);
+u8_t bt_mesh_relay_retransmit_get(void);
+u8_t bt_mesh_beacon_get(void);
+u8_t bt_mesh_gatt_proxy_get(void);
+u8_t bt_mesh_default_ttl_get(void);
+
+void bt_mesh_subnet_del(struct bt_mesh_subnet *sub, bool store);
+
+struct bt_mesh_app_key *bt_mesh_app_key_alloc(u16_t app_idx);
+void bt_mesh_app_key_del(struct bt_mesh_app_key *key, bool store);
+
+static inline void key_idx_pack(struct os_mbuf *buf,
+ u16_t idx1, u16_t idx2)
+{
+ net_buf_simple_add_le16(buf, idx1 | ((idx2 & 0x00f) << 12));
+ net_buf_simple_add_u8(buf, idx2 >> 4);
+}
+
+static inline void key_idx_unpack(struct os_mbuf *buf,
+ u16_t *idx1, u16_t *idx2)
+{
+ *idx1 = sys_get_le16(&buf->om_data[0]) & 0xfff;
+ *idx2 = sys_get_le16(&buf->om_data[1]) >> 4;
+ net_buf_simple_pull(buf, 3);
+}
+
+#endif
diff --git a/src/libs/mynewt-nimble/nimble/host/mesh/src/friend.c b/src/libs/mynewt-nimble/nimble/host/mesh/src/friend.c
new file mode 100644
index 00000000..9056a865
--- /dev/null
+++ b/src/libs/mynewt-nimble/nimble/host/mesh/src/friend.c
@@ -0,0 +1,1651 @@
+ /* Bluetooth Mesh */
+
+/*
+ * Copyright (c) 2017 Intel Corporation
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include "syscfg/syscfg.h"
+#define MESH_LOG_MODULE BLE_MESH_CRYPTO_LOG
+
+#if MYNEWT_VAL(BLE_MESH_FRIEND)
+
+#include <stdint.h>
+#include <errno.h>
+#include <assert.h>
+
+#include "mesh/mesh.h"
+#include "mesh/slist.h"
+#include "mesh_priv.h"
+#include "crypto.h"
+#include "adv.h"
+#include "net.h"
+#include "transport.h"
+#include "access.h"
+#include "foundation.h"
+#include "friend.h"
+
+/* We reserve one extra buffer for each friendship, since we need to be able
+ * to resend the last sent PDU, which sits separately outside of the queue.
+ */
+#define FRIEND_BUF_COUNT ((MYNEWT_VAL(BLE_MESH_FRIEND_QUEUE_SIZE) + 1) * MYNEWT_VAL(BLE_MESH_FRIEND_LPN_COUNT))
+
+static os_membuf_t friend_buf_mem[OS_MEMPOOL_SIZE(
+ FRIEND_BUF_COUNT,
+ BT_MESH_ADV_DATA_SIZE + BT_MESH_MBUF_HEADER_SIZE)];
+
+struct os_mbuf_pool friend_os_mbuf_pool;
+static struct os_mempool friend_buf_mempool;
+
+#define NET_BUF_FRAGS BIT(0)
+
+#define FRIEND_ADV(buf) CONTAINER_OF(BT_MESH_ADV(buf), struct friend_adv, adv)
+
+/* PDUs from Friend to the LPN should only be transmitted once with the
+ * smallest possible interval (20ms).
+ */
+#define FRIEND_XMIT BT_MESH_TRANSMIT(0, 20)
+
+struct friend_pdu_info {
+ u16_t src;
+ u16_t dst;
+
+ u8_t seq[3];
+
+ u8_t ttl:7,
+ ctl:1;
+
+ u32_t iv_index;
+};
+
+static struct friend_adv {
+ struct bt_mesh_adv adv;
+ u16_t app_idx;
+} adv_pool[FRIEND_BUF_COUNT];
+
+static struct bt_mesh_adv *adv_alloc(int id)
+{
+ adv_pool[id].app_idx = BT_MESH_KEY_UNUSED;
+ return &adv_pool[id].adv;
+}
+
+static bool is_lpn_unicast(struct bt_mesh_friend *frnd, u16_t addr)
+{
+ if (frnd->lpn == BT_MESH_ADDR_UNASSIGNED) {
+ return false;
+ }
+
+ return (addr >= frnd->lpn && addr < (frnd->lpn + frnd->num_elem));
+}
+
+struct bt_mesh_friend *bt_mesh_friend_find(u16_t net_idx, u16_t lpn_addr,
+ bool valid, bool established)
+{
+ int i;
+
+ BT_DBG("net_idx 0x%04x lpn_addr 0x%04x", net_idx, lpn_addr);
+
+ for (i = 0; i < ARRAY_SIZE(bt_mesh.frnd); i++) {
+ struct bt_mesh_friend *frnd = &bt_mesh.frnd[i];
+
+ if (valid && !frnd->valid) {
+ continue;
+ }
+
+ if (established && !frnd->established) {
+ continue;
+ }
+
+ if (net_idx != BT_MESH_KEY_ANY && frnd->net_idx != net_idx) {
+ continue;
+ }
+
+ if (is_lpn_unicast(frnd, lpn_addr)) {
+ return frnd;
+ }
+ }
+
+ return NULL;
+}
+
+static void purge_buffers(struct net_buf_slist_t *list)
+{
+ struct os_mbuf *buf;
+
+ while (!net_buf_slist_is_empty(list)) {
+ buf = (void *)net_buf_slist_get(list);
+ BT_MESH_ADV(buf)->flags &= ~NET_BUF_FRAGS;
+ net_buf_unref(buf);
+ }
+}
+
+/* Intentionally start a little bit late into the ReceiveWindow when
+ * it's large enough. This may improve reliability with some platforms,
+ * like the PTS, where the receiver might not have sufficiently compensated
+ * for internal latencies required to start scanning.
+ */
+static s32_t recv_delay(struct bt_mesh_friend *frnd)
+{
+#if CONFIG_BT_MESH_FRIEND_RECV_WIN > 50
+ return (s32_t)frnd->recv_delay + (CONFIG_BT_MESH_FRIEND_RECV_WIN / 5);
+#else
+ return frnd->recv_delay;
+#endif
+}
+
+static void friend_clear(struct bt_mesh_friend *frnd)
+{
+ int i;
+
+ BT_DBG("LPN 0x%04x", frnd->lpn);
+
+ k_delayed_work_cancel(&frnd->timer);
+
+ friend_cred_del(frnd->net_idx, frnd->lpn);
+
+ if (frnd->last) {
+ /* Cancel the sending if necessary */
+ if (frnd->pending_buf) {
+ BT_MESH_ADV(frnd->last)->busy = 0;
+ }
+
+ net_buf_unref(frnd->last);
+ frnd->last = NULL;
+ }
+
+ purge_buffers(&frnd->queue);
+
+ for (i = 0; i < ARRAY_SIZE(frnd->seg); i++) {
+ struct bt_mesh_friend_seg *seg = &frnd->seg[i];
+
+ purge_buffers(&seg->queue);
+ seg->seg_count = 0U;
+ }
+
+ frnd->valid = 0;
+ frnd->established = 0;
+ frnd->pending_buf = 0;
+ frnd->fsn = 0;
+ frnd->queue_size = 0;
+ frnd->pending_req = 0;
+ memset(frnd->sub_list, 0, sizeof(frnd->sub_list));
+}
+
+void bt_mesh_friend_clear_net_idx(u16_t net_idx)
+{
+ int i;
+
+ BT_DBG("net_idx 0x%04x", net_idx);
+
+ for (i = 0; i < ARRAY_SIZE(bt_mesh.frnd); i++) {
+ struct bt_mesh_friend *frnd = &bt_mesh.frnd[i];
+
+ if (frnd->net_idx == BT_MESH_KEY_UNUSED) {
+ continue;
+ }
+
+ if (net_idx == BT_MESH_KEY_ANY || frnd->net_idx == net_idx) {
+ friend_clear(frnd);
+ }
+ }
+}
+
+void bt_mesh_friend_sec_update(u16_t net_idx)
+{
+ int i;
+
+ BT_DBG("net_idx 0x%04x", net_idx);
+
+ for (i = 0; i < ARRAY_SIZE(bt_mesh.frnd); i++) {
+ struct bt_mesh_friend *frnd = &bt_mesh.frnd[i];
+
+ if (frnd->net_idx == BT_MESH_KEY_UNUSED) {
+ continue;
+ }
+
+ if (net_idx == BT_MESH_KEY_ANY || frnd->net_idx == net_idx) {
+ frnd->sec_update = 1;
+ }
+ }
+}
+
+int bt_mesh_friend_clear(struct bt_mesh_net_rx *rx, struct os_mbuf *buf)
+{
+ struct bt_mesh_ctl_friend_clear *msg = (void *)buf->om_data;
+ struct bt_mesh_friend *frnd;
+ u16_t lpn_addr, lpn_counter;
+ struct bt_mesh_net_tx tx = {
+ .sub = rx->sub,
+ .ctx = &rx->ctx,
+ .src = bt_mesh_primary_addr(),
+ .xmit = bt_mesh_net_transmit_get(),
+ };
+ struct bt_mesh_ctl_friend_clear_confirm cfm;
+
+ if (buf->om_len < sizeof(*msg)) {
+ BT_WARN("Too short Friend Clear");
+ return -EINVAL;
+ }
+
+ lpn_addr = sys_be16_to_cpu(msg->lpn_addr);
+ lpn_counter = sys_be16_to_cpu(msg->lpn_counter);
+
+ BT_DBG("LPN addr 0x%04x counter 0x%04x", lpn_addr, lpn_counter);
+
+ frnd = bt_mesh_friend_find(rx->sub->net_idx, lpn_addr, false, false);
+ if (!frnd) {
+ BT_WARN("No matching LPN addr 0x%04x", lpn_addr);
+ return 0;
+ }
+
+ /* A Friend Clear message is considered valid if the result of the
+ * subtraction of the value of the LPNCounter field of the Friend
+ * Request message (the one that initiated the friendship) from the
+ * value of the LPNCounter field of the Friend Clear message, modulo
+ * 65536, is in the range 0 to 255 inclusive.
+ */
+ if (lpn_counter - frnd->lpn_counter > 255) {
+ BT_WARN("LPN Counter out of range (old %u new %u)",
+ frnd->lpn_counter, lpn_counter);
+ return 0;
+ }
+
+ tx.ctx->send_ttl = BT_MESH_TTL_MAX;
+
+ cfm.lpn_addr = msg->lpn_addr;
+ cfm.lpn_counter = msg->lpn_counter;
+
+ bt_mesh_ctl_send(&tx, TRANS_CTL_OP_FRIEND_CLEAR_CFM, &cfm,
+ sizeof(cfm), NULL, NULL, NULL);
+
+ friend_clear(frnd);
+
+ return 0;
+}
+
+static void friend_sub_add(struct bt_mesh_friend *frnd, u16_t addr)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(frnd->sub_list); i++) {
+ if (frnd->sub_list[i] == BT_MESH_ADDR_UNASSIGNED) {
+ frnd->sub_list[i] = addr;
+ return;
+ }
+ }
+
+ BT_WARN("No space in friend subscription list");
+}
+
+static void friend_sub_rem(struct bt_mesh_friend *frnd, u16_t addr)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(frnd->sub_list); i++) {
+ if (frnd->sub_list[i] == addr) {
+ frnd->sub_list[i] = BT_MESH_ADDR_UNASSIGNED;
+ return;
+ }
+ }
+}
+
+static struct os_mbuf *create_friend_pdu(struct bt_mesh_friend *frnd,
+ struct friend_pdu_info *info,
+ struct os_mbuf *sdu)
+{
+ struct os_mbuf *buf;
+
+ buf = bt_mesh_adv_create_from_pool(&friend_os_mbuf_pool, adv_alloc,
+ BT_MESH_ADV_DATA,
+ FRIEND_XMIT, K_NO_WAIT);
+ if (!buf) {
+ return NULL;
+ }
+
+ net_buf_add_u8(buf, (info->iv_index & 1) << 7); /* Will be reset in encryption */
+
+ if (info->ctl) {
+ net_buf_add_u8(buf, info->ttl | 0x80);
+ } else {
+ net_buf_add_u8(buf, info->ttl);
+ }
+
+ net_buf_add_mem(buf, info->seq, sizeof(info->seq));
+
+ net_buf_add_be16(buf, info->src);
+ net_buf_add_be16(buf, info->dst);
+
+ net_buf_add_mem(buf, sdu->om_data, sdu->om_len);
+
+ return buf;
+}
+
+struct unseg_app_sdu_meta {
+ struct bt_mesh_net_rx net;
+ const u8_t *key;
+ struct bt_mesh_subnet *subnet;
+ bool is_dev_key;
+ u8_t aid;
+ u8_t *ad;
+};
+
+static int unseg_app_sdu_unpack(struct bt_mesh_friend *frnd,
+ struct os_mbuf *buf,
+ struct unseg_app_sdu_meta *meta)
+{
+ u16_t app_idx = FRIEND_ADV(buf)->app_idx;
+ int err;
+
+ meta->subnet = bt_mesh_subnet_get(frnd->net_idx);
+ meta->is_dev_key = (app_idx == BT_MESH_KEY_DEV);
+ meta->is_dev_key = BT_MESH_IS_DEV_KEY(app_idx);
+ bt_mesh_net_header_parse(buf, &meta->net);
+ err = bt_mesh_app_key_get(meta->subnet, app_idx, meta->net.ctx.recv_dst,
+ &meta->key, &meta->aid);
+ if (err) {
+ return err;
+ }
+
+ if (BT_MESH_ADDR_IS_VIRTUAL(meta->net.ctx.recv_dst)) {
+ meta->ad = bt_mesh_label_uuid_get(meta->net.ctx.recv_dst);
+ if (!meta->ad) {
+ return -ENOENT;
+ }
+ } else {
+ meta->ad = NULL;
+ }
+
+ return 0;
+}
+
+static int unseg_app_sdu_decrypt(struct bt_mesh_friend *frnd,
+ struct os_mbuf *buf,
+ const struct unseg_app_sdu_meta *meta)
+{
+ struct net_buf_simple_state state;
+ int err;
+
+ BT_DBG("");
+
+ net_buf_simple_save(buf, &state);
+ net_buf_simple_pull_mem(buf, 10);
+ buf->om_len -= 4;
+
+ err = bt_mesh_app_decrypt_in_place(meta->key, meta->is_dev_key,
+ 0, buf, meta->ad, meta->net.ctx.addr,
+ meta->net.ctx.recv_dst, meta->net.seq,
+ BT_MESH_NET_IVI_TX);
+
+ net_buf_simple_restore(buf, &state);
+ return err;
+}
+
+static int unseg_app_sdu_encrypt(struct bt_mesh_friend *frnd,
+ struct os_mbuf *buf,
+ const struct unseg_app_sdu_meta *meta)
+{
+ struct net_buf_simple_state state;
+ int err;
+
+ BT_DBG("");
+
+ net_buf_simple_save(buf, &state);
+ net_buf_simple_pull_mem(buf, 10);
+ buf->om_len -= 4;
+
+ err = bt_mesh_app_encrypt_in_place(meta->key, meta->is_dev_key, 0, buf,
+ meta->ad, meta->net.ctx.addr,
+ meta->net.ctx.recv_dst, bt_mesh.seq,
+ BT_MESH_NET_IVI_TX);
+
+ net_buf_simple_restore(buf, &state);
+ return err;
+}
+
+static int unseg_app_sdu_prepare(struct bt_mesh_friend *frnd,
+ struct os_mbuf *buf)
+{
+ struct unseg_app_sdu_meta meta;
+ int err;
+
+ BT_DBG("");
+
+ if (FRIEND_ADV(buf)->app_idx == BT_MESH_KEY_UNUSED) {
+ return 0;
+ }
+
+ err = unseg_app_sdu_unpack(frnd, buf, &meta);
+ if (err) {
+ return err;
+ }
+
+ /* No need to reencrypt the message if the sequence number is
+ * unchanged.
+ */
+ if (meta.net.seq == bt_mesh.seq) {
+ return 0;
+ }
+
+ err = unseg_app_sdu_decrypt(frnd, buf, &meta);
+ if (err) {
+ BT_WARN("Decryption failed! %d", err);
+ return err;
+ }
+
+ err = unseg_app_sdu_encrypt(frnd, buf, &meta);
+ if (err) {
+ BT_WARN("Re-encryption failed! %d", err);
+ }
+
+ return err;
+}
+
+static int encrypt_friend_pdu(struct bt_mesh_friend *frnd, struct os_mbuf *buf,
+ bool master_cred)
+{
+ struct bt_mesh_subnet *sub = bt_mesh_subnet_get(frnd->net_idx);
+ const u8_t *enc, *priv;
+ u32_t iv_index;
+ u16_t src;
+ u8_t nid;
+ int err;
+
+ if (master_cred) {
+ enc = sub->keys[sub->kr_flag].enc;
+ priv = sub->keys[sub->kr_flag].privacy;
+ nid = sub->keys[sub->kr_flag].nid;
+ } else {
+ if (friend_cred_get(sub, frnd->lpn, &nid, &enc, &priv)) {
+ BT_ERR("friend_cred_get failed");
+ return -ENOENT;
+ }
+ }
+
+ src = sys_get_be16(&buf->om_data[5]);
+
+ if (bt_mesh_elem_find(src)) {
+ u32_t seq;
+
+ if (FRIEND_ADV(buf)->app_idx != BT_MESH_KEY_UNUSED) {
+ err = unseg_app_sdu_prepare(frnd, buf);
+ if (err) {
+ return err;
+ }
+ }
+
+ seq = bt_mesh_next_seq();
+ buf->om_data[2] = seq >> 16;
+ buf->om_data[3] = seq >> 8;
+ buf->om_data[4] = seq;
+
+ iv_index = BT_MESH_NET_IVI_TX;
+ FRIEND_ADV(buf)->app_idx = BT_MESH_KEY_UNUSED;
+ } else {
+ u8_t ivi = (buf->om_data[0] >> 7);
+ iv_index = (bt_mesh.iv_index - ((bt_mesh.iv_index & 1) != ivi));
+ }
+
+ buf->om_data[0] = (nid | (iv_index & 1) << 7);
+
+ if (bt_mesh_net_encrypt(enc, buf, iv_index, false)) {
+ BT_ERR("Encrypting failed");
+ return -EINVAL;
+ }
+
+ if (bt_mesh_net_obfuscate(buf->om_data, iv_index, priv)) {
+ BT_ERR("Obfuscating failed");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static struct os_mbuf *encode_friend_ctl(struct bt_mesh_friend *frnd,
+ u8_t ctl_op,
+ struct os_mbuf *sdu)
+{
+ struct friend_pdu_info info;
+
+ BT_DBG("LPN 0x%04x", frnd->lpn);
+
+ net_buf_simple_push_u8(sdu, TRANS_CTL_HDR(ctl_op, 0));
+
+ info.src = bt_mesh_primary_addr();
+ info.dst = frnd->lpn;
+
+ info.ctl = 1;
+ info.ttl = 0;
+
+ memset(info.seq, 0, sizeof(info.seq));
+
+ info.iv_index = BT_MESH_NET_IVI_TX;
+
+ return create_friend_pdu(frnd, &info, sdu);
+}
+
+static struct os_mbuf *encode_update(struct bt_mesh_friend *frnd, u8_t md)
+{
+ struct bt_mesh_ctl_friend_update *upd;
+ struct os_mbuf *sdu = NET_BUF_SIMPLE(1 + sizeof(*upd));
+ struct bt_mesh_subnet *sub = bt_mesh_subnet_get(frnd->net_idx);
+ struct os_mbuf *buf;
+
+ __ASSERT_NO_MSG(sub != NULL);
+
+ BT_DBG("lpn 0x%04x md 0x%02x", frnd->lpn, md);
+
+ net_buf_simple_init(sdu, 1);
+
+ upd = net_buf_simple_add(sdu, sizeof(*upd));
+ upd->flags = bt_mesh_net_flags(sub);
+ upd->iv_index = sys_cpu_to_be32(bt_mesh.iv_index);
+ upd->md = md;
+
+ buf = encode_friend_ctl(frnd, TRANS_CTL_OP_FRIEND_UPDATE, sdu);
+
+ os_mbuf_free_chain(sdu);
+ return buf;
+}
+
+static void enqueue_sub_cfm(struct bt_mesh_friend *frnd, u8_t xact)
+{
+ struct bt_mesh_ctl_friend_sub_confirm *cfm;
+ struct os_mbuf *sdu = NET_BUF_SIMPLE(1 + sizeof(*cfm));
+ struct os_mbuf *buf;
+
+ BT_DBG("lpn 0x%04x xact 0x%02x", frnd->lpn, xact);
+
+ net_buf_simple_init(sdu, 1);
+
+ cfm = net_buf_simple_add(sdu, sizeof(*cfm));
+ cfm->xact = xact;
+
+ buf = encode_friend_ctl(frnd, TRANS_CTL_OP_FRIEND_SUB_CFM, sdu);
+ if (!buf) {
+ BT_ERR("Unable to encode Subscription List Confirmation");
+ goto done;
+ }
+
+ if (encrypt_friend_pdu(frnd, buf, false)) {
+ return;
+ }
+
+ if (frnd->last) {
+ BT_DBG("Discarding last PDU");
+ net_buf_unref(frnd->last);
+ }
+
+ frnd->last = buf;
+ frnd->send_last = 1;
+
+done:
+ os_mbuf_free_chain(sdu);
+}
+
+static void friend_recv_delay(struct bt_mesh_friend *frnd)
+{
+ frnd->pending_req = 1;
+ k_delayed_work_submit(&frnd->timer, recv_delay(frnd));
+ BT_DBG("Waiting RecvDelay of %d ms", (int) recv_delay(frnd));
+}
+
+int bt_mesh_friend_sub_add(struct bt_mesh_net_rx *rx,
+ struct os_mbuf *buf)
+{
+ struct bt_mesh_friend *frnd;
+ u8_t xact;
+
+ if (buf->om_len < BT_MESH_FRIEND_SUB_MIN_LEN) {
+ BT_WARN("Too short Friend Subscription Add");
+ return -EINVAL;
+ }
+
+ frnd = bt_mesh_friend_find(rx->sub->net_idx, rx->ctx.addr, true, true);
+ if (!frnd) {
+ BT_WARN("No matching LPN addr 0x%04x", rx->ctx.addr);
+ return 0;
+ }
+
+ if (frnd->pending_buf) {
+ BT_WARN("Previous buffer not yet sent!");
+ return 0;
+ }
+
+ friend_recv_delay(frnd);
+
+ xact = net_buf_simple_pull_u8(buf);
+
+ while (buf->om_len >= 2) {
+ friend_sub_add(frnd, net_buf_simple_pull_be16(buf));
+ }
+
+ enqueue_sub_cfm(frnd, xact);
+
+ return 0;
+}
+
+int bt_mesh_friend_sub_rem(struct bt_mesh_net_rx *rx,
+ struct os_mbuf *buf)
+{
+ struct bt_mesh_friend *frnd;
+ u8_t xact;
+
+ if (buf->om_len < BT_MESH_FRIEND_SUB_MIN_LEN) {
+ BT_WARN("Too short Friend Subscription Remove");
+ return -EINVAL;
+ }
+
+ frnd = bt_mesh_friend_find(rx->sub->net_idx, rx->ctx.addr, true, true);
+ if (!frnd) {
+ BT_WARN("No matching LPN addr 0x%04x", rx->ctx.addr);
+ return 0;
+ }
+
+ if (frnd->pending_buf) {
+ BT_WARN("Previous buffer not yet sent!");
+ return 0;
+ }
+
+ friend_recv_delay(frnd);
+
+ xact = net_buf_simple_pull_u8(buf);
+
+ while (buf->om_len >= 2) {
+ friend_sub_rem(frnd, net_buf_simple_pull_be16(buf));
+ }
+
+ enqueue_sub_cfm(frnd, xact);
+
+ return 0;
+}
+
+static void enqueue_buf(struct bt_mesh_friend *frnd, struct os_mbuf *buf)
+{
+ net_buf_slist_put(&frnd->queue, buf);
+ frnd->queue_size++;
+}
+
+static void enqueue_update(struct bt_mesh_friend *frnd, u8_t md)
+{
+ struct os_mbuf *buf;
+
+ buf = encode_update(frnd, md);
+ if (!buf) {
+ BT_ERR("Unable to encode Friend Update");
+ return;
+ }
+
+ frnd->sec_update = 0;
+ enqueue_buf(frnd, buf);
+}
+
+int bt_mesh_friend_poll(struct bt_mesh_net_rx *rx, struct os_mbuf *buf)
+{
+ struct bt_mesh_ctl_friend_poll *msg = (void *)buf->om_data;
+ struct bt_mesh_friend *frnd;
+
+ if (buf->om_len < sizeof(*msg)) {
+ BT_WARN("Too short Friend Poll");
+ return -EINVAL;
+ }
+
+ frnd = bt_mesh_friend_find(rx->sub->net_idx, rx->ctx.addr, true, false);
+ if (!frnd) {
+ BT_WARN("No matching LPN addr 0x%04x", rx->ctx.addr);
+ return 0;
+ }
+
+ if (msg->fsn & ~1) {
+ BT_WARN("Prohibited (non-zero) padding bits");
+ return -EINVAL;
+ }
+
+ if (frnd->pending_buf) {
+ BT_WARN("Previous buffer not yet sent");
+ return 0;
+ }
+
+ BT_DBG("msg->fsn %u frnd->fsn %u", (msg->fsn & 1), frnd->fsn);
+
+ friend_recv_delay(frnd);
+
+ if (!frnd->established) {
+ BT_DBG("Friendship established with 0x%04x", frnd->lpn);
+ frnd->established = 1;
+ }
+
+ if (msg->fsn == frnd->fsn && frnd->last) {
+ BT_DBG("Re-sending last PDU");
+ frnd->send_last = 1;
+ } else {
+ if (frnd->last) {
+ net_buf_unref(frnd->last);
+ frnd->last = NULL;
+ }
+
+ frnd->fsn = msg->fsn;
+
+ if (net_buf_slist_is_empty(&frnd->queue)) {
+ enqueue_update(frnd, 0);
+ BT_DBG("Enqueued Friend Update to empty queue");
+ }
+ }
+
+ return 0;
+}
+
+static struct bt_mesh_friend *find_clear(u16_t prev_friend)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(bt_mesh.frnd); i++) {
+ struct bt_mesh_friend *frnd = &bt_mesh.frnd[i];
+
+ if (frnd->clear.frnd == prev_friend) {
+ return frnd;
+ }
+ }
+
+ return NULL;
+}
+
+static void friend_clear_sent(int err, void *user_data)
+{
+ struct bt_mesh_friend *frnd = user_data;
+
+ k_delayed_work_submit(&frnd->clear.timer,
+ K_SECONDS(frnd->clear.repeat_sec));
+ frnd->clear.repeat_sec *= 2;
+}
+
+static const struct bt_mesh_send_cb clear_sent_cb = {
+ .end = friend_clear_sent,
+};
+
+static void send_friend_clear(struct bt_mesh_friend *frnd)
+{
+ struct bt_mesh_msg_ctx ctx = {
+ .net_idx = frnd->net_idx,
+ .app_idx = BT_MESH_KEY_UNUSED,
+ .addr = frnd->clear.frnd,
+ .send_ttl = BT_MESH_TTL_MAX,
+ };
+ struct bt_mesh_net_tx tx = {
+ .sub = &bt_mesh.sub[0],
+ .ctx = &ctx,
+ .src = bt_mesh_primary_addr(),
+ .xmit = bt_mesh_net_transmit_get(),
+ };
+ struct bt_mesh_ctl_friend_clear req = {
+ .lpn_addr = sys_cpu_to_be16(frnd->lpn),
+ .lpn_counter = sys_cpu_to_be16(frnd->lpn_counter),
+ };
+
+ BT_DBG("");
+
+ bt_mesh_ctl_send(&tx, TRANS_CTL_OP_FRIEND_CLEAR, &req,
+ sizeof(req), NULL, &clear_sent_cb, frnd);
+}
+
+static void clear_timeout(struct ble_npl_event *work)
+{
+ struct bt_mesh_friend *frnd = ble_npl_event_get_arg(work);
+ u32_t duration;
+
+ BT_DBG("LPN 0x%04x (old) Friend 0x%04x", frnd->lpn, frnd->clear.frnd);
+
+ duration = k_uptime_get_32() - frnd->clear.start;
+ if (duration > 2 * frnd->poll_to) {
+ BT_DBG("Clear Procedure timer expired");
+ frnd->clear.frnd = BT_MESH_ADDR_UNASSIGNED;
+ return;
+ }
+
+ send_friend_clear(frnd);
+}
+
+static void clear_procedure_start(struct bt_mesh_friend *frnd)
+{
+ BT_DBG("LPN 0x%04x (old) Friend 0x%04x", frnd->lpn, frnd->clear.frnd);
+
+ frnd->clear.start = k_uptime_get_32();
+ frnd->clear.repeat_sec = 1U;
+
+ send_friend_clear(frnd);
+}
+
+int bt_mesh_friend_clear_cfm(struct bt_mesh_net_rx *rx,
+ struct os_mbuf *buf)
+{
+ struct bt_mesh_ctl_friend_clear_confirm *msg = (void *)buf->om_data;
+ struct bt_mesh_friend *frnd;
+ u16_t lpn_addr, lpn_counter;
+
+ BT_DBG("");
+
+ if (buf->om_len < sizeof(*msg)) {
+ BT_WARN("Too short Friend Clear Confirm");
+ return -EINVAL;
+ }
+
+ frnd = find_clear(rx->ctx.addr);
+ if (!frnd) {
+ BT_WARN("No pending clear procedure for 0x%02x", rx->ctx.addr);
+ return 0;
+ }
+
+ lpn_addr = sys_be16_to_cpu(msg->lpn_addr);
+ if (lpn_addr != frnd->lpn) {
+ BT_WARN("LPN address mismatch (0x%04x != 0x%04x)",
+ lpn_addr, frnd->lpn);
+ return 0;
+ }
+
+ lpn_counter = sys_be16_to_cpu(msg->lpn_counter);
+ if (lpn_counter != frnd->lpn_counter) {
+ BT_WARN("LPN counter mismatch (0x%04x != 0x%04x)",
+ lpn_counter, frnd->lpn_counter);
+ return 0;
+ }
+
+ k_delayed_work_cancel(&frnd->clear.timer);
+ frnd->clear.frnd = BT_MESH_ADDR_UNASSIGNED;
+
+ return 0;
+}
+
+static void enqueue_offer(struct bt_mesh_friend *frnd, s8_t rssi)
+{
+ struct bt_mesh_ctl_friend_offer *off;
+ struct os_mbuf *sdu = NET_BUF_SIMPLE(1 + sizeof(*off));
+ struct os_mbuf *buf;
+
+ BT_DBG("");
+
+ net_buf_simple_init(sdu, 1);
+
+ off = net_buf_simple_add(sdu, sizeof(*off));
+
+ off->recv_win = CONFIG_BT_MESH_FRIEND_RECV_WIN,
+ off->queue_size = CONFIG_BT_MESH_FRIEND_QUEUE_SIZE,
+ off->sub_list_size = ARRAY_SIZE(frnd->sub_list),
+ off->rssi = rssi,
+ off->frnd_counter = sys_cpu_to_be16(frnd->counter);
+
+ buf = encode_friend_ctl(frnd, TRANS_CTL_OP_FRIEND_OFFER, sdu);
+ if (!buf) {
+ BT_ERR("Unable to encode Friend Offer");
+ goto done;
+ }
+
+ if (encrypt_friend_pdu(frnd, buf, true)) {
+ return;
+ }
+
+ frnd->counter++;
+
+ if (frnd->last) {
+ net_buf_unref(frnd->last);
+ }
+
+ frnd->last = buf;
+ frnd->send_last = 1;
+
+done:
+ os_mbuf_free_chain(sdu);
+}
+
+#define RECV_WIN CONFIG_BT_MESH_FRIEND_RECV_WIN
+#define RSSI_FACT(crit) (((crit) >> 5) & (u8_t)BIT_MASK(2))
+#define RECV_WIN_FACT(crit) (((crit) >> 3) & (u8_t)BIT_MASK(2))
+#define MIN_QUEUE_SIZE_LOG(crit) ((crit) & (u8_t)BIT_MASK(3))
+#define MIN_QUEUE_SIZE(crit) ((u32_t)BIT(MIN_QUEUE_SIZE_LOG(crit)))
+
+static s32_t offer_delay(struct bt_mesh_friend *frnd, s8_t rssi, u8_t crit)
+{
+ /* Scaling factors. The actual values are 1, 1.5, 2 & 2.5, but we
+ * want to avoid floating-point arithmetic.
+ */
+ static const u8_t fact[] = { 10, 15, 20, 25 };
+ s32_t delay;
+
+ BT_DBG("ReceiveWindowFactor %u ReceiveWindow %u RSSIFactor %u RSSI %d",
+ fact[RECV_WIN_FACT(crit)], RECV_WIN,
+ fact[RSSI_FACT(crit)], rssi);
+
+ /* Delay = ReceiveWindowFactor * ReceiveWindow - RSSIFactor * RSSI */
+ delay = (s32_t)fact[RECV_WIN_FACT(crit)] * RECV_WIN;
+ delay -= (s32_t)fact[RSSI_FACT(crit)] * rssi;
+ delay /= 10;
+
+ BT_DBG("Local Delay calculated as %d ms", (int) delay);
+
+ if (delay < 100) {
+ return K_MSEC(100);
+ }
+
+ return K_MSEC(delay);
+}
+
+int bt_mesh_friend_req(struct bt_mesh_net_rx *rx, struct os_mbuf *buf)
+{
+ struct bt_mesh_ctl_friend_req *msg = (void *)buf->om_data;
+ struct bt_mesh_friend *frnd = NULL;
+ u32_t poll_to;
+ int i;
+
+ if (buf->om_len < sizeof(*msg)) {
+ BT_WARN("Too short Friend Request");
+ return -EINVAL;
+ }
+
+ if (msg->recv_delay <= 0x09) {
+ BT_WARN("Prohibited ReceiveDelay (0x%02x)", msg->recv_delay);
+ return -EINVAL;
+ }
+
+ poll_to = (((u32_t)msg->poll_to[0] << 16) |
+ ((u32_t)msg->poll_to[1] << 8) |
+ ((u32_t)msg->poll_to[2]));
+
+ if (poll_to <= 0x000009 || poll_to >= 0x34bc00) {
+ BT_WARN("Prohibited PollTimeout (0x%06x)", (unsigned) poll_to);
+ return -EINVAL;
+ }
+
+ if (msg->num_elem == 0x00) {
+ BT_WARN("Prohibited NumElements value (0x00)");
+ return -EINVAL;
+ }
+
+ if (!BT_MESH_ADDR_IS_UNICAST(rx->ctx.addr + msg->num_elem - 1)) {
+ BT_WARN("LPN elements stretch outside of unicast range");
+ return -EINVAL;
+ }
+
+ if (!MIN_QUEUE_SIZE_LOG(msg->criteria)) {
+ BT_WARN("Prohibited Minimum Queue Size in Friend Request");
+ return -EINVAL;
+ }
+
+ if (CONFIG_BT_MESH_FRIEND_QUEUE_SIZE < MIN_QUEUE_SIZE(msg->criteria)) {
+ BT_WARN("We have a too small Friend Queue size (%u < %u)",
+ CONFIG_BT_MESH_FRIEND_QUEUE_SIZE,
+ (unsigned) MIN_QUEUE_SIZE(msg->criteria));
+ return 0;
+ }
+
+ frnd = bt_mesh_friend_find(rx->sub->net_idx, rx->ctx.addr, true, false);
+ if (frnd) {
+ BT_WARN("Existing LPN re-requesting Friendship");
+ friend_clear(frnd);
+ goto init_friend;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(bt_mesh.frnd); i++) {
+ if (!bt_mesh.frnd[i].valid) {
+ frnd = &bt_mesh.frnd[i];
+ frnd->valid = 1;
+ break;
+ }
+ }
+
+ if (!frnd) {
+ BT_WARN("No free Friend contexts for new LPN");
+ return -ENOMEM;
+ }
+
+init_friend:
+ frnd->lpn = rx->ctx.addr;
+ frnd->num_elem = msg->num_elem;
+ frnd->net_idx = rx->sub->net_idx;
+ frnd->recv_delay = msg->recv_delay;
+ frnd->poll_to = poll_to * 100;
+ frnd->lpn_counter = sys_be16_to_cpu(msg->lpn_counter);
+ frnd->clear.frnd = sys_be16_to_cpu(msg->prev_addr);
+
+ BT_DBG("LPN 0x%04x rssi %d recv_delay %u poll_to %ums",
+ frnd->lpn, rx->ctx.recv_rssi, frnd->recv_delay,
+ (unsigned) frnd->poll_to);
+
+ if (BT_MESH_ADDR_IS_UNICAST(frnd->clear.frnd) &&
+ !bt_mesh_elem_find(frnd->clear.frnd)) {
+ clear_procedure_start(frnd);
+ }
+
+ k_delayed_work_submit(&frnd->timer,
+ offer_delay(frnd, rx->ctx.recv_rssi,
+ msg->criteria));
+
+ friend_cred_create(rx->sub, frnd->lpn, frnd->lpn_counter,
+ frnd->counter);
+
+ enqueue_offer(frnd, rx->ctx.recv_rssi);
+
+ return 0;
+}
+
+static bool is_seg(struct bt_mesh_friend_seg *seg, u16_t src, u16_t seq_zero)
+{
+ struct os_mbuf *buf = (void *)net_buf_slist_peek_head(&seg->queue);
+ struct net_buf_simple_state state;
+ u16_t buf_seq_zero;
+ u16_t buf_src;
+
+ if (!buf) {
+ return false;
+ }
+
+ net_buf_simple_save(buf, &state);
+ net_buf_skip(buf, 5); /* skip IVI, NID, CTL, TTL, SEQ */
+ buf_src = net_buf_pull_be16(buf);
+ net_buf_skip(buf, 3); /* skip DST, OP/AID */
+ buf_seq_zero = ((net_buf_pull_be16(buf) >> 2) & TRANS_SEQ_ZERO_MASK);
+ net_buf_simple_restore(buf, &state);
+
+ return ((src == buf_src) && (seq_zero == buf_seq_zero));
+}
+
+static struct bt_mesh_friend_seg *get_seg(struct bt_mesh_friend *frnd,
+ u16_t src, u16_t seq_zero,
+ u8_t seg_count)
+{
+ struct bt_mesh_friend_seg *unassigned = NULL;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(frnd->seg); i++) {
+ struct bt_mesh_friend_seg *seg = &frnd->seg[i];
+
+ if (is_seg(seg, src, seq_zero)) {
+ return seg;
+ }
+
+ if (!unassigned && !net_buf_slist_peek_head(&seg->queue)) {
+ unassigned = seg;
+ }
+ }
+
+ if (unassigned) {
+ unassigned->seg_count = seg_count;
+ }
+
+ return unassigned;
+}
+
+static void enqueue_friend_pdu(struct bt_mesh_friend *frnd,
+ enum bt_mesh_friend_pdu_type type,
+ u16_t src, u8_t seg_count,
+ struct os_mbuf *buf)
+{
+ struct bt_mesh_friend_seg *seg;
+
+ BT_DBG("type %u", type);
+
+ if (type == BT_MESH_FRIEND_PDU_SINGLE) {
+ if (frnd->sec_update) {
+ enqueue_update(frnd, 1);
+ }
+
+ enqueue_buf(frnd, buf);
+ return;
+ }
+
+ u16_t seq_zero = (((buf->om_data[10] << 8 | buf->om_data[11]) >> 2) & TRANS_SEQ_ZERO_MASK);
+
+ seg = get_seg(frnd, src, seq_zero, seg_count);
+ if (!seg) {
+ BT_ERR("No free friend segment RX contexts for 0x%04x", src);
+ net_buf_unref(buf);
+ return;
+ }
+
+ net_buf_slist_put(&seg->queue, buf);
+
+ if (type == BT_MESH_FRIEND_PDU_COMPLETE) {
+ if (frnd->sec_update) {
+ enqueue_update(frnd, 1);
+ }
+
+ net_buf_slist_merge_slist(&frnd->queue, &seg->queue);
+
+ frnd->queue_size += seg->seg_count;
+ seg->seg_count = 0U;
+ } else {
+ /* Mark the buffer as having more to come after it */
+ BT_MESH_ADV(buf)->flags |= NET_BUF_FRAGS;
+ }
+}
+
+static void buf_send_start(u16_t duration, int err, void *user_data)
+{
+ struct bt_mesh_friend *frnd = user_data;
+
+ BT_DBG("err %d", err);
+
+ frnd->pending_buf = 0;
+
+ /* Friend Offer doesn't follow the re-sending semantics */
+ if (!frnd->established) {
+ net_buf_unref(frnd->last);
+ frnd->last = NULL;
+ }
+}
+
+static void buf_send_end(int err, void *user_data)
+{
+ struct bt_mesh_friend *frnd = user_data;
+
+ BT_DBG("err %d", err);
+
+ if (frnd->pending_req) {
+ BT_WARN("Another request before previous completed sending");
+ return;
+ }
+
+ if (frnd->established) {
+ k_delayed_work_submit(&frnd->timer, frnd->poll_to);
+ BT_DBG("Waiting %u ms for next poll",
+ (unsigned) frnd->poll_to);
+ } else {
+ /* Friend offer timeout is 1 second */
+ k_delayed_work_submit(&frnd->timer, K_SECONDS(1));
+ BT_DBG("Waiting for first poll");
+ }
+}
+
+static void friend_timeout(struct ble_npl_event *work)
+{
+ struct bt_mesh_friend *frnd = ble_npl_event_get_arg(work);
+ static const struct bt_mesh_send_cb buf_sent_cb = {
+ .start = buf_send_start,
+ .end = buf_send_end,
+ };
+
+ __ASSERT_NO_MSG(frnd->pending_buf == 0);
+
+ BT_DBG("lpn 0x%04x send_last %u last %p", frnd->lpn,
+ frnd->send_last, frnd->last);
+
+ if (frnd->send_last && frnd->last) {
+ BT_DBG("Sending frnd->last %p", frnd->last);
+ frnd->send_last = 0;
+ goto send_last;
+ }
+
+ if (frnd->established && !frnd->pending_req) {
+ BT_WARN("Friendship lost with 0x%04x", frnd->lpn);
+ friend_clear(frnd);
+ return;
+ }
+
+ frnd->last = (void *)net_buf_slist_get(&frnd->queue);
+ if (!frnd->last) {
+ BT_WARN("Friendship not established with 0x%04x",
+ frnd->lpn);
+ friend_clear(frnd);
+ return;
+ }
+
+ if (encrypt_friend_pdu(frnd, frnd->last, false)) {
+ return;
+ }
+
+ /* Clear the flag we use for segment tracking */
+ BT_MESH_ADV(frnd->last)->flags &= ~NET_BUF_FRAGS;
+
+ BT_DBG("Sending buf %p from Friend Queue of LPN 0x%04x",
+ frnd->last, frnd->lpn);
+ frnd->queue_size--;
+
+send_last:
+ frnd->pending_req = 0;
+ frnd->pending_buf = 1;
+ bt_mesh_adv_send(frnd->last, &buf_sent_cb, frnd);
+}
+
+int bt_mesh_friend_init(void)
+{
+ int rc;
+ int i;
+
+ rc = os_mempool_init(&friend_buf_mempool, FRIEND_BUF_COUNT,
+ BT_MESH_ADV_DATA_SIZE + BT_MESH_MBUF_HEADER_SIZE,
+ friend_buf_mem, "friend_buf_pool");
+ assert(rc == 0);
+
+ rc = os_mbuf_pool_init(&friend_os_mbuf_pool, &friend_buf_mempool,
+ BT_MESH_ADV_DATA_SIZE + BT_MESH_MBUF_HEADER_SIZE,
+ FRIEND_BUF_COUNT);
+ assert(rc == 0);
+
+ for (i = 0; i < ARRAY_SIZE(bt_mesh.frnd); i++) {
+ struct bt_mesh_friend *frnd = &bt_mesh.frnd[i];
+ int j;
+
+ frnd->net_idx = BT_MESH_KEY_UNUSED;
+
+ net_buf_slist_init(&frnd->queue);
+
+ k_delayed_work_init(&frnd->timer, friend_timeout);
+ k_delayed_work_add_arg(&frnd->timer, frnd);
+ k_delayed_work_init(&frnd->clear.timer, clear_timeout);
+ k_delayed_work_add_arg(&frnd->clear.timer, frnd);
+
+ for (j = 0; j < ARRAY_SIZE(frnd->seg); j++) {
+ net_buf_slist_init(&frnd->seg[j].queue);
+ }
+ }
+
+ return 0;
+}
+
+static bool is_segack(struct os_mbuf *buf, u64_t *seqauth, u16_t src)
+{
+ struct net_buf_simple_state state;
+ bool found = false;
+
+ if (buf->om_len != 16) {
+ return false;
+ }
+
+ net_buf_simple_save(buf, &state);
+
+ net_buf_skip(buf, 1); /* skip IVI, NID */
+
+ if (!(net_buf_pull_u8(buf) >> 7)) {
+ goto end;
+ }
+
+ net_buf_pull(buf, 3); /* skip SEQNUM */
+
+ if (src != net_buf_pull_be16(buf)) {
+ goto end;
+ }
+
+ net_buf_skip(buf, 2); /* skip dst */
+
+ if (TRANS_CTL_OP((u8_t *) net_buf_pull_mem(buf, 1)) != TRANS_CTL_OP_ACK) {
+ goto end;
+ }
+
+ found = ((net_buf_pull_be16(buf) >> 2) & TRANS_SEQ_ZERO_MASK) ==
+ (*seqauth & TRANS_SEQ_ZERO_MASK);
+end:
+ net_buf_simple_restore(buf, &state);
+ return found;
+}
+
+static void friend_purge_old_ack(struct bt_mesh_friend *frnd, u64_t *seq_auth,
+ u16_t src)
+{
+ struct os_mbuf *cur, *prev = NULL;
+
+ BT_DBG("SeqAuth %llx src 0x%04x", *seq_auth, src);
+
+ for (cur = net_buf_slist_peek_head(&frnd->queue);
+ cur != NULL; prev = cur, cur = net_buf_slist_peek_next(cur)) {
+ struct os_mbuf *buf = (void *)cur;
+
+ if (is_segack(buf, seq_auth, src)) {
+ BT_DBG("Removing old ack from Friend Queue");
+
+ net_buf_slist_remove(&frnd->queue, prev, cur);
+ frnd->queue_size--;
+
+ net_buf_unref(buf);
+ break;
+ }
+ }
+}
+
+static void friend_lpn_enqueue_rx(struct bt_mesh_friend *frnd,
+ struct bt_mesh_net_rx *rx,
+ enum bt_mesh_friend_pdu_type type,
+ u64_t *seq_auth, u8_t seg_count,
+ struct os_mbuf *sbuf)
+{
+ struct friend_pdu_info info;
+ struct os_mbuf *buf;
+
+ /* Because of network loopback, tx packets will also be passed into
+ * this rx function. These packets have already been added to the
+ * queue, and should be ignored.
+ */
+ if (bt_mesh_elem_find(rx->ctx.addr)) {
+ return;
+ }
+
+ BT_DBG("LPN 0x%04x queue_size %u", frnd->lpn,
+ (unsigned) frnd->queue_size);
+
+ if (type == BT_MESH_FRIEND_PDU_SINGLE && seq_auth) {
+ friend_purge_old_ack(frnd, seq_auth, rx->ctx.addr);
+ }
+
+ info.src = rx->ctx.addr;
+ info.dst = rx->ctx.recv_dst;
+
+ if (rx->net_if == BT_MESH_NET_IF_LOCAL) {
+ info.ttl = rx->ctx.recv_ttl;
+ } else {
+ info.ttl = rx->ctx.recv_ttl - 1;
+ }
+
+ info.ctl = rx->ctl;
+
+ info.seq[0] = (rx->seq >> 16);
+ info.seq[1] = (rx->seq >> 8);
+ info.seq[2] = rx->seq;
+
+ info.iv_index = BT_MESH_NET_IVI_RX(rx);
+
+ buf = create_friend_pdu(frnd, &info, sbuf);
+ if (!buf) {
+ BT_ERR("Failed to encode Friend buffer");
+ return;
+ }
+
+ enqueue_friend_pdu(frnd, type, info.src, seg_count, buf);
+
+ BT_DBG("Queued message for LPN 0x%04x, queue_size %u",
+ frnd->lpn, (unsigned) frnd->queue_size);
+}
+
+static void friend_lpn_enqueue_tx(struct bt_mesh_friend *frnd,
+ struct bt_mesh_net_tx *tx,
+ enum bt_mesh_friend_pdu_type type,
+ u64_t *seq_auth, u8_t seg_count,
+ struct os_mbuf *sbuf)
+{
+ struct friend_pdu_info info;
+ struct os_mbuf *buf;
+
+ BT_DBG("LPN 0x%04x", frnd->lpn);
+
+ if (type == BT_MESH_FRIEND_PDU_SINGLE && seq_auth) {
+ friend_purge_old_ack(frnd, seq_auth, tx->src);
+ }
+
+ info.src = tx->src;
+ info.dst = tx->ctx->addr;
+
+ info.ttl = tx->ctx->send_ttl;
+ info.ctl = (tx->ctx->app_idx == BT_MESH_KEY_UNUSED);
+
+ info.seq[0] = (bt_mesh.seq >> 16);
+ info.seq[1] = (bt_mesh.seq >> 8);
+ info.seq[2] = bt_mesh.seq;
+
+ info.iv_index = BT_MESH_NET_IVI_TX;
+
+ buf = create_friend_pdu(frnd, &info, sbuf);
+ if (!buf) {
+ BT_ERR("Failed to encode Friend buffer");
+ return;
+ }
+
+ if (type == BT_MESH_FRIEND_PDU_SINGLE && !info.ctl) {
+ /* Unsegmented application packets may be reencrypted later,
+ * as they depend on the the sequence number being the same
+ * when encrypting in transport and network.
+ */
+ FRIEND_ADV(buf)->app_idx = tx->ctx->app_idx;
+ }
+
+ enqueue_friend_pdu(frnd, type, info.src, seg_count, buf);
+
+ BT_DBG("Queued message for LPN 0x%04x", frnd->lpn);
+}
+
+static bool friend_lpn_matches(struct bt_mesh_friend *frnd, u16_t net_idx,
+ u16_t addr)
+{
+ int i;
+
+ if (!frnd->established) {
+ return false;
+ }
+
+ if (net_idx != frnd->net_idx) {
+ return false;
+ }
+
+ if (BT_MESH_ADDR_IS_UNICAST(addr)) {
+ return is_lpn_unicast(frnd, addr);
+ }
+
+ for (i = 0; i < ARRAY_SIZE(frnd->sub_list); i++) {
+ if (frnd->sub_list[i] == addr) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool bt_mesh_friend_match(u16_t net_idx, u16_t addr)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(bt_mesh.frnd); i++) {
+ struct bt_mesh_friend *frnd = &bt_mesh.frnd[i];
+
+ if (friend_lpn_matches(frnd, net_idx, addr)) {
+ BT_DBG("LPN 0x%04x matched address 0x%04x",
+ frnd->lpn, addr);
+ return true;
+ }
+ }
+
+ BT_DBG("No matching LPN for address 0x%04x", addr);
+
+ return false;
+}
+
+static bool friend_queue_has_space(struct bt_mesh_friend *frnd, u16_t addr,
+ u64_t *seq_auth, u8_t seg_count)
+{
+ u32_t total = 0;
+ int i;
+
+ if (seg_count > CONFIG_BT_MESH_FRIEND_QUEUE_SIZE) {
+ return false;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(frnd->seg); i++) {
+ struct bt_mesh_friend_seg *seg = &frnd->seg[i];
+
+ if (seq_auth && is_seg(seg, addr, *seq_auth & TRANS_SEQ_ZERO_MASK)) {
+ /* If there's a segment queue for this message then the
+ * space verification has already happened.
+ */
+ return true;
+ }
+
+ total += seg->seg_count;
+ }
+
+ /* If currently pending segments combined with this segmented message
+ * are more than the Friend Queue Size, then there's no space. This
+ * is because we don't have a mechanism of aborting already pending
+ * segmented messages to free up buffers.
+ */
+ return (CONFIG_BT_MESH_FRIEND_QUEUE_SIZE - total) > seg_count;
+}
+
+bool bt_mesh_friend_queue_has_space(u16_t net_idx, u16_t src, u16_t dst,
+ u64_t *seq_auth, u8_t seg_count)
+{
+ bool someone_has_space = false, friend_match = false;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(bt_mesh.frnd); i++) {
+ struct bt_mesh_friend *frnd = &bt_mesh.frnd[i];
+
+ if (!friend_lpn_matches(frnd, net_idx, dst)) {
+ continue;
+ }
+
+ friend_match = true;
+
+ if (friend_queue_has_space(frnd, src, seq_auth, seg_count)) {
+ someone_has_space = true;
+ }
+ }
+
+ /* If there were no matched LPNs treat this as success, so the
+ * transport layer can continue its work.
+ */
+ if (!friend_match) {
+ return true;
+ }
+
+ /* From the transport layers perspective it's good enough that at
+ * least one Friend Queue has space. If there were multiple Friend
+ * matches then the destination must be a group address, in which
+ * case e.g. segment acks are not sent.
+ */
+ return someone_has_space;
+}
+
+static bool friend_queue_prepare_space(struct bt_mesh_friend *frnd, u16_t addr,
+ u64_t *seq_auth, u8_t seg_count)
+{
+ bool pending_segments;
+ u8_t avail_space;
+
+ if (!friend_queue_has_space(frnd, addr, seq_auth, seg_count)) {
+ return false;
+ }
+
+ avail_space = CONFIG_BT_MESH_FRIEND_QUEUE_SIZE - frnd->queue_size;
+ pending_segments = false;
+
+ while (pending_segments || avail_space < seg_count) {
+ struct os_mbuf *buf = (void *)net_buf_slist_get(&frnd->queue);
+
+ if (!buf) {
+ BT_ERR("Unable to free up enough buffers");
+ return false;
+ }
+
+ frnd->queue_size--;
+ avail_space++;
+
+ pending_segments = (BT_MESH_ADV(buf)->flags & NET_BUF_FRAGS);
+ BT_DBG("PENDING SEGMENTS %d", pending_segments);
+
+ /* Make sure old slist entry state doesn't remain */
+ BT_MESH_ADV(buf)->flags &= ~NET_BUF_FRAGS;
+
+ net_buf_unref(buf);
+ }
+
+ return true;
+}
+
+void bt_mesh_friend_enqueue_rx(struct bt_mesh_net_rx *rx,
+ enum bt_mesh_friend_pdu_type type,
+ u64_t *seq_auth, u8_t seg_count,
+ struct os_mbuf *sbuf)
+{
+ int i;
+
+ if (!rx->friend_match ||
+ (rx->ctx.recv_ttl <= 1 && rx->net_if != BT_MESH_NET_IF_LOCAL) ||
+ bt_mesh_friend_get() != BT_MESH_FRIEND_ENABLED) {
+ return;
+ }
+
+ BT_DBG("recv_ttl %u net_idx 0x%04x src 0x%04x dst 0x%04x",
+ rx->ctx.recv_ttl, rx->sub->net_idx, rx->ctx.addr,
+ rx->ctx.recv_dst);
+
+ for (i = 0; i < ARRAY_SIZE(bt_mesh.frnd); i++) {
+ struct bt_mesh_friend *frnd = &bt_mesh.frnd[i];
+
+ if (!friend_lpn_matches(frnd, rx->sub->net_idx,
+ rx->ctx.recv_dst)) {
+ continue;
+ }
+
+ if (!friend_queue_prepare_space(frnd, rx->ctx.addr, seq_auth,
+ seg_count)) {
+ continue;
+ }
+
+ friend_lpn_enqueue_rx(frnd, rx, type, seq_auth, seg_count,
+ sbuf);
+ }
+}
+
+bool bt_mesh_friend_enqueue_tx(struct bt_mesh_net_tx *tx,
+ enum bt_mesh_friend_pdu_type type,
+ u64_t *seq_auth, u8_t seg_count,
+ struct os_mbuf *sbuf)
+{
+ bool matched = false;
+ int i;
+
+ if (!bt_mesh_friend_match(tx->sub->net_idx, tx->ctx->addr) ||
+ bt_mesh_friend_get() != BT_MESH_FRIEND_ENABLED) {
+ return matched;
+ }
+
+ BT_DBG("net_idx 0x%04x dst 0x%04x src 0x%04x", tx->sub->net_idx,
+ tx->ctx->addr, tx->src);
+
+ for (i = 0; i < ARRAY_SIZE(bt_mesh.frnd); i++) {
+ struct bt_mesh_friend *frnd = &bt_mesh.frnd[i];
+
+ if (!friend_lpn_matches(frnd, tx->sub->net_idx,
+ tx->ctx->addr)) {
+ continue;
+ }
+
+ if (!friend_queue_prepare_space(frnd, tx->src, seq_auth,
+ seg_count)) {
+ continue;
+ }
+
+ friend_lpn_enqueue_tx(frnd, tx, type, seq_auth, seg_count,
+ sbuf);
+ matched = true;
+ }
+
+ return matched;
+}
+
+void bt_mesh_friend_clear_incomplete(struct bt_mesh_subnet *sub, u16_t src,
+ u16_t dst, u64_t *seq_auth)
+{
+ int i;
+
+ BT_DBG("");
+
+ for (i = 0; i < ARRAY_SIZE(bt_mesh.frnd); i++) {
+ struct bt_mesh_friend *frnd = &bt_mesh.frnd[i];
+ int j;
+
+ if (!friend_lpn_matches(frnd, sub->net_idx, dst)) {
+ continue;
+ }
+
+ for (j = 0; j < ARRAY_SIZE(frnd->seg); j++) {
+ struct bt_mesh_friend_seg *seg = &frnd->seg[j];
+
+ if (!is_seg(seg, src, *seq_auth & TRANS_SEQ_ZERO_MASK)) {
+ continue;
+ }
+
+ BT_WARN("Clearing incomplete segments for 0x%04x", src);
+
+ purge_buffers(&seg->queue);
+ seg->seg_count = 0U;
+ break;
+ }
+ }
+}
+
+#endif /* MYNEWT_VAL(BLE_MESH_FRIEND) */
diff --git a/src/libs/mynewt-nimble/nimble/host/mesh/src/friend.h b/src/libs/mynewt-nimble/nimble/host/mesh/src/friend.h
new file mode 100644
index 00000000..10ffa819
--- /dev/null
+++ b/src/libs/mynewt-nimble/nimble/host/mesh/src/friend.h
@@ -0,0 +1,56 @@
+/* Bluetooth Mesh */
+
+/*
+ * Copyright (c) 2017 Intel Corporation
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#ifndef __FRIEND_H__
+#define __FRIEND_H__
+
+#include "mesh/mesh.h"
+
+enum bt_mesh_friend_pdu_type {
+ BT_MESH_FRIEND_PDU_SINGLE,
+ BT_MESH_FRIEND_PDU_PARTIAL,
+ BT_MESH_FRIEND_PDU_COMPLETE,
+};
+
+bool bt_mesh_friend_match(u16_t net_idx, u16_t addr);
+
+struct bt_mesh_friend *bt_mesh_friend_find(u16_t net_idx, u16_t lpn_addr,
+ bool valid, bool established);
+
+bool bt_mesh_friend_queue_has_space(u16_t net_idx, u16_t src, u16_t dst,
+ u64_t *seq_auth, u8_t seg_count);
+
+void bt_mesh_friend_enqueue_rx(struct bt_mesh_net_rx *rx,
+ enum bt_mesh_friend_pdu_type type,
+ u64_t *seq_auth, u8_t seg_count,
+ struct os_mbuf *sbuf);
+bool bt_mesh_friend_enqueue_tx(struct bt_mesh_net_tx *tx,
+ enum bt_mesh_friend_pdu_type type,
+ u64_t *seq_auth, u8_t seg_count,
+ struct os_mbuf *sbuf);
+
+void bt_mesh_friend_clear_incomplete(struct bt_mesh_subnet *sub, u16_t src,
+ u16_t dst, u64_t *seq_auth);
+
+void bt_mesh_friend_sec_update(u16_t net_idx);
+
+void bt_mesh_friend_clear_net_idx(u16_t net_idx);
+
+int bt_mesh_friend_poll(struct bt_mesh_net_rx *rx, struct os_mbuf *buf);
+int bt_mesh_friend_req(struct bt_mesh_net_rx *rx, struct os_mbuf *buf);
+int bt_mesh_friend_clear(struct bt_mesh_net_rx *rx, struct os_mbuf *buf);
+int bt_mesh_friend_clear_cfm(struct bt_mesh_net_rx *rx,
+ struct os_mbuf *buf);
+int bt_mesh_friend_sub_add(struct bt_mesh_net_rx *rx,
+ struct os_mbuf *buf);
+int bt_mesh_friend_sub_rem(struct bt_mesh_net_rx *rx,
+ struct os_mbuf *buf);
+
+int bt_mesh_friend_init(void);
+
+#endif
diff --git a/src/libs/mynewt-nimble/nimble/host/mesh/src/glue.c b/src/libs/mynewt-nimble/nimble/host/mesh/src/glue.c
new file mode 100644
index 00000000..896f3d1a
--- /dev/null
+++ b/src/libs/mynewt-nimble/nimble/host/mesh/src/glue.c
@@ -0,0 +1,870 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include "syscfg/syscfg.h"
+#define MESH_LOG_MODULE BLE_MESH_LOG
+
+#include "mesh/glue.h"
+#include "adv.h"
+#ifndef MYNEWT
+#include "nimble/nimble_port.h"
+#endif
+
+#if MYNEWT_VAL(BLE_MESH_SETTINGS)
+#include "base64/base64.h"
+#endif
+
+extern u8_t g_mesh_addr_type;
+
+#if MYNEWT_VAL(BLE_EXT_ADV)
+/* Store configuration for different bearers */
+#define BT_MESH_ADV_IDX (0)
+#define BT_MESH_GATT_IDX (1)
+static struct ble_gap_adv_params ble_adv_cur_conf[2];
+#endif
+
+const char *
+bt_hex(const void *buf, size_t len)
+{
+ static const char hex[] = "0123456789abcdef";
+ static char hexbufs[4][137];
+ static u8_t curbuf;
+ const u8_t *b = buf;
+ char *str;
+ int i;
+
+ str = hexbufs[curbuf++];
+ curbuf %= ARRAY_SIZE(hexbufs);
+
+ len = min(len, (sizeof(hexbufs[0]) - 1) / 2);
+
+ for (i = 0; i < len; i++) {
+ str[i * 2] = hex[b[i] >> 4];
+ str[i * 2 + 1] = hex[b[i] & 0xf];
+ }
+
+ str[i * 2] = '\0';
+
+ return str;
+}
+
+void
+net_buf_put(struct ble_npl_eventq *fifo, struct os_mbuf *om)
+{
+ struct ble_npl_event *ev;
+
+ assert(OS_MBUF_IS_PKTHDR(om));
+ ev = &BT_MESH_ADV(om)->ev;
+ assert(ev);
+ assert(ble_npl_event_get_arg(ev));
+
+ ble_npl_eventq_put(fifo, ev);
+}
+
+void *
+net_buf_ref(struct os_mbuf *om)
+{
+ struct bt_mesh_adv *adv;
+
+ /* For bufs with header we count refs*/
+ if (OS_MBUF_USRHDR_LEN(om) == 0) {
+ return om;
+ }
+
+ adv = BT_MESH_ADV(om);
+ adv->ref_cnt++;
+
+ return om;
+}
+
+void
+net_buf_unref(struct os_mbuf *om)
+{
+ struct bt_mesh_adv *adv;
+
+ /* For bufs with header we count refs*/
+ if (OS_MBUF_USRHDR_LEN(om) == 0) {
+ goto free;
+ }
+
+ adv = BT_MESH_ADV(om);
+ if (--adv->ref_cnt > 0) {
+ return;
+ }
+
+free:
+ os_mbuf_free_chain(om);
+}
+
+int
+bt_encrypt_be(const uint8_t *key, const uint8_t *plaintext, uint8_t *enc_data)
+{
+ struct tc_aes_key_sched_struct s;
+
+ if (tc_aes128_set_encrypt_key(&s, key) == TC_CRYPTO_FAIL) {
+ return BLE_HS_EUNKNOWN;
+ }
+
+ if (tc_aes_encrypt(enc_data, plaintext, &s) == TC_CRYPTO_FAIL) {
+ return BLE_HS_EUNKNOWN;
+ }
+
+ return 0;
+}
+
+uint16_t
+net_buf_simple_pull_le16(struct os_mbuf *om)
+{
+ uint16_t val;
+ struct os_mbuf *old = om;
+
+ om = os_mbuf_pullup(om, sizeof(val));
+ assert(om == old);
+ val = get_le16(om->om_data);
+ os_mbuf_adj(om, sizeof(val));
+
+ return val;
+}
+
+uint16_t
+net_buf_simple_pull_be16(struct os_mbuf *om)
+{
+ uint16_t val;
+ struct os_mbuf *old = om;
+
+ om = os_mbuf_pullup(om, sizeof(val));
+ assert(om == old);
+ val = get_be16(om->om_data);
+ os_mbuf_adj(om, sizeof(val));
+
+ return val;
+}
+
+uint32_t
+net_buf_simple_pull_be32(struct os_mbuf *om)
+{
+ uint32_t val;
+ struct os_mbuf *old = om;
+
+ om = os_mbuf_pullup(om, sizeof(val));
+ assert(om == old);
+ val = get_be32(om->om_data);
+ os_mbuf_adj(om, sizeof(val));
+
+ return val;
+}
+
+uint32_t
+net_buf_simple_pull_le32(struct os_mbuf *om)
+{
+ uint32_t val;
+ struct os_mbuf *old = om;
+
+ om = os_mbuf_pullup(om, sizeof(val));
+ assert(om == old);
+ val = get_le32(om->om_data);
+ os_mbuf_adj(om, sizeof(val));
+
+ return val;
+}
+
+uint8_t
+net_buf_simple_pull_u8(struct os_mbuf *om)
+{
+ uint8_t val;
+ struct os_mbuf *old = om;
+
+ om = os_mbuf_pullup(om, sizeof(val));
+ assert(om == old);
+ val = om->om_data[0];
+ os_mbuf_adj(om, 1);
+
+ return val;
+}
+
+void
+net_buf_simple_add_le16(struct os_mbuf *om, uint16_t val)
+{
+ val = htole16(val);
+ os_mbuf_append(om, &val, sizeof(val));
+ ASSERT_NOT_CHAIN(om);
+}
+
+void
+net_buf_simple_add_be16(struct os_mbuf *om, uint16_t val)
+{
+ val = htobe16(val);
+ os_mbuf_append(om, &val, sizeof(val));
+ ASSERT_NOT_CHAIN(om);
+}
+
+void
+net_buf_simple_add_be32(struct os_mbuf *om, uint32_t val)
+{
+ val = htobe32(val);
+ os_mbuf_append(om, &val, sizeof(val));
+ ASSERT_NOT_CHAIN(om);
+}
+
+void
+net_buf_simple_add_le32(struct os_mbuf *om, uint32_t val)
+{
+ val = htole32(val);
+ os_mbuf_append(om, &val, sizeof(val));
+ ASSERT_NOT_CHAIN(om);
+}
+
+void
+net_buf_simple_add_u8(struct os_mbuf *om, uint8_t val)
+{
+ os_mbuf_append(om, &val, 1);
+ ASSERT_NOT_CHAIN(om);
+}
+
+void
+net_buf_simple_push_le16(struct os_mbuf *om, uint16_t val)
+{
+ uint8_t headroom = om->om_data - &om->om_databuf[om->om_pkthdr_len];
+
+ assert(headroom >= 2);
+ om->om_data -= 2;
+ put_le16(om->om_data, val);
+ om->om_len += 2;
+
+ if (om->om_pkthdr_len) {
+ OS_MBUF_PKTHDR(om)->omp_len += 2;
+ }
+ ASSERT_NOT_CHAIN(om);
+}
+
+void
+net_buf_simple_push_be16(struct os_mbuf *om, uint16_t val)
+{
+ uint8_t headroom = om->om_data - &om->om_databuf[om->om_pkthdr_len];
+
+ assert(headroom >= 2);
+ om->om_data -= 2;
+ put_be16(om->om_data, val);
+ om->om_len += 2;
+
+ if (om->om_pkthdr_len) {
+ OS_MBUF_PKTHDR(om)->omp_len += 2;
+ }
+ ASSERT_NOT_CHAIN(om);
+}
+
+void
+net_buf_simple_push_u8(struct os_mbuf *om, uint8_t val)
+{
+ uint8_t headroom = om->om_data - &om->om_databuf[om->om_pkthdr_len];
+
+ assert(headroom >= 1);
+ om->om_data -= 1;
+ om->om_data[0] = val;
+ om->om_len += 1;
+
+ if (om->om_pkthdr_len) {
+ OS_MBUF_PKTHDR(om)->omp_len += 1;
+ }
+ ASSERT_NOT_CHAIN(om);
+}
+
+void
+net_buf_add_zeros(struct os_mbuf *om, uint8_t len)
+{
+ uint8_t z[len];
+ int rc;
+
+ memset(z, 0, len);
+
+ rc = os_mbuf_append(om, z, len);
+ if(rc) {
+ assert(0);
+ }
+ ASSERT_NOT_CHAIN(om);
+}
+
+void *
+net_buf_simple_pull(struct os_mbuf *om, uint8_t len)
+{
+ os_mbuf_adj(om, len);
+ return om->om_data;
+}
+
+void *
+net_buf_simple_pull_mem(struct os_mbuf *om, uint8_t len)
+{
+ void *data = om->om_data;
+
+ net_buf_simple_pull(om, len);
+ return data;
+}
+
+void*
+net_buf_simple_add(struct os_mbuf *om, uint8_t len)
+{
+ void * tmp;
+
+ tmp = os_mbuf_extend(om, len);
+ ASSERT_NOT_CHAIN(om);
+
+ return tmp;
+}
+
+bool
+k_fifo_is_empty(struct ble_npl_eventq *q)
+{
+ return ble_npl_eventq_is_empty(q);
+}
+
+void * net_buf_get(struct ble_npl_eventq *fifo, s32_t t)
+{
+ struct ble_npl_event *ev = ble_npl_eventq_get(fifo, 0);
+
+ if (ev) {
+ return ble_npl_event_get_arg(ev);
+ }
+
+ return NULL;
+}
+
+uint8_t *
+net_buf_simple_push(struct os_mbuf *om, uint8_t len)
+{
+ uint8_t headroom = om->om_data - &om->om_databuf[om->om_pkthdr_len];
+
+ assert(headroom >= len);
+ om->om_data -= len;
+ om->om_len += len;
+
+ return om->om_data;
+}
+
+void
+net_buf_reserve(struct os_mbuf *om, size_t reserve)
+{
+ /* We need reserve to be done on fresh buf */
+ assert(om->om_len == 0);
+ om->om_data += reserve;
+}
+
+void
+k_work_init(struct ble_npl_callout *work, ble_npl_event_fn handler)
+{
+#ifndef MYNEWT
+ ble_npl_callout_init(work, nimble_port_get_dflt_eventq(), handler, NULL);
+#else
+ ble_npl_callout_init(work, ble_npl_eventq_dflt_get(), handler, NULL);
+#endif
+}
+
+void
+k_delayed_work_init(struct k_delayed_work *w, ble_npl_event_fn *f)
+{
+#ifndef MYNEWT
+ ble_npl_callout_init(&w->work, nimble_port_get_dflt_eventq(), f, NULL);
+#else
+ ble_npl_callout_init(&w->work, ble_npl_eventq_dflt_get(), f, NULL);
+#endif
+}
+
+void
+k_delayed_work_cancel(struct k_delayed_work *w)
+{
+ ble_npl_callout_stop(&w->work);
+}
+
+void
+k_delayed_work_submit(struct k_delayed_work *w, uint32_t ms)
+{
+ uint32_t ticks;
+
+ if (ble_npl_time_ms_to_ticks(ms, &ticks) != 0) {
+ assert(0);
+ }
+ ble_npl_callout_reset(&w->work, ticks);
+}
+
+void
+k_work_submit(struct ble_npl_callout *w)
+{
+ ble_npl_callout_reset(w, 0);
+}
+
+void
+k_work_add_arg(struct ble_npl_callout *w, void *arg)
+{
+ ble_npl_callout_set_arg(w, arg);
+}
+
+void
+k_delayed_work_add_arg(struct k_delayed_work *w, void *arg)
+{
+ k_work_add_arg(&w->work, arg);
+}
+
+uint32_t
+k_delayed_work_remaining_get (struct k_delayed_work *w)
+{
+ int sr;
+ ble_npl_time_t t;
+
+ OS_ENTER_CRITICAL(sr);
+
+ t = ble_npl_callout_remaining_ticks(&w->work, ble_npl_time_get());
+
+ OS_EXIT_CRITICAL(sr);
+
+ return ble_npl_time_ticks_to_ms32(t);
+}
+
+int64_t k_uptime_get(void)
+{
+ /* We should return ms */
+ return ble_npl_time_ticks_to_ms32(ble_npl_time_get());
+}
+
+u32_t k_uptime_get_32(void)
+{
+ return k_uptime_get();
+}
+
+void k_sleep(int32_t duration)
+{
+ uint32_t ticks;
+
+ ticks = ble_npl_time_ms_to_ticks32(duration);
+
+ ble_npl_time_delay(ticks);
+}
+
+static uint8_t pub[64];
+static uint8_t priv[32];
+static bool has_pub = false;
+
+int
+bt_dh_key_gen(const u8_t remote_pk[64], bt_dh_key_cb_t cb)
+{
+ uint8_t dh[32];
+
+ if (ble_sm_alg_gen_dhkey((uint8_t *)&remote_pk[0], (uint8_t *)&remote_pk[32],
+ priv, dh)) {
+ return -1;
+ }
+
+ cb(dh);
+ return 0;
+}
+
+int
+bt_rand(void *buf, size_t len)
+{
+ int rc;
+ rc = ble_hs_hci_util_rand(buf, len);
+ if (rc != 0) {
+ return -1;
+ }
+
+ return 0;
+}
+
+int
+bt_pub_key_gen(struct bt_pub_key_cb *new_cb)
+{
+
+ if (ble_sm_alg_gen_key_pair(pub, priv)) {
+ assert(0);
+ return -1;
+ }
+
+ new_cb->func(pub);
+ has_pub = true;
+
+ return 0;
+}
+
+uint8_t *
+bt_pub_key_get(void)
+{
+ if (!has_pub) {
+ return NULL;
+ }
+
+ return pub;
+}
+
+static int
+set_ad(const struct bt_data *ad, size_t ad_len, u8_t *buf, u8_t *buf_len)
+{
+ int i;
+
+ for (i = 0; i < ad_len; i++) {
+ buf[(*buf_len)++] = ad[i].data_len + 1;
+ buf[(*buf_len)++] = ad[i].type;
+
+ memcpy(&buf[*buf_len], ad[i].data,
+ ad[i].data_len);
+ *buf_len += ad[i].data_len;
+ }
+
+ return 0;
+}
+
+#if MYNEWT_VAL(BLE_EXT_ADV)
+static void
+ble_adv_copy_to_ext_param(struct ble_gap_ext_adv_params *ext_param,
+ const struct ble_gap_adv_params *param)
+{
+ memset(ext_param, 0, sizeof(*ext_param));
+
+ ext_param->legacy_pdu = 1;
+
+ if (param->conn_mode != BLE_GAP_CONN_MODE_NON) {
+ ext_param->connectable = 1;
+ ext_param->scannable = 1;
+ }
+
+ ext_param->itvl_max = param->itvl_max;
+ ext_param->itvl_min = param->itvl_min;
+ ext_param->channel_map = param->channel_map;
+ ext_param->high_duty_directed = param->high_duty_cycle;
+ ext_param->own_addr_type = g_mesh_addr_type;
+}
+
+static int
+ble_adv_conf_adv_instance(const struct ble_gap_adv_params *param, int *instance)
+{
+ struct ble_gap_ext_adv_params ext_params;
+ struct ble_gap_adv_params *cur_conf;
+ int err = 0;
+
+ if (param->conn_mode == BLE_GAP_CONN_MODE_NON) {
+ *instance = BT_MESH_ADV_INST;
+ cur_conf = &ble_adv_cur_conf[BT_MESH_ADV_IDX];
+ } else {
+#if MYNEWT_VAL(BLE_MESH_PROXY)
+ *instance = BT_MESH_ADV_GATT_INST;
+ cur_conf = &ble_adv_cur_conf[BT_MESH_GATT_IDX];
+#else
+ assert(0);
+#endif
+ }
+
+ /* Checking interval max as it has to be in place if instance was configured
+ * before.
+ */
+ if (cur_conf->itvl_max == 0) {
+ goto configure;
+ }
+
+ if (memcmp(param, cur_conf, sizeof(*cur_conf)) == 0) {
+ /* Same parameters - skip reconfiguring */
+ goto done;
+ }
+
+ ble_gap_ext_adv_stop(*instance);
+ err = ble_gap_ext_adv_remove(*instance);
+ if (err) {
+ assert(0);
+ goto done;
+ }
+
+configure:
+ ble_adv_copy_to_ext_param(&ext_params, param);
+
+ err = ble_gap_ext_adv_configure(*instance, &ext_params, 0,
+ ble_adv_gap_mesh_cb, NULL);
+ if (!err) {
+ memcpy(cur_conf, param, sizeof(*cur_conf));
+ }
+
+done:
+ return err;
+}
+
+int
+bt_le_adv_start(const struct ble_gap_adv_params *param,
+ const struct bt_data *ad, size_t ad_len,
+ const struct bt_data *sd, size_t sd_len)
+{
+ struct os_mbuf *data;
+ int instance;
+ int err;
+ uint8_t buf[BLE_HS_ADV_MAX_SZ];
+ uint8_t buf_len = 0;
+
+ err = ble_adv_conf_adv_instance(param, &instance);
+ if (err) {
+ return err;
+ }
+
+ if (ad_len > 0) {
+ err = set_ad(ad, ad_len, buf, &buf_len);
+ if (err) {
+ return err;
+ }
+
+ /* For now let's use msys pool. We are not putting more then legacy */
+ data = os_msys_get_pkthdr(BLE_HS_ADV_MAX_SZ, 0);
+ if (!data) {
+ return OS_ENOMEM;
+ }
+
+ err = os_mbuf_append(data, buf, buf_len);
+ if (err) {
+ goto error;
+ }
+
+ err = ble_gap_ext_adv_set_data(instance, data);
+ if (err) {
+ return err;
+ }
+
+ data = NULL;
+ }
+
+ if (sd_len > 0) {
+ buf_len = 0;
+
+ err = set_ad(sd, sd_len, buf, &buf_len);
+ if (err) {
+ return err;
+ }
+
+ /* For now let's use msys pool. We are not putting more then legace*/
+ data = os_msys_get_pkthdr(BLE_HS_ADV_MAX_SZ, 0);
+ if (!data) {
+ return OS_ENOMEM;
+ }
+
+ err = os_mbuf_append(data, buf, buf_len);
+ if (err) {
+ goto error;
+ }
+
+ err = ble_gap_ext_adv_rsp_set_data(instance, data);
+ if (err) {
+ goto error;
+ }
+ }
+
+ /*TODO: We could use duration and max events in the future */
+ err = ble_gap_ext_adv_start(instance, 0, 0);
+ return err;
+
+error:
+ if (data) {
+ os_mbuf_free_chain(data);
+ }
+
+ return err;
+}
+
+int bt_le_adv_stop(bool proxy)
+{
+#if MYNEWT_VAL(BLE_MESH_PROXY)
+ int rc;
+
+ if (proxy) {
+ rc = ble_gap_ext_adv_stop(BT_MESH_ADV_GATT_INST);
+ } else {
+ rc = ble_gap_ext_adv_stop(BT_MESH_ADV_INST);
+ }
+
+ return rc;
+#else
+ return ble_gap_ext_adv_stop(BT_MESH_ADV_INST);
+#endif
+}
+
+#else
+
+int
+bt_le_adv_start(const struct ble_gap_adv_params *param,
+ const struct bt_data *ad, size_t ad_len,
+ const struct bt_data *sd, size_t sd_len)
+{
+ uint8_t buf[BLE_HS_ADV_MAX_SZ];
+ uint8_t buf_len = 0;
+ int err;
+
+ err = set_ad(ad, ad_len, buf, &buf_len);
+ if (err) {
+ return err;
+ }
+
+ err = ble_gap_adv_set_data(buf, buf_len);
+ if (err != 0) {
+ return err;
+ }
+
+ if (sd) {
+ buf_len = 0;
+
+ err = set_ad(sd, sd_len, buf, &buf_len);
+ if (err) {
+ BT_ERR("Advertising failed: err %d", err);
+ return err;
+ }
+
+ err = ble_gap_adv_rsp_set_data(buf, buf_len);
+ if (err != 0) {
+ BT_ERR("Advertising failed: err %d", err);
+ return err;
+ }
+ }
+
+ err = ble_gap_adv_start(g_mesh_addr_type, NULL, BLE_HS_FOREVER, param,
+ NULL, NULL);
+ if (err) {
+ BT_ERR("Advertising failed: err %d", err);
+ return err;
+ }
+
+ return 0;
+}
+
+int bt_le_adv_stop(bool proxy)
+{
+ return ble_gap_adv_stop();
+}
+
+#endif
+
+#if MYNEWT_VAL(BLE_MESH_PROXY)
+int bt_mesh_proxy_svcs_register(void);
+#endif
+
+void
+bt_mesh_register_gatt(void)
+{
+#if MYNEWT_VAL(BLE_MESH_PROXY)
+ bt_mesh_proxy_svcs_register();
+#endif
+}
+
+void net_buf_slist_init(struct net_buf_slist_t *list)
+{
+ STAILQ_INIT(list);
+}
+
+bool net_buf_slist_is_empty(struct net_buf_slist_t *list)
+{
+ return STAILQ_EMPTY(list);
+}
+
+struct os_mbuf *net_buf_slist_peek_head(struct net_buf_slist_t *list)
+{
+ struct os_mbuf_pkthdr *pkthdr;
+
+ /* Get mbuf pointer from packet header pointer */
+ pkthdr = STAILQ_FIRST(list);
+ if (!pkthdr) {
+ return NULL;
+ }
+
+ return OS_MBUF_PKTHDR_TO_MBUF(pkthdr);
+}
+
+struct os_mbuf *net_buf_slist_peek_next(struct os_mbuf *buf)
+{
+ struct os_mbuf_pkthdr *pkthdr;
+
+ /* Get mbuf pointer from packet header pointer */
+ pkthdr = OS_MBUF_PKTHDR(buf);
+ pkthdr = STAILQ_NEXT(pkthdr, omp_next);
+ if (!pkthdr) {
+ return NULL;
+ }
+
+ return OS_MBUF_PKTHDR_TO_MBUF(pkthdr);
+}
+
+struct os_mbuf *net_buf_slist_get(struct net_buf_slist_t *list)
+{
+ os_sr_t sr;
+ struct os_mbuf *m;
+
+ m = net_buf_slist_peek_head(list);
+ if (!m) {
+ return NULL;
+ }
+
+ /* Remove from queue */
+ OS_ENTER_CRITICAL(sr);
+ STAILQ_REMOVE_HEAD(list, omp_next);
+ OS_EXIT_CRITICAL(sr);
+ return m;
+}
+
+void net_buf_slist_put(struct net_buf_slist_t *list, struct os_mbuf *buf)
+{
+ struct os_mbuf_pkthdr *pkthdr;
+
+ pkthdr = OS_MBUF_PKTHDR(buf);
+ STAILQ_INSERT_TAIL(list, pkthdr, omp_next);
+}
+
+void net_buf_slist_remove(struct net_buf_slist_t *list, struct os_mbuf *prev,
+ struct os_mbuf *cur)
+{
+ struct os_mbuf_pkthdr *pkthdr, *cur_pkthdr;
+
+ cur_pkthdr = OS_MBUF_PKTHDR(cur);
+
+ STAILQ_FOREACH(pkthdr, list, omp_next) {
+ if (cur_pkthdr == pkthdr) {
+ STAILQ_REMOVE(list, cur_pkthdr, os_mbuf_pkthdr, omp_next);
+ break;
+ }
+ }
+}
+
+void net_buf_slist_merge_slist(struct net_buf_slist_t *list,
+ struct net_buf_slist_t *list_to_append)
+{
+ if (!STAILQ_EMPTY(list_to_append)) {
+ *(list)->stqh_last = list_to_append->stqh_first;
+ (list)->stqh_last = list_to_append->stqh_last;
+ STAILQ_INIT(list_to_append);
+ }
+}
+
+#if MYNEWT_VAL(BLE_MESH_SETTINGS)
+
+int settings_bytes_from_str(char *val_str, void *vp, int *len)
+{
+ *len = base64_decode(val_str, vp);
+ return 0;
+}
+
+char *settings_str_from_bytes(const void *vp, int vp_len,
+ char *buf, int buf_len)
+{
+ if (BASE64_ENCODE_SIZE(vp_len) > buf_len) {
+ return NULL;
+ }
+
+ base64_encode(vp, vp_len, buf, 1);
+
+ return buf;
+}
+
+#endif /* MYNEWT_VAL(BLE_MESH_SETTINGS) */
+
diff --git a/src/libs/mynewt-nimble/nimble/host/mesh/src/health_cli.c b/src/libs/mynewt-nimble/nimble/host/mesh/src/health_cli.c
new file mode 100644
index 00000000..193279c2
--- /dev/null
+++ b/src/libs/mynewt-nimble/nimble/host/mesh/src/health_cli.c
@@ -0,0 +1,556 @@
+/* Bluetooth Mesh */
+
+/*
+ * Copyright (c) 2017 Intel Corporation
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include "syscfg/syscfg.h"
+#define MESH_LOG_MODULE BLE_MESH_MODEL_LOG
+
+#include <string.h>
+#include <errno.h>
+#include <stdbool.h>
+
+#include "mesh/mesh.h"
+#include "mesh_priv.h"
+#include "adv.h"
+#include "net.h"
+#include "transport.h"
+#include "foundation.h"
+#include "mesh/health_cli.h"
+
+static s32_t msg_timeout = K_SECONDS(5);
+
+static struct bt_mesh_health_cli *health_cli;
+
+struct health_fault_param {
+ u16_t cid;
+ u8_t *expect_test_id;
+ u8_t *test_id;
+ u8_t *faults;
+ size_t *fault_count;
+};
+
+static void health_fault_status(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ struct health_fault_param *param;
+ u8_t test_id;
+ u16_t cid;
+
+ BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s",
+ ctx->net_idx, ctx->app_idx, ctx->addr, buf->om_len,
+ bt_hex(buf->om_data, buf->om_len));
+
+ if (health_cli->op_pending != OP_HEALTH_FAULT_STATUS) {
+ BT_WARN("Unexpected Health Fault Status message");
+ return;
+ }
+
+ param = health_cli->op_param;
+
+ test_id = net_buf_simple_pull_u8(buf);
+ if (param->expect_test_id && test_id != *param->expect_test_id) {
+ BT_WARN("Health fault with unexpected Test ID");
+ return;
+ }
+
+ cid = net_buf_simple_pull_le16(buf);
+ if (cid != param->cid) {
+ BT_WARN("Health fault with unexpected Company ID");
+ return;
+ }
+
+ if (param->test_id) {
+ *param->test_id = test_id;
+ }
+
+ if (buf->om_len > *param->fault_count) {
+ BT_WARN("Got more faults than there's space for");
+ } else {
+ *param->fault_count = buf->om_len;
+ }
+
+ memcpy(param->faults, buf->om_data, *param->fault_count);
+
+ k_sem_give(&health_cli->op_sync);
+}
+
+static void health_current_status(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ struct bt_mesh_health_cli *cli = model->user_data;
+ u8_t test_id;
+ u16_t cid;
+
+ BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s",
+ ctx->net_idx, ctx->app_idx, ctx->addr, buf->om_len,
+ bt_hex(buf->om_data, buf->om_len));
+
+ test_id = net_buf_simple_pull_u8(buf);
+ cid = net_buf_simple_pull_le16(buf);
+
+ BT_DBG("Test ID 0x%02x Company ID 0x%04x Fault Count %u",
+ test_id, cid, buf->om_len);
+
+ if (!cli->current_status) {
+ BT_WARN("No Current Status callback available");
+ return;
+ }
+
+ cli->current_status(cli, ctx->addr, test_id, cid, buf->om_data, buf->om_len);
+}
+
+struct health_period_param {
+ u8_t *divisor;
+};
+
+static void health_period_status(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ struct health_period_param *param;
+
+ BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s",
+ ctx->net_idx, ctx->app_idx, ctx->addr, buf->om_len,
+ bt_hex(buf->om_data, buf->om_len));
+
+ if (health_cli->op_pending != OP_HEALTH_PERIOD_STATUS) {
+ BT_WARN("Unexpected Health Period Status message");
+ return;
+ }
+
+ param = health_cli->op_param;
+
+ *param->divisor = net_buf_simple_pull_u8(buf);
+
+ k_sem_give(&health_cli->op_sync);
+}
+
+struct health_attention_param {
+ u8_t *attention;
+};
+
+static void health_attention_status(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ struct health_attention_param *param;
+
+ BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s",
+ ctx->net_idx, ctx->app_idx, ctx->addr, buf->om_len,
+ bt_hex(buf->om_data, buf->om_len));
+
+ if (health_cli->op_pending != OP_ATTENTION_STATUS) {
+ BT_WARN("Unexpected Health Attention Status message");
+ return;
+ }
+
+ param = health_cli->op_param;
+
+ if (param->attention) {
+ *param->attention = net_buf_simple_pull_u8(buf);
+ }
+
+ k_sem_give(&health_cli->op_sync);
+}
+
+const struct bt_mesh_model_op bt_mesh_health_cli_op[] = {
+ { OP_HEALTH_FAULT_STATUS, 3, health_fault_status },
+ { OP_HEALTH_CURRENT_STATUS, 3, health_current_status },
+ { OP_HEALTH_PERIOD_STATUS, 1, health_period_status },
+ { OP_ATTENTION_STATUS, 1, health_attention_status },
+ BT_MESH_MODEL_OP_END,
+};
+
+static int cli_prepare(void *param, u32_t op)
+{
+ if (!health_cli) {
+ BT_ERR("No available Health Client context!");
+ return -EINVAL;
+ }
+
+ if (health_cli->op_pending) {
+ BT_WARN("Another synchronous operation pending");
+ return -EBUSY;
+ }
+
+ health_cli->op_param = param;
+ health_cli->op_pending = op;
+
+ return 0;
+}
+
+static void cli_reset(void)
+{
+ health_cli->op_pending = 0;
+ health_cli->op_param = NULL;
+}
+
+static int cli_wait(void)
+{
+ int err;
+
+ err = k_sem_take(&health_cli->op_sync, msg_timeout);
+
+ cli_reset();
+
+ return err;
+}
+
+int bt_mesh_health_attention_get(u16_t net_idx, u16_t addr, u16_t app_idx,
+ u8_t *attention)
+{
+ struct os_mbuf *msg = BT_MESH_MODEL_BUF(OP_ATTENTION_GET, 0);
+ struct bt_mesh_msg_ctx ctx = {
+ .net_idx = net_idx,
+ .app_idx = app_idx,
+ .addr = addr,
+ .send_ttl = BT_MESH_TTL_DEFAULT,
+ };
+ struct health_attention_param param = {
+ .attention = attention,
+ };
+ int err;
+
+ err = cli_prepare(&param, OP_ATTENTION_STATUS);
+ if (err) {
+ goto done;
+ }
+
+ bt_mesh_model_msg_init(msg, OP_ATTENTION_GET);
+
+ err = bt_mesh_model_send(health_cli->model, &ctx, msg, NULL, NULL);
+ if (err) {
+ BT_ERR("model_send() failed (err %d)", err);
+ cli_reset();
+ goto done;
+ }
+
+ err = cli_wait();
+done:
+ os_mbuf_free_chain(msg);
+ return err;
+}
+
+int bt_mesh_health_attention_set(u16_t net_idx, u16_t addr, u16_t app_idx,
+ u8_t attention, u8_t *updated_attention)
+{
+ struct os_mbuf *msg = BT_MESH_MODEL_BUF(OP_ATTENTION_SET, 1);
+ struct bt_mesh_msg_ctx ctx = {
+ .net_idx = net_idx,
+ .app_idx = app_idx,
+ .addr = addr,
+ .send_ttl = BT_MESH_TTL_DEFAULT,
+ };
+ struct health_attention_param param = {
+ .attention = updated_attention,
+ };
+ int err;
+
+ err = cli_prepare(&param, OP_ATTENTION_STATUS);
+ if (err) {
+ goto done;
+ }
+
+ if (updated_attention) {
+ bt_mesh_model_msg_init(msg, OP_ATTENTION_SET);
+ } else {
+ bt_mesh_model_msg_init(msg, OP_ATTENTION_SET_UNREL);
+ }
+
+ net_buf_simple_add_u8(msg, attention);
+
+ err = bt_mesh_model_send(health_cli->model, &ctx, msg, NULL, NULL);
+ if (err) {
+ BT_ERR("model_send() failed (err %d)", err);
+ cli_reset();
+ goto done;
+ }
+
+ if (!updated_attention) {
+ cli_reset();
+ goto done;
+ }
+
+ err = cli_wait();
+done:
+ os_mbuf_free_chain(msg);
+ return err;
+}
+
+int bt_mesh_health_period_get(u16_t net_idx, u16_t addr, u16_t app_idx,
+ u8_t *divisor)
+{
+ struct os_mbuf *msg = BT_MESH_MODEL_BUF(OP_HEALTH_PERIOD_GET, 0);
+ struct bt_mesh_msg_ctx ctx = {
+ .net_idx = net_idx,
+ .app_idx = app_idx,
+ .addr = addr,
+ .send_ttl = BT_MESH_TTL_DEFAULT,
+ };
+ struct health_period_param param = {
+ .divisor = divisor,
+ };
+ int err;
+
+ err = cli_prepare(&param, OP_HEALTH_PERIOD_STATUS);
+ if (err) {
+ goto done;
+ }
+
+ bt_mesh_model_msg_init(msg, OP_HEALTH_PERIOD_GET);
+
+ err = bt_mesh_model_send(health_cli->model, &ctx, msg, NULL, NULL);
+ if (err) {
+ BT_ERR("model_send() failed (err %d)", err);
+ cli_reset();
+ goto done;
+ }
+
+ err = cli_wait();
+done:
+ os_mbuf_free_chain(msg);
+ return err;
+}
+
+int bt_mesh_health_period_set(u16_t net_idx, u16_t addr, u16_t app_idx,
+ u8_t divisor, u8_t *updated_divisor)
+{
+ struct os_mbuf *msg = BT_MESH_MODEL_BUF(OP_HEALTH_PERIOD_SET, 1);
+ struct bt_mesh_msg_ctx ctx = {
+ .net_idx = net_idx,
+ .app_idx = app_idx,
+ .addr = addr,
+ .send_ttl = BT_MESH_TTL_DEFAULT,
+ };
+ struct health_period_param param = {
+ .divisor = updated_divisor,
+ };
+ int err;
+
+ err = cli_prepare(&param, OP_HEALTH_PERIOD_STATUS);
+ if (err) {
+ goto done;
+ }
+
+ if (updated_divisor) {
+ bt_mesh_model_msg_init(msg, OP_HEALTH_PERIOD_SET);
+ } else {
+ bt_mesh_model_msg_init(msg, OP_HEALTH_PERIOD_SET_UNREL);
+ }
+
+ net_buf_simple_add_u8(msg, divisor);
+
+ err = bt_mesh_model_send(health_cli->model, &ctx, msg, NULL, NULL);
+ if (err) {
+ BT_ERR("model_send() failed (err %d)", err);
+ cli_reset();
+ goto done;
+ }
+
+ if (!updated_divisor) {
+ cli_reset();
+ goto done;
+ }
+
+ err = cli_wait();
+done:
+ os_mbuf_free_chain(msg);
+ return err;
+}
+
+int bt_mesh_health_fault_test(u16_t net_idx, u16_t addr, u16_t app_idx,
+ u16_t cid, u8_t test_id, u8_t *faults,
+ size_t *fault_count)
+{
+ struct os_mbuf *msg = BT_MESH_MODEL_BUF(OP_HEALTH_FAULT_TEST, 3);
+ struct bt_mesh_msg_ctx ctx = {
+ .net_idx = net_idx,
+ .app_idx = app_idx,
+ .addr = addr,
+ .send_ttl = BT_MESH_TTL_DEFAULT,
+ };
+ struct health_fault_param param = {
+ .cid = cid,
+ .expect_test_id = &test_id,
+ .faults = faults,
+ .fault_count = fault_count,
+ };
+ int err;
+
+ err = cli_prepare(&param, OP_HEALTH_FAULT_STATUS);
+ if (err) {
+ goto done;
+ }
+
+ if (faults) {
+ bt_mesh_model_msg_init(msg, OP_HEALTH_FAULT_TEST);
+ } else {
+ bt_mesh_model_msg_init(msg, OP_HEALTH_FAULT_TEST_UNREL);
+ }
+
+ net_buf_simple_add_u8(msg, test_id);
+ net_buf_simple_add_le16(msg, cid);
+
+ err = bt_mesh_model_send(health_cli->model, &ctx, msg, NULL, NULL);
+ if (err) {
+ BT_ERR("model_send() failed (err %d)", err);
+ cli_reset();
+ goto done;
+ }
+
+ if (!faults) {
+ cli_reset();
+ goto done;
+ }
+
+ err = cli_wait();
+done:
+ os_mbuf_free_chain(msg);
+ return err;
+}
+
+int bt_mesh_health_fault_clear(u16_t net_idx, u16_t addr, u16_t app_idx,
+ u16_t cid, u8_t *test_id, u8_t *faults,
+ size_t *fault_count)
+{
+ struct os_mbuf *msg = BT_MESH_MODEL_BUF(OP_HEALTH_FAULT_CLEAR, 2);
+ struct bt_mesh_msg_ctx ctx = {
+ .net_idx = net_idx,
+ .app_idx = app_idx,
+ .addr = addr,
+ .send_ttl = BT_MESH_TTL_DEFAULT,
+ };
+ struct health_fault_param param = {
+ .cid = cid,
+ .test_id = test_id,
+ .faults = faults,
+ .fault_count = fault_count,
+ };
+ int err;
+
+ err = cli_prepare(&param, OP_HEALTH_FAULT_STATUS);
+ if (err) {
+ goto done;
+ }
+
+ if (test_id) {
+ bt_mesh_model_msg_init(msg, OP_HEALTH_FAULT_CLEAR);
+ } else {
+ bt_mesh_model_msg_init(msg, OP_HEALTH_FAULT_CLEAR_UNREL);
+ }
+
+ net_buf_simple_add_le16(msg, cid);
+
+ err = bt_mesh_model_send(health_cli->model, &ctx, msg, NULL, NULL);
+ if (err) {
+ BT_ERR("model_send() failed (err %d)", err);
+ cli_reset();
+ goto done;
+ }
+
+ if (!test_id) {
+ cli_reset();
+ goto done;
+ }
+
+ err = cli_wait();
+done:
+ os_mbuf_free_chain(msg);
+ return err;
+}
+
+int bt_mesh_health_fault_get(u16_t net_idx, u16_t addr, u16_t app_idx,
+ u16_t cid, u8_t *test_id, u8_t *faults,
+ size_t *fault_count)
+{
+ struct os_mbuf *msg = BT_MESH_MODEL_BUF(OP_HEALTH_FAULT_GET, 2);
+ struct bt_mesh_msg_ctx ctx = {
+ .net_idx = net_idx,
+ .app_idx = app_idx,
+ .addr = addr,
+ .send_ttl = BT_MESH_TTL_DEFAULT,
+ };
+ struct health_fault_param param = {
+ .cid = cid,
+ .test_id = test_id,
+ .faults = faults,
+ .fault_count = fault_count,
+ };
+ int err;
+
+ err = cli_prepare(&param, OP_HEALTH_FAULT_STATUS);
+ if (err) {
+ goto done;
+ }
+
+ bt_mesh_model_msg_init(msg, OP_HEALTH_FAULT_GET);
+ net_buf_simple_add_le16(msg, cid);
+
+ err = bt_mesh_model_send(health_cli->model, &ctx, msg, NULL, NULL);
+ if (err) {
+ BT_ERR("model_send() failed (err %d)", err);
+ cli_reset();
+ goto done;
+ }
+
+ err = cli_wait();
+done:
+ os_mbuf_free_chain(msg);
+ return err;
+}
+
+s32_t bt_mesh_health_cli_timeout_get(void)
+{
+ return msg_timeout;
+}
+
+void bt_mesh_health_cli_timeout_set(s32_t timeout)
+{
+ msg_timeout = timeout;
+}
+
+int bt_mesh_health_cli_set(struct bt_mesh_model *model)
+{
+ if (!model->user_data) {
+ BT_ERR("No Health Client context for given model");
+ return -EINVAL;
+ }
+
+ health_cli = model->user_data;
+
+ return 0;
+}
+
+static int health_cli_init(struct bt_mesh_model *model)
+{
+ struct bt_mesh_health_cli *cli = model->user_data;
+
+ BT_DBG("primary %u", bt_mesh_model_in_primary(model));
+
+ if (!cli) {
+ BT_ERR("No Health Client context provided");
+ return -EINVAL;
+ }
+
+ cli = model->user_data;
+ cli->model = model;
+
+ k_sem_init(&cli->op_sync, 0, 1);
+
+ /* Set the default health client pointer */
+ if (!health_cli) {
+ health_cli = cli;
+ }
+
+ return 0;
+}
+
+const struct bt_mesh_model_cb bt_mesh_health_cli_cb = {
+ .init = health_cli_init,
+};
diff --git a/src/libs/mynewt-nimble/nimble/host/mesh/src/health_srv.c b/src/libs/mynewt-nimble/nimble/host/mesh/src/health_srv.c
new file mode 100644
index 00000000..16de83a9
--- /dev/null
+++ b/src/libs/mynewt-nimble/nimble/host/mesh/src/health_srv.c
@@ -0,0 +1,453 @@
+/* Bluetooth Mesh */
+
+/*
+ * Copyright (c) 2017 Intel Corporation
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include "syscfg/syscfg.h"
+#define MESH_LOG_MODULE BLE_MESH_MODEL_LOG
+
+#include <string.h>
+#include <errno.h>
+#include <stdbool.h>
+
+#include "mesh/mesh.h"
+#include "mesh_priv.h"
+#include "adv.h"
+#include "net.h"
+#include "transport.h"
+#include "access.h"
+#include "foundation.h"
+
+#define HEALTH_TEST_STANDARD 0x00
+
+/* Health Server context of the primary element */
+struct bt_mesh_health_srv *health_srv;
+
+static void health_get_registered(struct bt_mesh_model *mod,
+ u16_t company_id,
+ struct os_mbuf *msg)
+{
+ struct bt_mesh_health_srv *srv = mod->user_data;
+ u8_t *test_id;
+
+ BT_DBG("Company ID 0x%04x", company_id);
+
+ bt_mesh_model_msg_init(msg, OP_HEALTH_FAULT_STATUS);
+
+ test_id = net_buf_simple_add(msg, 1);
+ net_buf_simple_add_le16(msg, company_id);
+
+ if (srv->cb && srv->cb->fault_get_reg) {
+ u8_t fault_count = net_buf_simple_tailroom(msg) - 4;
+ int err;
+
+ err = srv->cb->fault_get_reg(mod, company_id, test_id,
+ net_buf_simple_tail(msg),
+ &fault_count);
+ if (err) {
+ BT_ERR("Failed to get faults (err %d)", err);
+ *test_id = HEALTH_TEST_STANDARD;
+ } else {
+ net_buf_simple_add(msg, fault_count);
+ }
+ } else {
+ BT_WARN("No callback for getting faults");
+ *test_id = HEALTH_TEST_STANDARD;
+ }
+}
+
+static size_t health_get_current(struct bt_mesh_model *mod,
+ struct os_mbuf *msg)
+{
+ struct bt_mesh_health_srv *srv = mod->user_data;
+ const struct bt_mesh_comp *comp;
+ u8_t *test_id, *company_ptr;
+ u16_t company_id;
+ u8_t fault_count;
+ int err;
+
+ bt_mesh_model_msg_init(msg, OP_HEALTH_CURRENT_STATUS);
+
+ test_id = net_buf_simple_add(msg, 1);
+ company_ptr = net_buf_simple_add(msg, sizeof(company_id));
+ comp = bt_mesh_comp_get();
+
+ if (srv->cb && srv->cb->fault_get_cur) {
+ fault_count = net_buf_simple_tailroom(msg);
+ err = srv->cb->fault_get_cur(mod, test_id, &company_id,
+ net_buf_simple_tail(msg),
+ &fault_count);
+ if (err) {
+ BT_ERR("Failed to get faults (err %d)", err);
+ sys_put_le16(comp->cid, company_ptr);
+ *test_id = HEALTH_TEST_STANDARD;
+ fault_count = 0;
+ } else {
+ sys_put_le16(company_id, company_ptr);
+ net_buf_simple_add(msg, fault_count);
+ }
+ } else {
+ BT_WARN("No callback for getting faults");
+ sys_put_le16(comp->cid, company_ptr);
+ *test_id = HEALTH_TEST_STANDARD;
+ fault_count = 0;
+ }
+
+ return fault_count;
+}
+
+static void health_fault_get(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ struct os_mbuf *sdu = NET_BUF_SIMPLE(BT_MESH_TX_SDU_MAX);
+ u16_t company_id;
+
+ company_id = net_buf_simple_pull_le16(buf);
+
+ BT_DBG("company_id 0x%04x", company_id);
+
+ health_get_registered(model, company_id, sdu);
+
+ if (bt_mesh_model_send(model, ctx, sdu, NULL, NULL)) {
+ BT_ERR("Unable to send Health Current Status response");
+ }
+
+ os_mbuf_free_chain(sdu);
+}
+
+static void health_fault_clear_unrel(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ struct bt_mesh_health_srv *srv = model->user_data;
+ u16_t company_id;
+
+ company_id = net_buf_simple_pull_le16(buf);
+
+ BT_DBG("company_id 0x%04x", company_id);
+
+ if (srv->cb && srv->cb->fault_clear) {
+ srv->cb->fault_clear(model, company_id);
+ }
+}
+
+static void health_fault_clear(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ struct os_mbuf *sdu = NET_BUF_SIMPLE(BT_MESH_TX_SDU_MAX);
+ struct bt_mesh_health_srv *srv = model->user_data;
+ u16_t company_id;
+
+ company_id = net_buf_simple_pull_le16(buf);
+
+ BT_DBG("company_id 0x%04x", company_id);
+
+ if (srv->cb && srv->cb->fault_clear) {
+ srv->cb->fault_clear(model, company_id);
+ }
+
+ health_get_registered(model, company_id, sdu);
+
+ if (bt_mesh_model_send(model, ctx, sdu, NULL, NULL)) {
+ BT_ERR("Unable to send Health Current Status response");
+ }
+
+ os_mbuf_free_chain(sdu);
+}
+
+static void health_fault_test_unrel(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ struct bt_mesh_health_srv *srv = model->user_data;
+ u16_t company_id;
+ u8_t test_id;
+
+ test_id = net_buf_simple_pull_u8(buf);
+ company_id = net_buf_simple_pull_le16(buf);
+
+ BT_DBG("test 0x%02x company 0x%04x", test_id, company_id);
+
+ if (srv->cb && srv->cb->fault_test) {
+ srv->cb->fault_test(model, test_id, company_id);
+ }
+}
+
+static void health_fault_test(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ struct os_mbuf *sdu = NET_BUF_SIMPLE(BT_MESH_TX_SDU_MAX);
+ struct bt_mesh_health_srv *srv = model->user_data;
+ u16_t company_id;
+ u8_t test_id;
+
+ BT_DBG("");
+
+ test_id = net_buf_simple_pull_u8(buf);
+ company_id = net_buf_simple_pull_le16(buf);
+
+ BT_DBG("test 0x%02x company 0x%04x", test_id, company_id);
+
+ if (srv->cb && srv->cb->fault_test) {
+ int err;
+
+ err = srv->cb->fault_test(model, test_id, company_id);
+ if (err) {
+ BT_WARN("Running fault test failed with err %d", err);
+ goto done;
+ }
+ }
+
+ health_get_registered(model, company_id, sdu);
+
+ if (bt_mesh_model_send(model, ctx, sdu, NULL, NULL)) {
+ BT_ERR("Unable to send Health Current Status response");
+ }
+
+done:
+ os_mbuf_free_chain(sdu);
+}
+
+static void send_attention_status(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx)
+{
+ struct os_mbuf *msg = BT_MESH_MODEL_BUF(OP_ATTENTION_STATUS, 1);
+ struct bt_mesh_health_srv *srv = model->user_data;
+ u8_t time;
+
+ time = k_delayed_work_remaining_get(&srv->attn_timer) / 1000;
+ BT_DBG("%u second%s", time, (time == 1) ? "" : "s");
+
+ bt_mesh_model_msg_init(msg, OP_ATTENTION_STATUS);
+
+ net_buf_simple_add_u8(msg, time);
+
+ if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) {
+ BT_ERR("Unable to send Attention Status");
+ }
+
+ os_mbuf_free_chain(msg);
+}
+
+static void attention_get(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ BT_DBG("");
+
+ send_attention_status(model, ctx);
+}
+
+static void attention_set_unrel(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ u8_t time;
+
+ time = net_buf_simple_pull_u8(buf);
+
+ BT_DBG("%u second%s", time, (time == 1) ? "" : "s");
+
+ bt_mesh_attention(model, time);
+}
+
+static void attention_set(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ BT_DBG("");
+
+ attention_set_unrel(model, ctx, buf);
+
+ send_attention_status(model, ctx);
+}
+
+static void send_health_period_status(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx)
+{
+ struct os_mbuf *msg = BT_MESH_MODEL_BUF(OP_HEALTH_PERIOD_STATUS, 1);
+
+ bt_mesh_model_msg_init(msg, OP_HEALTH_PERIOD_STATUS);
+
+ net_buf_simple_add_u8(msg, model->pub->period_div);
+
+ if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) {
+ BT_ERR("Unable to send Health Period Status");
+ }
+
+ os_mbuf_free_chain(msg);
+}
+
+static void health_period_get(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ BT_DBG("");
+
+ send_health_period_status(model, ctx);
+}
+
+static void health_period_set_unrel(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ u8_t period;
+
+ period = net_buf_simple_pull_u8(buf);
+ if (period > 15) {
+ BT_WARN("Prohibited period value %u", period);
+ return;
+ }
+
+ BT_DBG("period %u", period);
+
+ model->pub->period_div = period;
+}
+
+static void health_period_set(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ BT_DBG("");
+
+ health_period_set_unrel(model, ctx, buf);
+
+ send_health_period_status(model, ctx);
+}
+
+const struct bt_mesh_model_op bt_mesh_health_srv_op[] = {
+ { OP_HEALTH_FAULT_GET, 2, health_fault_get },
+ { OP_HEALTH_FAULT_CLEAR, 2, health_fault_clear },
+ { OP_HEALTH_FAULT_CLEAR_UNREL, 2, health_fault_clear_unrel },
+ { OP_HEALTH_FAULT_TEST, 3, health_fault_test },
+ { OP_HEALTH_FAULT_TEST_UNREL, 3, health_fault_test_unrel },
+ { OP_HEALTH_PERIOD_GET, 0, health_period_get },
+ { OP_HEALTH_PERIOD_SET, 1, health_period_set },
+ { OP_HEALTH_PERIOD_SET_UNREL, 1, health_period_set_unrel },
+ { OP_ATTENTION_GET, 0, attention_get },
+ { OP_ATTENTION_SET, 1, attention_set },
+ { OP_ATTENTION_SET_UNREL, 1, attention_set_unrel },
+ BT_MESH_MODEL_OP_END,
+};
+
+static int health_pub_update(struct bt_mesh_model *mod)
+{
+ struct bt_mesh_model_pub *pub = mod->pub;
+ size_t count;
+
+ BT_DBG("");
+
+ count = health_get_current(mod, pub->msg);
+ if (count) {
+ pub->fast_period = 1U;
+ } else {
+ pub->fast_period = 0U;
+ }
+
+ return 0;
+}
+
+int bt_mesh_fault_update(struct bt_mesh_elem *elem)
+{
+ struct bt_mesh_model *mod;
+
+ mod = bt_mesh_model_find(elem, BT_MESH_MODEL_ID_HEALTH_SRV);
+ if (!mod) {
+ return -EINVAL;
+ }
+
+ /* Let periodic publishing, if enabled, take care of sending the
+ * Health Current Status.
+ */
+ if (bt_mesh_model_pub_period_get(mod)) {
+ return 0;
+ }
+
+ health_pub_update(mod);
+
+ return bt_mesh_model_publish(mod);
+}
+
+static void attention_off(struct ble_npl_event *work)
+{
+ struct bt_mesh_health_srv *srv = ble_npl_event_get_arg(work);
+ BT_DBG("");
+
+ if (srv->cb && srv->cb->attn_off) {
+ srv->cb->attn_off(srv->model);
+ }
+}
+
+static int health_srv_init(struct bt_mesh_model *model)
+{
+ struct bt_mesh_health_srv *srv = model->user_data;
+
+ if (!srv) {
+ if (!bt_mesh_model_in_primary(model)) {
+ return 0;
+ }
+
+ BT_ERR("No Health Server context provided");
+ return -EINVAL;
+ }
+
+ if (!model->pub) {
+ BT_ERR("Health Server has no publication support");
+ return -EINVAL;
+ }
+
+ model->pub->update = health_pub_update;
+
+ k_delayed_work_init(&srv->attn_timer, attention_off);
+ k_delayed_work_add_arg(&srv->attn_timer, srv);
+
+ srv->model = model;
+
+ if (bt_mesh_model_in_primary(model)) {
+ health_srv = srv;
+ }
+
+ return 0;
+}
+
+const struct bt_mesh_model_cb bt_mesh_health_srv_cb = {
+ .init = health_srv_init,
+};
+
+void bt_mesh_attention(struct bt_mesh_model *model, u8_t time)
+{
+ struct bt_mesh_health_srv *srv;
+
+ BT_DBG("bt_mesh_attention");
+ if (!model) {
+ srv = health_srv;
+ if (!srv) {
+ BT_WARN("No Health Server available");
+ return;
+ }
+
+ model = srv->model;
+ } else {
+ srv = model->user_data;
+ }
+
+ if (time) {
+ if (srv->cb && srv->cb->attn_on) {
+ srv->cb->attn_on(model);
+ }
+
+ k_delayed_work_submit(&srv->attn_timer, time * 1000);
+ } else {
+ k_delayed_work_cancel(&srv->attn_timer);
+
+ if (srv->cb && srv->cb->attn_off) {
+ srv->cb->attn_off(model);
+ }
+ }
+}
diff --git a/src/libs/mynewt-nimble/nimble/host/mesh/src/light_model.c b/src/libs/mynewt-nimble/nimble/host/mesh/src/light_model.c
new file mode 100644
index 00000000..b6d83818
--- /dev/null
+++ b/src/libs/mynewt-nimble/nimble/host/mesh/src/light_model.c
@@ -0,0 +1,58 @@
+
+#include "syscfg/syscfg.h"
+
+#include "mesh/mesh.h"
+#include "console/console.h"
+#include "light_model.h"
+
+
+static u8_t gen_onoff_state;
+static s16_t gen_level_state;
+
+static void update_light_state(void)
+{
+ console_printf("Light state: onoff=%d lvl=0x%04x\n", gen_onoff_state, (u16_t)gen_level_state);
+}
+
+int light_model_gen_onoff_get(struct bt_mesh_model *model, u8_t *state)
+{
+ *state = gen_onoff_state;
+ return 0;
+}
+
+int light_model_gen_onoff_set(struct bt_mesh_model *model, u8_t state)
+{
+ gen_onoff_state = state;
+ update_light_state();
+ return 0;
+}
+
+int light_model_gen_level_get(struct bt_mesh_model *model, s16_t *level)
+{
+ *level = gen_level_state;
+ return 0;
+}
+
+int light_model_gen_level_set(struct bt_mesh_model *model, s16_t level)
+{
+ gen_level_state = level;
+ if ((u16_t)gen_level_state > 0x0000) {
+ gen_onoff_state = 1;
+ }
+ if ((u16_t)gen_level_state == 0x0000) {
+ gen_onoff_state = 0;
+ }
+ update_light_state();
+ return 0;
+}
+
+int light_model_light_lightness_get(struct bt_mesh_model *model, s16_t *lightness)
+{
+ return light_model_gen_level_get(model, lightness);
+}
+
+int light_model_light_lightness_set(struct bt_mesh_model *model, s16_t lightness)
+{
+ return light_model_gen_level_set(model, lightness);
+}
+
diff --git a/src/libs/mynewt-nimble/nimble/host/mesh/src/light_model.h b/src/libs/mynewt-nimble/nimble/host/mesh/src/light_model.h
new file mode 100644
index 00000000..95fcdb78
--- /dev/null
+++ b/src/libs/mynewt-nimble/nimble/host/mesh/src/light_model.h
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2017 Intel Corporation
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#ifndef __BT_MESH_LIGHT_MODEL_H
+#define __BT_MESH_LIGHT_MODEL_H
+
+#include "syscfg/syscfg.h"
+#include "mesh/mesh.h"
+
+int light_model_gen_onoff_get(struct bt_mesh_model *model, u8_t *state);
+int light_model_gen_onoff_set(struct bt_mesh_model *model, u8_t state);
+int light_model_gen_level_get(struct bt_mesh_model *model, s16_t *level);
+int light_model_gen_level_set(struct bt_mesh_model *model, s16_t level);
+int light_model_light_lightness_get(struct bt_mesh_model *model, s16_t *lightness);
+int light_model_light_lightness_set(struct bt_mesh_model *model, s16_t lightness);
+
+#endif
diff --git a/src/libs/mynewt-nimble/nimble/host/mesh/src/lpn.c b/src/libs/mynewt-nimble/nimble/host/mesh/src/lpn.c
new file mode 100644
index 00000000..ec012a5f
--- /dev/null
+++ b/src/libs/mynewt-nimble/nimble/host/mesh/src/lpn.c
@@ -0,0 +1,1056 @@
+/* Bluetooth Mesh */
+
+/*
+ * Copyright (c) 2017 Intel Corporation
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include "syscfg/syscfg.h"
+#define MESH_LOG_MODULE BLE_MESH_LOW_POWER_LOG
+
+#if MYNEWT_VAL(BLE_MESH_LOW_POWER)
+
+#include <stdint.h>
+
+#include "mesh/mesh.h"
+#include "mesh_priv.h"
+#include "crypto.h"
+#include "adv.h"
+#include "net.h"
+#include "transport.h"
+#include "access.h"
+#include "beacon.h"
+#include "foundation.h"
+#include "lpn.h"
+
+#if MYNEWT_VAL(BLE_MESH_LPN_AUTO)
+#define LPN_AUTO_TIMEOUT K_SECONDS(MYNEWT_VAL(BLE_MESH_LPN_AUTO_TIMEOUT))
+#else
+#define LPN_AUTO_TIMEOUT 0
+#endif
+
+#define LPN_RECV_DELAY MYNEWT_VAL(BLE_MESH_LPN_RECV_DELAY)
+#define SCAN_LATENCY min(MYNEWT_VAL(BLE_MESH_LPN_SCAN_LATENCY), \
+ LPN_RECV_DELAY)
+
+#define FRIEND_REQ_RETRY_TIMEOUT K_SECONDS(MYNEWT_VAL(BLE_MESH_LPN_RETRY_TIMEOUT))
+
+#define FRIEND_REQ_WAIT K_MSEC(100)
+#define FRIEND_REQ_SCAN K_SECONDS(1)
+#define FRIEND_REQ_TIMEOUT (FRIEND_REQ_WAIT + FRIEND_REQ_SCAN)
+
+#define POLL_RETRY_TIMEOUT K_MSEC(100)
+
+#define REQ_RETRY_DURATION(lpn) (4 * (LPN_RECV_DELAY + (lpn)->adv_duration + \
+ (lpn)->recv_win + POLL_RETRY_TIMEOUT))
+
+#define POLL_TIMEOUT_INIT (MYNEWT_VAL(BLE_MESH_LPN_INIT_POLL_TIMEOUT) * 100)
+#define POLL_TIMEOUT_MAX(lpn) ((MYNEWT_VAL(BLE_MESH_LPN_POLL_TIMEOUT) * 100) - \
+ REQ_RETRY_DURATION(lpn))
+#define REQ_ATTEMPTS(lpn) (POLL_TIMEOUT_MAX(lpn) < K_SECONDS(3) ? 2 : 4)
+
+#define CLEAR_ATTEMPTS 2
+
+#define LPN_CRITERIA ((MYNEWT_VAL(BLE_MESH_LPN_MIN_QUEUE_SIZE)) | \
+ (MYNEWT_VAL(BLE_MESH_LPN_RSSI_FACTOR) << 3) | \
+ (MYNEWT_VAL(BLE_MESH_LPN_RECV_WIN_FACTOR) << 5))
+
+#define POLL_TO(to) { (u8_t)((to) >> 16), (u8_t)((to) >> 8), (u8_t)(to) }
+#define LPN_POLL_TO POLL_TO(MYNEWT_VAL(BLE_MESH_LPN_POLL_TIMEOUT))
+
+/* 2 transmissions, 20ms interval */
+#define POLL_XMIT BT_MESH_TRANSMIT(1, 20)
+
+static void (*lpn_cb)(u16_t friend_addr, bool established);
+
+#if MYNEWT_VAL(BLE_MESH_LOW_POWER_LOG_LVL) == LOG_LEVEL_DEBUG
+static const char *state2str(int state)
+{
+ switch (state) {
+ case BT_MESH_LPN_DISABLED:
+ return "disabled";
+ case BT_MESH_LPN_CLEAR:
+ return "clear";
+ case BT_MESH_LPN_TIMER:
+ return "timer";
+ case BT_MESH_LPN_ENABLED:
+ return "enabled";
+ case BT_MESH_LPN_REQ_WAIT:
+ return "req wait";
+ case BT_MESH_LPN_WAIT_OFFER:
+ return "wait offer";
+ case BT_MESH_LPN_ESTABLISHED:
+ return "established";
+ case BT_MESH_LPN_RECV_DELAY:
+ return "recv delay";
+ case BT_MESH_LPN_WAIT_UPDATE:
+ return "wait update";
+ default:
+ return "(unknown)";
+ }
+}
+#endif
+
+static inline void lpn_set_state(int state)
+{
+#if MYNEWT_VAL(BLE_MESH_LOW_POWER_LOG_LVL) == LOG_LEVEL_DEBUG
+ BT_DBG("%s -> %s", state2str(bt_mesh.lpn.state), state2str(state));
+#endif
+ bt_mesh.lpn.state = state;
+}
+
+static inline void group_zero(atomic_t *target)
+{
+#if CONFIG_BT_MESH_LPN_GROUPS > 32
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(bt_mesh.lpn.added); i++) {
+ atomic_set(&target[i], 0);
+ }
+#else
+ atomic_set(target, 0);
+#endif
+}
+
+static inline void group_set(atomic_t *target, atomic_t *source)
+{
+#if CONFIG_BT_MESH_LPN_GROUPS > 32
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(bt_mesh.lpn.added); i++) {
+ atomic_or(&target[i], atomic_get(&source[i]));
+ }
+#else
+ atomic_or(target, atomic_get(source));
+#endif
+}
+
+static inline void group_clear(atomic_t *target, atomic_t *source)
+{
+#if CONFIG_BT_MESH_LPN_GROUPS > 32
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(bt_mesh.lpn.added); i++) {
+ atomic_and(&target[i], ~atomic_get(&source[i]));
+ }
+#else
+ atomic_and(target, ~atomic_get(source));
+#endif
+}
+
+static void clear_friendship(bool force, bool disable);
+
+static void friend_clear_sent(int err, void *user_data)
+{
+ struct bt_mesh_lpn *lpn = &bt_mesh.lpn;
+
+ /* We're switching away from Low Power behavior, so permanently
+ * enable scanning.
+ */
+ bt_mesh_scan_enable();
+
+ lpn->req_attempts++;
+
+ if (err) {
+ BT_ERR("Sending Friend Request failed (err %d)", err);
+ lpn_set_state(BT_MESH_LPN_ENABLED);
+ clear_friendship(false, lpn->disable);
+ return;
+ }
+
+ lpn_set_state(BT_MESH_LPN_CLEAR);
+ k_delayed_work_submit(&lpn->timer, FRIEND_REQ_TIMEOUT);
+}
+
+static const struct bt_mesh_send_cb clear_sent_cb = {
+ .end = friend_clear_sent,
+};
+
+static int send_friend_clear(void)
+{
+ struct bt_mesh_msg_ctx ctx = {
+ .net_idx = bt_mesh.sub[0].net_idx,
+ .app_idx = BT_MESH_KEY_UNUSED,
+ .addr = bt_mesh.lpn.frnd,
+ .send_ttl = 0,
+ };
+ struct bt_mesh_net_tx tx = {
+ .sub = &bt_mesh.sub[0],
+ .ctx = &ctx,
+ .src = bt_mesh_primary_addr(),
+ .xmit = bt_mesh_net_transmit_get(),
+ };
+ struct bt_mesh_ctl_friend_clear req = {
+ .lpn_addr = sys_cpu_to_be16(tx.src),
+ .lpn_counter = sys_cpu_to_be16(bt_mesh.lpn.counter),
+ };
+
+ BT_DBG("");
+
+ return bt_mesh_ctl_send(&tx, TRANS_CTL_OP_FRIEND_CLEAR, &req,
+ sizeof(req), NULL, &clear_sent_cb, NULL);
+}
+
+static void clear_friendship(bool force, bool disable)
+{
+ struct bt_mesh_cfg_srv *cfg = bt_mesh_cfg_get();
+ struct bt_mesh_lpn *lpn = &bt_mesh.lpn;
+
+ BT_DBG("force %u disable %u", force, disable);
+
+ if (!force && lpn->established && !lpn->clear_success &&
+ lpn->req_attempts < CLEAR_ATTEMPTS) {
+ send_friend_clear();
+ lpn->disable = disable;
+ return;
+ }
+
+ bt_mesh_rx_reset();
+
+ k_delayed_work_cancel(&lpn->timer);
+
+ friend_cred_del(bt_mesh.sub[0].net_idx, lpn->frnd);
+
+ if (lpn->clear_success) {
+ lpn->old_friend = BT_MESH_ADDR_UNASSIGNED;
+ } else {
+ lpn->old_friend = lpn->frnd;
+ }
+
+ if (lpn_cb && lpn->frnd != BT_MESH_ADDR_UNASSIGNED) {
+ lpn_cb(lpn->frnd, false);
+ }
+
+ lpn->frnd = BT_MESH_ADDR_UNASSIGNED;
+ lpn->fsn = 0;
+ lpn->req_attempts = 0;
+ lpn->recv_win = 0;
+ lpn->queue_size = 0;
+ lpn->disable = 0;
+ lpn->sent_req = 0;
+ lpn->established = 0;
+ lpn->clear_success = 0;
+
+ group_zero(lpn->added);
+ group_zero(lpn->pending);
+ group_zero(lpn->to_remove);
+
+ /* Set this to 1 to force group subscription when the next
+ * Friendship is created, in case lpn->groups doesn't get
+ * modified meanwhile.
+ */
+ lpn->groups_changed = 1;
+
+ if (cfg->hb_pub.feat & BT_MESH_FEAT_LOW_POWER) {
+ bt_mesh_heartbeat_send();
+ }
+
+ if (disable) {
+ lpn_set_state(BT_MESH_LPN_DISABLED);
+ return;
+ }
+
+ lpn_set_state(BT_MESH_LPN_ENABLED);
+ k_delayed_work_submit(&lpn->timer, FRIEND_REQ_RETRY_TIMEOUT);
+}
+
+static void friend_req_sent(u16_t duration, int err, void *user_data)
+{
+ struct bt_mesh_lpn *lpn = &bt_mesh.lpn;
+
+ if (err) {
+ BT_ERR("Sending Friend Request failed (err %d)", err);
+ return;
+ }
+
+ lpn->adv_duration = duration;
+
+ if (IS_ENABLED(CONFIG_BT_MESH_LPN_ESTABLISHMENT)) {
+ k_delayed_work_submit(&lpn->timer, FRIEND_REQ_WAIT);
+ lpn_set_state(BT_MESH_LPN_REQ_WAIT);
+ } else {
+ k_delayed_work_submit(&lpn->timer,
+ duration + FRIEND_REQ_TIMEOUT);
+ lpn_set_state(BT_MESH_LPN_WAIT_OFFER);
+ }
+}
+
+static const struct bt_mesh_send_cb friend_req_sent_cb = {
+ .start = friend_req_sent,
+};
+
+static int send_friend_req(struct bt_mesh_lpn *lpn)
+{
+ const struct bt_mesh_comp *comp = bt_mesh_comp_get();
+ struct bt_mesh_msg_ctx ctx = {
+ .net_idx = bt_mesh.sub[0].net_idx,
+ .app_idx = BT_MESH_KEY_UNUSED,
+ .addr = BT_MESH_ADDR_FRIENDS,
+ .send_ttl = 0,
+ };
+ struct bt_mesh_net_tx tx = {
+ .sub = &bt_mesh.sub[0],
+ .ctx = &ctx,
+ .src = bt_mesh_primary_addr(),
+ .xmit = POLL_XMIT,
+ };
+ struct bt_mesh_ctl_friend_req req = {
+ .criteria = LPN_CRITERIA,
+ .recv_delay = LPN_RECV_DELAY,
+ .poll_to = LPN_POLL_TO,
+ .prev_addr = lpn->old_friend,
+ .num_elem = comp->elem_count,
+ .lpn_counter = sys_cpu_to_be16(lpn->counter),
+ };
+
+ BT_DBG("");
+
+ return bt_mesh_ctl_send(&tx, TRANS_CTL_OP_FRIEND_REQ, &req,
+ sizeof(req), NULL, &friend_req_sent_cb, NULL);
+}
+
+static void req_sent(u16_t duration, int err, void *user_data)
+{
+ struct bt_mesh_lpn *lpn = &bt_mesh.lpn;
+
+#if MYNEWT_VAL(BLE_MESH_LOW_POWER_LOG_LVL) == LOG_LEVEL_DEBUG
+ BT_DBG("req 0x%02x duration %u err %d state %s",
+ lpn->sent_req, duration, err, state2str(lpn->state));
+#endif
+
+ if (err) {
+ BT_ERR("Sending request failed (err %d)", err);
+ lpn->sent_req = 0;
+ group_zero(lpn->pending);
+ return;
+ }
+
+ lpn->req_attempts++;
+ lpn->adv_duration = duration;
+
+ if (lpn->established || IS_ENABLED(CONFIG_BT_MESH_LPN_ESTABLISHMENT)) {
+ lpn_set_state(BT_MESH_LPN_RECV_DELAY);
+ /* We start scanning a bit early to elimitate risk of missing
+ * response data due to HCI and other latencies.
+ */
+ k_delayed_work_submit(&lpn->timer,
+ LPN_RECV_DELAY - SCAN_LATENCY);
+ } else {
+ k_delayed_work_submit(&lpn->timer,
+ LPN_RECV_DELAY + duration +
+ lpn->recv_win);
+ }
+}
+
+static const struct bt_mesh_send_cb req_sent_cb = {
+ .start = req_sent,
+};
+
+static int send_friend_poll(void)
+{
+ struct bt_mesh_msg_ctx ctx = {
+ .net_idx = bt_mesh.sub[0].net_idx,
+ .app_idx = BT_MESH_KEY_UNUSED,
+ .addr = bt_mesh.lpn.frnd,
+ .send_ttl = 0,
+ };
+ struct bt_mesh_net_tx tx = {
+ .sub = &bt_mesh.sub[0],
+ .ctx = &ctx,
+ .src = bt_mesh_primary_addr(),
+ .xmit = POLL_XMIT,
+ .friend_cred = true,
+ };
+ struct bt_mesh_lpn *lpn = &bt_mesh.lpn;
+ u8_t fsn = lpn->fsn;
+ int err;
+
+ BT_DBG("lpn->sent_req 0x%02x", lpn->sent_req);
+
+ if (lpn->sent_req) {
+ if (lpn->sent_req != TRANS_CTL_OP_FRIEND_POLL) {
+ lpn->pending_poll = 1;
+ }
+
+ return 0;
+ }
+
+ err = bt_mesh_ctl_send(&tx, TRANS_CTL_OP_FRIEND_POLL, &fsn, 1,
+ NULL, &req_sent_cb, NULL);
+ if (err == 0) {
+ lpn->pending_poll = 0;
+ lpn->sent_req = TRANS_CTL_OP_FRIEND_POLL;
+ }
+
+ return err;
+}
+
+void bt_mesh_lpn_disable(bool force)
+{
+ if (bt_mesh.lpn.state == BT_MESH_LPN_DISABLED) {
+ return;
+ }
+
+ clear_friendship(force, true);
+}
+
+int bt_mesh_lpn_set(bool enable)
+{
+ struct bt_mesh_lpn *lpn = &bt_mesh.lpn;
+
+ if (enable) {
+ if (lpn->state != BT_MESH_LPN_DISABLED) {
+ return 0;
+ }
+ } else {
+ if (lpn->state == BT_MESH_LPN_DISABLED) {
+ return 0;
+ }
+ }
+
+ if (!bt_mesh_is_provisioned()) {
+ if (enable) {
+ lpn_set_state(BT_MESH_LPN_ENABLED);
+ } else {
+ lpn_set_state(BT_MESH_LPN_DISABLED);
+ }
+
+ return 0;
+ }
+
+ if (enable) {
+ lpn_set_state(BT_MESH_LPN_ENABLED);
+
+ if (IS_ENABLED(CONFIG_BT_MESH_LPN_ESTABLISHMENT)) {
+ bt_mesh_scan_disable();
+ }
+
+ send_friend_req(lpn);
+ } else {
+ if (IS_ENABLED(CONFIG_BT_MESH_LPN_AUTO) &&
+ lpn->state == BT_MESH_LPN_TIMER) {
+ k_delayed_work_cancel(&lpn->timer);
+ lpn_set_state(BT_MESH_LPN_DISABLED);
+ } else {
+ bt_mesh_lpn_disable(false);
+ }
+ }
+
+ return 0;
+}
+
+static void friend_response_received(struct bt_mesh_lpn *lpn)
+{
+ BT_DBG("lpn->sent_req 0x%02x", lpn->sent_req);
+
+ if (lpn->sent_req == TRANS_CTL_OP_FRIEND_POLL) {
+ lpn->fsn++;
+ }
+
+ k_delayed_work_cancel(&lpn->timer);
+ bt_mesh_scan_disable();
+ lpn_set_state(BT_MESH_LPN_ESTABLISHED);
+ lpn->req_attempts = 0;
+ lpn->sent_req = 0;
+}
+
+void bt_mesh_lpn_msg_received(struct bt_mesh_net_rx *rx)
+{
+ struct bt_mesh_lpn *lpn = &bt_mesh.lpn;
+
+ if (lpn->state == BT_MESH_LPN_TIMER) {
+ BT_DBG("Restarting establishment timer");
+ k_delayed_work_submit(&lpn->timer, LPN_AUTO_TIMEOUT);
+ return;
+ }
+
+ if (lpn->sent_req != TRANS_CTL_OP_FRIEND_POLL) {
+ BT_WARN("Unexpected message withouth a preceding Poll");
+ return;
+ }
+
+ friend_response_received(lpn);
+
+ BT_DBG("Requesting more messages from Friend");
+
+ send_friend_poll();
+}
+
+int bt_mesh_lpn_friend_offer(struct bt_mesh_net_rx *rx,
+ struct os_mbuf *buf)
+{
+ struct bt_mesh_ctl_friend_offer *msg = (void *)buf->om_data;
+ struct bt_mesh_lpn *lpn = &bt_mesh.lpn;
+ struct bt_mesh_subnet *sub = rx->sub;
+ struct friend_cred *cred;
+ u16_t frnd_counter;
+ int err;
+
+ if (buf->om_len < sizeof(*msg)) {
+ BT_WARN("Too short Friend Offer");
+ return -EINVAL;
+ }
+
+ if (lpn->state != BT_MESH_LPN_WAIT_OFFER) {
+ BT_WARN("Ignoring unexpected Friend Offer");
+ return 0;
+ }
+
+ if (!msg->recv_win) {
+ BT_WARN("Prohibited ReceiveWindow value");
+ return -EINVAL;
+ }
+
+ frnd_counter = sys_be16_to_cpu(msg->frnd_counter);
+
+ BT_DBG("recv_win %u queue_size %u sub_list_size %u rssi %d counter %u",
+ msg->recv_win, msg->queue_size, msg->sub_list_size, msg->rssi,
+ frnd_counter);
+
+ lpn->frnd = rx->ctx.addr;
+
+ cred = friend_cred_create(sub, lpn->frnd, lpn->counter, frnd_counter);
+ if (!cred) {
+ lpn->frnd = BT_MESH_ADDR_UNASSIGNED;
+ return -ENOMEM;
+ }
+
+ /* TODO: Add offer acceptance criteria check */
+
+ k_delayed_work_cancel(&lpn->timer);
+
+ lpn->recv_win = msg->recv_win;
+ lpn->queue_size = msg->queue_size;
+
+ err = send_friend_poll();
+ if (err) {
+ friend_cred_clear(cred);
+ lpn->frnd = BT_MESH_ADDR_UNASSIGNED;
+ lpn->recv_win = 0;
+ lpn->queue_size = 0;
+ return err;
+ }
+
+ lpn->counter++;
+
+ return 0;
+}
+
+int bt_mesh_lpn_friend_clear_cfm(struct bt_mesh_net_rx *rx,
+ struct os_mbuf *buf)
+{
+ struct bt_mesh_ctl_friend_clear_confirm *msg = (void *)buf->om_data;
+ struct bt_mesh_lpn *lpn = &bt_mesh.lpn;
+ u16_t addr, counter;
+
+ if (buf->om_len < sizeof(*msg)) {
+ BT_WARN("Too short Friend Clear Confirm");
+ return -EINVAL;
+ }
+
+ if (lpn->state != BT_MESH_LPN_CLEAR) {
+ BT_WARN("Ignoring unexpected Friend Clear Confirm");
+ return 0;
+ }
+
+ addr = sys_be16_to_cpu(msg->lpn_addr);
+ counter = sys_be16_to_cpu(msg->lpn_counter);
+
+ BT_DBG("LPNAddress 0x%04x LPNCounter 0x%04x", addr, counter);
+
+ if (addr != bt_mesh_primary_addr() || counter != lpn->counter) {
+ BT_WARN("Invalid parameters in Friend Clear Confirm");
+ return 0;
+ }
+
+ lpn->clear_success = 1;
+ clear_friendship(false, lpn->disable);
+
+ return 0;
+}
+
+static void lpn_group_add(u16_t group)
+{
+ struct bt_mesh_lpn *lpn = &bt_mesh.lpn;
+ u16_t *free_slot = NULL;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(lpn->groups); i++) {
+ if (lpn->groups[i] == group) {
+ atomic_clear_bit(lpn->to_remove, i);
+ return;
+ }
+
+ if (!free_slot && lpn->groups[i] == BT_MESH_ADDR_UNASSIGNED) {
+ free_slot = &lpn->groups[i];
+ }
+ }
+
+ if (!free_slot) {
+ BT_WARN("Friend Subscription List exceeded!");
+ return;
+ }
+
+ *free_slot = group;
+ lpn->groups_changed = 1;
+}
+
+static void lpn_group_del(u16_t group)
+{
+ struct bt_mesh_lpn *lpn = &bt_mesh.lpn;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(lpn->groups); i++) {
+ if (lpn->groups[i] == group) {
+ if (atomic_test_bit(lpn->added, i) ||
+ atomic_test_bit(lpn->pending, i)) {
+ atomic_set_bit(lpn->to_remove, i);
+ lpn->groups_changed = 1;
+ } else {
+ lpn->groups[i] = BT_MESH_ADDR_UNASSIGNED;
+ }
+ }
+ }
+}
+
+static inline int group_popcount(atomic_t *target)
+{
+#if CONFIG_BT_MESH_LPN_GROUPS > 32
+ int i, count = 0;
+
+ for (i = 0; i < ARRAY_SIZE(bt_mesh.lpn.added); i++) {
+ count += popcount(atomic_get(&target[i]));
+ }
+#else
+ return popcount(atomic_get(target));
+#endif
+}
+
+static bool sub_update(u8_t op)
+{
+ struct bt_mesh_lpn *lpn = &bt_mesh.lpn;
+ int added_count = group_popcount(lpn->added);
+ struct bt_mesh_msg_ctx ctx = {
+ .net_idx = bt_mesh.sub[0].net_idx,
+ .app_idx = BT_MESH_KEY_UNUSED,
+ .addr = lpn->frnd,
+ .send_ttl = 0,
+ };
+ struct bt_mesh_net_tx tx = {
+ .sub = &bt_mesh.sub[0],
+ .ctx = &ctx,
+ .src = bt_mesh_primary_addr(),
+ .xmit = POLL_XMIT,
+ .friend_cred = true,
+ };
+ struct bt_mesh_ctl_friend_sub req;
+ size_t i, g;
+
+ BT_DBG("op 0x%02x sent_req 0x%02x", op, lpn->sent_req);
+
+ if (lpn->sent_req) {
+ return false;
+ }
+
+ for (i = 0, g = 0; i < ARRAY_SIZE(lpn->groups); i++) {
+ if (lpn->groups[i] == BT_MESH_ADDR_UNASSIGNED) {
+ continue;
+ }
+
+ if (op == TRANS_CTL_OP_FRIEND_SUB_ADD) {
+ if (atomic_test_bit(lpn->added, i)) {
+ continue;
+ }
+ } else {
+ if (!atomic_test_bit(lpn->to_remove, i)) {
+ continue;
+ }
+ }
+
+ if (added_count + g >= lpn->queue_size) {
+ BT_WARN("Friend Queue Size exceeded");
+ break;
+ }
+
+ req.addr_list[g++] = sys_cpu_to_be16(lpn->groups[i]);
+ atomic_set_bit(lpn->pending, i);
+
+ if (g == ARRAY_SIZE(req.addr_list)) {
+ break;
+ }
+ }
+
+ if (g == 0) {
+ group_zero(lpn->pending);
+ return false;
+ }
+
+ req.xact = lpn->xact_next++;
+
+ if (bt_mesh_ctl_send(&tx, op, &req, 1 + g * 2, NULL,
+ &req_sent_cb, NULL) < 0) {
+ group_zero(lpn->pending);
+ return false;
+ }
+
+ lpn->xact_pending = req.xact;
+ lpn->sent_req = op;
+ return true;
+}
+
+static void update_timeout(struct bt_mesh_lpn *lpn)
+{
+ if (lpn->established) {
+ BT_WARN("No response from Friend during ReceiveWindow");
+ bt_mesh_scan_disable();
+ lpn_set_state(BT_MESH_LPN_ESTABLISHED);
+ k_delayed_work_submit(&lpn->timer, POLL_RETRY_TIMEOUT);
+ } else {
+ if (IS_ENABLED(CONFIG_BT_MESH_LPN_ESTABLISHMENT)) {
+ bt_mesh_scan_disable();
+ }
+
+ if (lpn->req_attempts < 6) {
+ BT_WARN("Retrying first Friend Poll");
+ lpn->sent_req = 0;
+ if (send_friend_poll() == 0) {
+ return;
+ }
+ }
+
+ BT_ERR("Timed out waiting for first Friend Update");
+ clear_friendship(false, false);
+ }
+}
+
+static void lpn_timeout(struct ble_npl_event *work)
+{
+ struct bt_mesh_lpn *lpn = &bt_mesh.lpn;
+
+#if MYNEWT_VAL(BLE_MESH_LOW_POWER_LOG_LVL) == LOG_LEVEL_DEBUG
+ BT_DBG("state: %s", state2str(lpn->state));
+#endif
+
+ switch (lpn->state) {
+ case BT_MESH_LPN_DISABLED:
+ break;
+ case BT_MESH_LPN_CLEAR:
+ clear_friendship(false, bt_mesh.lpn.disable);
+ break;
+ case BT_MESH_LPN_TIMER:
+ BT_DBG("Starting to look for Friend nodes");
+ lpn_set_state(BT_MESH_LPN_ENABLED);
+ if (IS_ENABLED(CONFIG_BT_MESH_LPN_ESTABLISHMENT)) {
+ bt_mesh_scan_disable();
+ }
+ /* fall through */
+ case BT_MESH_LPN_ENABLED:
+ send_friend_req(lpn);
+ break;
+ case BT_MESH_LPN_REQ_WAIT:
+ bt_mesh_scan_enable();
+ k_delayed_work_submit(&lpn->timer,
+ lpn->adv_duration + FRIEND_REQ_SCAN);
+ lpn_set_state(BT_MESH_LPN_WAIT_OFFER);
+ break;
+ case BT_MESH_LPN_WAIT_OFFER:
+ BT_WARN("No acceptable Friend Offers received");
+ if (IS_ENABLED(CONFIG_BT_MESH_LPN_ESTABLISHMENT)) {
+ bt_mesh_scan_disable();
+ }
+ lpn->counter++;
+ lpn_set_state(BT_MESH_LPN_ENABLED);
+ lpn->sent_req = 0U;
+ k_delayed_work_submit(&lpn->timer, FRIEND_REQ_RETRY_TIMEOUT);
+ break;
+ case BT_MESH_LPN_ESTABLISHED:
+ if (lpn->req_attempts < REQ_ATTEMPTS(lpn)) {
+ u8_t req = lpn->sent_req;
+
+ lpn->sent_req = 0;
+
+ if (!req || req == TRANS_CTL_OP_FRIEND_POLL) {
+ send_friend_poll();
+ } else {
+ sub_update(req);
+ }
+
+ break;
+ }
+
+ BT_ERR("No response from Friend after %u retries",
+ lpn->req_attempts);
+ lpn->req_attempts = 0;
+ clear_friendship(false, false);
+ break;
+ case BT_MESH_LPN_RECV_DELAY:
+ k_delayed_work_submit(&lpn->timer,
+ lpn->adv_duration + SCAN_LATENCY +
+ lpn->recv_win);
+ bt_mesh_scan_enable();
+ lpn_set_state(BT_MESH_LPN_WAIT_UPDATE);
+ break;
+ case BT_MESH_LPN_WAIT_UPDATE:
+ update_timeout(lpn);
+ break;
+ default:
+ __ASSERT(0, "Unhandled LPN state");
+ break;
+ }
+}
+
+void bt_mesh_lpn_group_add(u16_t group)
+{
+ BT_DBG("group 0x%04x", group);
+
+ lpn_group_add(group);
+
+ if (!bt_mesh_lpn_established() || bt_mesh.lpn.sent_req) {
+ return;
+ }
+
+ sub_update(TRANS_CTL_OP_FRIEND_SUB_ADD);
+}
+
+void bt_mesh_lpn_group_del(u16_t *groups, size_t group_count)
+{
+ int i;
+
+ for (i = 0; i < group_count; i++) {
+ if (groups[i] != BT_MESH_ADDR_UNASSIGNED) {
+ BT_DBG("group 0x%04x", groups[i]);
+ lpn_group_del(groups[i]);
+ }
+ }
+
+ if (!bt_mesh_lpn_established() || bt_mesh.lpn.sent_req) {
+ return;
+ }
+
+ sub_update(TRANS_CTL_OP_FRIEND_SUB_REM);
+}
+
+static s32_t poll_timeout(struct bt_mesh_lpn *lpn)
+{
+ /* If we're waiting for segment acks keep polling at high freq */
+ if (bt_mesh_tx_in_progress()) {
+ return min(POLL_TIMEOUT_MAX(lpn), K_SECONDS(1));
+ }
+
+ if (lpn->poll_timeout < POLL_TIMEOUT_MAX(lpn)) {
+ lpn->poll_timeout *= 2;
+ lpn->poll_timeout = min(lpn->poll_timeout,
+ POLL_TIMEOUT_MAX(lpn));
+ }
+
+ BT_DBG("Poll Timeout is %ums", (unsigned) lpn->poll_timeout);
+
+ return lpn->poll_timeout;
+}
+
+int bt_mesh_lpn_friend_sub_cfm(struct bt_mesh_net_rx *rx,
+ struct os_mbuf *buf)
+{
+ struct bt_mesh_ctl_friend_sub_confirm *msg = (void *)buf->om_data;
+ struct bt_mesh_lpn *lpn = &bt_mesh.lpn;
+
+ if (buf->om_len < sizeof(*msg)) {
+ BT_WARN("Too short Friend Subscription Confirm");
+ return -EINVAL;
+ }
+
+ BT_DBG("xact 0x%02x", msg->xact);
+
+ if (!lpn->sent_req) {
+ BT_WARN("No pending subscription list message");
+ return 0;
+ }
+
+ if (msg->xact != lpn->xact_pending) {
+ BT_WARN("Transaction mismatch (0x%02x != 0x%02x)",
+ msg->xact, lpn->xact_pending);
+ return 0;
+ }
+
+ if (lpn->sent_req == TRANS_CTL_OP_FRIEND_SUB_ADD) {
+ group_set(lpn->added, lpn->pending);
+ group_zero(lpn->pending);
+ } else if (lpn->sent_req == TRANS_CTL_OP_FRIEND_SUB_REM) {
+ int i;
+
+ group_clear(lpn->added, lpn->pending);
+
+ for (i = 0; i < ARRAY_SIZE(lpn->groups); i++) {
+ if (atomic_test_and_clear_bit(lpn->pending, i) &&
+ atomic_test_and_clear_bit(lpn->to_remove, i)) {
+ lpn->groups[i] = BT_MESH_ADDR_UNASSIGNED;
+ }
+ }
+ } else {
+ BT_WARN("Unexpected Friend Subscription Confirm");
+ return 0;
+ }
+
+ friend_response_received(lpn);
+
+ if (lpn->groups_changed) {
+ sub_update(TRANS_CTL_OP_FRIEND_SUB_ADD);
+ sub_update(TRANS_CTL_OP_FRIEND_SUB_REM);
+
+ if (!lpn->sent_req) {
+ lpn->groups_changed = 0;
+ }
+ }
+
+ if (lpn->pending_poll) {
+ send_friend_poll();
+ }
+
+ if (!lpn->sent_req) {
+ k_delayed_work_submit(&lpn->timer, poll_timeout(lpn));
+ }
+
+ return 0;
+}
+
+int bt_mesh_lpn_friend_update(struct bt_mesh_net_rx *rx,
+ struct os_mbuf *buf)
+{
+ struct bt_mesh_ctl_friend_update *msg = (void *)buf->om_data;
+ struct bt_mesh_lpn *lpn = &bt_mesh.lpn;
+ struct bt_mesh_subnet *sub = rx->sub;
+ u32_t iv_index;
+
+ if (buf->om_len < sizeof(*msg)) {
+ BT_WARN("Too short Friend Update");
+ return -EINVAL;
+ }
+
+ if (lpn->sent_req != TRANS_CTL_OP_FRIEND_POLL) {
+ BT_WARN("Unexpected friend update");
+ return 0;
+ }
+
+ if (sub->kr_phase == BT_MESH_KR_PHASE_2 && !rx->new_key) {
+ BT_WARN("Ignoring Phase 2 KR Update secured using old key");
+ return 0;
+ }
+
+ if (atomic_test_bit(bt_mesh.flags, BT_MESH_IVU_INITIATOR) &&
+ (atomic_test_bit(bt_mesh.flags, BT_MESH_IVU_IN_PROGRESS) ==
+ BT_MESH_IV_UPDATE(msg->flags))) {
+ bt_mesh_beacon_ivu_initiator(false);
+ }
+
+ if (!lpn->established) {
+ struct bt_mesh_cfg_srv *cfg = bt_mesh_cfg_get();
+
+ /* This is normally checked on the transport layer, however
+ * in this state we're also still accepting master
+ * credentials so we need to ensure the right ones (Friend
+ * Credentials) were used for this message.
+ */
+ if (!rx->friend_cred) {
+ BT_WARN("Friend Update with wrong credentials");
+ return -EINVAL;
+ }
+
+ lpn->established = 1;
+
+ BT_INFO("Friendship established with 0x%04x", lpn->frnd);
+
+ if (cfg->hb_pub.feat & BT_MESH_FEAT_LOW_POWER) {
+ bt_mesh_heartbeat_send();
+ }
+
+ if (lpn_cb) {
+ lpn_cb(lpn->frnd, true);
+ }
+
+ /* Set initial poll timeout */
+ lpn->poll_timeout = min(POLL_TIMEOUT_MAX(lpn),
+ POLL_TIMEOUT_INIT);
+ }
+
+ friend_response_received(lpn);
+
+ iv_index = sys_be32_to_cpu(msg->iv_index);
+
+ BT_DBG("flags 0x%02x iv_index 0x%08x md %u", msg->flags,
+ (unsigned) iv_index, msg->md);
+
+ if (bt_mesh_kr_update(sub, BT_MESH_KEY_REFRESH(msg->flags),
+ rx->new_key)) {
+ bt_mesh_net_beacon_update(sub);
+ }
+
+ bt_mesh_net_iv_update(iv_index, BT_MESH_IV_UPDATE(msg->flags));
+
+ if (lpn->groups_changed) {
+ sub_update(TRANS_CTL_OP_FRIEND_SUB_ADD);
+ sub_update(TRANS_CTL_OP_FRIEND_SUB_REM);
+
+ if (!lpn->sent_req) {
+ lpn->groups_changed = 0;
+ }
+ }
+
+ if (msg->md) {
+ BT_DBG("Requesting for more messages");
+ send_friend_poll();
+ }
+
+ if (!lpn->sent_req) {
+ k_delayed_work_submit(&lpn->timer, poll_timeout(lpn));
+ }
+
+ return 0;
+}
+
+int bt_mesh_lpn_poll(void)
+{
+ if (!bt_mesh.lpn.established) {
+ return -EAGAIN;
+ }
+
+ BT_DBG("Requesting more messages");
+
+ return send_friend_poll();
+}
+
+void bt_mesh_lpn_set_cb(void (*cb)(u16_t friend_addr, bool established))
+{
+ lpn_cb = cb;
+}
+
+int bt_mesh_lpn_init(void)
+{
+ struct bt_mesh_lpn *lpn = &bt_mesh.lpn;
+
+ BT_DBG("");
+
+ k_delayed_work_init(&lpn->timer, lpn_timeout);
+
+ if (lpn->state == BT_MESH_LPN_ENABLED) {
+ if (IS_ENABLED(CONFIG_BT_MESH_LPN_ESTABLISHMENT)) {
+ bt_mesh_scan_disable();
+ } else {
+ bt_mesh_scan_enable();
+ }
+
+ send_friend_req(lpn);
+ } else {
+ bt_mesh_scan_enable();
+
+ if (IS_ENABLED(CONFIG_BT_MESH_LPN_AUTO)) {
+ BT_DBG("Waiting %u ms for messages", LPN_AUTO_TIMEOUT);
+ lpn_set_state(BT_MESH_LPN_TIMER);
+ k_delayed_work_submit(&lpn->timer, LPN_AUTO_TIMEOUT);
+ }
+ }
+
+ return 0;
+}
+
+#endif /* MYNEWT_VAL(BLE_MESH_LOW_POWER) */
diff --git a/src/libs/mynewt-nimble/nimble/host/mesh/src/lpn.h b/src/libs/mynewt-nimble/nimble/host/mesh/src/lpn.h
new file mode 100644
index 00000000..0ff6c9cf
--- /dev/null
+++ b/src/libs/mynewt-nimble/nimble/host/mesh/src/lpn.h
@@ -0,0 +1,68 @@
+/* Bluetooth Mesh */
+
+/*
+ * Copyright (c) 2017 Intel Corporation
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#ifndef __LPN_H__
+#define __LPN_H__
+
+#include "mesh/mesh.h"
+
+int bt_mesh_lpn_friend_update(struct bt_mesh_net_rx *rx,
+ struct os_mbuf *buf);
+int bt_mesh_lpn_friend_offer(struct bt_mesh_net_rx *rx,
+ struct os_mbuf *buf);
+int bt_mesh_lpn_friend_clear_cfm(struct bt_mesh_net_rx *rx,
+ struct os_mbuf *buf);
+int bt_mesh_lpn_friend_sub_cfm(struct bt_mesh_net_rx *rx,
+ struct os_mbuf *buf);
+
+static inline bool bt_mesh_lpn_established(void)
+{
+#if (MYNEWT_VAL(BLE_MESH_LOW_POWER))
+ return bt_mesh.lpn.established;
+#else
+ return false;
+#endif
+}
+
+static inline bool bt_mesh_lpn_match(u16_t addr)
+{
+#if (MYNEWT_VAL(BLE_MESH_LOW_POWER))
+ if (bt_mesh_lpn_established()) {
+ return (addr == bt_mesh.lpn.frnd);
+ }
+#endif
+ return false;
+}
+
+static inline bool bt_mesh_lpn_waiting_update(void)
+{
+#if (MYNEWT_VAL(BLE_MESH_LOW_POWER))
+ return (bt_mesh.lpn.state == BT_MESH_LPN_WAIT_UPDATE);
+#else
+ return false;
+#endif
+}
+
+static inline bool bt_mesh_lpn_timer(void)
+{
+#if MYNEWT_VAL(BLE_MESH_LOW_POWER) && MYNEWT_VAL(BLE_MESH_LPN_AUTO)
+ return (bt_mesh.lpn.state == BT_MESH_LPN_TIMER);
+#else
+ return false;
+#endif
+}
+
+void bt_mesh_lpn_msg_received(struct bt_mesh_net_rx *rx);
+
+void bt_mesh_lpn_group_add(u16_t group);
+void bt_mesh_lpn_group_del(u16_t *groups, size_t group_count);
+
+void bt_mesh_lpn_disable(bool force);
+
+int bt_mesh_lpn_init(void);
+
+#endif
diff --git a/src/libs/mynewt-nimble/nimble/host/mesh/src/mesh.c b/src/libs/mynewt-nimble/nimble/host/mesh/src/mesh.c
new file mode 100644
index 00000000..52fbdbf6
--- /dev/null
+++ b/src/libs/mynewt-nimble/nimble/host/mesh/src/mesh.c
@@ -0,0 +1,361 @@
+/* Bluetooth Mesh */
+
+/*
+ * Copyright (c) 2017 Intel Corporation
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include "syscfg/syscfg.h"
+#define MESH_LOG_MODULE BLE_MESH_LOG
+
+#include <stdbool.h>
+#include <errno.h>
+
+#include "os/os_mbuf.h"
+#include "mesh/mesh.h"
+#include "host/ble_uuid.h"
+
+#include "adv.h"
+#include "prov.h"
+#include "net.h"
+#include "beacon.h"
+#include "lpn.h"
+#include "friend.h"
+#include "transport.h"
+#include "access.h"
+#include "foundation.h"
+#include "proxy.h"
+#include "shell.h"
+#include "mesh_priv.h"
+#include "settings.h"
+
+u8_t g_mesh_addr_type;
+static struct ble_gap_event_listener mesh_event_listener;
+
+int bt_mesh_provision(const u8_t net_key[16], u16_t net_idx,
+ u8_t flags, u32_t iv_index, u16_t addr,
+ const u8_t dev_key[16])
+{
+ bool pb_gatt_enabled;
+ int err;
+
+ BT_INFO("Primary Element: 0x%04x", addr);
+ BT_DBG("net_idx 0x%04x flags 0x%02x iv_index 0x%04x",
+ net_idx, flags, (unsigned) iv_index);
+
+ if (atomic_test_and_set_bit(bt_mesh.flags, BT_MESH_VALID)) {
+ return -EALREADY;
+ }
+
+ if ((MYNEWT_VAL(BLE_MESH_PB_GATT))) {
+ if (bt_mesh_proxy_prov_disable(false) == 0) {
+ pb_gatt_enabled = true;
+ } else {
+ pb_gatt_enabled = false;
+ }
+ } else {
+ pb_gatt_enabled = false;
+ }
+
+ err = bt_mesh_net_create(net_idx, flags, net_key, iv_index);
+ if (err) {
+ atomic_clear_bit(bt_mesh.flags, BT_MESH_VALID);
+
+ if (MYNEWT_VAL(BLE_MESH_PB_GATT) && pb_gatt_enabled) {
+ bt_mesh_proxy_prov_enable();
+ }
+
+ return err;
+ }
+
+ bt_mesh.seq = 0;
+
+ bt_mesh_comp_provision(addr);
+
+ memcpy(bt_mesh.dev_key, dev_key, 16);
+
+ if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
+ BT_DBG("Storing network information persistently");
+ bt_mesh_store_net();
+ bt_mesh_store_subnet(&bt_mesh.sub[0]);
+ bt_mesh_store_iv(false);
+ }
+
+ bt_mesh_net_start();
+
+ return 0;
+}
+
+int bt_mesh_provision_adv(const u8_t uuid[16], u16_t net_idx, u16_t addr,
+ u8_t attention_duration)
+{
+ if (!atomic_test_bit(bt_mesh.flags, BT_MESH_VALID)) {
+ return -EINVAL;
+ }
+
+ if (bt_mesh_subnet_get(net_idx) == NULL) {
+ return -EINVAL;
+ }
+
+ if (IS_ENABLED(CONFIG_BT_MESH_PROVISIONER) &&
+ IS_ENABLED(CONFIG_BT_MESH_PB_ADV)) {
+ return bt_mesh_pb_adv_open(uuid, net_idx, addr,
+ attention_duration);
+ }
+
+ return -ENOTSUP;
+}
+
+void bt_mesh_reset(void)
+{
+ if (!atomic_test_bit(bt_mesh.flags, BT_MESH_VALID)) {
+ return;
+ }
+
+ bt_mesh.iv_index = 0U;
+ bt_mesh.seq = 0U;
+
+ memset(bt_mesh.flags, 0, sizeof(bt_mesh.flags));
+
+ k_delayed_work_cancel(&bt_mesh.ivu_timer);
+
+ bt_mesh_cfg_reset();
+
+ bt_mesh_rx_reset();
+ bt_mesh_tx_reset();
+
+ if ((MYNEWT_VAL(BLE_MESH_LOW_POWER))) {
+ bt_mesh_lpn_disable(true);
+ }
+
+ if ((MYNEWT_VAL(BLE_MESH_FRIEND))) {
+ bt_mesh_friend_clear_net_idx(BT_MESH_KEY_ANY);
+ }
+
+ if ((MYNEWT_VAL(BLE_MESH_GATT_PROXY))) {
+ bt_mesh_proxy_gatt_disable();
+ }
+
+ if ((MYNEWT_VAL(BLE_MESH_PB_GATT))) {
+ bt_mesh_proxy_prov_enable();
+ }
+
+ if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
+ bt_mesh_clear_net();
+ }
+
+ memset(bt_mesh.dev_key, 0, sizeof(bt_mesh.dev_key));
+
+ bt_mesh_scan_disable();
+ bt_mesh_beacon_disable();
+
+ bt_mesh_comp_unprovision();
+
+ if (IS_ENABLED(CONFIG_BT_MESH_PROV)) {
+ bt_mesh_prov_reset();
+ }
+}
+
+bool bt_mesh_is_provisioned(void)
+{
+ return atomic_test_bit(bt_mesh.flags, BT_MESH_VALID);
+}
+
+int bt_mesh_prov_enable(bt_mesh_prov_bearer_t bearers)
+{
+ if (bt_mesh_is_provisioned()) {
+ return -EALREADY;
+ }
+
+ char uuid_buf[BLE_UUID_STR_LEN];
+ const struct bt_mesh_prov *prov = bt_mesh_prov_get();
+ ble_uuid_t *uuid = BLE_UUID128_DECLARE();
+
+ memcpy(BLE_UUID128(uuid)->value, prov->uuid, 16);
+ BT_INFO("Device UUID: %s", ble_uuid_to_str(uuid, uuid_buf));
+
+ if (IS_ENABLED(CONFIG_BT_MESH_PB_ADV) &&
+ (bearers & BT_MESH_PROV_ADV)) {
+ /* Make sure we're scanning for provisioning inviations */
+ bt_mesh_scan_enable();
+ /* Enable unprovisioned beacon sending */
+ bt_mesh_beacon_enable();
+ }
+
+ if (IS_ENABLED(CONFIG_BT_MESH_PB_GATT) &&
+ (bearers & BT_MESH_PROV_GATT)) {
+ bt_mesh_proxy_prov_enable();
+ bt_mesh_adv_update();
+ }
+
+ return 0;
+}
+
+int bt_mesh_prov_disable(bt_mesh_prov_bearer_t bearers)
+{
+ if (bt_mesh_is_provisioned()) {
+ return -EALREADY;
+ }
+
+ if (IS_ENABLED(CONFIG_BT_MESH_PB_ADV) &&
+ (bearers & BT_MESH_PROV_ADV)) {
+ bt_mesh_beacon_disable();
+ bt_mesh_scan_disable();
+ }
+
+ if (IS_ENABLED(CONFIG_BT_MESH_PB_GATT) &&
+ (bearers & BT_MESH_PROV_GATT)) {
+ bt_mesh_proxy_prov_disable(true);
+ }
+
+ return 0;
+}
+
+static int bt_mesh_gap_event(struct ble_gap_event *event, void *arg)
+{
+ ble_adv_gap_mesh_cb(event, arg);
+
+#if (MYNEWT_VAL(BLE_MESH_PROXY))
+ ble_mesh_proxy_gap_event(event, arg);
+#endif
+
+ return 0;
+}
+
+static void model_suspend(struct bt_mesh_model *mod, struct bt_mesh_elem *elem,
+ bool vnd, bool primary, void *user_data)
+{
+ if (mod->pub && mod->pub->update) {
+ mod->pub->count = 0;
+ k_delayed_work_cancel(&mod->pub->timer);
+ }
+}
+
+int bt_mesh_suspend(void)
+{
+ int err;
+
+ if (!atomic_test_bit(bt_mesh.flags, BT_MESH_VALID)) {
+ return -EINVAL;
+ }
+
+ if (atomic_test_and_set_bit(bt_mesh.flags, BT_MESH_SUSPENDED)) {
+ return -EALREADY;
+ }
+
+ err = bt_mesh_scan_disable();
+ if (err) {
+ atomic_clear_bit(bt_mesh.flags, BT_MESH_SUSPENDED);
+ BT_WARN("Disabling scanning failed (err %d)", err);
+ return err;
+ }
+
+ bt_mesh_hb_pub_disable();
+
+ if (bt_mesh_beacon_get() == BT_MESH_BEACON_ENABLED) {
+ bt_mesh_beacon_disable();
+ }
+
+ bt_mesh_model_foreach(model_suspend, NULL);
+
+ return 0;
+}
+
+static void model_resume(struct bt_mesh_model *mod, struct bt_mesh_elem *elem,
+ bool vnd, bool primary, void *user_data)
+{
+ if (mod->pub && mod->pub->update) {
+ s32_t period_ms = bt_mesh_model_pub_period_get(mod);
+
+ if (period_ms) {
+ k_delayed_work_submit(&mod->pub->timer, period_ms);
+ }
+ }
+}
+
+int bt_mesh_resume(void)
+{
+ int err;
+
+ if (!atomic_test_bit(bt_mesh.flags, BT_MESH_VALID)) {
+ return -EINVAL;
+ }
+
+ if (!atomic_test_and_clear_bit(bt_mesh.flags, BT_MESH_SUSPENDED)) {
+ return -EALREADY;
+ }
+
+ err = bt_mesh_scan_enable();
+ if (err) {
+ BT_WARN("Re-enabling scanning failed (err %d)", err);
+ atomic_set_bit(bt_mesh.flags, BT_MESH_SUSPENDED);
+ return err;
+ }
+
+ if (bt_mesh_beacon_get() == BT_MESH_BEACON_ENABLED) {
+ bt_mesh_beacon_enable();
+ }
+
+ bt_mesh_model_foreach(model_resume, NULL);
+
+ return err;
+}
+
+int bt_mesh_init(uint8_t own_addr_type, const struct bt_mesh_prov *prov,
+ const struct bt_mesh_comp *comp)
+{
+ int err;
+
+ g_mesh_addr_type = own_addr_type;
+
+ /* initialize SM alg ECC subsystem (it is used directly from mesh code) */
+ ble_sm_alg_ecc_init();
+
+ err = bt_mesh_comp_register(comp);
+ if (err) {
+ return err;
+ }
+
+#if (MYNEWT_VAL(BLE_MESH_PROV))
+ err = bt_mesh_prov_init(prov);
+ if (err) {
+ return err;
+ }
+#endif
+
+#if (MYNEWT_VAL(BLE_MESH_PROXY))
+ bt_mesh_proxy_init();
+#endif
+
+#if (MYNEWT_VAL(BLE_MESH_PROV))
+ /* Need this to proper link.rx.buf allocation */
+ bt_mesh_prov_reset_link();
+#endif
+
+ bt_mesh_net_init();
+ bt_mesh_trans_init();
+ bt_mesh_beacon_init();
+ bt_mesh_adv_init();
+
+#if (MYNEWT_VAL(BLE_MESH_PB_ADV))
+ /* Make sure we're scanning for provisioning inviations */
+ bt_mesh_scan_enable();
+ /* Enable unprovisioned beacon sending */
+
+ bt_mesh_beacon_enable();
+#endif
+
+#if (MYNEWT_VAL(BLE_MESH_PB_GATT))
+ bt_mesh_proxy_prov_enable();
+#endif
+
+ ble_gap_event_listener_register(&mesh_event_listener,
+ bt_mesh_gap_event, NULL);
+
+#if (MYNEWT_VAL(BLE_MESH_SETTINGS))
+ bt_mesh_settings_init();
+#endif
+
+ return 0;
+}
diff --git a/src/libs/mynewt-nimble/nimble/host/mesh/src/mesh_priv.h b/src/libs/mynewt-nimble/nimble/host/mesh/src/mesh_priv.h
new file mode 100644
index 00000000..f09bb230
--- /dev/null
+++ b/src/libs/mynewt-nimble/nimble/host/mesh/src/mesh_priv.h
@@ -0,0 +1,39 @@
+/* Bluetooth Mesh */
+
+/*
+ * Copyright (c) 2017 Intel Corporation
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#ifndef __MESH_PRIV_H
+#define __MESH_PRIV_H
+
+#define BT_MESH_KEY_PRIMARY 0x0000
+#define BT_MESH_KEY_ANY 0xffff
+
+#define BT_MESH_ADDR_IS_UNICAST(addr) ((addr) && (addr) < 0x8000)
+#define BT_MESH_ADDR_IS_GROUP(addr) ((addr) >= 0xc000 && (addr) <= 0xff00)
+#define BT_MESH_ADDR_IS_VIRTUAL(addr) ((addr) >= 0x8000 && (addr) < 0xc000)
+#define BT_MESH_ADDR_IS_RFU(addr) ((addr) >= 0xff00 && (addr) <= 0xfffb)
+struct bt_mesh_net;
+
+#define OP_GEN_ONOFF_GET BT_MESH_MODEL_OP_2(0x82, 0x01)
+#define OP_GEN_ONOFF_SET BT_MESH_MODEL_OP_2(0x82, 0x02)
+#define OP_GEN_ONOFF_SET_UNACK BT_MESH_MODEL_OP_2(0x82, 0x03)
+#define OP_GEN_ONOFF_STATUS BT_MESH_MODEL_OP_2(0x82, 0x04)
+#define OP_GEN_LEVEL_GET BT_MESH_MODEL_OP_2(0x82, 0x05)
+#define OP_GEN_LEVEL_SET BT_MESH_MODEL_OP_2(0x82, 0x06)
+#define OP_GEN_LEVEL_SET_UNACK BT_MESH_MODEL_OP_2(0x82, 0x07)
+#define OP_GEN_LEVEL_STATUS BT_MESH_MODEL_OP_2(0x82, 0x08)
+#define OP_GEN_DELTA_SET BT_MESH_MODEL_OP_2(0x82, 0x09)
+#define OP_GEN_DELTA_SET_UNACK BT_MESH_MODEL_OP_2(0x82, 0x0a)
+#define OP_GEN_MOVE_SET BT_MESH_MODEL_OP_2(0x82, 0x0b)
+#define OP_GEN_MOVE_SET_UNACK BT_MESH_MODEL_OP_2(0x82, 0x0c)
+#define OP_LIGHT_LIGHTNESS_GET BT_MESH_MODEL_OP_2(0x82, 0x4b)
+#define OP_LIGHT_LIGHTNESS_SET BT_MESH_MODEL_OP_2(0x82, 0x4c)
+#define OP_LIGHT_LIGHTNESS_SET_UNACK BT_MESH_MODEL_OP_2(0x82, 0x4d)
+#define OP_LIGHT_LIGHTNESS_STATUS BT_MESH_MODEL_OP_2(0x82, 0x4e)
+
+bool bt_mesh_is_provisioned(void);
+
+#endif
diff --git a/src/libs/mynewt-nimble/nimble/host/mesh/src/model_cli.c b/src/libs/mynewt-nimble/nimble/host/mesh/src/model_cli.c
new file mode 100644
index 00000000..b00cfa52
--- /dev/null
+++ b/src/libs/mynewt-nimble/nimble/host/mesh/src/model_cli.c
@@ -0,0 +1,301 @@
+/*
+ * Copyright (c) 2017 Intel Corporation
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include "syscfg/syscfg.h"
+#define MESH_LOG_MODULE BLE_MESH_MODEL_LOG
+
+#include "mesh/mesh.h"
+#include "mesh/model_cli.h"
+#include "mesh_priv.h"
+
+static s32_t msg_timeout = K_SECONDS(5);
+
+static struct bt_mesh_gen_model_cli *gen_onoff_cli;
+static struct bt_mesh_gen_model_cli *gen_level_cli;
+
+static u8_t transaction_id = 0;
+
+struct gen_onoff_param {
+ u8_t *state;
+};
+
+struct gen_level_param {
+ s16_t *level;
+};
+
+static void gen_onoff_status(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ struct bt_mesh_gen_model_cli *cli = model->user_data;
+ struct gen_onoff_param *param;
+ u8_t state;
+
+
+ BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s",
+ ctx->net_idx, ctx->app_idx, ctx->addr, buf->om_len,
+ bt_hex(buf->om_data, buf->om_len));
+
+ if (cli->op_pending != OP_GEN_ONOFF_STATUS) {
+ BT_WARN("Unexpected Generic OnOff Status message");
+ return;
+ }
+
+ param = cli->op_param;
+
+ state = net_buf_simple_pull_u8(buf);
+ if (param->state) {
+ *param->state = state;
+ }
+
+ BT_DBG("state: %d", state);
+
+ k_sem_give(&cli->op_sync);
+}
+
+static void gen_level_status(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ struct bt_mesh_gen_model_cli *cli = model->user_data;
+ struct gen_level_param *param;
+ s16_t level;
+
+
+ BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s",
+ ctx->net_idx, ctx->app_idx, ctx->addr, buf->om_len,
+ bt_hex(buf->om_data, buf->om_len));
+
+ if (cli->op_pending != OP_GEN_LEVEL_STATUS) {
+ BT_WARN("Unexpected Generic LEVEL Status message");
+ return;
+ }
+
+ param = cli->op_param;
+
+ level = net_buf_simple_pull_le16(buf);
+ if (param->level) {
+ *param->level = level;
+ }
+
+ BT_DBG("level: %d", level);
+
+ k_sem_give(&cli->op_sync);
+}
+
+const struct bt_mesh_model_op gen_onoff_cli_op[] = {
+ { OP_GEN_ONOFF_STATUS, 1, gen_onoff_status },
+ BT_MESH_MODEL_OP_END,
+};
+
+static int onoff_cli_init(struct bt_mesh_model *model)
+{
+ BT_DBG("");
+
+ if (!model->user_data) {
+ BT_ERR("No Generic OnOff Client context provided");
+ return -EINVAL;
+ }
+
+ gen_onoff_cli = model->user_data;
+ gen_onoff_cli->model = model;
+
+ k_sem_init(&gen_onoff_cli->op_sync, 0, 1);
+
+ return 0;
+}
+
+const struct bt_mesh_model_cb bt_mesh_gen_onoff_cli_cb = {
+ .init = onoff_cli_init,
+};
+
+const struct bt_mesh_model_op gen_level_cli_op[] = {
+ { OP_GEN_LEVEL_STATUS, 2, gen_level_status },
+ BT_MESH_MODEL_OP_END,
+};
+
+static int level_cli_init(struct bt_mesh_model *model)
+{
+ BT_DBG("");
+
+ if (!model->user_data) {
+ BT_ERR("No Generic Level Client context provided");
+ return -EINVAL;
+ }
+
+ gen_level_cli = model->user_data;
+ gen_level_cli->model = model;
+
+ k_sem_init(&gen_level_cli->op_sync, 0, 1);
+
+ return 0;
+}
+
+const struct bt_mesh_model_cb bt_mesh_gen_level_cli_cb = {
+ .init = level_cli_init,
+};
+
+static int cli_wait(struct bt_mesh_gen_model_cli *cli, void *param, u32_t op)
+{
+ int err;
+
+ BT_DBG("");
+
+ cli->op_param = param;
+ cli->op_pending = op;
+
+ err = k_sem_take(&cli->op_sync, msg_timeout);
+
+ cli->op_pending = 0;
+ cli->op_param = NULL;
+
+ return err;
+}
+
+int bt_mesh_gen_onoff_get(u16_t net_idx, u16_t addr, u16_t app_idx,
+ u8_t *state)
+{
+ struct os_mbuf *msg = NET_BUF_SIMPLE(2 + 0 + 4);
+ struct bt_mesh_msg_ctx ctx = {
+ .net_idx = net_idx,
+ .app_idx = app_idx,
+ .addr = addr,
+ .send_ttl = BT_MESH_TTL_DEFAULT,
+ };
+ struct gen_onoff_param param = {
+ .state = state,
+ };
+ int err;
+
+ bt_mesh_model_msg_init(msg, OP_GEN_ONOFF_GET);
+
+ err = bt_mesh_model_send(gen_onoff_cli->model, &ctx, msg, NULL, NULL);
+ if (err) {
+ BT_ERR("model_send() failed (err %d)", err);
+ goto done;
+ }
+
+ err = cli_wait(gen_onoff_cli, &param, OP_GEN_ONOFF_STATUS);
+done:
+ os_mbuf_free_chain(msg);
+ return err;
+}
+
+int bt_mesh_gen_onoff_set(u16_t net_idx, u16_t addr, u16_t app_idx,
+ u8_t val, u8_t *state)
+{
+ struct os_mbuf *msg = NET_BUF_SIMPLE(2 + 2 + 4);
+ struct bt_mesh_msg_ctx ctx = {
+ .net_idx = net_idx,
+ .app_idx = app_idx,
+ .addr = addr,
+ .send_ttl = BT_MESH_TTL_DEFAULT,
+ };
+ struct gen_onoff_param param = {
+ .state = state,
+ };
+ int err;
+
+ if (state) {
+ bt_mesh_model_msg_init(msg, OP_GEN_ONOFF_SET);
+ } else {
+ bt_mesh_model_msg_init(msg, OP_GEN_ONOFF_SET_UNACK);
+ }
+
+ net_buf_simple_add_u8(msg, val);
+ net_buf_simple_add_u8(msg, transaction_id);
+
+ err = bt_mesh_model_send(gen_onoff_cli->model, &ctx, msg, NULL, NULL);
+ if (err) {
+ BT_ERR("model_send() failed (err %d)", err);
+ goto done;
+ }
+
+ if (!state) {
+ goto done;
+ }
+
+ err = cli_wait(gen_onoff_cli, &param, OP_GEN_ONOFF_STATUS);
+done:
+ if (err == 0) {
+ transaction_id++;
+ }
+ os_mbuf_free_chain(msg);
+ return err;
+}
+
+int bt_mesh_gen_level_get(u16_t net_idx, u16_t addr, u16_t app_idx,
+ s16_t *level)
+{
+ struct os_mbuf *msg = NET_BUF_SIMPLE(2 + 0 + 4);
+ struct bt_mesh_msg_ctx ctx = {
+ .net_idx = net_idx,
+ .app_idx = app_idx,
+ .addr = addr,
+ .send_ttl = BT_MESH_TTL_DEFAULT,
+ };
+ struct gen_level_param param = {
+ .level = level,
+ };
+ int err;
+
+ bt_mesh_model_msg_init(msg, OP_GEN_LEVEL_GET);
+
+ err = bt_mesh_model_send(gen_level_cli->model, &ctx, msg, NULL, NULL);
+ if (err) {
+ BT_ERR("model_send() failed (err %d)", err);
+ goto done;
+ }
+
+ err = cli_wait(gen_level_cli, &param, OP_GEN_LEVEL_STATUS);
+done:
+ os_mbuf_free_chain(msg);
+ return err;
+}
+
+int bt_mesh_gen_level_set(u16_t net_idx, u16_t addr, u16_t app_idx,
+ s16_t val, s16_t *state)
+{
+ struct os_mbuf *msg = NET_BUF_SIMPLE(2 + 3 + 4);
+ struct bt_mesh_msg_ctx ctx = {
+ .net_idx = net_idx,
+ .app_idx = app_idx,
+ .addr = addr,
+ .send_ttl = BT_MESH_TTL_DEFAULT,
+ };
+ struct gen_level_param param = {
+ .level = state,
+ };
+ int err;
+
+ if (state) {
+ bt_mesh_model_msg_init(msg, OP_GEN_LEVEL_SET);
+ } else {
+ bt_mesh_model_msg_init(msg, OP_GEN_LEVEL_SET_UNACK);
+ }
+
+ net_buf_simple_add_le16(msg, val);
+ net_buf_simple_add_u8(msg, transaction_id);
+
+ err = bt_mesh_model_send(gen_level_cli->model, &ctx, msg, NULL, NULL);
+ if (err) {
+ BT_ERR("model_send() failed (err %d)", err);
+ goto done;
+ }
+
+ if (!state) {
+ goto done;
+ }
+
+ err = cli_wait(gen_level_cli, &param, OP_GEN_LEVEL_STATUS);
+done:
+ if (err == 0) {
+ transaction_id++;
+ }
+ os_mbuf_free_chain(msg);
+ return err;
+}
+
diff --git a/src/libs/mynewt-nimble/nimble/host/mesh/src/model_srv.c b/src/libs/mynewt-nimble/nimble/host/mesh/src/model_srv.c
new file mode 100644
index 00000000..5f5a8df4
--- /dev/null
+++ b/src/libs/mynewt-nimble/nimble/host/mesh/src/model_srv.c
@@ -0,0 +1,266 @@
+/*
+ * Copyright (c) 2017 Intel Corporation
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include "syscfg/syscfg.h"
+#define MESH_LOG_MODULE BLE_MESH_MODEL_LOG
+
+#include "mesh/mesh.h"
+#include "mesh/model_srv.h"
+#include "mesh_priv.h"
+
+static struct bt_mesh_gen_onoff_srv *gen_onoff_srv;
+static struct bt_mesh_gen_level_srv *gen_level_srv;
+static struct bt_mesh_light_lightness_srv *light_lightness_srv;
+
+static void gen_onoff_status(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx)
+{
+ struct bt_mesh_gen_onoff_srv *cb = model->user_data;
+ struct os_mbuf *msg = NET_BUF_SIMPLE(3);
+ u8_t *state;
+
+ bt_mesh_model_msg_init(msg, OP_GEN_ONOFF_STATUS);
+ state = net_buf_simple_add(msg, 1);
+ if (cb && cb->get) {
+ cb->get(model, state);
+ }
+
+ BT_DBG("state: %d", *state);
+
+ if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) {
+ BT_ERR("Send status failed");
+ }
+
+ os_mbuf_free_chain(msg);
+}
+
+static void gen_onoff_get(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ BT_DBG("");
+
+ gen_onoff_status(model, ctx);
+}
+
+static void gen_onoff_set_unack(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ struct bt_mesh_gen_onoff_srv *cb = model->user_data;
+ u8_t state;
+
+ state = buf->om_data[0];
+
+ BT_DBG("state: %d", state);
+
+ if (cb && cb->set) {
+ cb->set(model, state);
+ }
+}
+
+static void gen_onoff_set(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ BT_DBG("");
+
+ gen_onoff_set_unack(model, ctx, buf);
+ gen_onoff_status(model, ctx);
+}
+
+static void gen_level_status(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx)
+{
+ struct bt_mesh_gen_level_srv *cb = model->user_data;
+ struct os_mbuf *msg = NET_BUF_SIMPLE(4);
+ s16_t *level;
+
+ bt_mesh_model_msg_init(msg, OP_GEN_LEVEL_STATUS);
+ level = net_buf_simple_add(msg, 2);
+ if (cb && cb->get) {
+ cb->get(model, level);
+ }
+
+ BT_DBG("level: %d", *level);
+
+ if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) {
+ BT_ERR("Send status failed");
+ }
+
+ os_mbuf_free_chain(msg);
+}
+
+static void gen_level_get(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ BT_DBG("");
+
+ gen_level_status(model, ctx);
+}
+
+static void gen_level_set_unack(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf) {
+ struct bt_mesh_gen_level_srv *cb = model->user_data;
+ s16_t level;
+
+ level = (s16_t) net_buf_simple_pull_le16(buf);
+ BT_DBG("level: %d", level);
+
+ if (cb && cb->set) {
+ cb->set(model, level);
+ }
+}
+
+static void gen_level_set(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ gen_level_set_unack(model, ctx, buf);
+ gen_level_status(model, ctx);
+}
+
+static void light_lightness_status(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx)
+{
+ struct bt_mesh_light_lightness_srv *cb = model->user_data;
+ struct os_mbuf *msg = NET_BUF_SIMPLE(4);
+ s16_t *lightness;
+
+ bt_mesh_model_msg_init(msg, OP_LIGHT_LIGHTNESS_STATUS);
+ lightness = net_buf_simple_add(msg, 2);
+ if (cb && cb->get) {
+ cb->get(model, lightness);
+ }
+
+ BT_DBG("lightness: %d", *lightness);
+
+ if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) {
+ BT_ERR("Send status failed");
+ }
+
+ os_mbuf_free_chain(msg);
+}
+
+static void light_lightness_get(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ BT_DBG("");
+
+ light_lightness_status(model, ctx);
+}
+
+static void light_lightness_set_unack(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf) {
+ struct bt_mesh_light_lightness_srv *cb = model->user_data;
+ s16_t lightness;
+
+ lightness = (s16_t) net_buf_simple_pull_le16(buf);
+ BT_DBG("lightness: %d", lightness);
+
+ if (cb && cb->set) {
+ cb->set(model, lightness);
+ }
+}
+
+static void light_lightness_set(struct bt_mesh_model *model,
+ struct bt_mesh_msg_ctx *ctx,
+ struct os_mbuf *buf)
+{
+ light_lightness_set_unack(model, ctx, buf);
+ light_lightness_status(model, ctx);
+}
+
+const struct bt_mesh_model_op gen_onoff_srv_op[] = {
+ { OP_GEN_ONOFF_GET, 0, gen_onoff_get },
+ { OP_GEN_ONOFF_SET, 2, gen_onoff_set },
+ { OP_GEN_ONOFF_SET_UNACK, 2, gen_onoff_set_unack },
+ BT_MESH_MODEL_OP_END,
+};
+
+const struct bt_mesh_model_op gen_level_srv_op[] = {
+ { OP_GEN_LEVEL_GET, 0, gen_level_get },
+ { OP_GEN_LEVEL_SET, 3, gen_level_set },
+ { OP_GEN_LEVEL_SET_UNACK, 3, gen_level_set_unack },
+ BT_MESH_MODEL_OP_END,
+};
+
+const struct bt_mesh_model_op light_lightness_srv_op[] = {
+ { OP_LIGHT_LIGHTNESS_GET, 0, light_lightness_get },
+ { OP_LIGHT_LIGHTNESS_SET, 3, light_lightness_set },
+ { OP_LIGHT_LIGHTNESS_SET_UNACK, 3, light_lightness_set_unack },
+ BT_MESH_MODEL_OP_END,
+};
+
+static int onoff_srv_init(struct bt_mesh_model *model)
+{
+ struct bt_mesh_gen_onoff_srv *cfg = model->user_data;
+
+ BT_DBG("");
+
+ if (!cfg) {
+ BT_ERR("No Generic OnOff Server context provided");
+ return -EINVAL;
+ }
+
+ cfg->model = model;
+
+ gen_onoff_srv = cfg;
+
+ return 0;
+}
+
+const struct bt_mesh_model_cb gen_onoff_srv_cb = {
+ .init = onoff_srv_init,
+};
+
+static int level_srv_init(struct bt_mesh_model *model)
+{
+ struct bt_mesh_gen_level_srv *cfg = model->user_data;
+
+ BT_DBG("");
+
+ if (!cfg) {
+ BT_ERR("No Generic Level Server context provided");
+ return -EINVAL;
+ }
+
+ cfg->model = model;
+
+ gen_level_srv = cfg;
+
+ return 0;
+}
+
+const struct bt_mesh_model_cb gen_level_srv_cb = {
+ .init = level_srv_init,
+};
+
+static int lightness_srv_init(struct bt_mesh_model *model)
+{
+ struct bt_mesh_light_lightness_srv *cfg = model->user_data;
+
+ BT_DBG("");
+
+ if (!cfg) {
+ BT_ERR("No Light Lightness Server context provided");
+ return -EINVAL;
+ }
+
+ cfg->model = model;
+
+ light_lightness_srv = cfg;
+
+ return 0;
+}
+
+const struct bt_mesh_model_cb light_lightness_srv_cb = {
+ .init = lightness_srv_init,
+};
diff --git a/src/libs/mynewt-nimble/nimble/host/mesh/src/net.c b/src/libs/mynewt-nimble/nimble/host/mesh/src/net.c
new file mode 100644
index 00000000..240314d4
--- /dev/null
+++ b/src/libs/mynewt-nimble/nimble/host/mesh/src/net.c
@@ -0,0 +1,1433 @@
+/* Bluetooth Mesh */
+
+/*
+ * Copyright (c) 2017 Intel Corporation
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include "syscfg/syscfg.h"
+#define MESH_LOG_MODULE BLE_MESH_NET_LOG
+
+#include <string.h>
+#include <errno.h>
+#include <stdbool.h>
+
+#include "os/os_mbuf.h"
+#include "mesh/mesh.h"
+
+#include "crypto.h"
+#include "adv.h"
+#include "mesh_priv.h"
+#include "net.h"
+#include "lpn.h"
+#include "friend.h"
+#include "proxy.h"
+#include "transport.h"
+#include "access.h"
+#include "foundation.h"
+#include "beacon.h"
+#include "settings.h"
+#include "prov.h"
+
+/* Minimum valid Mesh Network PDU length. The Network headers
+ * themselves take up 9 bytes. After that there is a minumum of 1 byte
+ * payload for both CTL=1 and CTL=0 PDUs (smallest OpCode is 1 byte). CTL=1
+ * PDUs must use a 64-bit (8 byte) NetMIC, whereas CTL=0 PDUs have at least
+ * a 32-bit (4 byte) NetMIC and AppMIC giving again a total of 8 bytes.
+ */
+#define BT_MESH_NET_MIN_PDU_LEN (BT_MESH_NET_HDR_LEN + 1 + 8)
+
+/* Seq limit after IV Update is triggered */
+#define IV_UPDATE_SEQ_LIMIT 8000000
+
+#define IVI(pdu) ((pdu)[0] >> 7)
+#define NID(pdu) ((pdu)[0] & 0x7f)
+#define CTL(pdu) ((pdu)[1] >> 7)
+#define TTL(pdu) ((pdu)[1] & 0x7f)
+#define SEQ(pdu) (((u32_t)(pdu)[2] << 16) | \
+ ((u32_t)(pdu)[3] << 8) | (u32_t)(pdu)[4]);
+#define SRC(pdu) (sys_get_be16(&(pdu)[5]))
+#define DST(pdu) (sys_get_be16(&(pdu)[7]))
+
+/* Determine how many friendship credentials we need */
+#if (MYNEWT_VAL(BLE_MESH_FRIEND))
+#define FRIEND_CRED_COUNT MYNEWT_VAL(BLE_MESH_FRIEND_LPN_COUNT)
+#elif (MYNEWT_VAL(BLE_MESH_LOW_POWER))
+#define FRIEND_CRED_COUNT MYNEWT_VAL(BLE_MESH_SUBNET_COUNT)
+#else
+#define FRIEND_CRED_COUNT 0
+#endif
+
+static struct friend_cred friend_cred[FRIEND_CRED_COUNT];
+
+static u64_t msg_cache[MYNEWT_VAL(BLE_MESH_MSG_CACHE_SIZE)];
+static u16_t msg_cache_next;
+
+/* Singleton network context (the implementation only supports one) */
+struct bt_mesh_net bt_mesh = {
+ .local_queue = STAILQ_HEAD_INITIALIZER(bt_mesh.local_queue),
+ .sub = {
+ [0 ... (MYNEWT_VAL(BLE_MESH_SUBNET_COUNT) - 1)] = {
+ .net_idx = BT_MESH_KEY_UNUSED,
+ }
+ },
+ .app_keys = {
+ [0 ... (MYNEWT_VAL(BLE_MESH_APP_KEY_COUNT) - 1)] = {
+ .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[4];
+static int dup_cache_next;
+
+static bool check_dup(struct os_mbuf *data)
+{
+ const u8_t *tail = net_buf_simple_tail(data);
+ u32_t val;
+ int i;
+
+ val = sys_get_be32(tail - 4) ^ sys_get_be32(tail - 8);
+
+ for (i = 0; i < ARRAY_SIZE(dup_cache); i++) {
+ if (dup_cache[i] == val) {
+ return true;
+ }
+ }
+
+ dup_cache[dup_cache_next++] = val;
+ dup_cache_next %= ARRAY_SIZE(dup_cache);
+
+ return false;
+}
+
+static u64_t msg_hash(struct bt_mesh_net_rx *rx, struct os_mbuf *pdu)
+{
+ u32_t hash1, hash2;
+
+ /* Three least significant bytes of IVI + first byte of SEQ */
+ hash1 = (BT_MESH_NET_IVI_RX(rx) << 8) | pdu->om_data[2];
+
+ /* Two last bytes of SEQ + SRC */
+ memcpy(&hash2, &pdu->om_data[3], 4);
+
+ return (u64_t)hash1 << 32 | (u64_t)hash2;
+}
+
+static bool msg_cache_match(struct bt_mesh_net_rx *rx,
+ struct os_mbuf *pdu)
+{
+ u64_t hash = msg_hash(rx, pdu);
+ u16_t i;
+
+ for (i = 0; i < ARRAY_SIZE(msg_cache); i++) {
+ if (msg_cache[i] == hash) {
+ return true;
+ }
+ }
+
+ /* Add to the cache */
+ rx->msg_cache_idx = msg_cache_next++;
+ msg_cache[rx->msg_cache_idx] = hash;
+ msg_cache_next %= ARRAY_SIZE(msg_cache);
+
+ return false;
+}
+
+struct bt_mesh_subnet *bt_mesh_subnet_get(u16_t net_idx)
+{
+ int i;
+
+ if (net_idx == BT_MESH_KEY_ANY) {
+ return &bt_mesh.sub[0];
+ }
+
+ for (i = 0; i < ARRAY_SIZE(bt_mesh.sub); i++) {
+ if (bt_mesh.sub[i].net_idx == net_idx) {
+ return &bt_mesh.sub[i];
+ }
+ }
+
+ return NULL;
+}
+
+int bt_mesh_net_keys_create(struct bt_mesh_subnet_keys *keys,
+ const u8_t key[16])
+{
+ u8_t p[] = { 0 };
+ u8_t nid;
+ int err;
+
+ err = bt_mesh_k2(key, p, sizeof(p), &nid, keys->enc, keys->privacy);
+ if (err) {
+ BT_ERR("Unable to generate NID, EncKey & PrivacyKey");
+ return err;
+ }
+
+ memcpy(keys->net, key, 16);
+
+ keys->nid = nid;
+
+ BT_DBG("NID 0x%02x EncKey %s", keys->nid, bt_hex(keys->enc, 16));
+ BT_DBG("PrivacyKey %s", bt_hex(keys->privacy, 16));
+
+ err = bt_mesh_k3(key, keys->net_id);
+ if (err) {
+ BT_ERR("Unable to generate Net ID");
+ return err;
+ }
+
+ BT_DBG("NetID %s", bt_hex(keys->net_id, 8));
+
+#if (MYNEWT_VAL(BLE_MESH_GATT_PROXY))
+ err = bt_mesh_identity_key(key, keys->identity);
+ if (err) {
+ BT_ERR("Unable to generate IdentityKey");
+ return err;
+ }
+
+ BT_DBG("IdentityKey %s", bt_hex(keys->identity, 16));
+#endif /* GATT_PROXY */
+
+ err = bt_mesh_beacon_key(key, keys->beacon);
+ if (err) {
+ BT_ERR("Unable to generate beacon key");
+ return err;
+ }
+
+ BT_DBG("BeaconKey %s", bt_hex(keys->beacon, 16));
+
+ return 0;
+}
+
+int friend_cred_set(struct friend_cred *cred, u8_t idx, const u8_t net_key[16])
+{
+ u16_t lpn_addr, frnd_addr;
+ int err;
+ u8_t p[9];
+
+#if (MYNEWT_VAL(BLE_MESH_LOW_POWER))
+ if (cred->addr == bt_mesh.lpn.frnd) {
+ lpn_addr = bt_mesh_primary_addr();
+ frnd_addr = cred->addr;
+ } else {
+ lpn_addr = cred->addr;
+ frnd_addr = bt_mesh_primary_addr();
+ }
+#else
+ lpn_addr = cred->addr;
+ frnd_addr = bt_mesh_primary_addr();
+#endif
+
+ BT_DBG("LPNAddress 0x%04x FriendAddress 0x%04x", lpn_addr, frnd_addr);
+ BT_DBG("LPNCounter 0x%04x FriendCounter 0x%04x", cred->lpn_counter,
+ cred->frnd_counter);
+
+ p[0] = 0x01;
+ sys_put_be16(lpn_addr, p + 1);
+ sys_put_be16(frnd_addr, p + 3);
+ sys_put_be16(cred->lpn_counter, p + 5);
+ sys_put_be16(cred->frnd_counter, p + 7);
+
+ err = bt_mesh_k2(net_key, p, sizeof(p), &cred->cred[idx].nid,
+ cred->cred[idx].enc, cred->cred[idx].privacy);
+ if (err) {
+ BT_ERR("Unable to generate NID, EncKey & PrivacyKey");
+ return err;
+ }
+
+ BT_DBG("Friend NID 0x%02x EncKey %s", cred->cred[idx].nid,
+ bt_hex(cred->cred[idx].enc, 16));
+ BT_DBG("Friend PrivacyKey %s", bt_hex(cred->cred[idx].privacy, 16));
+
+ return 0;
+}
+
+void friend_cred_refresh(u16_t net_idx)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(friend_cred); i++) {
+ struct friend_cred *cred = &friend_cred[i];
+
+ if (cred->addr != BT_MESH_ADDR_UNASSIGNED &&
+ cred->net_idx == net_idx) {
+ memcpy(&cred->cred[0], &cred->cred[1],
+ sizeof(cred->cred[0]));
+ }
+ }
+}
+
+int friend_cred_update(struct bt_mesh_subnet *sub)
+{
+ int err, i;
+
+ BT_DBG("net_idx 0x%04x", sub->net_idx);
+
+ for (i = 0; i < ARRAY_SIZE(friend_cred); i++) {
+ struct friend_cred *cred = &friend_cred[i];
+
+ if (cred->addr == BT_MESH_ADDR_UNASSIGNED ||
+ cred->net_idx != sub->net_idx) {
+ continue;
+ }
+
+ err = friend_cred_set(cred, 1, sub->keys[1].net);
+ if (err) {
+ return err;
+ }
+ }
+
+ return 0;
+}
+
+struct friend_cred *friend_cred_create(struct bt_mesh_subnet *sub, u16_t addr,
+ u16_t lpn_counter, u16_t frnd_counter)
+{
+ struct friend_cred *cred;
+ int i, err;
+
+ BT_DBG("net_idx 0x%04x addr 0x%04x", sub->net_idx, addr);
+
+ for (cred = NULL, i = 0; i < ARRAY_SIZE(friend_cred); i++) {
+ if ((friend_cred[i].addr == BT_MESH_ADDR_UNASSIGNED) ||
+ (friend_cred[i].addr == addr &&
+ friend_cred[i].net_idx == sub->net_idx)) {
+ cred = &friend_cred[i];
+ break;
+ }
+ }
+
+ if (!cred) {
+ BT_WARN("No free friend credential slots");
+ return NULL;
+ }
+
+ cred->net_idx = sub->net_idx;
+ cred->addr = addr;
+ cred->lpn_counter = lpn_counter;
+ cred->frnd_counter = frnd_counter;
+
+ err = friend_cred_set(cred, 0, sub->keys[0].net);
+ if (err) {
+ friend_cred_clear(cred);
+ return NULL;
+ }
+
+ if (sub->kr_flag) {
+ err = friend_cred_set(cred, 1, sub->keys[1].net);
+ if (err) {
+ friend_cred_clear(cred);
+ return NULL;
+ }
+ }
+
+ return cred;
+}
+
+void friend_cred_clear(struct friend_cred *cred)
+{
+ cred->net_idx = BT_MESH_KEY_UNUSED;
+ cred->addr = BT_MESH_ADDR_UNASSIGNED;
+ cred->lpn_counter = 0;
+ cred->frnd_counter = 0;
+ memset(cred->cred, 0, sizeof(cred->cred));
+}
+
+int friend_cred_del(u16_t net_idx, u16_t addr)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(friend_cred); i++) {
+ struct friend_cred *cred = &friend_cred[i];
+
+ if (cred->addr == addr && cred->net_idx == net_idx) {
+ friend_cred_clear(cred);
+ return 0;
+ }
+ }
+
+ return -ENOENT;
+}
+
+int friend_cred_get(struct bt_mesh_subnet *sub, u16_t addr, u8_t *nid,
+ const u8_t **enc, const u8_t **priv)
+{
+ int i;
+
+ BT_DBG("net_idx 0x%04x addr 0x%04x", sub->net_idx, addr);
+
+ for (i = 0; i < ARRAY_SIZE(friend_cred); i++) {
+ struct friend_cred *cred = &friend_cred[i];
+
+ if (cred->net_idx != sub->net_idx) {
+ continue;
+ }
+
+ if (addr != BT_MESH_ADDR_UNASSIGNED && cred->addr != addr) {
+ continue;
+ }
+
+ if (nid) {
+ *nid = cred->cred[sub->kr_flag].nid;
+ }
+
+ if (enc) {
+ *enc = cred->cred[sub->kr_flag].enc;
+ }
+
+ if (priv) {
+ *priv = cred->cred[sub->kr_flag].privacy;
+ }
+
+ return 0;
+ }
+
+ return -ENOENT;
+}
+
+u8_t bt_mesh_net_flags(struct bt_mesh_subnet *sub)
+{
+ u8_t flags = 0x00;
+
+ if (sub && sub->kr_flag) {
+ flags |= BT_MESH_NET_FLAG_KR;
+ }
+
+ if (atomic_test_bit(bt_mesh.flags, BT_MESH_IVU_IN_PROGRESS)) {
+ flags |= BT_MESH_NET_FLAG_IVU;
+ }
+
+ return flags;
+}
+
+int bt_mesh_net_beacon_update(struct bt_mesh_subnet *sub)
+{
+ u8_t flags = bt_mesh_net_flags(sub);
+ struct bt_mesh_subnet_keys *keys;
+
+ if (sub->kr_flag) {
+ BT_DBG("NetIndex %u Using new key", sub->net_idx);
+ keys = &sub->keys[1];
+ } else {
+ BT_DBG("NetIndex %u Using current key", sub->net_idx);
+ keys = &sub->keys[0];
+ }
+
+ BT_DBG("flags 0x%02x, IVI 0x%08x", flags, (unsigned) bt_mesh.iv_index);
+
+ return bt_mesh_beacon_auth(keys->beacon, flags, keys->net_id,
+ bt_mesh.iv_index, sub->auth);
+}
+
+int bt_mesh_net_create(u16_t idx, u8_t flags, const u8_t key[16],
+ u32_t iv_index)
+{
+ struct bt_mesh_subnet *sub;
+ int err;
+
+ BT_DBG("idx %u flags 0x%02x iv_index %u", idx, flags,
+ (unsigned) iv_index);
+
+ BT_DBG("NetKey %s", bt_hex(key, 16));
+
+ (void)memset(msg_cache, 0, sizeof(msg_cache));
+ msg_cache_next = 0U;
+
+ sub = &bt_mesh.sub[0];
+
+ sub->kr_flag = BT_MESH_KEY_REFRESH(flags);
+ if (sub->kr_flag) {
+ err = bt_mesh_net_keys_create(&sub->keys[1], key);
+ if (err) {
+ return -EIO;
+ }
+
+ sub->kr_phase = BT_MESH_KR_PHASE_2;
+ } else {
+ err = bt_mesh_net_keys_create(&sub->keys[0], key);
+ if (err) {
+ return -EIO;
+ }
+ }
+
+ sub->net_idx = idx;
+
+ if ((MYNEWT_VAL(BLE_MESH_GATT_PROXY))) {
+ sub->node_id = BT_MESH_NODE_IDENTITY_STOPPED;
+ } else {
+ sub->node_id = BT_MESH_NODE_IDENTITY_NOT_SUPPORTED;
+ }
+
+ bt_mesh.iv_index = iv_index;
+ atomic_set_bit_to(bt_mesh.flags, BT_MESH_IVU_IN_PROGRESS,
+ BT_MESH_IV_UPDATE(flags));
+
+ /* Set minimum required hours, since the 96-hour minimum requirement
+ * doesn't apply straight after provisioning (since we can't know how
+ * long has actually passed since the network changed its state).
+ */
+ bt_mesh.ivu_duration = BT_MESH_IVU_MIN_HOURS;
+
+ /* Make sure we have valid beacon data to be sent */
+ bt_mesh_net_beacon_update(sub);
+
+ return 0;
+}
+
+void bt_mesh_net_revoke_keys(struct bt_mesh_subnet *sub)
+{
+ int i;
+
+ BT_DBG("idx 0x%04x", sub->net_idx);
+
+ memcpy(&sub->keys[0], &sub->keys[1], sizeof(sub->keys[0]));
+
+ for (i = 0; i < ARRAY_SIZE(bt_mesh.app_keys); i++) {
+ struct bt_mesh_app_key *key = &bt_mesh.app_keys[i];
+
+ if (key->net_idx != sub->net_idx || !key->updated) {
+ continue;
+ }
+
+ memcpy(&key->keys[0], &key->keys[1], sizeof(key->keys[0]));
+ key->updated = false;
+ }
+}
+
+bool bt_mesh_kr_update(struct bt_mesh_subnet *sub, u8_t new_kr, bool new_key)
+{
+ if (new_kr != sub->kr_flag && sub->kr_phase == BT_MESH_KR_NORMAL) {
+ BT_WARN("KR change in normal operation. Are we blacklisted?");
+ return false;
+ }
+
+ sub->kr_flag = new_kr;
+
+ if (sub->kr_flag) {
+ if (sub->kr_phase == BT_MESH_KR_PHASE_1) {
+ BT_DBG("Phase 1 -> Phase 2");
+ sub->kr_phase = BT_MESH_KR_PHASE_2;
+ return true;
+ }
+ } else {
+ switch (sub->kr_phase) {
+ case BT_MESH_KR_PHASE_1:
+ if (!new_key) {
+ /* Ignore */
+ break;
+ }
+ /* Upon receiving a Secure Network beacon with the KR flag set
+ * to 0 using the new NetKey in Phase 1, the node shall
+ * immediately transition to Phase 3, which effectively skips
+ * Phase 2.
+ *
+ * Intentional fall-through.
+ */
+ case BT_MESH_KR_PHASE_2:
+ BT_DBG("KR Phase 0x%02x -> Normal", sub->kr_phase);
+ bt_mesh_net_revoke_keys(sub);
+ if ((MYNEWT_VAL(BLE_MESH_LOW_POWER)) ||
+ (MYNEWT_VAL(BLE_MESH_FRIEND))) {
+ friend_cred_refresh(sub->net_idx);
+ }
+ sub->kr_phase = BT_MESH_KR_NORMAL;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void bt_mesh_rpl_reset(void)
+{
+ int i;
+
+ /* Discard "old old" IV Index entries from RPL and flag
+ * any other ones (which are valid) as old.
+ */
+ for (i = 0; i < ARRAY_SIZE(bt_mesh.rpl); i++) {
+ struct bt_mesh_rpl *rpl = &bt_mesh.rpl[i];
+
+ if (rpl->src) {
+ if (rpl->old_iv) {
+ memset(rpl, 0, sizeof(*rpl));
+ } else {
+ rpl->old_iv = true;
+ }
+ }
+ }
+}
+
+#if MYNEWT_VAL(BLE_MESH_IV_UPDATE_TEST)
+void bt_mesh_iv_update_test(bool enable)
+{
+ atomic_set_bit_to(bt_mesh.flags, BT_MESH_IVU_TEST, enable);
+ /* Reset the duration variable - needed for some PTS tests */
+ bt_mesh.ivu_duration = 0;
+}
+
+bool bt_mesh_iv_update(void)
+{
+ if (!bt_mesh_is_provisioned()) {
+ BT_ERR("Not yet provisioned");
+ return false;
+ }
+
+ if (atomic_test_bit(bt_mesh.flags, BT_MESH_IVU_IN_PROGRESS)) {
+ bt_mesh_net_iv_update(bt_mesh.iv_index, false);
+ } else {
+ bt_mesh_net_iv_update(bt_mesh.iv_index + 1, true);
+ }
+
+ bt_mesh_net_sec_update(NULL);
+
+ return atomic_test_bit(bt_mesh.flags, BT_MESH_IVU_IN_PROGRESS);
+}
+#endif /* CONFIG_BT_MESH_IV_UPDATE_TEST */
+
+/* Used for sending immediate beacons to Friend queues and GATT clients */
+void bt_mesh_net_sec_update(struct bt_mesh_subnet *sub)
+{
+ if (IS_ENABLED(CONFIG_BT_MESH_FRIEND)) {
+ bt_mesh_friend_sec_update(sub ? sub->net_idx : BT_MESH_KEY_ANY);
+ }
+
+ if (IS_ENABLED(CONFIG_BT_MESH_GATT_PROXY) &&
+ bt_mesh_gatt_proxy_get() == BT_MESH_GATT_PROXY_ENABLED) {
+ bt_mesh_proxy_beacon_send(sub);
+ }
+}
+
+bool bt_mesh_net_iv_update(u32_t iv_index, bool iv_update)
+{
+ int i;
+
+ if (atomic_test_bit(bt_mesh.flags, BT_MESH_IVU_IN_PROGRESS)) {
+ /* We're currently in IV Update mode */
+
+ if (iv_index != bt_mesh.iv_index) {
+ BT_WARN("IV Index mismatch: 0x%08x != 0x%08x",
+ (unsigned) iv_index,
+ (unsigned) bt_mesh.iv_index);
+ return false;
+ }
+
+ if (iv_update) {
+ /* Nothing to do */
+ BT_DBG("Already in IV Update in Progress state");
+ return false;
+ }
+ } else {
+ /* We're currently in Normal mode */
+
+ if (iv_index == bt_mesh.iv_index) {
+ BT_DBG("Same IV Index in normal mode");
+ return false;
+ }
+
+ if (iv_index < bt_mesh.iv_index ||
+ iv_index > bt_mesh.iv_index + 42) {
+ BT_ERR("IV Index out of sync: 0x%08x != 0x%08x",
+ (unsigned) iv_index,
+ (unsigned) bt_mesh.iv_index);
+ return false;
+ }
+
+ if (iv_index > bt_mesh.iv_index + 1) {
+ BT_WARN("Performing IV Index Recovery");
+ memset(bt_mesh.rpl, 0, sizeof(bt_mesh.rpl));
+ bt_mesh.iv_index = iv_index;
+ bt_mesh.seq = 0;
+ goto do_update;
+ }
+
+ if (iv_index == bt_mesh.iv_index + 1 && !iv_update) {
+ BT_WARN("Ignoring new index in normal mode");
+ return false;
+ }
+
+ if (!iv_update) {
+ /* Nothing to do */
+ BT_DBG("Already in Normal state");
+ return false;
+ }
+ }
+
+ if (!(IS_ENABLED(CONFIG_BT_MESH_IV_UPDATE_TEST) &&
+ atomic_test_bit(bt_mesh.flags, BT_MESH_IVU_TEST))) {
+ if (bt_mesh.ivu_duration < BT_MESH_IVU_MIN_HOURS) {
+ BT_WARN("IV Update before minimum duration");
+ return false;
+ }
+ }
+
+ /* Defer change to Normal Operation if there are pending acks */
+ if (!iv_update && bt_mesh_tx_in_progress()) {
+ BT_WARN("IV Update deferred because of pending transfer");
+ atomic_set_bit(bt_mesh.flags, BT_MESH_IVU_PENDING);
+ return false;
+ }
+
+do_update:
+ atomic_set_bit_to(bt_mesh.flags, BT_MESH_IVU_IN_PROGRESS, iv_update);
+ bt_mesh.ivu_duration = 0U;
+
+ if (iv_update) {
+ bt_mesh.iv_index = iv_index;
+ BT_DBG("IV Update state entered. New index 0x%08x",
+ (unsigned) bt_mesh.iv_index);
+
+ bt_mesh_rpl_reset();
+ } else {
+ BT_DBG("Normal mode entered");
+ bt_mesh.seq = 0;
+ }
+
+ k_delayed_work_submit(&bt_mesh.ivu_timer, BT_MESH_IVU_TIMEOUT);
+
+ for (i = 0; i < ARRAY_SIZE(bt_mesh.sub); i++) {
+ if (bt_mesh.sub[i].net_idx != BT_MESH_KEY_UNUSED) {
+ bt_mesh_net_beacon_update(&bt_mesh.sub[i]);
+ }
+ }
+
+ if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
+ bt_mesh_store_iv(false);
+ }
+
+ return true;
+}
+
+u32_t bt_mesh_next_seq(void)
+{
+ u32_t seq = bt_mesh.seq++;
+
+ if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
+ bt_mesh_store_seq();
+ }
+
+ if (!atomic_test_bit(bt_mesh.flags, BT_MESH_IVU_IN_PROGRESS) &&
+ bt_mesh.seq > IV_UPDATE_SEQ_LIMIT &&
+ bt_mesh_subnet_get(BT_MESH_KEY_PRIMARY)) {
+ bt_mesh_beacon_ivu_initiator(true);
+ bt_mesh_net_iv_update(bt_mesh.iv_index + 1, true);
+ bt_mesh_net_sec_update(NULL);
+ }
+
+ return seq;
+}
+
+int bt_mesh_net_resend(struct bt_mesh_subnet *sub, struct os_mbuf *buf,
+ bool new_key, const struct bt_mesh_send_cb *cb,
+ void *cb_data)
+{
+ const u8_t *enc, *priv;
+ u32_t seq;
+ u16_t dst;
+ int err;
+
+ BT_DBG("net_idx 0x%04x new_key %u len %u", sub->net_idx, new_key,
+ buf->om_len);
+
+ enc = sub->keys[new_key].enc;
+ priv = sub->keys[new_key].privacy;
+
+ err = bt_mesh_net_obfuscate(buf->om_data, BT_MESH_NET_IVI_TX, priv);
+ if (err) {
+ BT_ERR("deobfuscate failed (err %d)", err);
+ return err;
+ }
+
+ err = bt_mesh_net_decrypt(enc, buf, BT_MESH_NET_IVI_TX, false);
+ if (err) {
+ BT_ERR("decrypt failed (err %d)", err);
+ return err;
+ }
+
+ seq = bt_mesh_next_seq();
+ buf->om_data[2] = seq >> 16;
+ buf->om_data[3] = seq >> 8;
+ buf->om_data[4] = seq;
+
+ /* Get destination, in case it's a proxy client */
+ dst = DST(buf->om_data);
+
+ err = bt_mesh_net_encrypt(enc, buf, BT_MESH_NET_IVI_TX, false);
+ if (err) {
+ BT_ERR("encrypt failed (err %d)", err);
+ return err;
+ }
+
+ err = bt_mesh_net_obfuscate(buf->om_data, BT_MESH_NET_IVI_TX, priv);
+ if (err) {
+ BT_ERR("obfuscate failed (err %d)", err);
+ return err;
+ }
+
+ if (IS_ENABLED(CONFIG_BT_MESH_GATT_PROXY) &&
+ bt_mesh_proxy_relay(buf, dst)) {
+ send_cb_finalize(cb, cb_data);
+ } else {
+ bt_mesh_adv_send(buf, cb, cb_data);
+ }
+
+ return 0;
+}
+
+static void bt_mesh_net_local(struct ble_npl_event *work)
+{
+ struct os_mbuf *buf;
+
+ while ((buf = net_buf_slist_get(&bt_mesh.local_queue))) {
+ BT_DBG("len %u: %s", buf->om_len, bt_hex(buf->om_data, buf->om_len));
+ bt_mesh_net_recv(buf, 0, BT_MESH_NET_IF_LOCAL);
+ net_buf_unref(buf);
+ }
+}
+
+int bt_mesh_net_encode(struct bt_mesh_net_tx *tx, struct os_mbuf *buf,
+ bool proxy)
+{
+ const bool ctl = (tx->ctx->app_idx == BT_MESH_KEY_UNUSED);
+ u32_t seq_val;
+ u8_t nid;
+ const u8_t *enc, *priv;
+ u8_t *seq;
+ int err;
+
+ if (ctl && net_buf_simple_tailroom(buf) < 8) {
+ BT_ERR("Insufficient MIC space for CTL PDU");
+ return -EINVAL;
+ } else if (net_buf_simple_tailroom(buf) < 4) {
+ BT_ERR("Insufficient MIC space for PDU");
+ return -EINVAL;
+ }
+
+ BT_DBG("src 0x%04x dst 0x%04x ctl %u seq 0x%06x",
+ tx->src, tx->ctx->addr, ctl, bt_mesh.seq);
+
+ net_buf_simple_push_be16(buf, tx->ctx->addr);
+ net_buf_simple_push_be16(buf, tx->src);
+
+ seq = net_buf_simple_push(buf, 3);
+ seq_val = bt_mesh_next_seq();
+ seq[0] = seq_val >> 16;
+ seq[1] = seq_val >> 8;
+ seq[2] = seq_val;
+
+ if (ctl) {
+ net_buf_simple_push_u8(buf, tx->ctx->send_ttl | 0x80);
+ } else {
+ net_buf_simple_push_u8(buf, tx->ctx->send_ttl);
+ }
+
+ if (IS_ENABLED(CONFIG_BT_MESH_LOW_POWER) && tx->friend_cred) {
+ if (friend_cred_get(tx->sub, BT_MESH_ADDR_UNASSIGNED,
+ &nid, &enc, &priv)) {
+ BT_WARN("Falling back to master credentials");
+
+ tx->friend_cred = 0;
+
+ nid = tx->sub->keys[tx->sub->kr_flag].nid;
+ enc = tx->sub->keys[tx->sub->kr_flag].enc;
+ priv = tx->sub->keys[tx->sub->kr_flag].privacy;
+ }
+ } else {
+ tx->friend_cred = 0;
+ nid = tx->sub->keys[tx->sub->kr_flag].nid;
+ enc = tx->sub->keys[tx->sub->kr_flag].enc;
+ priv = tx->sub->keys[tx->sub->kr_flag].privacy;
+ }
+
+ net_buf_simple_push_u8(buf, (nid | (BT_MESH_NET_IVI_TX & 1) << 7));
+
+ err = bt_mesh_net_encrypt(enc, buf, BT_MESH_NET_IVI_TX, proxy);
+ if (err) {
+ return err;
+ }
+
+ return bt_mesh_net_obfuscate(buf->om_data, BT_MESH_NET_IVI_TX, priv);
+}
+
+int bt_mesh_net_send(struct bt_mesh_net_tx *tx, struct os_mbuf *buf,
+ const struct bt_mesh_send_cb *cb, void *cb_data)
+{
+ int err;
+
+ BT_DBG("src 0x%04x dst 0x%04x len %u headroom %zu tailroom %zu",
+ tx->src, tx->ctx->addr, buf->om_len, net_buf_headroom(buf),
+ net_buf_tailroom(buf));
+ BT_DBG("Payload len %u: %s", buf->om_len, bt_hex(buf->om_data, buf->om_len));
+ BT_DBG("Seq 0x%06x", bt_mesh.seq);
+
+ if (tx->ctx->send_ttl == BT_MESH_TTL_DEFAULT) {
+ tx->ctx->send_ttl = bt_mesh_default_ttl_get();
+ }
+
+ err = bt_mesh_net_encode(tx, buf, false);
+ if (err) {
+ goto done;
+ }
+
+ BT_DBG("encoded %u bytes: %s", buf->om_len,
+ bt_hex(buf->om_data, buf->om_len));
+
+ /* Deliver to GATT Proxy Clients if necessary. Mesh spec 3.4.5.2:
+ * "The output filter of the interface connected to advertising or
+ * GATT bearers shall drop all messages with TTL value set to 1."
+ */
+ if (MYNEWT_VAL(BLE_MESH_GATT_PROXY) &&
+ tx->ctx->send_ttl != 1) {
+ if (bt_mesh_proxy_relay(buf, tx->ctx->addr) &&
+ BT_MESH_ADDR_IS_UNICAST(tx->ctx->addr)) {
+ /* Notify completion if this only went
+ * through the Mesh Proxy.
+ */
+ send_cb_finalize(cb, cb_data);
+
+ err = 0;
+ goto done;
+ }
+ }
+
+ /* Deliver to local network interface if necessary */
+ if (bt_mesh_fixed_group_match(tx->ctx->addr) ||
+ bt_mesh_elem_find(tx->ctx->addr)) {
+ if (cb && cb->start) {
+ cb->start(0, 0, cb_data);
+ }
+ net_buf_slist_put(&bt_mesh.local_queue, net_buf_ref(buf));
+ if (cb && cb->end) {
+ cb->end(0, cb_data);
+ }
+ k_work_submit(&bt_mesh.local_work);
+ } else if (tx->ctx->send_ttl != 1) {
+ /* Deliver to to the advertising network interface. Mesh spec
+ * 3.4.5.2: "The output filter of the interface connected to
+ * advertising or GATT bearers shall drop all messages with
+ * TTL value set to 1."
+ */
+ bt_mesh_adv_send(buf, cb, cb_data);
+ }
+
+done:
+ net_buf_unref(buf);
+ return err;
+}
+
+static bool auth_match(struct bt_mesh_subnet_keys *keys,
+ const u8_t net_id[8], u8_t flags,
+ u32_t iv_index, const u8_t auth[8])
+{
+ u8_t net_auth[8];
+
+ if (memcmp(net_id, keys->net_id, 8)) {
+ return false;
+ }
+
+ bt_mesh_beacon_auth(keys->beacon, flags, keys->net_id, iv_index,
+ net_auth);
+
+ if (memcmp(auth, net_auth, 8)) {
+ BT_WARN("Authentication Value %s != %s",
+ bt_hex(auth, 8), bt_hex(net_auth, 8));
+ return false;
+ }
+
+ return true;
+}
+
+struct bt_mesh_subnet *bt_mesh_subnet_find(const u8_t net_id[8], u8_t flags,
+ u32_t iv_index, const u8_t auth[8],
+ bool *new_key)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(bt_mesh.sub); i++) {
+ struct bt_mesh_subnet *sub = &bt_mesh.sub[i];
+
+ if (sub->net_idx == BT_MESH_KEY_UNUSED) {
+ continue;
+ }
+
+ if (auth_match(&sub->keys[0], net_id, flags, iv_index, auth)) {
+ *new_key = false;
+ return sub;
+ }
+
+ if (sub->kr_phase == BT_MESH_KR_NORMAL) {
+ continue;
+ }
+
+ if (auth_match(&sub->keys[1], net_id, flags, iv_index, auth)) {
+ *new_key = true;
+ return sub;
+ }
+ }
+
+ return NULL;
+}
+
+static int net_decrypt(struct bt_mesh_subnet *sub, const u8_t *enc,
+ const u8_t *priv, const u8_t *data,
+ size_t data_len, struct bt_mesh_net_rx *rx,
+ struct os_mbuf *buf)
+{
+ BT_DBG("NID 0x%02x net_idx 0x%04x", NID(data), sub->net_idx);
+ BT_DBG("IVI %u net->iv_index 0x%08x", IVI(data),
+ (unsigned) bt_mesh.iv_index);
+
+ rx->old_iv = (IVI(data) != (bt_mesh.iv_index & 0x01));
+
+ net_buf_simple_init(buf, 0);
+ memcpy(net_buf_simple_add(buf, data_len), data, data_len);
+
+ if (bt_mesh_net_obfuscate(buf->om_data, BT_MESH_NET_IVI_RX(rx), priv)) {
+ return -ENOENT;
+ }
+
+ if (rx->net_if == BT_MESH_NET_IF_ADV && msg_cache_match(rx, buf)) {
+ BT_WARN("Duplicate found in Network Message Cache");
+ return -EALREADY;
+ }
+
+ rx->ctx.addr = SRC(buf->om_data);
+ if (!BT_MESH_ADDR_IS_UNICAST(rx->ctx.addr)) {
+ BT_WARN("Ignoring non-unicast src addr 0x%04x", rx->ctx.addr);
+ return -EINVAL;
+ }
+
+ BT_DBG("src 0x%04x", rx->ctx.addr);
+
+ if ((MYNEWT_VAL(BLE_MESH_PROXY)) &&
+ rx->net_if == BT_MESH_NET_IF_PROXY_CFG) {
+ return bt_mesh_net_decrypt(enc, buf, BT_MESH_NET_IVI_RX(rx),
+ true);
+ }
+
+ return bt_mesh_net_decrypt(enc, buf, BT_MESH_NET_IVI_RX(rx), false);
+}
+
+static int friend_decrypt(struct bt_mesh_subnet *sub, const u8_t *data,
+ size_t data_len, struct bt_mesh_net_rx *rx,
+ struct os_mbuf *buf)
+{
+ int i;
+
+ BT_DBG("NID 0x%02x net_idx 0x%04x", NID(data), sub->net_idx);
+
+ for (i = 0; i < ARRAY_SIZE(friend_cred); i++) {
+ struct friend_cred *cred = &friend_cred[i];
+
+ if (cred->net_idx != sub->net_idx) {
+ continue;
+ }
+
+ if (NID(data) == cred->cred[0].nid &&
+ !net_decrypt(sub, cred->cred[0].enc, cred->cred[0].privacy,
+ data, data_len, rx, buf)) {
+ return 0;
+ }
+
+ if (sub->kr_phase == BT_MESH_KR_NORMAL) {
+ continue;
+ }
+
+ if (NID(data) == cred->cred[1].nid &&
+ !net_decrypt(sub, cred->cred[1].enc, cred->cred[1].privacy,
+ data, data_len, rx, buf)) {
+ rx->new_key = 1;
+ return 0;
+ }
+ }
+
+ return -ENOENT;
+}
+
+static bool net_find_and_decrypt(const u8_t *data, size_t data_len,
+ struct bt_mesh_net_rx *rx,
+ struct os_mbuf *buf)
+{
+ struct bt_mesh_subnet *sub;
+ unsigned int i;
+
+ BT_DBG("");
+
+ for (i = 0; i < ARRAY_SIZE(bt_mesh.sub); i++) {
+ sub = &bt_mesh.sub[i];
+ if (sub->net_idx == BT_MESH_KEY_UNUSED) {
+ continue;
+ }
+
+ if ((IS_ENABLED(CONFIG_BT_MESH_LOW_POWER) ||
+ IS_ENABLED(CONFIG_BT_MESH_FRIEND)) &&
+ !friend_decrypt(sub, data, data_len, rx, buf)) {
+ rx->friend_cred = 1U;
+ rx->ctx.net_idx = sub->net_idx;
+ rx->sub = sub;
+ return true;
+ }
+
+ if (NID(data) == sub->keys[0].nid &&
+ !net_decrypt(sub, sub->keys[0].enc, sub->keys[0].privacy,
+ data, data_len, rx, buf)) {
+ rx->ctx.net_idx = sub->net_idx;
+ rx->sub = sub;
+ return true;
+ }
+
+ if (sub->kr_phase == BT_MESH_KR_NORMAL) {
+ continue;
+ }
+
+ if (NID(data) == sub->keys[1].nid &&
+ !net_decrypt(sub, sub->keys[1].enc, sub->keys[1].privacy,
+ data, data_len, rx, buf)) {
+ rx->new_key = 1;
+ rx->ctx.net_idx = sub->net_idx;
+ rx->sub = sub;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/* Relaying from advertising to the advertising bearer should only happen
+ * if the Relay state is set to enabled. Locally originated packets always
+ * get sent to the advertising bearer. If the packet came in through GATT,
+ * then we should only relay it if the GATT Proxy state is enabled.
+ */
+static bool relay_to_adv(enum bt_mesh_net_if net_if)
+{
+ switch (net_if) {
+ case BT_MESH_NET_IF_LOCAL:
+ return true;
+ case BT_MESH_NET_IF_ADV:
+ return (bt_mesh_relay_get() == BT_MESH_RELAY_ENABLED);
+ case BT_MESH_NET_IF_PROXY:
+ return (bt_mesh_gatt_proxy_get() == BT_MESH_GATT_PROXY_ENABLED);
+ default:
+ return false;
+ }
+}
+
+static void bt_mesh_net_relay(struct os_mbuf *sbuf,
+ struct bt_mesh_net_rx *rx)
+{
+ const u8_t *enc, *priv;
+ struct os_mbuf *buf;
+ u8_t nid, transmit;
+
+ if (rx->net_if == BT_MESH_NET_IF_LOCAL) {
+ /* Locally originated PDUs with TTL=1 will only be delivered
+ * to local elements as per Mesh Profile 1.0 section 3.4.5.2:
+ * "The output filter of the interface connected to
+ * advertising or GATT bearers shall drop all messages with
+ * TTL value set to 1."
+ */
+ if (rx->ctx.recv_ttl == 1) {
+ return;
+ }
+ } else {
+ if (rx->ctx.recv_ttl <= 1) {
+ return;
+ }
+ }
+
+ if (rx->net_if == BT_MESH_NET_IF_ADV &&
+ bt_mesh_relay_get() != BT_MESH_RELAY_ENABLED &&
+ bt_mesh_gatt_proxy_get() != BT_MESH_GATT_PROXY_ENABLED) {
+ return;
+ }
+
+ BT_DBG("TTL %u CTL %u dst 0x%04x", rx->ctx.recv_ttl, rx->ctl,
+ rx->ctx.recv_dst);
+
+ /* The Relay Retransmit state is only applied to adv-adv relaying.
+ * Anything else (like GATT to adv, or locally originated packets)
+ * use the Network Transmit state.
+ */
+ if (rx->net_if == BT_MESH_NET_IF_ADV) {
+ transmit = bt_mesh_relay_retransmit_get();
+ } else {
+ transmit = bt_mesh_net_transmit_get();
+ }
+
+ buf = bt_mesh_adv_create(BT_MESH_ADV_DATA, transmit, K_NO_WAIT);
+ if (!buf) {
+ BT_ERR("Out of relay buffers");
+ return;
+ }
+
+ /* Only decrement TTL for non-locally originated packets */
+ if (rx->net_if != BT_MESH_NET_IF_LOCAL) {
+ /* Leave CTL bit intact */
+ sbuf->om_data[1] &= 0x80;
+ sbuf->om_data[1] |= rx->ctx.recv_ttl - 1;
+ }
+
+ net_buf_add_mem(buf, sbuf->om_data, sbuf->om_len);
+
+ enc = rx->sub->keys[rx->sub->kr_flag].enc;
+ priv = rx->sub->keys[rx->sub->kr_flag].privacy;
+ nid = rx->sub->keys[rx->sub->kr_flag].nid;
+
+ BT_DBG("Relaying packet. TTL is now %u", TTL(buf->om_data));
+
+ /* Update NID if RX or RX was with friend credentials */
+ if (rx->friend_cred) {
+ buf->om_data[0] &= 0x80; /* Clear everything except IVI */
+ buf->om_data[0] |= nid;
+ }
+
+ /* We re-encrypt and obfuscate using the received IVI rather than
+ * the normal TX IVI (which may be different) since the transport
+ * layer nonce includes the IVI.
+ */
+ if (bt_mesh_net_encrypt(enc, buf, BT_MESH_NET_IVI_RX(rx), false)) {
+ BT_ERR("Re-encrypting failed");
+ goto done;
+ }
+
+ if (bt_mesh_net_obfuscate(buf->om_data, BT_MESH_NET_IVI_RX(rx), priv)) {
+ BT_ERR("Re-obfuscating failed");
+ goto done;
+ }
+
+ BT_DBG("encoded %u bytes: %s", buf->om_len,
+ bt_hex(buf->om_data, buf->om_len));
+
+ /* Sending to the GATT bearer should only happen if GATT Proxy
+ * is enabled or the message originates from the local node.
+ */
+ if (IS_ENABLED(CONFIG_BT_MESH_GATT_PROXY) &&
+ (bt_mesh_gatt_proxy_get() == BT_MESH_GATT_PROXY_ENABLED ||
+ rx->net_if == BT_MESH_NET_IF_LOCAL)) {
+ if (bt_mesh_proxy_relay(buf, rx->ctx.recv_dst) &&
+ BT_MESH_ADDR_IS_UNICAST(rx->ctx.recv_dst)) {
+ goto done;
+ }
+ }
+
+ if (relay_to_adv(rx->net_if)) {
+ bt_mesh_adv_send(buf, NULL, NULL);
+ }
+
+done:
+ net_buf_unref(buf);
+}
+
+void bt_mesh_net_header_parse(struct os_mbuf *buf,
+ struct bt_mesh_net_rx *rx)
+{
+ rx->old_iv = (IVI(buf->om_data) != (bt_mesh.iv_index & 0x01));
+ rx->ctl = CTL(buf->om_data);
+ rx->ctx.recv_ttl = TTL(buf->om_data);
+ rx->seq = SEQ(buf->om_data);
+ rx->ctx.addr = SRC(buf->om_data);
+ rx->ctx.recv_dst = DST(buf->om_data);
+}
+
+int bt_mesh_net_decode(struct os_mbuf *data, enum bt_mesh_net_if net_if,
+ struct bt_mesh_net_rx *rx, struct os_mbuf *buf)
+{
+ if (data->om_len < BT_MESH_NET_MIN_PDU_LEN) {
+ BT_WARN("Dropping too short mesh packet (len %u)", data->om_len);
+ BT_WARN("%s", bt_hex(data->om_data, data->om_len));
+ return -EINVAL;
+ }
+
+ if (net_if == BT_MESH_NET_IF_ADV && check_dup(data)) {
+ BT_DBG("duplicate packet; dropping %u bytes: %s", data->om_len,
+ bt_hex(data->om_data, data->om_len));
+ return -EINVAL;
+ }
+
+ BT_DBG("%u bytes: %s", data->om_len, bt_hex(data->om_data, data->om_len));
+
+ rx->net_if = net_if;
+
+ if (!net_find_and_decrypt(data->om_data, data->om_len, rx, buf)) {
+ BT_DBG("Unable to find matching net for packet");
+ return -ENOENT;
+ }
+
+ /* Initialize AppIdx to a sane value */
+ rx->ctx.app_idx = BT_MESH_KEY_UNUSED;
+
+ rx->ctx.recv_ttl = TTL(buf->om_data);
+
+ /* Default to responding with TTL 0 for non-routed messages */
+ if (rx->ctx.recv_ttl == 0) {
+ rx->ctx.send_ttl = 0;
+ } else {
+ rx->ctx.send_ttl = BT_MESH_TTL_DEFAULT;
+ }
+
+ rx->ctl = CTL(buf->om_data);
+ rx->seq = SEQ(buf->om_data);
+ rx->ctx.recv_dst = DST(buf->om_data);
+
+ BT_DBG("Decryption successful. Payload len %u: %s", buf->om_len,
+ bt_hex(buf->om_data, buf->om_len));
+
+ if (net_if != BT_MESH_NET_IF_PROXY_CFG &&
+ rx->ctx.recv_dst == BT_MESH_ADDR_UNASSIGNED) {
+ BT_ERR("Destination address is unassigned; dropping packet");
+ return -EBADMSG;
+ }
+
+ if (BT_MESH_ADDR_IS_RFU(rx->ctx.recv_dst)) {
+ BT_ERR("Destination address is RFU; dropping packet");
+ return -EBADMSG;
+ }
+
+ if (net_if != BT_MESH_NET_IF_LOCAL && bt_mesh_elem_find(rx->ctx.addr)) {
+ BT_DBG("Dropping locally originated packet");
+ return -EBADMSG;
+ }
+
+ BT_DBG("src 0x%04x dst 0x%04x ttl %u", rx->ctx.addr, rx->ctx.recv_dst,
+ rx->ctx.recv_ttl);
+ BT_DBG("PDU: %s", bt_hex(buf->om_data, buf->om_len));
+
+ return 0;
+}
+
+void bt_mesh_net_recv(struct os_mbuf *data, s8_t rssi,
+ enum bt_mesh_net_if net_if)
+{
+ struct os_mbuf *buf = NET_BUF_SIMPLE(29);
+ struct bt_mesh_net_rx rx = { .ctx.recv_rssi = rssi };
+ struct net_buf_simple_state state;
+
+ BT_DBG("rssi %d net_if %u", rssi, net_if);
+
+ if (!bt_mesh_is_provisioned()) {
+ BT_ERR("Not provisioned; dropping packet");
+ goto done;
+ }
+
+ if (bt_mesh_net_decode(data, net_if, &rx, buf)) {
+ goto done;
+ }
+
+ /* Save the state so the buffer can later be relayed */
+ net_buf_simple_save(buf, &state);
+
+ rx.local_match = (bt_mesh_fixed_group_match(rx.ctx.recv_dst) ||
+ bt_mesh_elem_find(rx.ctx.recv_dst));
+
+ if ((MYNEWT_VAL(BLE_MESH_GATT_PROXY)) &&
+ net_if == BT_MESH_NET_IF_PROXY) {
+ bt_mesh_proxy_addr_add(data, rx.ctx.addr);
+
+ if (bt_mesh_gatt_proxy_get() == BT_MESH_GATT_PROXY_DISABLED &&
+ !rx.local_match) {
+ BT_INFO("Proxy is disabled; ignoring message");
+ goto done;
+ }
+ }
+
+ /* The transport layer has indicated that it has rejected the message,
+ * but would like to see it again if it is received in the future.
+ * This can happen if a message is received when the device is in
+ * Low Power mode, but the message was not encrypted with the friend
+ * credentials. Remove it from the message cache so that we accept
+ * it again in the future.
+ */
+ if (bt_mesh_trans_recv(buf, &rx) == -EAGAIN) {
+ BT_WARN("Removing rejected message from Network Message Cache");
+ msg_cache[rx.msg_cache_idx] = 0ULL;
+ /* Rewind the next index now that we're not using this entry */
+ msg_cache_next = rx.msg_cache_idx;
+ }
+
+ /* Relay if this was a group/virtual address, or if the destination
+ * was neither a local element nor an LPN we're Friends for.
+ */
+ if (!BT_MESH_ADDR_IS_UNICAST(rx.ctx.recv_dst) ||
+ (!rx.local_match && !rx.friend_match)) {
+ net_buf_simple_restore(buf, &state);
+ bt_mesh_net_relay(buf, &rx);
+ }
+
+done:
+ os_mbuf_free_chain(buf);
+}
+
+static void ivu_refresh(struct ble_npl_event *work)
+{
+ bt_mesh.ivu_duration += BT_MESH_IVU_HOURS;
+
+ BT_DBG("%s for %u hour%s",
+ atomic_test_bit(bt_mesh.flags, BT_MESH_IVU_IN_PROGRESS) ?
+ "IVU in Progress" : "IVU Normal mode",
+ bt_mesh.ivu_duration, bt_mesh.ivu_duration == 1 ? "" : "s");
+
+ if (bt_mesh.ivu_duration < BT_MESH_IVU_MIN_HOURS) {
+ if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
+ bt_mesh_store_iv(true);
+ }
+
+ k_delayed_work_submit(&bt_mesh.ivu_timer, BT_MESH_IVU_TIMEOUT);
+ return;
+ }
+
+ if (atomic_test_bit(bt_mesh.flags, BT_MESH_IVU_IN_PROGRESS)) {
+ bt_mesh_beacon_ivu_initiator(true);
+ bt_mesh_net_iv_update(bt_mesh.iv_index, false);
+ } else if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
+ bt_mesh_store_iv(true);
+ }
+}
+
+void bt_mesh_net_start(void)
+{
+ if (bt_mesh_beacon_get() == BT_MESH_BEACON_ENABLED) {
+ bt_mesh_beacon_enable();
+ } else {
+ bt_mesh_beacon_disable();
+ }
+
+ if (IS_ENABLED(CONFIG_BT_MESH_GATT_PROXY) &&
+ bt_mesh_gatt_proxy_get() != BT_MESH_GATT_PROXY_NOT_SUPPORTED) {
+ bt_mesh_proxy_gatt_enable();
+ bt_mesh_adv_update();
+ }
+
+ if (IS_ENABLED(CONFIG_BT_MESH_LOW_POWER)) {
+ bt_mesh_lpn_init();
+ } else {
+ bt_mesh_scan_enable();
+ }
+
+ if (IS_ENABLED(CONFIG_BT_MESH_FRIEND)) {
+ bt_mesh_friend_init();
+ }
+
+ if (IS_ENABLED(CONFIG_BT_MESH_PROV)) {
+ u16_t net_idx = bt_mesh.sub[0].net_idx;
+ u16_t addr = bt_mesh_primary_addr();
+
+ bt_mesh_prov_complete(net_idx, addr);
+ }
+}
+
+void bt_mesh_net_init(void)
+{
+ k_delayed_work_init(&bt_mesh.ivu_timer, ivu_refresh);
+
+ k_work_init(&bt_mesh.local_work, bt_mesh_net_local);
+ net_buf_slist_init(&bt_mesh.local_queue);
+}
diff --git a/src/libs/mynewt-nimble/nimble/host/mesh/src/net.h b/src/libs/mynewt-nimble/nimble/host/mesh/src/net.h
new file mode 100644
index 00000000..976da005
--- /dev/null
+++ b/src/libs/mynewt-nimble/nimble/host/mesh/src/net.h
@@ -0,0 +1,412 @@
+/* Bluetooth Mesh */
+
+/*
+ * Copyright (c) 2017 Intel Corporation
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#ifndef __NET_H__
+#define __NET_H__
+
+#define BT_MESH_NET_FLAG_KR BIT(0)
+#define BT_MESH_NET_FLAG_IVU BIT(1)
+
+#define BT_MESH_KR_NORMAL 0x00
+#define BT_MESH_KR_PHASE_1 0x01
+#define BT_MESH_KR_PHASE_2 0x02
+#define BT_MESH_KR_PHASE_3 0x03
+
+#define BT_MESH_IV_UPDATE(flags) ((flags >> 1) & 0x01)
+#define BT_MESH_KEY_REFRESH(flags) (flags & 0x01)
+
+#include <stdbool.h>
+#include "atomic.h"
+#include "mesh/mesh.h"
+#include "mesh/glue.h"
+
+/* How many hours in between updating IVU duration */
+#define BT_MESH_IVU_MIN_HOURS 96
+#define BT_MESH_IVU_HOURS (BT_MESH_IVU_MIN_HOURS / \
+ CONFIG_BT_MESH_IVU_DIVIDER)
+#define BT_MESH_IVU_TIMEOUT K_HOURS(BT_MESH_IVU_HOURS)
+
+struct bt_mesh_app_key {
+ u16_t net_idx;
+ u16_t app_idx;
+ bool updated;
+ struct bt_mesh_app_keys {
+ u8_t id;
+ u8_t val[16];
+ } keys[2];
+};
+
+struct bt_mesh_node {
+ u16_t addr;
+ u16_t net_idx;
+ u8_t dev_key[16];
+ u8_t num_elem;
+};
+
+struct bt_mesh_subnet {
+ u32_t beacon_sent; /* Timestamp of last sent beacon */
+ u8_t beacons_last; /* Number of beacons during last
+ * observation window
+ */
+ u8_t beacons_cur; /* Number of beaconds observed during
+ * currently ongoing window.
+ */
+
+ u8_t beacon_cache[21]; /* Cached last authenticated beacon */
+
+ u16_t net_idx; /* NetKeyIndex */
+
+ bool kr_flag; /* Key Refresh Flag */
+ u8_t kr_phase; /* Key Refresh Phase */
+
+ u8_t node_id; /* Node Identity State */
+ u32_t node_id_start; /* Node Identity started timestamp */
+
+ u8_t auth[8]; /* Beacon Authentication Value */
+
+ struct bt_mesh_subnet_keys {
+ u8_t net[16]; /* NetKey */
+ u8_t nid; /* NID */
+ u8_t enc[16]; /* EncKey */
+ u8_t net_id[8]; /* Network ID */
+#if (MYNEWT_VAL(BLE_MESH_GATT_PROXY))
+ u8_t identity[16]; /* IdentityKey */
+#endif
+ u8_t privacy[16]; /* PrivacyKey */
+ u8_t beacon[16]; /* BeaconKey */
+ } keys[2];
+};
+
+struct bt_mesh_rpl {
+ u16_t src;
+ bool old_iv;
+#if (MYNEWT_VAL(BLE_MESH_SETTINGS))
+ bool store;
+#endif
+ u32_t seq;
+};
+
+#if MYNEWT_VAL(BLE_MESH_FRIEND)
+#define FRIEND_SEG_RX MYNEWT_VAL(BLE_MESH_FRIEND_SEG_RX)
+#define FRIEND_SUB_LIST_SIZE MYNEWT_VAL(BLE_MESH_FRIEND_SUB_LIST_SIZE)
+#else
+#define FRIEND_SEG_RX 0
+#define FRIEND_SUB_LIST_SIZE 0
+#endif
+
+struct bt_mesh_friend {
+ u16_t lpn;
+ u8_t recv_delay;
+ u8_t fsn:1,
+ send_last:1,
+ pending_req:1,
+ sec_update:1,
+ pending_buf:1,
+ valid:1,
+ established:1;
+ s32_t poll_to;
+ u8_t num_elem;
+ u16_t lpn_counter;
+ u16_t counter;
+
+ u16_t net_idx;
+
+ u16_t sub_list[FRIEND_SUB_LIST_SIZE];
+
+ struct k_delayed_work timer;
+
+ struct bt_mesh_friend_seg {
+ struct net_buf_slist_t queue;
+
+ /* The target number of segments, i.e. not necessarily
+ * the current number of segments, in the queue. This is
+ * used for Friend Queue free space calculations.
+ */
+ u8_t seg_count;
+ } seg[FRIEND_SEG_RX];
+
+ struct os_mbuf *last;
+
+ struct net_buf_slist_t queue;
+ u32_t queue_size;
+
+ /* Friend Clear Procedure */
+ struct {
+ u32_t start; /* Clear Procedure start */
+ u16_t frnd; /* Previous Friend's address */
+ u16_t repeat_sec; /* Repeat timeout in seconds */
+ struct k_delayed_work timer; /* Repeat timer */
+ } clear;
+};
+
+#if (MYNEWT_VAL(BLE_MESH_LOW_POWER))
+#define LPN_GROUPS CONFIG_BT_MESH_LPN_GROUPS
+#else
+#define LPN_GROUPS 0
+#endif
+
+/* Low Power Node state */
+struct bt_mesh_lpn {
+ enum __packed {
+ BT_MESH_LPN_DISABLED, /* LPN feature is disabled */
+ BT_MESH_LPN_CLEAR, /* Clear in progress */
+ BT_MESH_LPN_TIMER, /* Waiting for auto timer expiry */
+ BT_MESH_LPN_ENABLED, /* LPN enabled, but no Friend */
+ BT_MESH_LPN_REQ_WAIT, /* Wait before scanning for offers */
+ BT_MESH_LPN_WAIT_OFFER, /* Friend Req sent */
+ BT_MESH_LPN_ESTABLISHED, /* Friendship established */
+ BT_MESH_LPN_RECV_DELAY, /* Poll sent, waiting ReceiveDelay */
+ BT_MESH_LPN_WAIT_UPDATE, /* Waiting for Update or message */
+ } state;
+
+ /* Transaction Number (used for subscription list) */
+ u8_t xact_next;
+ u8_t xact_pending;
+ u8_t sent_req;
+
+ /* Address of our Friend when we're a LPN. Unassigned if we don't
+ * have a friend yet.
+ */
+ u16_t frnd;
+
+ /* Value from the friend offer */
+ u8_t recv_win;
+
+ u8_t req_attempts; /* Number of Request attempts */
+
+ s32_t poll_timeout;
+
+ u8_t groups_changed:1, /* Friend Subscription List needs updating */
+ pending_poll:1, /* Poll to be sent after subscription */
+ disable:1, /* Disable LPN after clearing */
+ fsn:1, /* Friend Sequence Number */
+ established:1, /* Friendship established */
+ clear_success:1; /* Friend Clear Confirm received */
+
+ /* Friend Queue Size */
+ u8_t queue_size;
+
+ /* LPNCounter */
+ u16_t counter;
+
+ /* Previous Friend of this LPN */
+ u16_t old_friend;
+
+ /* Duration reported for last advertising packet */
+ u16_t adv_duration;
+
+ /* Next LPN related action timer */
+ struct k_delayed_work timer;
+
+ /* Subscribed groups */
+ u16_t groups[LPN_GROUPS];
+
+ /* Bit fields for tracking which groups the Friend knows about */
+ ATOMIC_DEFINE(added, LPN_GROUPS);
+ ATOMIC_DEFINE(pending, LPN_GROUPS);
+ ATOMIC_DEFINE(to_remove, LPN_GROUPS);
+};
+
+/* bt_mesh_net.flags */
+enum {
+ BT_MESH_VALID, /* We have been provisioned */
+ BT_MESH_SUSPENDED, /* Network is temporarily suspended */
+ BT_MESH_IVU_IN_PROGRESS, /* IV Update in Progress */
+ BT_MESH_IVU_INITIATOR, /* IV Update initiated by us */
+ BT_MESH_IVU_TEST, /* IV Update test mode */
+ BT_MESH_IVU_PENDING, /* Update blocked by SDU in progress */
+
+ /* pending storage actions, must reside within first 32 flags */
+ BT_MESH_RPL_PENDING,
+ BT_MESH_KEYS_PENDING,
+ BT_MESH_NET_PENDING,
+ BT_MESH_IV_PENDING,
+ BT_MESH_SEQ_PENDING,
+ BT_MESH_HB_PUB_PENDING,
+ 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,
+};
+
+struct bt_mesh_net {
+ u32_t iv_index; /* Current IV Index */
+ u32_t seq; /* Next outgoing sequence number (24 bits) */
+
+ ATOMIC_DEFINE(flags, BT_MESH_FLAG_COUNT);
+
+ /* Local network interface */
+ struct ble_npl_callout local_work;
+ struct net_buf_slist_t local_queue;
+
+#if MYNEWT_VAL(BLE_MESH_FRIEND)
+ /* Friend state, unique for each LPN that we're Friends for */
+ struct bt_mesh_friend frnd[MYNEWT_VAL(BLE_MESH_FRIEND_LPN_COUNT)];
+#endif
+
+#if (MYNEWT_VAL(BLE_MESH_LOW_POWER))
+ struct bt_mesh_lpn lpn; /* Low Power Node state */
+#endif
+
+ /* Number of hours in current IV Update state */
+ u8_t ivu_duration;
+
+ /* Timer to track duration in current IV Update state */
+ struct k_delayed_work ivu_timer;
+
+ 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)];
+
+ struct bt_mesh_rpl rpl[MYNEWT_VAL(BLE_MESH_CRPL)];
+};
+
+/* Network interface */
+enum bt_mesh_net_if {
+ BT_MESH_NET_IF_ADV,
+ BT_MESH_NET_IF_LOCAL,
+ BT_MESH_NET_IF_PROXY,
+ BT_MESH_NET_IF_PROXY_CFG,
+};
+
+/* Decoding context for Network/Transport data */
+struct bt_mesh_net_rx {
+ struct bt_mesh_subnet *sub;
+ struct bt_mesh_msg_ctx ctx;
+ u32_t seq; /* Sequence Number */
+ u8_t old_iv:1, /* iv_index - 1 was used */
+ new_key:1, /* Data was encrypted with updated key */
+ friend_cred:1, /* Data was encrypted with friend cred */
+ ctl:1, /* Network Control */
+ net_if:2, /* Network interface */
+ local_match:1, /* Matched a local element */
+ friend_match:1; /* Matched an LPN we're friends for */
+ u16_t msg_cache_idx; /* Index of entry in message cache */
+};
+
+/* Encoding context for Network/Transport data */
+struct bt_mesh_net_tx {
+ struct bt_mesh_subnet *sub;
+ struct bt_mesh_msg_ctx *ctx;
+ u16_t src;
+ u8_t xmit;
+ u8_t friend_cred:1,
+ aszmic:1,
+ aid:6;
+};
+
+extern struct bt_mesh_net bt_mesh;
+
+#define BT_MESH_NET_IVI_TX (bt_mesh.iv_index - \
+ atomic_test_bit(bt_mesh.flags, \
+ BT_MESH_IVU_IN_PROGRESS))
+#define BT_MESH_NET_IVI_RX(rx) (bt_mesh.iv_index - (rx)->old_iv)
+
+#define BT_MESH_NET_HDR_LEN 9
+
+int bt_mesh_net_keys_create(struct bt_mesh_subnet_keys *keys,
+ const u8_t key[16]);
+
+int bt_mesh_net_create(u16_t idx, u8_t flags, const u8_t key[16],
+ u32_t iv_index);
+
+u8_t bt_mesh_net_flags(struct bt_mesh_subnet *sub);
+
+bool bt_mesh_kr_update(struct bt_mesh_subnet *sub, u8_t new_kr, bool new_key);
+
+void bt_mesh_net_revoke_keys(struct bt_mesh_subnet *sub);
+
+int bt_mesh_net_beacon_update(struct bt_mesh_subnet *sub);
+
+void bt_mesh_rpl_reset(void);
+
+bool bt_mesh_net_iv_update(u32_t iv_index, bool iv_update);
+
+void bt_mesh_net_sec_update(struct bt_mesh_subnet *sub);
+
+struct bt_mesh_subnet *bt_mesh_subnet_get(u16_t net_idx);
+
+struct bt_mesh_subnet *bt_mesh_subnet_find(const u8_t net_id[8], u8_t flags,
+ u32_t iv_index, const u8_t auth[8],
+ bool *new_key);
+
+int bt_mesh_net_encode(struct bt_mesh_net_tx *tx, struct os_mbuf *buf,
+ bool proxy);
+
+int bt_mesh_net_send(struct bt_mesh_net_tx *tx, struct os_mbuf *buf,
+ const struct bt_mesh_send_cb *cb, void *cb_data);
+
+int bt_mesh_net_resend(struct bt_mesh_subnet *sub, struct os_mbuf *buf,
+ bool new_key, const struct bt_mesh_send_cb *cb,
+ void *cb_data);
+
+int bt_mesh_net_decode(struct os_mbuf *data, enum bt_mesh_net_if net_if,
+ struct bt_mesh_net_rx *rx, struct os_mbuf *buf);
+
+void bt_mesh_net_recv(struct os_mbuf *data, s8_t rssi,
+ enum bt_mesh_net_if net_if);
+
+u32_t bt_mesh_next_seq(void);
+
+void bt_mesh_net_start(void);
+
+void bt_mesh_net_init(void);
+void bt_mesh_net_header_parse(struct os_mbuf *buf,
+ struct bt_mesh_net_rx *rx);
+
+/* Friendship Credential Management */
+struct friend_cred {
+ u16_t net_idx;
+ u16_t addr;
+
+ u16_t lpn_counter;
+ u16_t frnd_counter;
+
+ struct {
+ u8_t nid; /* NID */
+ u8_t enc[16]; /* EncKey */
+ u8_t privacy[16]; /* PrivacyKey */
+ } cred[2];
+};
+
+int friend_cred_get(struct bt_mesh_subnet *sub, u16_t addr, u8_t *nid,
+ const u8_t **enc, const u8_t **priv);
+int friend_cred_set(struct friend_cred *cred, u8_t idx, const u8_t net_key[16]);
+void friend_cred_refresh(u16_t net_idx);
+int friend_cred_update(struct bt_mesh_subnet *sub);
+struct friend_cred *friend_cred_create(struct bt_mesh_subnet *sub, u16_t addr,
+ u16_t lpn_counter, u16_t frnd_counter);
+void friend_cred_clear(struct friend_cred *cred);
+int friend_cred_del(u16_t net_idx, u16_t addr);
+
+static inline void send_cb_finalize(const struct bt_mesh_send_cb *cb,
+ void *cb_data)
+{
+ if (!cb) {
+ return;
+ }
+
+ if (cb->start) {
+ cb->start(0, 0, cb_data);
+ }
+
+ if (cb->end) {
+ cb->end(0, cb_data);
+ }
+}
+
+#endif
diff --git a/src/libs/mynewt-nimble/nimble/host/mesh/src/nodes.c b/src/libs/mynewt-nimble/nimble/host/mesh/src/nodes.c
new file mode 100644
index 00000000..127ef21e
--- /dev/null
+++ b/src/libs/mynewt-nimble/nimble/host/mesh/src/nodes.c
@@ -0,0 +1,161 @@
+/*
+ * Copyright (c) 2019 Tobias Svehagen
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include "syscfg/syscfg.h"
+#define MESH_LOG_MODULE BLE_MESH_PROV_LOG
+
+#if MYNEWT_VAL(BLE_MESH_PROVISIONER)
+
+#include "mesh/mesh.h"
+
+#include "mesh_priv.h"
+#include "net.h"
+#include "access.h"
+#include "settings.h"
+
+/*
+ * Check if an address range from addr_start for addr_start + num_elem - 1 is
+ * free for use. When a conflict is found, next will be set to the next address
+ * available after the conflicting range and -EAGAIN will be returned.
+ */
+static int addr_is_free(u16_t addr_start, u8_t num_elem, u16_t *next)
+{
+ const struct bt_mesh_comp *comp = bt_mesh_comp_get();
+ u16_t addr_end = addr_start + num_elem - 1;
+ u16_t other_start, other_end;
+ int i;
+
+ if (comp == NULL) {
+ return -EINVAL;
+ }
+
+ if (!BT_MESH_ADDR_IS_UNICAST(addr_start) ||
+ !BT_MESH_ADDR_IS_UNICAST(addr_end) ||
+ num_elem == 0 || next == NULL) {
+ return -EINVAL;
+ }
+
+ other_start = bt_mesh_primary_addr();
+ other_end = other_start + comp->elem_count - 1;
+
+ /* Compare with local element addresses */
+ if (!(addr_end < other_start || addr_start > other_end)) {
+ *next = other_end + 1;
+ return -EAGAIN;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(bt_mesh.nodes); i++) {
+ struct bt_mesh_node *node = &bt_mesh.nodes[i];
+
+ if (node->net_idx == BT_MESH_KEY_UNUSED) {
+ continue;
+ }
+
+ other_start = node->addr;
+ other_end = other_start + node->num_elem - 1;
+
+ if (!(addr_end < other_start || addr_start > other_end)) {
+ *next = other_end + 1;
+ return -EAGAIN;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Find the lowest possible starting address that can fit num_elem elements. If
+ * a free address range cannot be found, BT_MESH_ADDR_UNASSIGNED will be
+ * returned. Otherwise the first address in the range is returned.
+ *
+ * NOTE: This is quite an ineffective algorithm as it might need to look
+ * through the array of nodes N+2 times. A more effective algorithm
+ * could be used if the nodes were stored in a sorted list.
+ */
+static u16_t find_lowest_free_addr(u8_t num_elem)
+{
+ u16_t addr = 1, next;
+ int err, i;
+
+ /*
+ * It takes a maximum of node count + 2 to find a free address if there
+ * is any. +1 for our own address and +1 for making sure that the
+ * address range is valid.
+ */
+ for (i = 0; i < ARRAY_SIZE(bt_mesh.nodes) + 2; ++i) {
+ err = addr_is_free(addr, num_elem, &next);
+ if (err == 0) {
+ break;
+ } else if (err != -EAGAIN) {
+ addr = BT_MESH_ADDR_UNASSIGNED;
+ break;
+ }
+
+ addr = next;
+ }
+
+ return addr;
+}
+
+struct bt_mesh_node *bt_mesh_node_find(u16_t addr)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(bt_mesh.nodes); i++) {
+ struct bt_mesh_node *node = &bt_mesh.nodes[i];
+
+ if (addr >= node->addr &&
+ addr <= node->addr + node->num_elem - 1) {
+ return node;
+ }
+ }
+
+ return NULL;
+}
+
+struct bt_mesh_node *bt_mesh_node_alloc(u16_t addr, u8_t num_elem,
+ u16_t net_idx)
+{
+ int i;
+
+ BT_DBG("");
+
+ if (addr == BT_MESH_ADDR_UNASSIGNED) {
+ addr = find_lowest_free_addr(num_elem);
+ if (addr == BT_MESH_ADDR_UNASSIGNED) {
+ return NULL;
+ }
+ } else if (!addr_is_free(addr, num_elem, NULL)) {
+ return NULL;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(bt_mesh.nodes); i++) {
+ struct bt_mesh_node *node = &bt_mesh.nodes[i];
+
+ if (node->addr == BT_MESH_ADDR_UNASSIGNED) {
+ node->addr = addr;
+ node->num_elem = num_elem;
+ node->net_idx = net_idx;
+ return node;
+ }
+ }
+
+ return NULL;
+}
+
+void bt_mesh_node_del(struct bt_mesh_node *node, bool store)
+{
+ BT_DBG("Node addr 0x%04x store %u", node->addr, store);
+
+ if (IS_ENABLED(CONFIG_BT_SETTINGS) && store) {
+ bt_mesh_clear_node(node);
+ }
+
+ node->addr = BT_MESH_ADDR_UNASSIGNED;
+ (void)memset(node->dev_key, 0, sizeof(node->dev_key));
+}
+
+#endif
diff --git a/src/libs/mynewt-nimble/nimble/host/mesh/src/nodes.h b/src/libs/mynewt-nimble/nimble/host/mesh/src/nodes.h
new file mode 100644
index 00000000..f86193d9
--- /dev/null
+++ b/src/libs/mynewt-nimble/nimble/host/mesh/src/nodes.h
@@ -0,0 +1,10 @@
+/*
+ * 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/src/libs/mynewt-nimble/nimble/host/mesh/src/prov.c b/src/libs/mynewt-nimble/nimble/host/mesh/src/prov.c
new file mode 100644
index 00000000..fe92c0e3
--- /dev/null
+++ b/src/libs/mynewt-nimble/nimble/host/mesh/src/prov.c
@@ -0,0 +1,2008 @@
+/* Bluetooth Mesh */
+
+/*
+ * Copyright (c) 2017 Intel Corporation
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include "syscfg/syscfg.h"
+#define MESH_LOG_MODULE BLE_MESH_PROV_LOG
+
+#if MYNEWT_VAL(BLE_MESH_PROV)
+
+#include <errno.h>
+
+#include "mesh/mesh.h"
+#include "mesh_priv.h"
+
+#include "crypto.h"
+#include "atomic.h"
+#include "adv.h"
+#include "net.h"
+#include "access.h"
+#include "foundation.h"
+#include "proxy.h"
+#include "prov.h"
+#include "testing.h"
+#include "settings.h"
+#include "nodes.h"
+
+/* 3 transmissions, 20ms interval */
+#define PROV_XMIT BT_MESH_TRANSMIT(2, 20)
+
+#define AUTH_METHOD_NO_OOB 0x00
+#define AUTH_METHOD_STATIC 0x01
+#define AUTH_METHOD_OUTPUT 0x02
+#define AUTH_METHOD_INPUT 0x03
+
+#define OUTPUT_OOB_BLINK 0x00
+#define OUTPUT_OOB_BEEP 0x01
+#define OUTPUT_OOB_VIBRATE 0x02
+#define OUTPUT_OOB_NUMBER 0x03
+#define OUTPUT_OOB_STRING 0x04
+
+#define INPUT_OOB_PUSH 0x00
+#define INPUT_OOB_TWIST 0x01
+#define INPUT_OOB_NUMBER 0x02
+#define INPUT_OOB_STRING 0x03
+
+#define PUB_KEY_NO_OOB 0x00
+#define PUB_KEY_OOB 0x01
+
+#define PROV_ERR_NONE 0x00
+#define PROV_ERR_NVAL_PDU 0x01
+#define PROV_ERR_NVAL_FMT 0x02
+#define PROV_ERR_UNEXP_PDU 0x03
+#define PROV_ERR_CFM_FAILED 0x04
+#define PROV_ERR_RESOURCES 0x05
+#define PROV_ERR_DECRYPT 0x06
+#define PROV_ERR_UNEXP_ERR 0x07
+#define PROV_ERR_ADDR 0x08
+
+#define PROV_INVITE 0x00
+#define PROV_CAPABILITIES 0x01
+#define PROV_START 0x02
+#define PROV_PUB_KEY 0x03
+#define PROV_INPUT_COMPLETE 0x04
+#define PROV_CONFIRM 0x05
+#define PROV_RANDOM 0x06
+#define PROV_DATA 0x07
+#define PROV_COMPLETE 0x08
+#define PROV_FAILED 0x09
+
+#define PROV_NO_PDU 0xff
+
+#define PROV_ALG_P256 0x00
+
+#define GPCF(gpc) (gpc & 0x03)
+#define GPC_START(last_seg) (((last_seg) << 2) | 0x00)
+#define GPC_ACK 0x01
+#define GPC_CONT(seg_id) (((seg_id) << 2) | 0x02)
+#define GPC_CTL(op) (((op) << 2) | 0x03)
+
+#define START_PAYLOAD_MAX 20
+#define CONT_PAYLOAD_MAX 23
+
+#define START_LAST_SEG(gpc) (gpc >> 2)
+#define CONT_SEG_INDEX(gpc) (gpc >> 2)
+
+#define BEARER_CTL(gpc) (gpc >> 2)
+#define LINK_OPEN 0x00
+#define LINK_ACK 0x01
+#define LINK_CLOSE 0x02
+
+#define CLOSE_REASON_SUCCESS 0x00
+#define CLOSE_REASON_TIMEOUT 0x01
+#define CLOSE_REASON_FAILED 0x02
+
+#define XACT_SEG_DATA(_seg) (&link.rx.buf->om_data[20 + ((_seg - 1) * 23)])
+#define XACT_SEG_RECV(_seg) (link.rx.seg &= ~(1 << (_seg)))
+
+#define XACT_NVAL 0xff
+
+enum {
+ WAIT_PUB_KEY, /* Waiting for local PubKey to be generated */
+ LINK_ACTIVE, /* Link has been opened */
+ LINK_ACK_RECVD, /* Ack for link has been received */
+ LINK_CLOSING, /* Link is closing down */
+ SEND_PUB_KEY, /* Waiting to send PubKey */
+ WAIT_NUMBER, /* Waiting for number input from user */
+ WAIT_STRING, /* Waiting for string input from user */
+ NOTIFY_INPUT_COMPLETE, /* Notify that input has been completed. */
+ LINK_INVALID, /* Error occurred during provisioning */
+ PROVISIONER, /* The link was opened as provisioner */
+
+ NUM_FLAGS,
+};
+
+#if MYNEWT_VAL(BLE_MESH_PROVISIONER)
+#define PROVISIONER_LINK 1
+#else
+#define PROVISIONER_LINK 0
+#endif
+
+struct provisioner_link {
+ struct bt_mesh_node *node;
+ u16_t addr;
+ u16_t net_idx;
+ u8_t attention_duration;
+};
+
+struct prov_link {
+ ATOMIC_DEFINE(flags, NUM_FLAGS);
+#if (MYNEWT_VAL(BLE_MESH_PB_GATT))
+ uint16_t conn_handle; /* GATT connection */
+#endif
+ struct provisioner_link provisioner[PROVISIONER_LINK];
+
+ u8_t dhkey[32]; /* Calculated DHKey */
+ u8_t expect; /* Next expected PDU */
+
+ u8_t oob_method;
+ u8_t oob_action;
+ u8_t oob_size;
+
+ u8_t conf[16]; /* Remote Confirmation */
+ u8_t rand[16]; /* Local Random */
+ u8_t auth[16]; /* Authentication Value */
+
+ u8_t conf_salt[16]; /* ConfirmationSalt */
+ u8_t conf_key[16]; /* ConfirmationKey */
+ u8_t conf_inputs[145]; /* ConfirmationInputs */
+ u8_t prov_salt[16]; /* Provisioning Salt */
+
+#if (MYNEWT_VAL(BLE_MESH_PB_ADV))
+ u32_t id; /* Link ID */
+
+ struct {
+ u8_t id; /* Transaction ID */
+ u8_t prev_id; /* Previous Transaction ID */
+ u8_t seg; /* Bit-field of unreceived segments */
+ u8_t last_seg; /* Last segment (to check length) */
+ u8_t fcs; /* Expected FCS value */
+ struct os_mbuf *buf;
+ } rx;
+
+ struct {
+ /* Start timestamp of the transaction */
+ s64_t start;
+
+ /* Transaction id*/
+ u8_t id;
+
+ /* Pending outgoing buffer(s) */
+ struct os_mbuf *buf[3];
+
+ /* Retransmit timer */
+ struct k_delayed_work retransmit;
+ } tx;
+#endif
+
+ struct k_delayed_work prot_timer;
+};
+
+struct prov_rx {
+ u32_t link_id;
+ u8_t xact_id;
+ u8_t gpc;
+};
+
+#define RETRANSMIT_TIMEOUT K_MSEC(500)
+#define BUF_TIMEOUT K_MSEC(400)
+#define CLOSING_TIMEOUT K_SECONDS(3)
+#define TRANSACTION_TIMEOUT K_SECONDS(30)
+#define PROTOCOL_TIMEOUT K_SECONDS(60)
+
+#if (MYNEWT_VAL(BLE_MESH_PB_GATT))
+#define PROV_BUF_HEADROOM 5
+#else
+#define PROV_BUF_HEADROOM 0
+static struct os_mbuf *rx_buf;
+#endif
+
+#define PROV_BUF(len) NET_BUF_SIMPLE(PROV_BUF_HEADROOM + len)
+
+static struct prov_link link;
+
+static const struct bt_mesh_prov *prov;
+
+static void pub_key_ready(const u8_t *pkey);
+
+static int reset_state(void)
+{
+ static struct bt_pub_key_cb pub_key_cb = {
+ .func = pub_key_ready,
+ };
+ int err;
+
+ k_delayed_work_cancel(&link.prot_timer);
+
+ /* Disable Attention Timer if it was set */
+ if (link.conf_inputs[0]) {
+ bt_mesh_attention(NULL, 0);
+ }
+
+ if (IS_ENABLED(CONFIG_BT_MESH_PROVISIONER) &&
+ link.provisioner->node != NULL) {
+ bt_mesh_node_del(link.provisioner->node, false);
+ }
+
+#if (MYNEWT_VAL(BLE_MESH_PB_ADV))
+ /* Clear everything except the retransmit and protocol timer
+ * delayed work objects.
+ */
+ (void)memset(&link, 0, offsetof(struct prov_link, tx.retransmit));
+ link.rx.prev_id = XACT_NVAL;
+
+#if (MYNEWT_VAL(BLE_MESH_PB_GATT))
+ link.rx.buf = bt_mesh_proxy_get_buf();
+#else
+ if (!rx_buf) {
+ rx_buf = NET_BUF_SIMPLE(65);
+ }
+ net_buf_simple_init(rx_buf, 0);
+ link.rx.buf = rx_buf;
+#endif /* PB_GATT */
+
+#else /* !PB_ADV */
+ /* Clear everything except the protocol timer (k_delayed_work) */
+ (void)memset(&link, 0, offsetof(struct prov_link, prot_timer));
+#endif /* PB_ADV */
+
+#if (MYNEWT_VAL(BLE_MESH_PB_GATT))
+ link.conn_handle = BLE_HS_CONN_HANDLE_NONE;
+#endif
+
+ err = bt_pub_key_gen(&pub_key_cb);
+ if (err) {
+ BT_ERR("Failed to generate public key (%d)", err);
+ return err;
+ }
+
+ return 0;
+}
+
+#if (MYNEWT_VAL(BLE_MESH_PB_ADV))
+static void buf_sent(int err, void *user_data)
+{
+ BT_DBG("buf_sent");
+
+ if (!link.tx.buf[0]) {
+ return;
+ }
+
+ k_delayed_work_submit(&link.tx.retransmit, RETRANSMIT_TIMEOUT);
+}
+
+static struct bt_mesh_send_cb buf_sent_cb = {
+ .end = buf_sent,
+};
+
+static void free_segments(void)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(link.tx.buf); i++) {
+ struct os_mbuf *buf = link.tx.buf[i];
+
+ if (!buf) {
+ break;
+ }
+
+ link.tx.buf[i] = NULL;
+ /* Mark as canceled */
+ BT_MESH_ADV(buf)->busy = 0;
+ net_buf_unref(buf);
+ }
+}
+
+static void prov_clear_tx(void)
+{
+ BT_DBG("");
+
+ k_delayed_work_cancel(&link.tx.retransmit);
+
+ free_segments();
+}
+
+static void reset_adv_link(void)
+{
+ prov_clear_tx();
+
+ if (prov->link_close) {
+ prov->link_close(BT_MESH_PROV_ADV);
+ }
+
+ reset_state();
+}
+
+static struct os_mbuf *adv_buf_create(void)
+{
+ struct os_mbuf *buf;
+
+ buf = bt_mesh_adv_create(BT_MESH_ADV_PROV, PROV_XMIT, BUF_TIMEOUT);
+ if (!buf) {
+ BT_ERR("Out of provisioning buffers");
+ assert(0);
+ return NULL;
+ }
+
+ return buf;
+}
+
+static u8_t pending_ack = XACT_NVAL;
+
+static void ack_complete(u16_t duration, int err, void *user_data)
+{
+ BT_DBG("xact %u complete", (u8_t)pending_ack);
+ pending_ack = XACT_NVAL;
+}
+
+static void gen_prov_ack_send(u8_t xact_id)
+{
+ static const struct bt_mesh_send_cb cb = {
+ .start = ack_complete,
+ };
+ const struct bt_mesh_send_cb *complete;
+ struct os_mbuf *buf;
+
+ BT_DBG("xact_id %u", xact_id);
+
+ if (pending_ack == xact_id) {
+ BT_DBG("Not sending duplicate ack");
+ return;
+ }
+
+ buf = adv_buf_create();
+ if (!buf) {
+ return;
+ }
+
+ if (pending_ack == XACT_NVAL) {
+ pending_ack = xact_id;
+ complete = &cb;
+ } else {
+ complete = NULL;
+ }
+
+ net_buf_add_be32(buf, link.id);
+ net_buf_add_u8(buf, xact_id);
+ net_buf_add_u8(buf, GPC_ACK);
+
+ bt_mesh_adv_send(buf, complete, NULL);
+ net_buf_unref(buf);
+}
+
+static void send_reliable(void)
+{
+ int i;
+
+ link.tx.start = k_uptime_get();
+
+ for (i = 0; i < ARRAY_SIZE(link.tx.buf); i++) {
+ struct os_mbuf *buf = link.tx.buf[i];
+
+ if (!buf) {
+ break;
+ }
+
+ if (i + 1 < ARRAY_SIZE(link.tx.buf) && link.tx.buf[i + 1]) {
+ bt_mesh_adv_send(buf, NULL, NULL);
+ } else {
+ bt_mesh_adv_send(buf, &buf_sent_cb, NULL);
+ }
+ }
+}
+
+static int bearer_ctl_send(u8_t op, const void *data, u8_t data_len)
+{
+ struct os_mbuf *buf;
+
+ BT_DBG("op 0x%02x data_len %u", op, data_len);
+
+ prov_clear_tx();
+
+ buf = adv_buf_create();
+ if (!buf) {
+ return -ENOBUFS;
+ }
+
+ net_buf_add_be32(buf, link.id);
+ /* Transaction ID, always 0 for Bearer messages */
+ net_buf_add_u8(buf, 0x00);
+ net_buf_add_u8(buf, GPC_CTL(op));
+ net_buf_add_mem(buf, data, data_len);
+
+ link.tx.buf[0] = buf;
+ send_reliable();
+
+ return 0;
+}
+
+static u8_t last_seg(u8_t len)
+{
+ if (len <= START_PAYLOAD_MAX) {
+ return 0;
+ }
+
+ len -= START_PAYLOAD_MAX;
+
+ return 1 + (len / CONT_PAYLOAD_MAX);
+}
+
+static inline u8_t next_transaction_id(void)
+{
+ if (atomic_test_bit(link.flags, PROVISIONER)) {
+ if (link.tx.id != 0x7F) {
+ link.tx.id++;
+ } else {
+ link.tx.id = 0;
+ }
+ } else {
+ if (link.tx.id != 0U && link.tx.id != 0xFF) {
+ link.tx.id++;
+ } else {
+ link.tx.id = 0x80;
+ }
+ }
+
+ return link.tx.id;
+}
+
+static int prov_send_adv(struct os_mbuf *msg)
+{
+ struct os_mbuf *start, *buf;
+ u8_t seg_len, seg_id;
+ u8_t xact_id;
+
+ BT_DBG("len %u: %s", msg->om_len, bt_hex(msg->om_data, msg->om_len));
+
+ prov_clear_tx();
+
+ start = adv_buf_create();
+ if (!start) {
+ return -ENOBUFS;
+ }
+
+ xact_id = next_transaction_id();
+ net_buf_add_be32(start, link.id);
+ net_buf_add_u8(start, xact_id);
+
+ net_buf_add_u8(start, GPC_START(last_seg(msg->om_len)));
+ net_buf_add_be16(start, msg->om_len);
+ net_buf_add_u8(start, bt_mesh_fcs_calc(msg->om_data, msg->om_len));
+
+ link.tx.buf[0] = start;
+
+ seg_len = min(msg->om_len, START_PAYLOAD_MAX);
+ BT_DBG("seg 0 len %u: %s", seg_len, bt_hex(msg->om_data, seg_len));
+ net_buf_add_mem(start, msg->om_data, seg_len);
+ net_buf_simple_pull(msg, seg_len);
+
+ buf = start;
+ for (seg_id = 1; msg->om_len > 0; seg_id++) {
+ if (seg_id >= ARRAY_SIZE(link.tx.buf)) {
+ BT_ERR("Too big message");
+ free_segments();
+ return -E2BIG;
+ }
+
+ buf = adv_buf_create();
+ if (!buf) {
+ free_segments();
+ return -ENOBUFS;
+ }
+
+ link.tx.buf[seg_id] = buf;
+
+ seg_len = min(msg->om_len, CONT_PAYLOAD_MAX);
+
+ BT_DBG("seg_id %u len %u: %s", seg_id, seg_len,
+ bt_hex(msg->om_data, seg_len));
+
+ net_buf_add_be32(buf, link.id);
+ net_buf_add_u8(buf, xact_id);
+ net_buf_add_u8(buf, GPC_CONT(seg_id));
+ net_buf_add_mem(buf, msg->om_data, seg_len);
+ net_buf_simple_pull(msg, seg_len);
+ }
+
+ send_reliable();
+
+ return 0;
+}
+
+#endif /* MYNEWT_VAL(BLE_MESH_PB_ADV) */
+
+#if (MYNEWT_VAL(BLE_MESH_PB_GATT))
+static int prov_send_gatt(struct os_mbuf *msg)
+{
+ if (link.conn_handle == BLE_HS_CONN_HANDLE_NONE) {
+ BT_ERR("No connection handle!?");
+ return -ENOTCONN;
+ }
+
+ return bt_mesh_proxy_send(link.conn_handle, BT_MESH_PROXY_PROV, msg);
+}
+#endif /* MYNEWT_VAL(BLE_MESH_PB_GATT) */
+
+static inline int prov_send(struct os_mbuf *buf)
+{
+ k_delayed_work_submit(&link.prot_timer, PROTOCOL_TIMEOUT);
+
+#if (MYNEWT_VAL(BLE_MESH_PB_GATT))
+ if (link.conn_handle != BLE_HS_CONN_HANDLE_NONE) {
+ return prov_send_gatt(buf);
+ }
+#endif
+#if (MYNEWT_VAL(BLE_MESH_PB_ADV))
+ return prov_send_adv(buf);
+#else
+ return 0;
+#endif
+}
+
+static void prov_buf_init(struct os_mbuf *buf, u8_t type)
+{
+ net_buf_simple_init(buf, PROV_BUF_HEADROOM);
+ net_buf_simple_add_u8(buf, type);
+}
+
+static void prov_send_fail_msg(u8_t err)
+{
+ struct os_mbuf *buf = PROV_BUF(2);
+
+ prov_buf_init(buf, PROV_FAILED);
+ net_buf_simple_add_u8(buf, err);
+
+ if (prov_send(buf)) {
+ BT_ERR("Failed to send Provisioning Failed message");
+ }
+
+ atomic_set_bit(link.flags, LINK_INVALID);
+
+ os_mbuf_free_chain(buf);
+}
+
+static void prov_invite(const u8_t *data)
+{
+ struct os_mbuf *buf = PROV_BUF(12);
+
+ BT_DBG("Attention Duration: %u seconds", data[0]);
+
+ if (data[0]) {
+ bt_mesh_attention(NULL, data[0]);
+ }
+
+ link.conf_inputs[0] = data[0];
+
+ prov_buf_init(buf, PROV_CAPABILITIES);
+
+ /* Number of Elements supported */
+ net_buf_simple_add_u8(buf, bt_mesh_elem_count());
+
+ /* Supported algorithms - FIPS P-256 Eliptic Curve */
+ net_buf_simple_add_be16(buf, BIT(PROV_ALG_P256));
+
+ /* Public Key Type, Only "No OOB" Public Key is supported*/
+ net_buf_simple_add_u8(buf, PUB_KEY_NO_OOB);
+
+ /* Static OOB Type */
+ net_buf_simple_add_u8(buf, prov->static_val ? BIT(0) : 0x00);
+
+ /* Output OOB Size */
+ net_buf_simple_add_u8(buf, prov->output_size);
+
+ /* Output OOB Action */
+ net_buf_simple_add_be16(buf, prov->output_actions);
+
+ /* Input OOB Size */
+ net_buf_simple_add_u8(buf, prov->input_size);
+
+ /* Input OOB Action */
+ net_buf_simple_add_be16(buf, prov->input_actions);
+
+ memcpy(&link.conf_inputs[1], &buf->om_data[1], 11);
+
+ if (prov_send(buf)) {
+ BT_ERR("Failed to send capabilities");
+ goto done;
+ }
+
+ link.expect = PROV_START;
+
+done:
+ os_mbuf_free_chain(buf);
+}
+
+#if MYNEWT_VAL(BLE_MESH_PB_ADV)
+static void send_invite(void)
+{
+ struct os_mbuf *inv = PROV_BUF(2);
+
+ BT_DBG("");
+
+ prov_buf_init(inv, PROV_INVITE);
+ net_buf_simple_add_u8(inv, link.provisioner->attention_duration);
+
+ link.conf_inputs[0] = link.provisioner->attention_duration;
+
+ if (prov_send(inv)) {
+ BT_ERR("Failed to send invite");
+ goto done;
+ }
+
+ link.expect = PROV_CAPABILITIES;
+
+done:
+ os_mbuf_free_chain(inv);
+}
+#endif
+
+static void send_start(void)
+{
+ struct os_mbuf *start = PROV_BUF(6);
+
+ BT_DBG("");
+
+ prov_buf_init(start, PROV_START);
+
+ net_buf_simple_add_u8(start, PROV_ALG_P256);
+ net_buf_simple_add_u8(start, PUB_KEY_NO_OOB);
+ net_buf_simple_add_u8(start, AUTH_METHOD_NO_OOB);
+ memset(link.auth, 0, sizeof(link.auth));
+
+ net_buf_simple_add_u8(start, 0); /* Auth Action */
+ net_buf_simple_add_u8(start, 0); /* Auth Size */
+
+ memcpy(&link.conf_inputs[12], &start->om_data[1], 5);
+
+ if (prov_send(start)) {
+ BT_ERR("Failed to send start");
+ }
+
+ os_mbuf_free_chain(start);
+}
+
+static void prov_capabilities(const u8_t *data)
+{
+ u16_t algorithms, output_action, input_action;
+
+ if (!IS_ENABLED(CONFIG_BT_MESH_PROVISIONER)) {
+ return;
+ }
+
+ BT_DBG("Elements: %u", data[0]);
+
+ algorithms = sys_get_be16(&data[1]);
+ BT_DBG("Algorithms: %u", algorithms);
+
+ BT_DBG("Public Key Type: 0x%02x", data[3]);
+ BT_DBG("Static OOB Type: 0x%02x", data[4]);
+ BT_DBG("Output OOB Size: %u", data[5]);
+
+ output_action = sys_get_be16(&data[6]);
+ BT_DBG("Output OOB Action: 0x%04x", output_action);
+
+ BT_DBG("Input OOB Size: %u", data[8]);
+
+ input_action = sys_get_be16(&data[9]);
+ BT_DBG("Input OOB Action: 0x%04x", input_action);
+
+ if (data[0] == 0) {
+ BT_ERR("Invalid number of elements");
+ prov_send_fail_msg(PROV_ERR_NVAL_FMT);
+ return;
+ }
+
+ link.provisioner->node = bt_mesh_node_alloc(link.provisioner->addr,
+ data[0],
+ link.provisioner->net_idx);
+ if (link.provisioner->node == NULL) {
+ prov_send_fail_msg(PROV_ERR_RESOURCES);
+ return;
+ }
+
+ memcpy(&link.conf_inputs[1], data, 11);
+
+ atomic_set_bit(link.flags, SEND_PUB_KEY);
+
+ send_start();
+}
+
+static bt_mesh_output_action_t output_action(u8_t action)
+{
+ switch (action) {
+ case OUTPUT_OOB_BLINK:
+ return BT_MESH_BLINK;
+ case OUTPUT_OOB_BEEP:
+ return BT_MESH_BEEP;
+ case OUTPUT_OOB_VIBRATE:
+ return BT_MESH_VIBRATE;
+ case OUTPUT_OOB_NUMBER:
+ return BT_MESH_DISPLAY_NUMBER;
+ case OUTPUT_OOB_STRING:
+ return BT_MESH_DISPLAY_STRING;
+ default:
+ return BT_MESH_NO_OUTPUT;
+ }
+}
+
+static bt_mesh_input_action_t input_action(u8_t action)
+{
+ switch (action) {
+ case INPUT_OOB_PUSH:
+ return BT_MESH_PUSH;
+ case INPUT_OOB_TWIST:
+ return BT_MESH_TWIST;
+ case INPUT_OOB_NUMBER:
+ return BT_MESH_ENTER_NUMBER;
+ case INPUT_OOB_STRING:
+ return BT_MESH_ENTER_STRING;
+ default:
+ return BT_MESH_NO_INPUT;
+ }
+}
+
+static int prov_auth(u8_t method, u8_t action, u8_t size)
+{
+ bt_mesh_output_action_t output;
+ bt_mesh_input_action_t input;
+
+ switch (method) {
+ case AUTH_METHOD_NO_OOB:
+ if (action || size) {
+ return -EINVAL;
+ }
+
+ memset(link.auth, 0, sizeof(link.auth));
+ return 0;
+ case AUTH_METHOD_STATIC:
+ if (action || size) {
+ return -EINVAL;
+ }
+
+ memcpy(link.auth + 16 - prov->static_val_len,
+ prov->static_val, prov->static_val_len);
+ memset(link.auth, 0, sizeof(link.auth) - prov->static_val_len);
+ return 0;
+
+ case AUTH_METHOD_OUTPUT:
+ output = output_action(action);
+ if (!output) {
+ return -EINVAL;
+ }
+
+ if (!(prov->output_actions & output)) {
+ return -EINVAL;
+ }
+
+ if (size > prov->output_size) {
+ return -EINVAL;
+ }
+
+ atomic_set_bit(link.flags, NOTIFY_INPUT_COMPLETE);
+
+ if (output == BT_MESH_DISPLAY_STRING) {
+ unsigned char str[9];
+ u8_t i;
+
+ bt_rand(str, size);
+
+ /* Normalize to '0' .. '9' & 'A' .. 'Z' */
+ for (i = 0; i < size; i++) {
+ str[i] %= 36;
+ if (str[i] < 10) {
+ str[i] += '0';
+ } else {
+ str[i] += 'A' - 10;
+ }
+ }
+ str[size] = '\0';
+
+ memcpy(link.auth, str, size);
+ memset(link.auth + size, 0, sizeof(link.auth) - size);
+
+ return prov->output_string((char *)str);
+ } else {
+ u32_t div[8] = { 10, 100, 1000, 10000, 100000,
+ 1000000, 10000000, 100000000 };
+ u32_t num;
+
+ bt_rand(&num, sizeof(num));
+ num %= div[size - 1];
+
+ sys_put_be32(num, &link.auth[12]);
+ memset(link.auth, 0, 12);
+
+ return prov->output_number(output, num);
+ }
+
+ case AUTH_METHOD_INPUT:
+ input = input_action(action);
+ if (!input) {
+ return -EINVAL;
+ }
+
+ if (!(prov->input_actions & input)) {
+ return -EINVAL;
+ }
+
+ if (size > prov->input_size) {
+ return -EINVAL;
+ }
+
+ if (input == BT_MESH_ENTER_STRING) {
+ atomic_set_bit(link.flags, WAIT_STRING);
+ } else {
+ atomic_set_bit(link.flags, WAIT_NUMBER);
+ }
+
+ return prov->input(input, size);
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static void prov_start(const u8_t *data)
+{
+ BT_DBG("Algorithm: 0x%02x", data[0]);
+ BT_DBG("Public Key: 0x%02x", data[1]);
+ BT_DBG("Auth Method: 0x%02x", data[2]);
+ BT_DBG("Auth Action: 0x%02x", data[3]);
+ BT_DBG("Auth Size: 0x%02x", data[4]);
+
+ if (data[0] != PROV_ALG_P256) {
+ BT_ERR("Unknown algorithm 0x%02x", data[0]);
+ prov_send_fail_msg(PROV_ERR_NVAL_FMT);
+ return;
+ }
+
+ if (data[1] != PUB_KEY_NO_OOB) {
+ BT_ERR("Invalid public key type: 0x%02x", data[1]);
+ prov_send_fail_msg(PROV_ERR_NVAL_FMT);
+ return;
+ }
+
+ memcpy(&link.conf_inputs[12], data, 5);
+
+ /* TODO: reset link when auth fails? */
+ link.expect = PROV_PUB_KEY;
+
+ if (prov_auth(data[2], data[3], data[4]) < 0) {
+ BT_ERR("Invalid authentication method: 0x%02x; "
+ "action: 0x%02x; size: 0x%02x", data[2], data[3],
+ data[4]);
+ prov_send_fail_msg(PROV_ERR_NVAL_FMT);
+ }
+}
+
+static void send_confirm(void)
+{
+ struct os_mbuf *cfm = PROV_BUF(17);
+
+ BT_DBG("ConfInputs[0] %s", bt_hex(link.conf_inputs, 64));
+ BT_DBG("ConfInputs[64] %s", bt_hex(&link.conf_inputs[64], 64));
+ BT_DBG("ConfInputs[128] %s", bt_hex(&link.conf_inputs[128], 17));
+
+ if (bt_mesh_prov_conf_salt(link.conf_inputs, link.conf_salt)) {
+ BT_ERR("Unable to generate confirmation salt");
+ prov_send_fail_msg(PROV_ERR_UNEXP_ERR);
+ goto done;
+ }
+
+ BT_DBG("ConfirmationSalt: %s", bt_hex(link.conf_salt, 16));
+
+ if (bt_mesh_prov_conf_key(link.dhkey, link.conf_salt, link.conf_key)) {
+ BT_ERR("Unable to generate confirmation key");
+ prov_send_fail_msg(PROV_ERR_UNEXP_ERR);
+ goto done;
+ }
+
+ BT_DBG("ConfirmationKey: %s", bt_hex(link.conf_key, 16));
+
+ if (bt_rand(link.rand, 16)) {
+ BT_ERR("Unable to generate random number");
+ prov_send_fail_msg(PROV_ERR_UNEXP_ERR);
+ goto done;
+ }
+
+ BT_DBG("LocalRandom: %s", bt_hex(link.rand, 16));
+
+ prov_buf_init(cfm, PROV_CONFIRM);
+
+ if (bt_mesh_prov_conf(link.conf_key, link.rand, link.auth,
+ net_buf_simple_add(cfm, 16))) {
+ BT_ERR("Unable to generate confirmation value");
+ prov_send_fail_msg(PROV_ERR_UNEXP_ERR);
+ goto done;
+ }
+
+ if (prov_send(cfm)) {
+ BT_ERR("Failed to send Provisioning Confirm");
+ goto done;
+ }
+
+ if (atomic_test_bit(link.flags, PROVISIONER)) {
+ link.expect = PROV_CONFIRM;
+ } else {
+ link.expect = PROV_RANDOM;
+ }
+
+done:
+ os_mbuf_free_chain(cfm);
+}
+
+static void send_input_complete(void)
+{
+ struct os_mbuf *buf = PROV_BUF(1);
+
+ prov_buf_init(buf, PROV_INPUT_COMPLETE);
+ if (prov_send(buf)) {
+ BT_ERR("Failed to send Provisioning Input Complete");
+ }
+ link.expect = PROV_CONFIRM;
+
+ os_mbuf_free_chain(buf);
+}
+
+int bt_mesh_input_number(u32_t num)
+{
+ BT_DBG("%u", (unsigned) num);
+
+ if (!atomic_test_and_clear_bit(link.flags, WAIT_NUMBER)) {
+ return -EINVAL;
+ }
+
+ sys_put_be32(num, &link.auth[12]);
+
+ send_input_complete();
+
+ return 0;
+}
+
+int bt_mesh_input_string(const char *str)
+{
+ BT_DBG("%s", str);
+
+ if (!atomic_test_and_clear_bit(link.flags, WAIT_STRING)) {
+ return -EINVAL;
+ }
+
+ strncpy((char *)link.auth, str, prov->input_size);
+
+ send_input_complete();
+
+ return 0;
+}
+
+static void send_pub_key(void)
+{
+ struct os_mbuf *buf = PROV_BUF(65);
+ const u8_t *key;
+
+ key = bt_pub_key_get();
+ if (!key) {
+ BT_ERR("No public key available");
+ prov_send_fail_msg(PROV_ERR_UNEXP_ERR);
+ goto done;
+ }
+
+ BT_DBG("Local Public Key: %s", bt_hex(key, 64));
+
+ prov_buf_init(buf, PROV_PUB_KEY);
+
+ /* Swap X and Y halves independently to big-endian */
+ sys_memcpy_swap(net_buf_simple_add(buf, 32), key, 32);
+ sys_memcpy_swap(net_buf_simple_add(buf, 32), &key[32], 32);
+
+ if (atomic_test_bit(link.flags, PROVISIONER)) {
+ /* PublicKeyProvisioner */
+ memcpy(&link.conf_inputs[17], &buf->om_data[1], 64);
+ } else {
+ /* PublicKeyRemote */
+ memcpy(&link.conf_inputs[81], &buf->om_data[1], 64);
+ }
+
+ if (prov_send(buf)) {
+ BT_ERR("Failed to send Public Key");
+ goto done;
+ }
+
+ if (atomic_test_bit(link.flags, PROVISIONER)) {
+ link.expect = PROV_PUB_KEY;
+ } else {
+ if (atomic_test_bit(link.flags, WAIT_NUMBER) ||
+ atomic_test_bit(link.flags, WAIT_STRING)) {
+ link.expect = PROV_NO_PDU; /* Wait for input */
+ } else {
+ link.expect = PROV_CONFIRM;
+ }
+ }
+
+done:
+ os_mbuf_free_chain(buf);
+}
+
+static void prov_dh_key_cb(const u8_t dhkey[32])
+{
+ BT_DBG("%p", dhkey);
+
+ if (!dhkey) {
+ BT_ERR("DHKey generation failed");
+ prov_send_fail_msg(PROV_ERR_UNEXP_ERR);
+ return;
+ }
+
+ sys_memcpy_swap(link.dhkey, dhkey, 32);
+
+ BT_DBG("DHkey: %s", bt_hex(link.dhkey, 32));
+
+ if (atomic_test_bit(link.flags, PROVISIONER)) {
+ send_confirm();
+ } else {
+ send_pub_key();
+ }
+}
+
+static void prov_dh_key_gen(void)
+{
+ u8_t remote_pk_le[64], *remote_pk;
+
+ if (atomic_test_bit(link.flags, PROVISIONER)) {
+ remote_pk = &link.conf_inputs[81];
+ } else {
+ remote_pk = &link.conf_inputs[17];
+ }
+
+ /* Copy remote key in little-endian for bt_dh_key_gen().
+ * X and Y halves are swapped independently. The bt_dh_key_gen()
+ * will also take care of validating the remote public key.
+ */
+ sys_memcpy_swap(remote_pk_le, remote_pk, 32);
+ sys_memcpy_swap(&remote_pk_le[32], &remote_pk[32], 32);
+
+ if (bt_dh_key_gen(remote_pk_le, prov_dh_key_cb)) {
+ BT_ERR("Failed to generate DHKey");
+ prov_send_fail_msg(PROV_ERR_UNEXP_ERR);
+ }
+}
+
+static void prov_pub_key(const u8_t *data)
+{
+ BT_DBG("Remote Public Key: %s", bt_hex(data, 64));
+
+ if (atomic_test_bit(link.flags, PROVISIONER)) {
+ /* PublicKeyDevice */
+ memcpy(&link.conf_inputs[81], data, 64);
+
+#if (MYNEWT_VAL(BLE_MESH_PB_ADV))
+ prov_clear_tx();
+#endif
+ } else {
+ /* PublicKeyProvisioner */
+ memcpy(&link.conf_inputs[17], data, 64);
+
+ if (!bt_pub_key_get()) {
+ /* Clear retransmit timer */
+#if (MYNEWT_VAL(BLE_MESH_PB_ADV))
+ prov_clear_tx();
+#endif
+
+ atomic_set_bit(link.flags, WAIT_PUB_KEY);
+ BT_WARN("Waiting for local public key");
+ return;
+ }
+ }
+
+ prov_dh_key_gen();
+}
+
+static void pub_key_ready(const u8_t *pkey)
+{
+ if (!pkey) {
+ BT_WARN("Public key not available");
+ return;
+ }
+
+ BT_DBG("Local public key ready");
+
+ if (atomic_test_and_clear_bit(link.flags, WAIT_PUB_KEY)) {
+ if (atomic_test_bit(link.flags, PROVISIONER)) {
+ send_pub_key();
+ } else {
+ prov_dh_key_gen();
+ }
+ }
+}
+
+static void notify_input_complete(void)
+{
+ if (atomic_test_and_clear_bit(link.flags, NOTIFY_INPUT_COMPLETE) &&
+ prov->input_complete) {
+ prov->input_complete();
+ }
+}
+
+static void prov_input_complete(const u8_t *data)
+{
+ BT_DBG("");
+ notify_input_complete();
+}
+
+static void send_prov_data(void)
+{
+ struct os_mbuf *pdu = PROV_BUF(34);
+ struct bt_mesh_subnet *sub;
+ u8_t session_key[16];
+ u8_t nonce[13];
+ int err;
+
+ err = bt_mesh_session_key(link.dhkey, link.prov_salt, session_key);
+ if (err) {
+ BT_ERR("Unable to generate session key");
+ prov_send_fail_msg(PROV_ERR_UNEXP_ERR);
+ goto done;
+ }
+
+ BT_DBG("SessionKey: %s", bt_hex(session_key, 16));
+
+ err = bt_mesh_prov_nonce(link.dhkey, link.prov_salt, nonce);
+ if (err) {
+ BT_ERR("Unable to generate session nonce");
+ prov_send_fail_msg(PROV_ERR_UNEXP_ERR);
+ goto done;
+ }
+
+ BT_DBG("Nonce: %s", bt_hex(nonce, 13));
+
+ err = bt_mesh_dev_key(link.dhkey, link.prov_salt,
+ link.provisioner->node->dev_key);
+ if (err) {
+ BT_ERR("Unable to generate device key");
+ prov_send_fail_msg(PROV_ERR_UNEXP_ERR);
+ goto done;
+ }
+
+ BT_DBG("DevKey: %s", bt_hex(link.provisioner->node->dev_key, 16));
+
+ sub = bt_mesh_subnet_get(link.provisioner->node->net_idx);
+ if (sub == NULL) {
+ BT_ERR("No subnet with net_idx %u",
+ link.provisioner->node->net_idx);
+ prov_send_fail_msg(PROV_ERR_UNEXP_ERR);
+ goto done;
+ }
+
+ prov_buf_init(pdu, PROV_DATA);
+ net_buf_simple_add_mem(pdu, sub->keys[sub->kr_flag].net, 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_be16(pdu, link.provisioner->node->addr);
+ net_buf_simple_add(pdu, 8); /* For MIC */
+
+ BT_DBG("net_idx %u, iv_index 0x%08x, addr 0x%04x",
+ link.provisioner->node->net_idx, bt_mesh.iv_index,
+ link.provisioner->node->addr);
+
+ err = bt_mesh_prov_encrypt(session_key, nonce, &pdu->om_data[1],
+ &pdu->om_data[1]);
+ if (err) {
+ BT_ERR("Unable to encrypt provisioning data");
+ prov_send_fail_msg(PROV_ERR_DECRYPT);
+ goto done;
+ }
+
+ if (prov_send(pdu)) {
+ BT_ERR("Failed to send Provisioning Data");
+ goto done;
+ }
+
+ link.expect = PROV_COMPLETE;
+
+done:
+ os_mbuf_free_chain(pdu);
+}
+
+static void prov_complete(const u8_t *data)
+{
+ if (!IS_ENABLED(CONFIG_BT_MESH_PROVISIONER)) {
+ return;
+ }
+
+ struct bt_mesh_node *node = link.provisioner->node;
+#if MYNEWT_VAL(BLE_MESH_PB_ADV)
+ u8_t reason = CLOSE_REASON_SUCCESS;
+#endif
+
+ BT_DBG("key %s, net_idx %u, num_elem %u, addr 0x%04x",
+ bt_hex(node->dev_key, 16), node->net_idx, node->num_elem,
+ node->addr);
+
+ if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
+ bt_mesh_store_node(node);
+ }
+
+ link.provisioner->node = NULL;
+ link.expect = PROV_NO_PDU;
+ atomic_set_bit(link.flags, LINK_CLOSING);
+
+#if MYNEWT_VAL(BLE_MESH_PB_ADV)
+ bearer_ctl_send(LINK_CLOSE, &reason, sizeof(reason));
+#endif
+
+ bt_mesh_prov_node_added(node->net_idx, node->addr, node->num_elem);
+
+ /*
+ * According to mesh profile spec (5.3.1.4.3), the close message should
+ * be restransmitted at least three times. Retransmit the LINK_CLOSE
+ * message until CLOSING_TIMEOUT has elapsed instead of resetting the
+ * link here.
+ */
+}
+
+static void send_random(void)
+{
+ struct os_mbuf *rnd = PROV_BUF(17);
+
+ prov_buf_init(rnd, PROV_RANDOM);
+ net_buf_simple_add_mem(rnd, link.rand, 16);
+
+ if (prov_send(rnd)) {
+ BT_ERR("Failed to send Provisioning Random");
+ goto done;
+ }
+
+ if (atomic_test_bit(link.flags, PROVISIONER)) {
+ link.expect = PROV_RANDOM;
+ } else {
+ link.expect = PROV_DATA;
+ }
+
+done:
+ os_mbuf_free_chain(rnd);
+}
+
+static void prov_random(const u8_t *data)
+{
+ u8_t conf_verify[16];
+ const u8_t *prov_rand, *dev_rand;
+
+ BT_DBG("Remote Random: %s", bt_hex(data, 16));
+
+ if (bt_mesh_prov_conf(link.conf_key, data, link.auth, conf_verify)) {
+ BT_ERR("Unable to calculate confirmation verification");
+ prov_send_fail_msg(PROV_ERR_UNEXP_ERR);
+ return;
+ }
+
+ if (memcmp(conf_verify, link.conf, 16)) {
+ BT_ERR("Invalid confirmation value");
+ BT_DBG("Received: %s", bt_hex(link.conf, 16));
+ BT_DBG("Calculated: %s", bt_hex(conf_verify, 16));
+ prov_send_fail_msg(PROV_ERR_CFM_FAILED);
+ return;
+ }
+
+ if (atomic_test_bit(link.flags, PROVISIONER)) {
+ prov_rand = link.rand;
+ dev_rand = data;
+ } else {
+ prov_rand = data;
+ dev_rand = link.rand;
+ }
+
+ if (bt_mesh_prov_salt(link.conf_salt, prov_rand, dev_rand,
+ link.prov_salt)) {
+ BT_ERR("Failed to generate provisioning salt");
+ prov_send_fail_msg(PROV_ERR_UNEXP_ERR);
+ return;
+ }
+
+ BT_DBG("ProvisioningSalt: %s", bt_hex(link.prov_salt, 16));
+
+ if (IS_ENABLED(CONFIG_BT_MESH_PROVISIONER) &&
+ atomic_test_bit(link.flags, PROVISIONER)) {
+ send_prov_data();
+ } else {
+ send_random();
+ }
+}
+
+static void prov_confirm(const u8_t *data)
+{
+ BT_DBG("Remote Confirm: %s", bt_hex(data, 16));
+
+ memcpy(link.conf, data, 16);
+
+ notify_input_complete();
+
+ if (atomic_test_bit(link.flags, PROVISIONER)) {
+ send_random();
+ } else {
+ send_confirm();
+ }
+}
+
+static inline bool is_pb_gatt(void)
+{
+#if MYNEWT_VAL(BLE_MESH_PB_GATT)
+ return (link.conn_handle != BLE_HS_CONN_HANDLE_NONE);
+#else
+ return false;
+#endif
+}
+
+static void prov_data(const u8_t *data)
+{
+ struct os_mbuf *msg = PROV_BUF(1);
+ u8_t session_key[16];
+ u8_t nonce[13];
+ u8_t dev_key[16];
+ u8_t pdu[25];
+ u8_t flags;
+ u32_t iv_index;
+ u16_t addr;
+ u16_t net_idx;
+ int err;
+ bool identity_enable;
+
+ BT_DBG("");
+
+ err = bt_mesh_session_key(link.dhkey, link.prov_salt, session_key);
+ if (err) {
+ BT_ERR("Unable to generate session key");
+ prov_send_fail_msg(PROV_ERR_UNEXP_ERR);
+ goto done;
+ }
+
+ BT_DBG("SessionKey: %s", bt_hex(session_key, 16));
+
+ err = bt_mesh_prov_nonce(link.dhkey, link.prov_salt, nonce);
+ if (err) {
+ BT_ERR("Unable to generate session nonce");
+ prov_send_fail_msg(PROV_ERR_UNEXP_ERR);
+ goto done;
+ }
+
+ BT_DBG("Nonce: %s", bt_hex(nonce, 13));
+
+ err = bt_mesh_prov_decrypt(session_key, nonce, data, pdu);
+ if (err) {
+ BT_ERR("Unable to decrypt provisioning data");
+ prov_send_fail_msg(PROV_ERR_DECRYPT);
+ goto done;
+ }
+
+ err = bt_mesh_dev_key(link.dhkey, link.prov_salt, dev_key);
+ if (err) {
+ BT_ERR("Unable to generate device key");
+ prov_send_fail_msg(PROV_ERR_UNEXP_ERR);
+ goto done;
+ }
+
+ BT_DBG("DevKey: %s", bt_hex(dev_key, 16));
+
+ net_idx = sys_get_be16(&pdu[16]);
+ flags = pdu[18];
+ iv_index = sys_get_be32(&pdu[19]);
+ addr = sys_get_be16(&pdu[23]);
+
+ BT_DBG("net_idx %u iv_index 0x%08x, addr 0x%04x",
+ net_idx, (unsigned) iv_index, addr);
+
+ prov_buf_init(msg, PROV_COMPLETE);
+ if (prov_send(msg)) {
+ BT_ERR("Failed to send Provisioning Complete");
+ goto done;
+ }
+
+ /* Ignore any further PDUs on this link */
+ link.expect = PROV_NO_PDU;
+
+ /* Store info, since bt_mesh_provision() will end up clearing it */
+ if (IS_ENABLED(CONFIG_BT_MESH_GATT_PROXY)) {
+ identity_enable = is_pb_gatt();
+ } else {
+ identity_enable = false;
+ }
+
+ err = bt_mesh_provision(pdu, net_idx, flags, iv_index, addr, dev_key);
+ if (err) {
+ BT_ERR("Failed to provision (err %d)", err);
+ goto done;
+ }
+
+ /* After PB-GATT provisioning we should start advertising
+ * using Node Identity.
+ */
+ if (IS_ENABLED(CONFIG_BT_MESH_GATT_PROXY) && identity_enable) {
+ bt_mesh_proxy_identity_enable();
+ }
+
+done:
+ os_mbuf_free_chain(msg);
+}
+
+static void prov_failed(const u8_t *data)
+{
+ BT_WARN("Error: 0x%02x", data[0]);
+}
+
+static const struct {
+ void (*func)(const u8_t *data);
+ u16_t len;
+} prov_handlers[] = {
+ { prov_invite, 1 },
+ { prov_capabilities, 11 },
+ { prov_start, 5, },
+ { prov_pub_key, 64 },
+ { prov_input_complete, 0 },
+ { prov_confirm, 16 },
+ { prov_random, 16 },
+ { prov_data, 33 },
+ { prov_complete, 0 },
+ { prov_failed, 1 },
+};
+
+#if (MYNEWT_VAL(BLE_MESH_PB_ADV))
+static void prov_retransmit(struct ble_npl_event *work)
+{
+ int i, timeout;
+
+ BT_DBG("");
+
+ if (!atomic_test_bit(link.flags, LINK_ACTIVE)) {
+ BT_WARN("Link not active");
+ return;
+ }
+
+ if (atomic_test_bit(link.flags, LINK_CLOSING)) {
+ timeout = CLOSING_TIMEOUT;
+ } else {
+ timeout = TRANSACTION_TIMEOUT;
+ }
+
+ if (k_uptime_get() - link.tx.start > timeout) {
+ BT_WARN("Giving up transaction");
+ reset_adv_link();
+ return;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(link.tx.buf); i++) {
+ struct os_mbuf *buf = link.tx.buf[i];
+
+ if (!buf) {
+ break;
+ }
+
+ if (BT_MESH_ADV(buf)->busy) {
+ continue;
+ }
+
+ BT_DBG("%u bytes: %s", buf->om_len, bt_hex(buf->om_data, buf->om_len));
+
+ if (i + 1 < ARRAY_SIZE(link.tx.buf) && link.tx.buf[i + 1]) {
+ bt_mesh_adv_send(buf, NULL, NULL);
+ } else {
+ bt_mesh_adv_send(buf, &buf_sent_cb, NULL);
+ }
+
+ }
+}
+
+static void link_open(struct prov_rx *rx, struct os_mbuf *buf)
+{
+ BT_DBG("link open: len %u", buf->om_len);
+
+ if (buf->om_len < 16) {
+ BT_ERR("Too short bearer open message (len %u)", buf->om_len);
+ return;
+ }
+
+ if (atomic_test_bit(link.flags, LINK_ACTIVE)) {
+ /* Send another link ack if the provisioner missed the last */
+ if (link.id == rx->link_id && link.expect == PROV_INVITE) {
+ BT_DBG("Resending link ack");
+ bearer_ctl_send(LINK_ACK, NULL, 0);
+ } else {
+ BT_WARN("Ignoring bearer open: link already active");
+ }
+
+ return;
+ }
+
+ if (memcmp(buf->om_data, prov->uuid, 16)) {
+ BT_DBG("Bearer open message not for us");
+ return;
+ }
+
+ if (prov->link_open) {
+ prov->link_open(BT_MESH_PROV_ADV);
+ }
+
+ link.id = rx->link_id;
+ atomic_set_bit(link.flags, LINK_ACTIVE);
+ net_buf_simple_init(link.rx.buf, 0);
+
+ bearer_ctl_send(LINK_ACK, NULL, 0);
+
+ link.expect = PROV_INVITE;
+}
+
+static void link_ack(struct prov_rx *rx, struct os_mbuf *buf)
+{
+ BT_DBG("Link ack: len %u", buf->om_len);
+
+ if (IS_ENABLED(CONFIG_BT_MESH_PROVISIONER) &&
+ atomic_test_bit(link.flags, PROVISIONER)) {
+ if (atomic_test_and_set_bit(link.flags, LINK_ACK_RECVD)) {
+ return;
+ }
+
+ prov_clear_tx();
+
+ if (prov->link_open) {
+ prov->link_open(BT_MESH_PROV_ADV);
+ }
+
+ send_invite();
+ }
+}
+
+static void link_close(struct prov_rx *rx, struct os_mbuf *buf)
+{
+ BT_DBG("Link close: len %u", buf->om_len);
+
+ reset_adv_link();
+}
+
+static void gen_prov_ctl(struct prov_rx *rx, struct os_mbuf *buf)
+{
+ BT_DBG("op 0x%02x len %u", BEARER_CTL(rx->gpc), buf->om_len);
+
+ switch (BEARER_CTL(rx->gpc)) {
+ case LINK_OPEN:
+ link_open(rx, buf);
+ break;
+ case LINK_ACK:
+ if (!atomic_test_bit(link.flags, LINK_ACTIVE)) {
+ return;
+ }
+
+ link_ack(rx, buf);
+ break;
+ case LINK_CLOSE:
+ if (!atomic_test_bit(link.flags, LINK_ACTIVE)) {
+ return;
+ }
+
+ link_close(rx, buf);
+ break;
+ default:
+ BT_ERR("Unknown bearer opcode: 0x%02x", BEARER_CTL(rx->gpc));
+
+ if (IS_ENABLED(CONFIG_BT_TESTING)) {
+ bt_test_mesh_prov_invalid_bearer(BEARER_CTL(rx->gpc));
+ }
+
+ return;
+ }
+}
+
+static void prov_msg_recv(void)
+{
+ u8_t type = link.rx.buf->om_data[0];
+
+ BT_DBG("type 0x%02x len %u", type, link.rx.buf->om_len);
+
+ k_delayed_work_submit(&link.prot_timer, PROTOCOL_TIMEOUT);
+
+ if (!bt_mesh_fcs_check(link.rx.buf, link.rx.fcs)) {
+ BT_ERR("Incorrect FCS");
+ return;
+ }
+
+ gen_prov_ack_send(link.rx.id);
+ link.rx.prev_id = link.rx.id;
+ link.rx.id = 0;
+
+ if (atomic_test_bit(link.flags, LINK_INVALID)) {
+ BT_WARN("Unexpected msg 0x%02x on invalidated link", type);
+ prov_send_fail_msg(PROV_ERR_UNEXP_PDU);
+ return;
+ }
+
+ if (type != PROV_FAILED && type != link.expect) {
+ BT_WARN("Unexpected msg 0x%02x != 0x%02x", type, link.expect);
+ prov_send_fail_msg(PROV_ERR_UNEXP_PDU);
+ return;
+ }
+
+ if (type >= ARRAY_SIZE(prov_handlers)) {
+ BT_ERR("Unknown provisioning PDU type 0x%02x", type);
+ prov_send_fail_msg(PROV_ERR_NVAL_PDU);
+ return;
+ }
+
+ if (1 + prov_handlers[type].len != link.rx.buf->om_len) {
+ BT_ERR("Invalid length %u for type 0x%02x",
+ link.rx.buf->om_len, type);
+ prov_send_fail_msg(PROV_ERR_NVAL_FMT);
+ return;
+ }
+
+ prov_handlers[type].func(&link.rx.buf->om_data[1]);
+}
+
+static void gen_prov_cont(struct prov_rx *rx, struct os_mbuf *buf)
+{
+ u8_t seg = CONT_SEG_INDEX(rx->gpc);
+
+ BT_DBG("len %u, seg_index %u", buf->om_len, seg);
+
+ if (!link.rx.seg && link.rx.prev_id == rx->xact_id) {
+ BT_WARN("Resending ack");
+ gen_prov_ack_send(rx->xact_id);
+ return;
+ }
+
+ if (rx->xact_id != link.rx.id) {
+ BT_WARN("Data for unknown transaction (%u != %u)",
+ rx->xact_id, link.rx.id);
+ return;
+ }
+
+ if (seg > link.rx.last_seg) {
+ BT_ERR("Invalid segment index %u", seg);
+ prov_send_fail_msg(PROV_ERR_NVAL_FMT);
+ return;
+ } else if (seg == link.rx.last_seg) {
+ u8_t expect_len;
+
+ expect_len = (link.rx.buf->om_len - 20 -
+ ((link.rx.last_seg - 1) * 23));
+ if (expect_len != buf->om_len) {
+ BT_ERR("Incorrect last seg len: %u != %u",
+ expect_len, buf->om_len);
+ prov_send_fail_msg(PROV_ERR_NVAL_FMT);
+ return;
+ }
+ }
+
+ if (!(link.rx.seg & BIT(seg))) {
+ BT_WARN("Ignoring already received segment");
+ return;
+ }
+
+ memcpy(XACT_SEG_DATA(seg), buf->om_data, buf->om_len);
+ XACT_SEG_RECV(seg);
+
+ if (!link.rx.seg) {
+ prov_msg_recv();
+ }
+}
+
+static void gen_prov_ack(struct prov_rx *rx, struct os_mbuf *buf)
+{
+ BT_DBG("len %u", buf->om_len);
+
+ if (!link.tx.buf[0]) {
+ return;
+ }
+
+ if (rx->xact_id == link.tx.id) {
+ /* Don't clear resending of LINK_CLOSE messages */
+ if (!atomic_test_bit(link.flags, LINK_CLOSING)) {
+ prov_clear_tx();
+ }
+
+ /* Send the PubKey when the the Start message is ACK'ed */
+ if (IS_ENABLED(CONFIG_BT_MESH_PROVISIONER) &&
+ atomic_test_and_clear_bit(link.flags, SEND_PUB_KEY)) {
+ if (!bt_pub_key_get()) {
+ atomic_set_bit(link.flags, WAIT_PUB_KEY);
+ BT_WARN("Waiting for local public key");
+ } else {
+ send_pub_key();
+ }
+ }
+ }
+}
+
+static void gen_prov_start(struct prov_rx *rx, struct os_mbuf *buf)
+{
+ u16_t trailing_space = 0;
+
+ if (link.rx.seg) {
+ BT_WARN("Got Start while there are unreceived segments");
+ return;
+ }
+
+ if (link.rx.prev_id == rx->xact_id) {
+ BT_WARN("Resending ack");
+ gen_prov_ack_send(rx->xact_id);
+ return;
+ }
+
+ trailing_space = OS_MBUF_TRAILINGSPACE(link.rx.buf);
+
+ link.rx.buf->om_len = net_buf_simple_pull_be16(buf);
+ link.rx.id = rx->xact_id;
+ link.rx.fcs = net_buf_simple_pull_u8(buf);
+
+ BT_DBG("len %u last_seg %u total_len %u fcs 0x%02x", buf->om_len,
+ START_LAST_SEG(rx->gpc), link.rx.buf->om_len, link.rx.fcs);
+
+ if (link.rx.buf->om_len < 1) {
+ BT_ERR("Ignoring zero-length provisioning PDU");
+ prov_send_fail_msg(PROV_ERR_NVAL_FMT);
+ return;
+ }
+
+ if (link.rx.buf->om_len > trailing_space) {
+ BT_ERR("Too large provisioning PDU (%u bytes)",
+ link.rx.buf->om_len);
+ prov_send_fail_msg(PROV_ERR_NVAL_FMT);
+ return;
+ }
+
+ if (START_LAST_SEG(rx->gpc) > 0 && link.rx.buf->om_len <= 20) {
+ BT_ERR("Too small total length for multi-segment PDU");
+ prov_send_fail_msg(PROV_ERR_NVAL_FMT);
+ return;
+ }
+
+ link.rx.seg = (1 << (START_LAST_SEG(rx->gpc) + 1)) - 1;
+ link.rx.last_seg = START_LAST_SEG(rx->gpc);
+ memcpy(link.rx.buf->om_data, buf->om_data, buf->om_len);
+ XACT_SEG_RECV(0);
+
+ if (!link.rx.seg) {
+ prov_msg_recv();
+ }
+}
+
+static const struct {
+ void (*func)(struct prov_rx *rx, struct os_mbuf *buf);
+ bool require_link;
+ u8_t min_len;
+} gen_prov[] = {
+ { gen_prov_start, true, 3 },
+ { gen_prov_ack, true, 0 },
+ { gen_prov_cont, true, 0 },
+ { gen_prov_ctl, false, 0 },
+};
+
+static void gen_prov_recv(struct prov_rx *rx, struct os_mbuf *buf)
+{
+ if (buf->om_len < gen_prov[GPCF(rx->gpc)].min_len) {
+ BT_ERR("Too short GPC message type %u", GPCF(rx->gpc));
+ return;
+ }
+
+ if (!atomic_test_bit(link.flags, LINK_ACTIVE) &&
+ gen_prov[GPCF(rx->gpc)].require_link) {
+ BT_DBG("Ignoring message that requires active link");
+ return;
+ }
+
+ BT_DBG("prov_action: %d", GPCF(rx->gpc));
+ gen_prov[GPCF(rx->gpc)].func(rx, buf);
+}
+
+void bt_mesh_pb_adv_recv(struct os_mbuf *buf)
+{
+ struct prov_rx rx;
+
+ if (!bt_prov_active() && bt_mesh_is_provisioned()) {
+ BT_DBG("Ignoring provisioning PDU - already provisioned");
+ return;
+ }
+
+ if (buf->om_len < 6) {
+ BT_WARN("Too short provisioning packet (len %u)", buf->om_len);
+ return;
+ }
+
+ rx.link_id = net_buf_simple_pull_be32(buf);
+ rx.xact_id = net_buf_simple_pull_u8(buf);
+ rx.gpc = net_buf_simple_pull_u8(buf);
+
+ BT_DBG("link_id 0x%08x xact_id %u", (unsigned) rx.link_id, rx.xact_id);
+
+ if (atomic_test_bit(link.flags, LINK_ACTIVE) && link.id != rx.link_id) {
+ BT_DBG("Ignoring mesh beacon for unknown link");
+ return;
+ }
+
+ gen_prov_recv(&rx, buf);
+}
+
+int bt_mesh_pb_adv_open(const u8_t uuid[16], u16_t net_idx, u16_t addr,
+ u8_t attention_duration)
+{
+ BT_DBG("uuid %s", bt_hex(uuid, 16));
+
+ if (atomic_test_and_set_bit(link.flags, LINK_ACTIVE)) {
+ return -EBUSY;
+ }
+
+ atomic_set_bit(link.flags, PROVISIONER);
+
+ bt_rand(&link.id, sizeof(link.id));
+ link.tx.id = 0x7F;
+ link.provisioner->addr = addr;
+ link.provisioner->net_idx = net_idx;
+ link.provisioner->attention_duration = attention_duration;
+
+ net_buf_simple_init(link.rx.buf, 0);
+
+ bearer_ctl_send(LINK_OPEN, uuid, 16);
+
+ return 0;
+}
+
+#endif /* MYNEWT_VAL(BLE_MESH_PB_ADV) */
+
+#if (MYNEWT_VAL(BLE_MESH_PB_GATT))
+int bt_mesh_pb_gatt_recv(uint16_t conn_handle, struct os_mbuf *buf)
+{
+ u8_t type;
+
+ BT_DBG("%u bytes: %s", buf->om_len, bt_hex(buf->om_data, buf->om_len));
+
+ if (link.conn_handle != conn_handle) {
+ BT_WARN("Data for unexpected connection");
+ return -ENOTCONN;
+ }
+
+ if (buf->om_len < 1) {
+ BT_WARN("Too short provisioning packet (len %u)", buf->om_len);
+ return -EINVAL;
+ }
+
+ type = net_buf_simple_pull_u8(buf);
+ if (type != PROV_FAILED && type != link.expect) {
+ BT_WARN("Unexpected msg 0x%02x != 0x%02x", type, link.expect);
+ prov_send_fail_msg(PROV_ERR_UNEXP_PDU);
+ return -EINVAL;
+ }
+
+ if (type >= ARRAY_SIZE(prov_handlers)) {
+ BT_ERR("Unknown provisioning PDU type 0x%02x", type);
+ return -EINVAL;
+ }
+
+ if (prov_handlers[type].len != buf->om_len) {
+ BT_ERR("Invalid length %u for type 0x%02x", buf->om_len, type);
+ return -EINVAL;
+ }
+
+ prov_handlers[type].func(buf->om_data);
+
+ return 0;
+}
+
+int bt_mesh_pb_gatt_open(uint16_t conn_handle)
+{
+ BT_DBG("conn_handle %d", conn_handle);
+
+ if (atomic_test_and_set_bit(link.flags, LINK_ACTIVE)) {
+ BT_ERR("Link already opened?");
+ return -EBUSY;
+ }
+
+ link.conn_handle = conn_handle;
+ link.expect = PROV_INVITE;
+
+ if (prov->link_open) {
+ prov->link_open(BT_MESH_PROV_GATT);
+ }
+
+ return 0;
+}
+
+int bt_mesh_pb_gatt_close(uint16_t conn_handle)
+{
+ BT_DBG("conn_handle %d", conn_handle);
+
+ if (link.conn_handle != conn_handle) {
+ BT_ERR("Not connected");
+ return -ENOTCONN;
+ }
+
+ if (prov->link_close) {
+ prov->link_close(BT_MESH_PROV_GATT);
+ }
+
+ return reset_state();
+}
+#endif /* MYNEWT_VAL(BLE_MESH_PB_GATT) */
+
+const struct bt_mesh_prov *bt_mesh_prov_get(void)
+{
+ return prov;
+}
+
+bool bt_prov_active(void)
+{
+ return atomic_test_bit(link.flags, LINK_ACTIVE);
+}
+
+static void protocol_timeout(struct ble_npl_event *work)
+{
+ BT_DBG("Protocol timeout");
+
+#if MYNEWT_VAL(BLE_MESH_PB_GATT)
+ if (link.conn_handle != BLE_HS_CONN_HANDLE_NONE) {
+ bt_mesh_pb_gatt_close(link.conn_handle);
+ return;
+ }
+#endif
+
+#if MYNEWT_VAL(BLE_MESH_PB_ADV)
+ u8_t reason = CLOSE_REASON_TIMEOUT;
+
+ link.rx.seg = 0U;
+ bearer_ctl_send(LINK_CLOSE, &reason, sizeof(reason));
+
+ reset_state();
+#endif
+}
+
+int bt_mesh_prov_init(const struct bt_mesh_prov *prov_info)
+{
+ if (!prov_info) {
+ BT_ERR("No provisioning context provided");
+ return -EINVAL;
+ }
+
+ k_delayed_work_init(&link.prot_timer, protocol_timeout);
+
+ prov = prov_info;
+
+#if MYNEWT_VAL(BLE_MESH_PB_ADV)
+ k_delayed_work_init(&link.tx.retransmit, prov_retransmit);
+#endif
+
+ return reset_state();
+}
+
+void bt_mesh_prov_reset_link(void)
+{
+#if (MYNEWT_VAL(BLE_MESH_PB_ADV))
+#if (MYNEWT_VAL(BLE_MESH_PB_GATT))
+ link.rx.buf = bt_mesh_proxy_get_buf();
+#else
+ net_buf_simple_init(rx_buf, 0);
+ link.rx.buf = rx_buf;
+#endif
+#endif
+}
+
+void bt_mesh_prov_complete(u16_t net_idx, u16_t addr)
+{
+ if (prov->complete) {
+ prov->complete(net_idx, 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) {
+ prov->reset();
+ }
+}
+
+#endif /* MYNEWT_VAL(BLE_MESH_PROV) */
diff --git a/src/libs/mynewt-nimble/nimble/host/mesh/src/prov.h b/src/libs/mynewt-nimble/nimble/host/mesh/src/prov.h
new file mode 100644
index 00000000..96e5a447
--- /dev/null
+++ b/src/libs/mynewt-nimble/nimble/host/mesh/src/prov.h
@@ -0,0 +1,37 @@
+/* Bluetooth Mesh */
+
+/*
+ * Copyright (c) 2017 Intel Corporation
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#ifndef __PROV_H__
+#define __PROV_H__
+
+#include "os/os_mbuf.h"
+#include "mesh/mesh.h"
+#include "../src/ble_hs_conn_priv.h"
+
+int bt_mesh_pb_adv_open(const u8_t uuid[16], u16_t net_idx, u16_t addr,
+ u8_t attention_duration);
+
+void bt_mesh_pb_adv_recv(struct os_mbuf *buf);
+
+bool bt_prov_active(void);
+
+int bt_mesh_pb_gatt_open(uint16_t conn_handle);
+int bt_mesh_pb_gatt_close(uint16_t conn_handle);
+int bt_mesh_pb_gatt_recv(uint16_t conn_handle, struct os_mbuf *buf);
+
+const struct bt_mesh_prov *bt_mesh_prov_get(void);
+
+int bt_mesh_prov_init(const struct bt_mesh_prov *prov);
+
+void bt_mesh_prov_reset_link(void);
+
+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);
+void bt_mesh_prov_reset(void);
+
+#endif
diff --git a/src/libs/mynewt-nimble/nimble/host/mesh/src/proxy.c b/src/libs/mynewt-nimble/nimble/host/mesh/src/proxy.c
new file mode 100644
index 00000000..134a36dd
--- /dev/null
+++ b/src/libs/mynewt-nimble/nimble/host/mesh/src/proxy.c
@@ -0,0 +1,1499 @@
+/* Bluetooth Mesh */
+
+/*
+ * Copyright (c) 2017 Intel Corporation
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include "syscfg/syscfg.h"
+#define MESH_LOG_MODULE BLE_MESH_PROXY_LOG
+
+#if MYNEWT_VAL(BLE_MESH_PROXY)
+
+#include "mesh/mesh.h"
+#include "host/ble_att.h"
+#include "services/gatt/ble_svc_gatt.h"
+#include "../../host/src/ble_hs_priv.h"
+
+#include "mesh_priv.h"
+#include "adv.h"
+#include "net.h"
+#include "prov.h"
+#include "beacon.h"
+#include "foundation.h"
+#include "access.h"
+#include "proxy.h"
+
+#define PDU_TYPE(data) (data[0] & BIT_MASK(6))
+#define PDU_SAR(data) (data[0] >> 6)
+
+/* Mesh Profile 1.0 Section 6.6:
+ * "The timeout for the SAR transfer is 20 seconds. When the timeout
+ * expires, the Proxy Server shall disconnect."
+ */
+#define PROXY_SAR_TIMEOUT K_SECONDS(20)
+
+#define SAR_COMPLETE 0x00
+#define SAR_FIRST 0x01
+#define SAR_CONT 0x02
+#define SAR_LAST 0x03
+
+#define CFG_FILTER_SET 0x00
+#define CFG_FILTER_ADD 0x01
+#define CFG_FILTER_REMOVE 0x02
+#define CFG_FILTER_STATUS 0x03
+
+/** @def BT_UUID_MESH_PROV
+ * @brief Mesh Provisioning Service
+ */
+ble_uuid16_t BT_UUID_MESH_PROV = BLE_UUID16_INIT(0x1827);
+#define BT_UUID_MESH_PROV_VAL 0x1827
+/** @def BT_UUID_MESH_PROXY
+ * @brief Mesh Proxy Service
+ */
+ble_uuid16_t BT_UUID_MESH_PROXY = BLE_UUID16_INIT(0x1828);
+#define BT_UUID_MESH_PROXY_VAL 0x1828
+/** @def BT_UUID_GATT_CCC
+ * @brief GATT Client Characteristic Configuration
+ */
+ble_uuid16_t BT_UUID_GATT_CCC = BLE_UUID16_INIT(0x2902);
+#define BT_UUID_GATT_CCC_VAL 0x2902
+/** @def BT_UUID_MESH_PROV_DATA_IN
+ * @brief Mesh Provisioning Data In
+ */
+ble_uuid16_t BT_UUID_MESH_PROV_DATA_IN = BLE_UUID16_INIT(0x2adb);
+#define BT_UUID_MESH_PROV_DATA_IN_VAL 0x2adb
+/** @def BT_UUID_MESH_PROV_DATA_OUT
+ * @brief Mesh Provisioning Data Out
+ */
+ble_uuid16_t BT_UUID_MESH_PROV_DATA_OUT = BLE_UUID16_INIT(0x2adc);
+#define BT_UUID_MESH_PROV_DATA_OUT_VAL 0x2adc
+/** @def BT_UUID_MESH_PROXY_DATA_IN
+ * @brief Mesh Proxy Data In
+ */
+ble_uuid16_t BT_UUID_MESH_PROXY_DATA_IN = BLE_UUID16_INIT(0x2add);
+#define BT_UUID_MESH_PROXY_DATA_IN_VAL 0x2add
+/** @def BT_UUID_MESH_PROXY_DATA_OUT
+ * @brief Mesh Proxy Data Out
+ */
+ble_uuid16_t BT_UUID_MESH_PROXY_DATA_OUT = BLE_UUID16_INIT(0x2ade);
+#define BT_UUID_MESH_PROXY_DATA_OUT_VAL 0x2ade
+
+#define PDU_HDR(sar, type) (sar << 6 | (type & BIT_MASK(6)))
+
+#define CLIENT_BUF_SIZE 68
+
+static const struct ble_gap_adv_params slow_adv_param = {
+ .conn_mode = (BLE_GAP_CONN_MODE_UND),
+ .disc_mode = (BLE_GAP_DISC_MODE_GEN),
+ .itvl_min = BT_GAP_ADV_SLOW_INT_MIN,
+ .itvl_max = BT_GAP_ADV_SLOW_INT_MAX,
+};
+
+static const struct ble_gap_adv_params fast_adv_param = {
+ .conn_mode = (BLE_GAP_CONN_MODE_UND),
+ .disc_mode = (BLE_GAP_DISC_MODE_GEN),
+ .itvl_min = BT_GAP_ADV_FAST_INT_MIN_2,
+ .itvl_max = BT_GAP_ADV_FAST_INT_MAX_2,
+};
+
+static bool proxy_adv_enabled;
+
+#if (MYNEWT_VAL(BLE_MESH_GATT_PROXY))
+static void proxy_send_beacons(struct ble_npl_event *work);
+#endif
+
+#if (MYNEWT_VAL(BLE_MESH_PB_GATT))
+static bool prov_fast_adv;
+#endif
+
+static struct bt_mesh_proxy_client {
+ uint16_t conn_handle;
+ u16_t filter[MYNEWT_VAL(BLE_MESH_PROXY_FILTER_SIZE)];
+ enum __packed {
+ NONE,
+ WHITELIST,
+ BLACKLIST,
+ PROV,
+ } filter_type;
+ u8_t msg_type;
+#if (MYNEWT_VAL(BLE_MESH_GATT_PROXY))
+ struct ble_npl_callout send_beacons;
+#endif
+ struct k_delayed_work sar_timer;
+ struct os_mbuf *buf;
+} clients[MYNEWT_VAL(BLE_MAX_CONNECTIONS)] = {
+ [0 ... (MYNEWT_VAL(BLE_MAX_CONNECTIONS) - 1)] = { 0 },
+};
+
+/* Track which service is enabled */
+static enum {
+ MESH_GATT_NONE,
+ MESH_GATT_PROV,
+ MESH_GATT_PROXY,
+} gatt_svc = MESH_GATT_NONE;
+
+static struct {
+ uint16_t proxy_h;
+ uint16_t proxy_data_out_h;
+ uint16_t prov_h;
+ uint16_t prov_data_in_h;
+ uint16_t prov_data_out_h;
+} svc_handles;
+
+static void resolve_svc_handles(void)
+{
+ int rc;
+
+ /* Either all handles are already resolved, or none of them */
+ if (svc_handles.prov_data_out_h) {
+ return;
+ }
+
+ /*
+ * We assert if attribute is not found since at this stage all attributes
+ * shall be already registered and thus shall be found.
+ */
+
+ rc = ble_gatts_find_svc(BLE_UUID16_DECLARE(BT_UUID_MESH_PROXY_VAL),
+ &svc_handles.proxy_h);
+ assert(rc == 0);
+
+ rc = ble_gatts_find_chr(BLE_UUID16_DECLARE(BT_UUID_MESH_PROXY_VAL),
+ BLE_UUID16_DECLARE(BT_UUID_MESH_PROXY_DATA_OUT_VAL),
+ NULL, &svc_handles.proxy_data_out_h);
+ assert(rc == 0);
+
+ rc = ble_gatts_find_svc(BLE_UUID16_DECLARE(BT_UUID_MESH_PROV_VAL),
+ &svc_handles.prov_h);
+ assert(rc == 0);
+
+ rc = ble_gatts_find_chr(BLE_UUID16_DECLARE(BT_UUID_MESH_PROV_VAL),
+ BLE_UUID16_DECLARE(BT_UUID_MESH_PROV_DATA_IN_VAL),
+ NULL, &svc_handles.prov_data_in_h);
+ assert(rc == 0);
+
+ rc = ble_gatts_find_chr(BLE_UUID16_DECLARE(BT_UUID_MESH_PROV_VAL),
+ BLE_UUID16_DECLARE(BT_UUID_MESH_PROV_DATA_OUT_VAL),
+ NULL, &svc_handles.prov_data_out_h);
+ assert(rc == 0);
+}
+
+static struct bt_mesh_proxy_client *find_client(uint16_t conn_handle)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(clients); i++) {
+ if (clients[i].conn_handle == conn_handle) {
+ return &clients[i];
+ }
+ }
+
+ return NULL;
+}
+
+#if (MYNEWT_VAL(BLE_MESH_GATT_PROXY))
+/* Next subnet in queue to be advertised */
+static int next_idx;
+
+static int proxy_segment_and_send(uint16_t conn_handle, u8_t type,
+ struct os_mbuf *msg);
+
+static int filter_set(struct bt_mesh_proxy_client *client,
+ struct os_mbuf *buf)
+{
+ u8_t type;
+
+ if (buf->om_len < 1) {
+ BT_WARN("Too short Filter Set message");
+ return -EINVAL;
+ }
+
+ type = net_buf_simple_pull_u8(buf);
+ BT_DBG("type 0x%02x", type);
+
+ switch (type) {
+ case 0x00:
+ memset(client->filter, 0, sizeof(client->filter));
+ client->filter_type = WHITELIST;
+ break;
+ case 0x01:
+ memset(client->filter, 0, sizeof(client->filter));
+ client->filter_type = BLACKLIST;
+ break;
+ default:
+ BT_WARN("Prohibited Filter Type 0x%02x", type);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void filter_add(struct bt_mesh_proxy_client *client, u16_t addr)
+{
+ int i;
+
+ BT_DBG("addr 0x%04x", addr);
+
+ if (addr == BT_MESH_ADDR_UNASSIGNED) {
+ return;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(client->filter); i++) {
+ if (client->filter[i] == addr) {
+ return;
+ }
+ }
+
+ for (i = 0; i < ARRAY_SIZE(client->filter); i++) {
+ if (client->filter[i] == BT_MESH_ADDR_UNASSIGNED) {
+ client->filter[i] = addr;
+ return;
+ }
+ }
+}
+
+static void filter_remove(struct bt_mesh_proxy_client *client, u16_t addr)
+{
+ int i;
+
+ BT_DBG("addr 0x%04x", addr);
+
+ if (addr == BT_MESH_ADDR_UNASSIGNED) {
+ return;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(client->filter); i++) {
+ if (client->filter[i] == addr) {
+ client->filter[i] = BT_MESH_ADDR_UNASSIGNED;
+ return;
+ }
+ }
+}
+
+static void send_filter_status(struct bt_mesh_proxy_client *client,
+ struct bt_mesh_net_rx *rx,
+ struct os_mbuf *buf)
+{
+ struct bt_mesh_net_tx tx = {
+ .sub = rx->sub,
+ .ctx = &rx->ctx,
+ .src = bt_mesh_primary_addr(),
+ };
+ u16_t filter_size;
+ int i, err;
+
+ /* Configuration messages always have dst unassigned */
+ tx.ctx->addr = BT_MESH_ADDR_UNASSIGNED;
+
+ net_buf_simple_init(buf, 10);
+
+ net_buf_simple_add_u8(buf, CFG_FILTER_STATUS);
+
+ if (client->filter_type == WHITELIST) {
+ net_buf_simple_add_u8(buf, 0x00);
+ } else {
+ net_buf_simple_add_u8(buf, 0x01);
+ }
+
+ for (filter_size = 0, i = 0; i < ARRAY_SIZE(client->filter); i++) {
+ if (client->filter[i] != BT_MESH_ADDR_UNASSIGNED) {
+ filter_size++;
+ }
+ }
+
+ net_buf_simple_add_be16(buf, filter_size);
+
+ BT_DBG("%u bytes: %s", buf->om_len, bt_hex(buf->om_data, buf->om_len));
+
+ err = bt_mesh_net_encode(&tx, buf, true);
+ if (err) {
+ BT_ERR("Encoding Proxy cfg message failed (err %d)", err);
+ return;
+ }
+
+ err = proxy_segment_and_send(client->conn_handle, BT_MESH_PROXY_CONFIG, buf);
+ if (err) {
+ BT_ERR("Failed to send proxy cfg message (err %d)", err);
+ }
+}
+
+static void proxy_cfg(struct bt_mesh_proxy_client *client)
+{
+ struct os_mbuf *buf = NET_BUF_SIMPLE(29);
+ struct bt_mesh_net_rx rx;
+ u8_t opcode;
+ int err;
+
+ err = bt_mesh_net_decode(client->buf, BT_MESH_NET_IF_PROXY_CFG,
+ &rx, buf);
+ if (err) {
+ BT_ERR("Failed to decode Proxy Configuration (err %d)", err);
+ goto done;
+ }
+
+ /* Remove network headers */
+ net_buf_simple_pull(buf, BT_MESH_NET_HDR_LEN);
+
+ BT_DBG("%u bytes: %s", buf->om_len, bt_hex(buf->om_data, buf->om_len));
+
+ if (buf->om_len < 1) {
+ BT_WARN("Too short proxy configuration PDU");
+ goto done;
+ }
+
+ opcode = net_buf_simple_pull_u8(buf);
+ switch (opcode) {
+ case CFG_FILTER_SET:
+ filter_set(client, buf);
+ send_filter_status(client, &rx, buf);
+ break;
+ case CFG_FILTER_ADD:
+ while (buf->om_len >= 2) {
+ u16_t addr;
+
+ addr = net_buf_simple_pull_be16(buf);
+ filter_add(client, addr);
+ }
+ send_filter_status(client, &rx, buf);
+ break;
+ case CFG_FILTER_REMOVE:
+ while (buf->om_len >= 2) {
+ u16_t addr;
+
+ addr = net_buf_simple_pull_be16(buf);
+ filter_remove(client, addr);
+ }
+ send_filter_status(client, &rx, buf);
+ break;
+ default:
+ BT_WARN("Unhandled configuration OpCode 0x%02x", opcode);
+ break;
+ }
+
+done:
+ os_mbuf_free_chain(buf);
+}
+
+static int beacon_send(uint16_t conn_handle, struct bt_mesh_subnet *sub)
+{
+ struct os_mbuf *buf = NET_BUF_SIMPLE(23);
+ int rc;
+
+ net_buf_simple_init(buf, 1);
+ bt_mesh_beacon_create(sub, buf);
+
+ rc = proxy_segment_and_send(conn_handle, BT_MESH_PROXY_BEACON, buf);
+ os_mbuf_free_chain(buf);
+ return rc;
+}
+
+static void proxy_send_beacons(struct ble_npl_event *work)
+{
+ struct bt_mesh_proxy_client *client;
+ int i;
+
+
+ client = ble_npl_event_get_arg(work);
+
+ for (i = 0; i < ARRAY_SIZE(bt_mesh.sub); i++) {
+ struct bt_mesh_subnet *sub = &bt_mesh.sub[i];
+
+ if (sub->net_idx != BT_MESH_KEY_UNUSED) {
+ beacon_send(client->conn_handle, sub);
+ }
+ }
+}
+
+static void proxy_sar_timeout(struct ble_npl_event *work)
+{
+ struct bt_mesh_proxy_client *client;
+ int rc;
+
+ BT_WARN("Proxy SAR timeout");
+
+ client = ble_npl_event_get_arg(work);
+ assert(client != NULL);
+
+ if ((client->conn_handle != BLE_HS_CONN_HANDLE_NONE)) {
+ rc = ble_gap_terminate(client->conn_handle,
+ BLE_ERR_REM_USER_CONN_TERM);
+ assert(rc == 0);
+ }
+}
+
+void bt_mesh_proxy_beacon_send(struct bt_mesh_subnet *sub)
+{
+ int i;
+
+ if (!sub) {
+ /* NULL means we send on all subnets */
+ for (i = 0; i < ARRAY_SIZE(bt_mesh.sub); i++) {
+ if (bt_mesh.sub[i].net_idx != BT_MESH_KEY_UNUSED) {
+ bt_mesh_proxy_beacon_send(&bt_mesh.sub[i]);
+ }
+ }
+
+ return;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(clients); i++) {
+ if (clients[i].conn_handle != BLE_HS_CONN_HANDLE_NONE) {
+ beacon_send(clients[i].conn_handle, sub);
+ }
+ }
+}
+
+void bt_mesh_proxy_identity_start(struct bt_mesh_subnet *sub)
+{
+ sub->node_id = BT_MESH_NODE_IDENTITY_RUNNING;
+ sub->node_id_start = k_uptime_get_32();
+
+ /* Prioritize the recently enabled subnet */
+ next_idx = sub - bt_mesh.sub;
+}
+
+void bt_mesh_proxy_identity_stop(struct bt_mesh_subnet *sub)
+{
+ sub->node_id = BT_MESH_NODE_IDENTITY_STOPPED;
+ sub->node_id_start = 0;
+}
+
+int bt_mesh_proxy_identity_enable(void)
+{
+ int i, count = 0;
+
+ BT_DBG("");
+
+ if (!bt_mesh_is_provisioned()) {
+ return -EAGAIN;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(bt_mesh.sub); i++) {
+ struct bt_mesh_subnet *sub = &bt_mesh.sub[i];
+
+ if (sub->net_idx == BT_MESH_KEY_UNUSED) {
+ continue;
+ }
+
+ if (sub->node_id == BT_MESH_NODE_IDENTITY_NOT_SUPPORTED) {
+ continue;
+ }
+
+ bt_mesh_proxy_identity_start(sub);
+ count++;
+ }
+
+ if (count) {
+ bt_mesh_adv_update();
+ }
+
+ return 0;
+}
+
+#endif /* GATT_PROXY */
+
+static void proxy_complete_pdu(struct bt_mesh_proxy_client *client)
+{
+ switch (client->msg_type) {
+#if (MYNEWT_VAL(BLE_MESH_GATT_PROXY))
+ case BT_MESH_PROXY_NET_PDU:
+ BT_INFO("Mesh Network PDU");
+ bt_mesh_net_recv(client->buf, 0, BT_MESH_NET_IF_PROXY);
+ break;
+ case BT_MESH_PROXY_BEACON:
+ BT_INFO("Mesh Beacon PDU");
+ bt_mesh_beacon_recv(client->buf);
+ break;
+ case BT_MESH_PROXY_CONFIG:
+ BT_INFO("Mesh Configuration PDU");
+ proxy_cfg(client);
+ break;
+#endif
+#if (MYNEWT_VAL(BLE_MESH_PB_GATT))
+ case BT_MESH_PROXY_PROV:
+ BT_INFO("Mesh Provisioning PDU");
+ bt_mesh_pb_gatt_recv(client->conn_handle, client->buf);
+ break;
+#endif
+ default:
+ BT_WARN("Unhandled Message Type 0x%02x", client->msg_type);
+ break;
+ }
+
+ net_buf_simple_init(client->buf, 0);
+}
+
+static int proxy_recv(uint16_t conn_handle, uint16_t attr_handle,
+ struct ble_gatt_access_ctxt *ctxt, void *arg)
+{
+ struct bt_mesh_proxy_client *client;
+ const u8_t *data = ctxt->om->om_data;
+ u16_t len = ctxt->om->om_len;
+
+ client = find_client(conn_handle);
+
+ if (!client) {
+ return -ENOTCONN;
+ }
+
+ if (len < 1) {
+ BT_WARN("Too small Proxy PDU");
+ return -EINVAL;
+ }
+
+ if ((attr_handle == svc_handles.prov_data_in_h) !=
+ (PDU_TYPE(data) == BT_MESH_PROXY_PROV)) {
+ BT_WARN("Proxy PDU type doesn't match GATT service");
+ return -EINVAL;
+ }
+
+ if (len - 1 > net_buf_simple_tailroom(client->buf)) {
+ BT_WARN("Too big proxy PDU");
+ return -EINVAL;
+ }
+
+ switch (PDU_SAR(data)) {
+ case SAR_COMPLETE:
+ if (client->buf->om_len) {
+ BT_WARN("Complete PDU while a pending incomplete one");
+ return -EINVAL;
+ }
+
+ client->msg_type = PDU_TYPE(data);
+ net_buf_simple_add_mem(client->buf, data + 1, len - 1);
+ proxy_complete_pdu(client);
+ break;
+
+ case SAR_FIRST:
+ if (client->buf->om_len) {
+ BT_WARN("First PDU while a pending incomplete one");
+ return -EINVAL;
+ }
+
+ k_delayed_work_submit(&client->sar_timer, PROXY_SAR_TIMEOUT);
+ client->msg_type = PDU_TYPE(data);
+ net_buf_simple_add_mem(client->buf, data + 1, len - 1);
+ break;
+
+ case SAR_CONT:
+ if (!client->buf->om_len) {
+ BT_WARN("Continuation with no prior data");
+ return -EINVAL;
+ }
+
+ if (client->msg_type != PDU_TYPE(data)) {
+ BT_WARN("Unexpected message type in continuation");
+ return -EINVAL;
+ }
+
+ k_delayed_work_submit(&client->sar_timer, PROXY_SAR_TIMEOUT);
+ net_buf_simple_add_mem(client->buf, data + 1, len - 1);
+ break;
+
+ case SAR_LAST:
+ if (!client->buf->om_len) {
+ BT_WARN("Last SAR PDU with no prior data");
+ return -EINVAL;
+ }
+
+ if (client->msg_type != PDU_TYPE(data)) {
+ BT_WARN("Unexpected message type in last SAR PDU");
+ return -EINVAL;
+ }
+
+ k_delayed_work_cancel(&client->sar_timer);
+ net_buf_simple_add_mem(client->buf, data + 1, len - 1);
+ proxy_complete_pdu(client);
+ break;
+ }
+
+ return len;
+}
+
+static int conn_count;
+
+static void proxy_connected(uint16_t conn_handle)
+{
+ struct bt_mesh_proxy_client *client;
+ int i;
+
+ BT_INFO("conn_handle %d", conn_handle);
+
+ conn_count++;
+
+ /* Since we use ADV_OPT_ONE_TIME */
+ proxy_adv_enabled = false;
+
+ /* Try to re-enable advertising in case it's possible */
+ if (conn_count < CONFIG_BT_MAX_CONN) {
+ bt_mesh_adv_update();
+ }
+
+ for (client = NULL, i = 0; i < ARRAY_SIZE(clients); i++) {
+ if (clients[i].conn_handle == BLE_HS_CONN_HANDLE_NONE) {
+ client = &clients[i];
+ break;
+ }
+ }
+
+ if (!client) {
+ BT_ERR("No free Proxy Client objects");
+ return;
+ }
+
+ client->conn_handle = conn_handle;
+ client->filter_type = NONE;
+ memset(client->filter, 0, sizeof(client->filter));
+ net_buf_simple_init(client->buf, 0);
+}
+
+static void proxy_disconnected(uint16_t conn_handle, int reason)
+{
+ int i;
+ bool disconnected = false;
+
+ for (i = 0; i < ARRAY_SIZE(clients); i++) {
+ struct bt_mesh_proxy_client *client = &clients[i];
+
+ if (client->conn_handle == conn_handle) {
+ if ((MYNEWT_VAL(BLE_MESH_PB_GATT)) &&
+ client->filter_type == PROV) {
+ bt_mesh_pb_gatt_close(conn_handle);
+ }
+
+ k_delayed_work_cancel(&client->sar_timer);
+ client->conn_handle = BLE_HS_CONN_HANDLE_NONE;
+ conn_count--;
+ disconnected = true;
+ break;
+ }
+ }
+
+ if (disconnected) {
+ BT_INFO("conn_handle %d reason %d", conn_handle, reason);
+ bt_mesh_adv_update();
+ }
+}
+
+struct os_mbuf *bt_mesh_proxy_get_buf(void)
+{
+ struct os_mbuf *buf = clients[0].buf;
+
+ if (buf != NULL) {
+ net_buf_simple_init(buf, 0);
+ }
+
+ return buf;
+}
+
+#if (MYNEWT_VAL(BLE_MESH_PB_GATT))
+static void prov_ccc_write(uint16_t conn_handle)
+{
+ struct bt_mesh_proxy_client *client;
+
+ BT_DBG("conn_handle %d", conn_handle);
+
+ /* If a connection exists there must be a client */
+ client = find_client(conn_handle);
+ __ASSERT(client, "No client for connection");
+
+ if (client->filter_type == NONE) {
+ client->filter_type = PROV;
+ bt_mesh_pb_gatt_open(conn_handle);
+ }
+}
+
+int bt_mesh_proxy_prov_enable(void)
+{
+ uint16_t handle;
+ int rc;
+ int i;
+
+ BT_DBG("");
+
+ if (gatt_svc == MESH_GATT_PROV) {
+ return -EALREADY;
+ }
+
+ if (gatt_svc != MESH_GATT_NONE) {
+ return -EBUSY;
+ }
+
+ rc = ble_gatts_find_svc(BLE_UUID16_DECLARE(BT_UUID_MESH_PROV_VAL), &handle);
+ assert(rc == 0);
+ ble_gatts_svc_set_visibility(handle, 1);
+ /* FIXME: figure out end handle */
+ ble_svc_gatt_changed(svc_handles.prov_h, 0xffff);
+
+ gatt_svc = MESH_GATT_PROV;
+ prov_fast_adv = true;
+
+ for (i = 0; i < ARRAY_SIZE(clients); i++) {
+ if (clients[i].conn_handle != BLE_HS_CONN_HANDLE_NONE) {
+ clients[i].filter_type = PROV;
+ }
+ }
+
+
+ return 0;
+}
+
+int bt_mesh_proxy_prov_disable(bool disconnect)
+{
+ uint16_t handle;
+ int rc;
+ int i;
+
+ BT_DBG("");
+
+ if (gatt_svc == MESH_GATT_NONE) {
+ return -EALREADY;
+ }
+
+ if (gatt_svc != MESH_GATT_PROV) {
+ return -EBUSY;
+ }
+
+ rc = ble_gatts_find_svc(BLE_UUID16_DECLARE(BT_UUID_MESH_PROV_VAL), &handle);
+ assert(rc == 0);
+ ble_gatts_svc_set_visibility(handle, 0);
+ /* FIXME: figure out end handle */
+ ble_svc_gatt_changed(svc_handles.prov_h, 0xffff);
+
+ gatt_svc = MESH_GATT_NONE;
+
+ for (i = 0; i < ARRAY_SIZE(clients); i++) {
+ struct bt_mesh_proxy_client *client = &clients[i];
+
+ if ((client->conn_handle == BLE_HS_CONN_HANDLE_NONE)
+ || (client->filter_type != PROV)) {
+ continue;
+ }
+
+ if (disconnect) {
+ rc = ble_gap_terminate(client->conn_handle,
+ BLE_ERR_REM_USER_CONN_TERM);
+ assert(rc == 0);
+ } else {
+ bt_mesh_pb_gatt_close(client->conn_handle);
+ client->filter_type = NONE;
+ }
+ }
+
+ bt_mesh_adv_update();
+
+ return 0;
+}
+#endif /* MYNEWT_VAL(BLE_MESH_PB_GATT) */
+
+#if (MYNEWT_VAL(BLE_MESH_GATT_PROXY))
+static void proxy_ccc_write(uint16_t conn_handle)
+{
+ struct bt_mesh_proxy_client *client;
+
+ BT_DBG("conn_handle %d", conn_handle);
+
+ client = find_client(conn_handle);
+ __ASSERT(client, "No client for connection");
+
+ if (client->filter_type == NONE) {
+ client->filter_type = WHITELIST;
+ k_work_add_arg(&client->send_beacons, client);
+ k_work_submit(&client->send_beacons);
+ }
+}
+
+int bt_mesh_proxy_gatt_enable(void)
+{
+ uint16_t handle;
+ int rc;
+ int i;
+
+ BT_DBG("");
+
+ if (gatt_svc == MESH_GATT_PROXY) {
+ return -EALREADY;
+ }
+
+ if (gatt_svc != MESH_GATT_NONE) {
+ return -EBUSY;
+ }
+
+ rc = ble_gatts_find_svc(BLE_UUID16_DECLARE(BT_UUID_MESH_PROXY_VAL), &handle);
+ assert(rc == 0);
+ ble_gatts_svc_set_visibility(handle, 1);
+ /* FIXME: figure out end handle */
+ ble_svc_gatt_changed(svc_handles.proxy_h, 0xffff);
+
+ gatt_svc = MESH_GATT_PROXY;
+
+ for (i = 0; i < ARRAY_SIZE(clients); i++) {
+ if (clients[i].conn_handle != BLE_HS_CONN_HANDLE_NONE) {
+ clients[i].filter_type = WHITELIST;
+ }
+ }
+
+ return 0;
+}
+
+void bt_mesh_proxy_gatt_disconnect(void)
+{
+ int rc;
+ int i;
+
+ BT_DBG("");
+
+ for (i = 0; i < ARRAY_SIZE(clients); i++) {
+ struct bt_mesh_proxy_client *client = &clients[i];
+
+ if ((client->conn_handle != BLE_HS_CONN_HANDLE_NONE) &&
+ (client->filter_type == WHITELIST ||
+ client->filter_type == BLACKLIST)) {
+ client->filter_type = NONE;
+ rc = ble_gap_terminate(client->conn_handle,
+ BLE_ERR_REM_USER_CONN_TERM);
+ assert(rc == 0);
+ }
+ }
+}
+
+int bt_mesh_proxy_gatt_disable(void)
+{
+ uint16_t handle;
+ int rc;
+
+ BT_DBG("");
+
+ if (gatt_svc == MESH_GATT_NONE) {
+ return -EALREADY;
+ }
+
+ if (gatt_svc != MESH_GATT_PROXY) {
+ return -EBUSY;
+ }
+
+ bt_mesh_proxy_gatt_disconnect();
+
+ rc = ble_gatts_find_svc(BLE_UUID16_DECLARE(BT_UUID_MESH_PROXY_VAL), &handle);
+ assert(rc == 0);
+ ble_gatts_svc_set_visibility(handle, 0);
+ /* FIXME: figure out end handle */
+ ble_svc_gatt_changed(svc_handles.proxy_h, 0xffff);
+
+ gatt_svc = MESH_GATT_NONE;
+
+ return 0;
+}
+
+void bt_mesh_proxy_addr_add(struct os_mbuf *buf, u16_t addr)
+{
+ struct bt_mesh_proxy_client *client = NULL;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(clients); i++) {
+ client = &clients[i];
+ if (client->buf == buf) {
+ break;
+ }
+ }
+
+ assert(client);
+
+ BT_DBG("filter_type %u addr 0x%04x", client->filter_type, addr);
+
+ if (client->filter_type == WHITELIST) {
+ filter_add(client, addr);
+ } else if (client->filter_type == BLACKLIST) {
+ filter_remove(client, addr);
+ }
+}
+
+static bool client_filter_match(struct bt_mesh_proxy_client *client,
+ u16_t addr)
+{
+ int i;
+
+ BT_DBG("filter_type %u addr 0x%04x", client->filter_type, addr);
+
+ if (client->filter_type == BLACKLIST) {
+ for (i = 0; i < ARRAY_SIZE(client->filter); i++) {
+ if (client->filter[i] == addr) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ if (addr == BT_MESH_ADDR_ALL_NODES) {
+ return true;
+ }
+
+ if (client->filter_type == WHITELIST) {
+ for (i = 0; i < ARRAY_SIZE(client->filter); i++) {
+ if (client->filter[i] == addr) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+bool bt_mesh_proxy_relay(struct os_mbuf *buf, u16_t dst)
+{
+ bool relayed = false;
+ int i;
+
+ BT_DBG("%u bytes to dst 0x%04x", buf->om_len, dst);
+
+ for (i = 0; i < ARRAY_SIZE(clients); i++) {
+ struct bt_mesh_proxy_client *client = &clients[i];
+ struct os_mbuf *msg;
+
+ if (client->conn_handle == BLE_HS_CONN_HANDLE_NONE) {
+ continue;
+ }
+
+ if (!client_filter_match(client, dst)) {
+ continue;
+ }
+
+ /* Proxy PDU sending modifies the original buffer,
+ * so we need to make a copy.
+ */
+ msg = NET_BUF_SIMPLE(32);
+ net_buf_simple_init(msg, 1);
+ net_buf_simple_add_mem(msg, buf->om_data, buf->om_len);
+
+ bt_mesh_proxy_send(client->conn_handle, BT_MESH_PROXY_NET_PDU, msg);
+ os_mbuf_free_chain(msg);
+ relayed = true;
+ }
+
+ return relayed;
+}
+
+#endif /* MYNEWT_VAL(BLE_MESH_GATT_PROXY) */
+
+static int proxy_send(uint16_t conn_handle, const void *data, u16_t len)
+{
+ struct os_mbuf *om;
+
+ BT_DBG("%u bytes: %s", len, bt_hex(data, len));
+
+#if (MYNEWT_VAL(BLE_MESH_GATT_PROXY))
+ if (gatt_svc == MESH_GATT_PROXY) {
+ om = ble_hs_mbuf_from_flat(data, len);
+ assert(om);
+ ble_gattc_notify_custom(conn_handle, svc_handles.proxy_data_out_h, om);
+ }
+#endif
+
+#if (MYNEWT_VAL(BLE_MESH_PB_GATT))
+ if (gatt_svc == MESH_GATT_PROV) {
+ om = ble_hs_mbuf_from_flat(data, len);
+ assert(om);
+ ble_gattc_notify_custom(conn_handle, svc_handles.prov_data_out_h, om);
+ }
+#endif
+
+ return 0;
+}
+
+static int proxy_segment_and_send(uint16_t conn_handle, u8_t type,
+ struct os_mbuf *msg)
+{
+ u16_t mtu;
+
+ BT_DBG("conn_handle %d type 0x%02x len %u: %s", conn_handle, type, msg->om_len,
+ bt_hex(msg->om_data, msg->om_len));
+
+ /* ATT_MTU - OpCode (1 byte) - Handle (2 bytes) */
+ mtu = ble_att_mtu(conn_handle) - 3;
+ if (mtu > msg->om_len) {
+ net_buf_simple_push_u8(msg, PDU_HDR(SAR_COMPLETE, type));
+ return proxy_send(conn_handle, msg->om_data, msg->om_len);
+ }
+
+ net_buf_simple_push_u8(msg, PDU_HDR(SAR_FIRST, type));
+ proxy_send(conn_handle, msg->om_data, mtu);
+ net_buf_simple_pull(msg, mtu);
+
+ while (msg->om_len) {
+ if (msg->om_len + 1 < mtu) {
+ net_buf_simple_push_u8(msg, PDU_HDR(SAR_LAST, type));
+ proxy_send(conn_handle, msg->om_data, msg->om_len);
+ break;
+ }
+
+ net_buf_simple_push_u8(msg, PDU_HDR(SAR_CONT, type));
+ proxy_send(conn_handle, msg->om_data, mtu);
+ net_buf_simple_pull(msg, mtu);
+ }
+
+ return 0;
+}
+
+int bt_mesh_proxy_send(uint16_t conn_handle, u8_t type,
+ struct os_mbuf *msg)
+{
+ struct bt_mesh_proxy_client *client = find_client(conn_handle);
+
+ if (!client) {
+ BT_ERR("No Proxy Client found");
+ return -ENOTCONN;
+ }
+
+ if ((client->filter_type == PROV) != (type == BT_MESH_PROXY_PROV)) {
+ BT_ERR("Invalid PDU type for Proxy Client");
+ return -EINVAL;
+ }
+
+ return proxy_segment_and_send(conn_handle, type, msg);
+}
+
+#if (MYNEWT_VAL(BLE_MESH_PB_GATT))
+static u8_t prov_svc_data[20] = { 0x27, 0x18, };
+
+static const struct bt_data prov_ad[] = {
+ BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)),
+ BT_DATA_BYTES(BT_DATA_UUID16_ALL, 0x27, 0x18),
+ BT_DATA(BT_DATA_SVC_DATA16, prov_svc_data, sizeof(prov_svc_data)),
+};
+#endif /* PB_GATT */
+
+#if (MYNEWT_VAL(BLE_MESH_GATT_PROXY))
+
+#define ID_TYPE_NET 0x00
+#define ID_TYPE_NODE 0x01
+
+#define NODE_ID_LEN 19
+#define NET_ID_LEN 11
+
+#define NODE_ID_TIMEOUT K_SECONDS(CONFIG_BT_MESH_NODE_ID_TIMEOUT)
+
+static u8_t proxy_svc_data[NODE_ID_LEN] = { 0x28, 0x18, };
+
+static const struct bt_data node_id_ad[] = {
+ BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)),
+ BT_DATA_BYTES(BT_DATA_UUID16_ALL, 0x28, 0x18),
+ BT_DATA(BT_DATA_SVC_DATA16, proxy_svc_data, NODE_ID_LEN),
+};
+
+static const struct bt_data net_id_ad[] = {
+ BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)),
+ BT_DATA_BYTES(BT_DATA_UUID16_ALL, 0x28, 0x18),
+ BT_DATA(BT_DATA_SVC_DATA16, proxy_svc_data, NET_ID_LEN),
+};
+
+static int node_id_adv(struct bt_mesh_subnet *sub)
+{
+ u8_t tmp[16];
+ int err;
+
+ BT_DBG("");
+
+ proxy_svc_data[2] = ID_TYPE_NODE;
+
+ err = bt_rand(proxy_svc_data + 11, 8);
+ if (err) {
+ return err;
+ }
+
+ memset(tmp, 0, 6);
+ memcpy(tmp + 6, proxy_svc_data + 11, 8);
+ sys_put_be16(bt_mesh_primary_addr(), tmp + 14);
+
+ err = bt_encrypt_be(sub->keys[sub->kr_flag].identity, tmp, tmp);
+ if (err) {
+ return err;
+ }
+
+ memcpy(proxy_svc_data + 3, tmp + 8, 8);
+
+ err = bt_le_adv_start(&fast_adv_param, node_id_ad,
+ ARRAY_SIZE(node_id_ad), NULL, 0);
+ if (err) {
+ BT_WARN("Failed to advertise using Node ID (err %d)", err);
+ return err;
+ }
+
+ proxy_adv_enabled = true;
+
+ return 0;
+}
+
+static int net_id_adv(struct bt_mesh_subnet *sub)
+{
+ int err;
+
+ BT_DBG("");
+
+ proxy_svc_data[2] = ID_TYPE_NET;
+
+ BT_DBG("Advertising with NetId %s",
+ bt_hex(sub->keys[sub->kr_flag].net_id, 8));
+
+ memcpy(proxy_svc_data + 3, sub->keys[sub->kr_flag].net_id, 8);
+
+ err = bt_le_adv_start(&slow_adv_param, net_id_ad,
+ ARRAY_SIZE(net_id_ad), NULL, 0);
+ if (err) {
+ BT_WARN("Failed to advertise using Network ID (err %d)", err);
+ return err;
+ }
+
+ proxy_adv_enabled = true;
+
+ return 0;
+}
+
+static bool advertise_subnet(struct bt_mesh_subnet *sub)
+{
+ if (sub->net_idx == BT_MESH_KEY_UNUSED) {
+ return false;
+ }
+
+ return (sub->node_id == BT_MESH_NODE_IDENTITY_RUNNING ||
+ bt_mesh_gatt_proxy_get() != BT_MESH_GATT_PROXY_NOT_SUPPORTED);
+}
+
+static struct bt_mesh_subnet *next_sub(void)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(bt_mesh.sub); i++) {
+ struct bt_mesh_subnet *sub;
+
+ sub = &bt_mesh.sub[(i + next_idx) % ARRAY_SIZE(bt_mesh.sub)];
+ if (advertise_subnet(sub)) {
+ next_idx = (next_idx + 1) % ARRAY_SIZE(bt_mesh.sub);
+ return sub;
+ }
+ }
+
+ return NULL;
+}
+
+static int sub_count(void)
+{
+ int i, count = 0;
+
+ for (i = 0; i < ARRAY_SIZE(bt_mesh.sub); i++) {
+ struct bt_mesh_subnet *sub = &bt_mesh.sub[i];
+
+ if (advertise_subnet(sub)) {
+ count++;
+ }
+ }
+
+ return count;
+}
+
+static s32_t gatt_proxy_advertise(struct bt_mesh_subnet *sub)
+{
+ s32_t remaining = K_FOREVER;
+ int subnet_count;
+
+ BT_DBG("");
+
+ if (conn_count == CONFIG_BT_MAX_CONN) {
+ BT_DBG("Connectable advertising deferred (max connections)");
+ return remaining;
+ }
+
+ if (!sub) {
+ BT_WARN("No subnets to advertise on");
+ return remaining;
+ }
+
+ if (sub->node_id == BT_MESH_NODE_IDENTITY_RUNNING) {
+ u32_t active = k_uptime_get_32() - sub->node_id_start;
+
+ if (active < NODE_ID_TIMEOUT) {
+ remaining = NODE_ID_TIMEOUT - active;
+ BT_DBG("Node ID active for %u ms, %d ms remaining",
+ (unsigned) active, (int) remaining);
+ node_id_adv(sub);
+ } else {
+ bt_mesh_proxy_identity_stop(sub);
+ BT_DBG("Node ID stopped");
+ }
+ }
+
+ if (sub->node_id == BT_MESH_NODE_IDENTITY_STOPPED) {
+ net_id_adv(sub);
+ }
+
+ subnet_count = sub_count();
+ BT_DBG("sub_count %u", subnet_count);
+ if (subnet_count > 1) {
+ s32_t max_timeout;
+
+ /* We use NODE_ID_TIMEOUT as a starting point since it may
+ * be less than 60 seconds. Divide this period into at least
+ * 6 slices, but make sure that a slice is at least one
+ * second long (to avoid excessive rotation).
+ */
+ max_timeout = NODE_ID_TIMEOUT / max(subnet_count, 6);
+ max_timeout = max(max_timeout, K_SECONDS(1));
+
+ if (remaining > max_timeout || remaining < 0) {
+ remaining = max_timeout;
+ }
+ }
+
+ BT_DBG("Advertising %d ms for net_idx 0x%04x",
+ (int) remaining, sub->net_idx);
+
+ return remaining;
+}
+#endif /* GATT_PROXY */
+
+#if (MYNEWT_VAL(BLE_MESH_PB_GATT))
+static size_t gatt_prov_adv_create(struct bt_data prov_sd[2])
+{
+ const struct bt_mesh_prov *prov = bt_mesh_prov_get();
+ const char *name = CONFIG_BT_DEVICE_NAME;
+ size_t name_len = strlen(name);
+ size_t prov_sd_len = 0;
+ size_t sd_space = 31;
+
+ memcpy(prov_svc_data + 2, prov->uuid, 16);
+ sys_put_be16(prov->oob_info, prov_svc_data + 18);
+
+ if (prov->uri) {
+ size_t uri_len = strlen(prov->uri);
+
+ if (uri_len > 29) {
+ /* There's no way to shorten an URI */
+ BT_WARN("Too long URI to fit advertising packet");
+ } else {
+ prov_sd[0].type = BT_DATA_URI;
+ prov_sd[0].data_len = uri_len;
+ prov_sd[0].data = (void *)prov->uri;
+ sd_space -= 2 + uri_len;
+ prov_sd_len++;
+ }
+ }
+
+ if (sd_space > 2 && name_len > 0) {
+ sd_space -= 2;
+
+ if (sd_space < name_len) {
+ prov_sd[prov_sd_len].type = BT_DATA_NAME_SHORTENED;
+ prov_sd[prov_sd_len].data_len = sd_space;
+ } else {
+ prov_sd[prov_sd_len].type = BT_DATA_NAME_COMPLETE;
+ prov_sd[prov_sd_len].data_len = name_len;
+ }
+
+ prov_sd[prov_sd_len].data = (void *)name;
+ prov_sd_len++;
+ }
+
+ return prov_sd_len;
+}
+#endif /* PB_GATT */
+
+s32_t bt_mesh_proxy_adv_start(void)
+{
+ BT_DBG("");
+
+ if (gatt_svc == MESH_GATT_NONE) {
+ return K_FOREVER;
+ }
+
+#if (MYNEWT_VAL(BLE_MESH_PB_GATT))
+ if (!bt_mesh_is_provisioned()) {
+ const struct ble_gap_adv_params *param;
+ struct bt_data prov_sd[2];
+ size_t prov_sd_len;
+
+ if (prov_fast_adv) {
+ param = &fast_adv_param;
+ } else {
+ param = &slow_adv_param;
+ }
+
+ prov_sd_len = gatt_prov_adv_create(prov_sd);
+
+ if (bt_le_adv_start(param, prov_ad, ARRAY_SIZE(prov_ad),
+ prov_sd, prov_sd_len) == 0) {
+ proxy_adv_enabled = true;
+
+ /* Advertise 60 seconds using fast interval */
+ if (prov_fast_adv) {
+ prov_fast_adv = false;
+ return K_SECONDS(60);
+ }
+ }
+ }
+#endif /* PB_GATT */
+
+#if (MYNEWT_VAL(BLE_MESH_GATT_PROXY))
+ if (bt_mesh_is_provisioned()) {
+ return gatt_proxy_advertise(next_sub());
+ }
+#endif /* GATT_PROXY */
+
+ return K_FOREVER;
+}
+
+void bt_mesh_proxy_adv_stop(void)
+{
+ int err;
+
+ BT_DBG("adv_enabled %u", proxy_adv_enabled);
+
+ if (!proxy_adv_enabled) {
+ return;
+ }
+
+ err = bt_le_adv_stop(true);
+ if (err) {
+ BT_ERR("Failed to stop advertising (err %d)", err);
+ } else {
+ proxy_adv_enabled = false;
+ }
+}
+
+static void ble_mesh_handle_connect(struct ble_gap_event *event, void *arg)
+{
+#if MYNEWT_VAL(BLE_EXT_ADV)
+ /* When EXT ADV is enabled then mesh proxy is connected
+ * when proxy advertising instance is completed.
+ * Therefore no need to handle BLE_GAP_EVENT_CONNECT
+ */
+ if (event->type == BLE_GAP_EVENT_ADV_COMPLETE) {
+ /* Reason 0 means advertising has been completed because
+ * connection has been established
+ */
+ if (event->adv_complete.reason != 0) {
+ return;
+ }
+
+ if (event->adv_complete.instance != BT_MESH_ADV_GATT_INST) {
+ return;
+ }
+
+ proxy_connected(event->adv_complete.conn_handle);
+ }
+#else
+ if (event->type == BLE_GAP_EVENT_CONNECT) {
+ proxy_connected(event->connect.conn_handle);
+ }
+#endif
+}
+
+int ble_mesh_proxy_gap_event(struct ble_gap_event *event, void *arg)
+{
+ if ((event->type == BLE_GAP_EVENT_CONNECT) ||
+ (event->type == BLE_GAP_EVENT_ADV_COMPLETE)) {
+ ble_mesh_handle_connect(event, arg);
+ } else if (event->type == BLE_GAP_EVENT_DISCONNECT) {
+ proxy_disconnected(event->disconnect.conn.conn_handle,
+ event->disconnect.reason);
+ } else if (event->type == BLE_GAP_EVENT_SUBSCRIBE) {
+ if (event->subscribe.attr_handle == svc_handles.proxy_data_out_h) {
+#if (MYNEWT_VAL(BLE_MESH_GATT_PROXY))
+ proxy_ccc_write(event->subscribe.conn_handle);
+#endif
+ } else if (event->subscribe.attr_handle ==
+ svc_handles.prov_data_out_h) {
+#if (MYNEWT_VAL(BLE_MESH_PB_GATT))
+ prov_ccc_write(event->subscribe.conn_handle);
+#endif
+ }
+ }
+
+ return 0;
+}
+
+static int
+dummy_access_cb(uint16_t conn_handle, uint16_t attr_handle,
+ struct ble_gatt_access_ctxt *ctxt, void *arg)
+{
+ /*
+ * We should never never enter this callback - it's attached to notify-only
+ * characteristic which are notified directly from mbuf. And we can't pass
+ * NULL as access_cb because gatts will assert on init...
+ */
+ BLE_HS_DBG_ASSERT(0);
+ return 0;
+}
+
+static const struct ble_gatt_svc_def svc_defs [] = {
+ {
+ .type = BLE_GATT_SVC_TYPE_PRIMARY,
+ .uuid = BLE_UUID16_DECLARE(BT_UUID_MESH_PROXY_VAL),
+ .characteristics = (struct ble_gatt_chr_def[]) { {
+ .uuid = BLE_UUID16_DECLARE(BT_UUID_MESH_PROXY_DATA_IN_VAL),
+ .access_cb = proxy_recv,
+ .flags = BLE_GATT_CHR_F_WRITE_NO_RSP,
+ }, {
+ .uuid = BLE_UUID16_DECLARE(BT_UUID_MESH_PROXY_DATA_OUT_VAL),
+ .access_cb = dummy_access_cb,
+ .flags = BLE_GATT_CHR_F_NOTIFY,
+ }, {
+ 0, /* No more characteristics in this service. */
+ } },
+ }, {
+ .type = BLE_GATT_SVC_TYPE_PRIMARY,
+ .uuid = BLE_UUID16_DECLARE(BT_UUID_MESH_PROV_VAL),
+ .characteristics = (struct ble_gatt_chr_def[]) { {
+ .uuid = BLE_UUID16_DECLARE(BT_UUID_MESH_PROV_DATA_IN_VAL),
+ .access_cb = proxy_recv,
+ .flags = BLE_GATT_CHR_F_WRITE_NO_RSP,
+ }, {
+ .uuid = BLE_UUID16_DECLARE(BT_UUID_MESH_PROV_DATA_OUT_VAL),
+ .access_cb = dummy_access_cb,
+ .flags = BLE_GATT_CHR_F_NOTIFY,
+ }, {
+ 0, /* No more characteristics in this service. */
+ } },
+ }, {
+ 0, /* No more services. */
+ },
+};
+
+int bt_mesh_proxy_svcs_register(void)
+{
+ int rc;
+
+ rc = ble_gatts_count_cfg(svc_defs);
+ assert(rc == 0);
+
+ rc = ble_gatts_add_svcs(svc_defs);
+ assert(rc == 0);
+
+ return 0;
+}
+
+int bt_mesh_proxy_init(void)
+{
+ int i;
+
+ for (i = 0; i < MYNEWT_VAL(BLE_MAX_CONNECTIONS); ++i) {
+#if (MYNEWT_VAL(BLE_MESH_GATT_PROXY))
+ k_work_init(&clients[i].send_beacons, proxy_send_beacons);
+#endif
+ clients[i].buf = NET_BUF_SIMPLE(CLIENT_BUF_SIZE);
+ clients[i].conn_handle = BLE_HS_CONN_HANDLE_NONE;
+
+ k_delayed_work_init(&clients[i].sar_timer, proxy_sar_timeout);
+ k_delayed_work_add_arg(&clients[i].sar_timer, &clients[i]);
+ }
+
+ resolve_svc_handles();
+
+ ble_gatts_svc_set_visibility(svc_handles.proxy_h, 0);
+ ble_gatts_svc_set_visibility(svc_handles.prov_h, 0);
+
+ return 0;
+}
+
+#endif /* MYNEWT_VAL(BLE_MESH_PROXY) */
diff --git a/src/libs/mynewt-nimble/nimble/host/mesh/src/proxy.h b/src/libs/mynewt-nimble/nimble/host/mesh/src/proxy.h
new file mode 100644
index 00000000..64338a0a
--- /dev/null
+++ b/src/libs/mynewt-nimble/nimble/host/mesh/src/proxy.h
@@ -0,0 +1,45 @@
+/* Bluetooth Mesh */
+
+/*
+ * Copyright (c) 2017 Intel Corporation
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#ifndef __PROXY_H__
+#define __PROXY_H__
+
+#define BT_MESH_PROXY_NET_PDU 0x00
+#define BT_MESH_PROXY_BEACON 0x01
+#define BT_MESH_PROXY_CONFIG 0x02
+#define BT_MESH_PROXY_PROV 0x03
+
+#include "mesh/mesh.h"
+
+int bt_mesh_proxy_send(uint16_t conn_handle, u8_t type, struct os_mbuf *msg);
+
+int bt_mesh_proxy_prov_enable(void);
+int bt_mesh_proxy_prov_disable(bool disconnect);
+
+int bt_mesh_proxy_gatt_enable(void);
+int bt_mesh_proxy_gatt_disable(void);
+void bt_mesh_proxy_gatt_disconnect(void);
+
+void bt_mesh_proxy_beacon_send(struct bt_mesh_subnet *sub);
+
+struct os_mbuf *bt_mesh_proxy_get_buf(void);
+
+s32_t bt_mesh_proxy_adv_start(void);
+void bt_mesh_proxy_adv_stop(void);
+
+void bt_mesh_proxy_identity_start(struct bt_mesh_subnet *sub);
+void bt_mesh_proxy_identity_stop(struct bt_mesh_subnet *sub);
+
+bool bt_mesh_proxy_relay(struct os_mbuf *buf, u16_t dst);
+void bt_mesh_proxy_addr_add(struct os_mbuf *buf, u16_t addr);
+
+int bt_mesh_proxy_init(void);
+
+int ble_mesh_proxy_gap_event(struct ble_gap_event *event, void *arg);
+
+#endif
diff --git a/src/libs/mynewt-nimble/nimble/host/mesh/src/settings.c b/src/libs/mynewt-nimble/nimble/host/mesh/src/settings.c
new file mode 100644
index 00000000..88d9b302
--- /dev/null
+++ b/src/libs/mynewt-nimble/nimble/host/mesh/src/settings.c
@@ -0,0 +1,2083 @@
+/*
+ * Copyright (c) 2018 Intel Corporation
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include "syscfg/syscfg.h"
+#define MESH_LOG_MODULE BLE_MESH_SETTINGS_LOG
+
+#if MYNEWT_VAL(BLE_MESH_SETTINGS)
+
+#include "mesh/mesh.h"
+#include "mesh/glue.h"
+#include "net.h"
+#include "crypto.h"
+#include "transport.h"
+#include "access.h"
+#include "foundation.h"
+#include "proxy.h"
+#include "settings.h"
+#include "nodes.h"
+
+#include "config/config.h"
+
+/* Tracking of what storage changes are pending for App and Net Keys. We
+ * track this in a separate array here instead of within the respective
+ * 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 {
+ 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 k_delayed_work pending_store;
+
+/* Mesh network storage information */
+struct net_val {
+ u16_t primary_addr;
+ u8_t dev_key[16];
+} __packed;
+
+/* Sequence number storage */
+struct seq_val {
+ u8_t val[3];
+} __packed;
+
+/* Heartbeat Publication storage */
+struct hb_pub_val {
+ u16_t dst;
+ u8_t period;
+ u8_t ttl;
+ u16_t feat;
+ u16_t net_idx:12,
+ indefinite:1;
+};
+
+/* Miscelaneous configuration server model states */
+struct cfg_val {
+ u8_t net_transmit;
+ u8_t relay;
+ u8_t relay_retransmit;
+ u8_t beacon;
+ u8_t gatt_proxy;
+ u8_t frnd;
+ u8_t default_ttl;
+};
+
+/* IV Index & IV Update storage */
+struct iv_val {
+ u32_t iv_index;
+ u8_t iv_update:1,
+ iv_duration:7;
+} __packed;
+
+/* Replay Protection List storage */
+struct rpl_val {
+ u32_t seq:24,
+ old_iv:1;
+};
+
+/* NetKey storage information */
+struct net_key_val {
+ u8_t kr_flag:1,
+ kr_phase:7;
+ u8_t val[2][16];
+} __packed;
+
+/* AppKey storage information */
+struct app_key_val {
+ u16_t net_idx;
+ bool updated;
+ u8_t val[2][16];
+} __packed;
+
+struct mod_pub_val {
+ u16_t addr;
+ u16_t key;
+ u8_t ttl;
+ u8_t retransmit;
+ u8_t period;
+ u8_t period_div:4,
+ cred:1;
+};
+
+/* Virtual Address information */
+struct va_val {
+ u16_t ref;
+ u16_t addr;
+ u8_t uuid[16];
+} __packed;
+
+/* Node storage information */
+struct node_val {
+ u16_t net_idx;
+ u8_t dev_key[16];
+ u8_t num_elem;
+} __packed;
+
+struct node_update {
+ u16_t addr;
+ bool clear;
+};
+
+#if MYNEWT_VAL(BLE_MESH_PROVISIONER)
+static struct node_update node_updates[CONFIG_BT_MESH_NODE_COUNT];
+#else
+static struct node_update node_updates[0];
+#endif
+
+/* 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.
+ */
+static struct {
+ bool valid;
+ struct cfg_val cfg;
+} stored_cfg;
+
+static int net_set(int argc, char **argv, char *val)
+{
+ struct net_val net;
+ int len, err;
+
+ BT_DBG("val %s", val ? val : "(null)");
+
+ if (!val) {
+ bt_mesh_comp_unprovision();
+ memset(bt_mesh.dev_key, 0, sizeof(bt_mesh.dev_key));
+ return 0;
+ }
+
+ len = sizeof(net);
+ err = settings_bytes_from_str(val, &net, &len);
+ if (err) {
+ BT_ERR("Failed to decode value %s (err %d)", val, err);
+ return err;
+ }
+
+ if (len != sizeof(net)) {
+ BT_ERR("Unexpected value length (%d != %zu)", len, sizeof(net));
+ return -EINVAL;
+ }
+
+ memcpy(bt_mesh.dev_key, net.dev_key, sizeof(bt_mesh.dev_key));
+ bt_mesh_comp_provision(net.primary_addr);
+
+ BT_DBG("Provisioned with primary address 0x%04x", net.primary_addr);
+ BT_DBG("Recovered DevKey %s", bt_hex(bt_mesh.dev_key, 16));
+
+ return 0;
+}
+
+static int iv_set(int argc, char **argv, char *val)
+{
+ struct iv_val iv;
+ int len, err;
+
+ BT_DBG("val %s", val ? val : "(null)");
+
+ if (!val) {
+ bt_mesh.iv_index = 0U;
+ atomic_clear_bit(bt_mesh.flags, BT_MESH_IVU_IN_PROGRESS);
+ return 0;
+ }
+
+ len = sizeof(iv);
+ err = settings_bytes_from_str(val, &iv, &len);
+ if (err) {
+ BT_ERR("Failed to decode value %s (err %d)", val, err);
+ return err;
+ }
+
+ if (len != sizeof(iv)) {
+ BT_ERR("Unexpected value length (%d != %zu)", len, sizeof(iv));
+ return -EINVAL;
+ }
+
+ bt_mesh.iv_index = iv.iv_index;
+ atomic_set_bit_to(bt_mesh.flags, BT_MESH_IVU_IN_PROGRESS, iv.iv_update);
+ bt_mesh.ivu_duration = iv.iv_duration;
+
+ BT_DBG("IV Index 0x%04x (IV Update Flag %u) duration %u hours",
+ (unsigned) iv.iv_index, iv.iv_update, iv.iv_duration);
+
+ return 0;
+}
+
+static int seq_set(int argc, char **argv, char *val)
+{
+ struct seq_val seq;
+ int len, err;
+
+ BT_DBG("val %s", val ? val : "(null)");
+
+ if (!val) {
+ bt_mesh.seq = 0;
+ return 0;
+ }
+
+ len = sizeof(seq);
+ err = settings_bytes_from_str(val, &seq, &len);
+ if (err) {
+ BT_ERR("Failed to decode value %s (err %d)", val, err);
+ return err;
+ }
+
+ if (len != sizeof(seq)) {
+ BT_ERR("Unexpected value length (%d != %zu)", len, sizeof(seq));
+ return -EINVAL;
+ }
+
+ bt_mesh.seq = ((u32_t)seq.val[0] | ((u32_t)seq.val[1] << 8) |
+ ((u32_t)seq.val[2] << 16));
+
+ if (CONFIG_BT_MESH_SEQ_STORE_RATE > 0) {
+ /* Make sure we have a large enough sequence number. We
+ * subtract 1 so that the first transmission causes a write
+ * to the settings storage.
+ */
+ bt_mesh.seq += (CONFIG_BT_MESH_SEQ_STORE_RATE -
+ (bt_mesh.seq % CONFIG_BT_MESH_SEQ_STORE_RATE));
+ bt_mesh.seq--;
+ }
+
+ BT_DBG("Sequence Number 0x%06x", bt_mesh.seq);
+
+ return 0;
+}
+
+static struct bt_mesh_rpl *rpl_find(u16_t src)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(bt_mesh.rpl); i++) {
+ if (bt_mesh.rpl[i].src == src) {
+ return &bt_mesh.rpl[i];
+ }
+ }
+
+ return NULL;
+}
+
+static struct bt_mesh_rpl *rpl_alloc(u16_t src)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(bt_mesh.rpl); i++) {
+ if (!bt_mesh.rpl[i].src) {
+ bt_mesh.rpl[i].src = src;
+ return &bt_mesh.rpl[i];
+ }
+ }
+
+ return NULL;
+}
+
+static int rpl_set(int argc, char **argv, char *val)
+{
+ struct bt_mesh_rpl *entry;
+ struct rpl_val rpl;
+ int len, err;
+ u16_t src;
+
+ if (argc < 1) {
+ BT_ERR("Invalid argc (%d)", argc);
+ return -ENOENT;
+ }
+
+ BT_DBG("argv[0] %s val %s", argv[0], val ? val : "(null)");
+
+ src = strtol(argv[0], NULL, 16);
+ entry = rpl_find(src);
+
+ if (!val) {
+ if (entry) {
+ memset(entry, 0, sizeof(*entry));
+ } else {
+ BT_WARN("Unable to find RPL entry for 0x%04x", src);
+ }
+
+ return 0;
+ }
+
+ if (!entry) {
+ entry = rpl_alloc(src);
+ if (!entry) {
+ BT_ERR("Unable to allocate RPL entry for 0x%04x", src);
+ return -ENOMEM;
+ }
+ }
+
+ len = sizeof(rpl);
+ err = settings_bytes_from_str(val, &rpl, &len);
+ if (err) {
+ BT_ERR("Failed to decode value %s (err %d)", val, err);
+ return err;
+ }
+
+ if (len != sizeof(rpl)) {
+ BT_ERR("Unexpected value length (%d != %zu)", len, sizeof(rpl));
+ return -EINVAL;
+ }
+
+ entry->seq = rpl.seq;
+ entry->old_iv = rpl.old_iv;
+
+ BT_DBG("RPL entry for 0x%04x: Seq 0x%06x old_iv %u", entry->src,
+ (unsigned) entry->seq, entry->old_iv);
+
+ return 0;
+}
+
+static int net_key_set(int argc, char **argv, char *val)
+{
+ struct bt_mesh_subnet *sub;
+ struct net_key_val key;
+ int len, i, err;
+ u16_t net_idx;
+
+ BT_DBG("argv[0] %s val %s", argv[0], val ? val : "(null)");
+
+ net_idx = strtol(argv[0], NULL, 16);
+ sub = bt_mesh_subnet_get(net_idx);
+
+ if (!val) {
+ if (!sub) {
+ BT_ERR("No subnet with NetKeyIndex 0x%03x", net_idx);
+ return -ENOENT;
+ }
+
+ BT_DBG("Deleting NetKeyIndex 0x%03x", net_idx);
+ bt_mesh_subnet_del(sub, false);
+ return 0;
+ }
+
+ len = sizeof(key);
+ err = settings_bytes_from_str(val, &key, &len);
+ if (err) {
+ BT_ERR("Failed to decode value %s (err %d)", val, err);
+ return err;
+ }
+
+ if (len != sizeof(key)) {
+ BT_ERR("Unexpected value length (%d != %zu)", len, sizeof(key));
+ return -EINVAL;
+ }
+
+ 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.val[0], 16);
+ memcpy(sub->keys[1].net, &key.val[1], 16);
+
+ return 0;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(bt_mesh.sub); i++) {
+ if (bt_mesh.sub[i].net_idx == BT_MESH_KEY_UNUSED) {
+ sub = &bt_mesh.sub[i];
+ break;
+ }
+ }
+
+ if (!sub) {
+ BT_ERR("No space to allocate a new subnet");
+ return -ENOMEM;
+ }
+
+ sub->net_idx = net_idx;
+ sub->kr_flag = key.kr_flag;
+ sub->kr_phase = key.kr_phase;
+ memcpy(sub->keys[0].net, &key.val[0], 16);
+ memcpy(sub->keys[1].net, &key.val[1], 16);
+
+ BT_DBG("NetKeyIndex 0x%03x recovered from storage", net_idx);
+
+ return 0;
+}
+
+static int app_key_set(int argc, char **argv, char *val)
+{
+ struct bt_mesh_app_key *app;
+ struct app_key_val key;
+ u16_t app_idx;
+ int len, err;
+
+ BT_DBG("argv[0] %s val %s", argv[0], val ? val : "(null)");
+
+ app_idx = strtol(argv[0], NULL, 16);
+
+ if (!val) {
+ BT_DBG("Deleting AppKeyIndex 0x%03x", app_idx);
+
+ app = bt_mesh_app_key_find(app_idx);
+ if (app) {
+ bt_mesh_app_key_del(app, false);
+ }
+
+ return 0;
+ }
+
+ len = sizeof(key);
+ err = settings_bytes_from_str(val, &key, &len);
+ if (err) {
+ BT_ERR("Failed to decode value %s (err %d)", val, err);
+ return err;
+ }
+
+ if (len != sizeof(key)) {
+ BT_ERR("Unexpected value length (%d != %zu)", len, sizeof(key));
+ return -EINVAL;
+ }
+
+ app = bt_mesh_app_key_find(app_idx);
+ if (!app) {
+ app = bt_mesh_app_key_alloc(app_idx);
+ }
+
+ if (!app) {
+ BT_ERR("No space for a new app key");
+ return -ENOMEM;
+ }
+
+ app->net_idx = key.net_idx;
+ app->app_idx = app_idx;
+ app->updated = key.updated;
+ memcpy(app->keys[0].val, key.val[0], 16);
+ memcpy(app->keys[1].val, key.val[1], 16);
+
+ bt_mesh_app_id(app->keys[0].val, &app->keys[0].id);
+ bt_mesh_app_id(app->keys[1].val, &app->keys[1].id);
+
+ BT_DBG("AppKeyIndex 0x%03x recovered from storage", app_idx);
+
+ return 0;
+}
+
+static int hb_pub_set(int argc, char **argv, char *val)
+{
+ struct bt_mesh_hb_pub *pub = bt_mesh_hb_pub_get();
+ struct hb_pub_val hb_val;
+ int len, err;
+
+ BT_DBG("val %s", val ? val : "(null)");
+
+ if (!pub) {
+ return -ENOENT;
+ }
+
+ if (!val) {
+ pub->dst = BT_MESH_ADDR_UNASSIGNED;
+ pub->count = 0;
+ pub->ttl = 0;
+ pub->period = 0;
+ pub->feat = 0;
+
+ BT_DBG("Cleared heartbeat publication");
+ return 0;
+ }
+
+ len = sizeof(hb_val);
+ err = settings_bytes_from_str(val, &hb_val, &len);
+ if (err) {
+ BT_ERR("Failed to decode value %s (err %d)", val, err);
+ return err;
+ }
+
+ if (len != sizeof(hb_val)) {
+ BT_ERR("Unexpected value length (%d != %zu)", len,
+ sizeof(hb_val));
+ return -EINVAL;
+ }
+
+ pub->dst = hb_val.dst;
+ pub->period = hb_val.period;
+ pub->ttl = hb_val.ttl;
+ pub->feat = hb_val.feat;
+ pub->net_idx = hb_val.net_idx;
+
+ if (hb_val.indefinite) {
+ pub->count = 0xffff;
+ } else {
+ pub->count = 0;
+ }
+
+ BT_DBG("Restored heartbeat publication");
+
+ return 0;
+}
+
+static int cfg_set(int argc, char **argv, char *val)
+{
+ struct bt_mesh_cfg_srv *cfg = bt_mesh_cfg_get();
+ int len, err;
+
+ BT_DBG("val %s", val ? val : "(null)");
+
+ if (!cfg) {
+ return -ENOENT;
+ }
+
+ if (!val) {
+ stored_cfg.valid = false;
+ BT_DBG("Cleared configuration state");
+ return 0;
+ }
+
+ len = sizeof(stored_cfg.cfg);
+ err = settings_bytes_from_str(val, &stored_cfg.cfg, &len);
+ if (err) {
+ BT_ERR("Failed to decode value %s (err %d)", val, err);
+ return err;
+ }
+
+ if (len != sizeof(stored_cfg.cfg)) {
+ BT_ERR("Unexpected value length (%d != %zu)", len,
+ sizeof(stored_cfg.cfg));
+ return -EINVAL;
+ }
+
+ stored_cfg.valid = true;
+ BT_DBG("Restored configuration state");
+
+ return 0;
+}
+
+static int mod_set_bind(struct bt_mesh_model *mod, char *val)
+{
+ int len, err, i;
+
+ /* Start with empty array regardless of cleared or set value */
+ for (i = 0; i < ARRAY_SIZE(mod->keys); i++) {
+ mod->keys[i] = BT_MESH_KEY_UNUSED;
+ }
+
+ if (!val) {
+ BT_DBG("Cleared bindings for model");
+ return 0;
+ }
+
+ len = sizeof(mod->keys);
+ err = settings_bytes_from_str(val, mod->keys, &len);
+ if (err) {
+ BT_ERR("Failed to decode value %s (err %d)", val, err);
+ return -EINVAL;
+ }
+
+ BT_DBG("Decoded %u bound keys for model", len / sizeof(mod->keys[0]));
+ return 0;
+}
+
+static int mod_set_sub(struct bt_mesh_model *mod, char *val)
+{
+ int len, err;
+
+ /* Start with empty array regardless of cleared or set value */
+ memset(mod->groups, 0, sizeof(mod->groups));
+
+ if (!val) {
+ BT_DBG("Cleared subscriptions for model");
+ return 0;
+ }
+
+ len = sizeof(mod->groups);
+ err = settings_bytes_from_str(val, mod->groups, &len);
+ if (err) {
+ BT_ERR("Failed to decode value %s (err %d)", val, err);
+ return -EINVAL;
+ }
+
+ BT_DBG("Decoded %u subscribed group addresses for model",
+ len / sizeof(mod->groups[0]));
+ return 0;
+}
+
+static int mod_set_pub(struct bt_mesh_model *mod, char *val)
+{
+ struct mod_pub_val pub;
+ int len, err;
+
+ if (!mod->pub) {
+ BT_WARN("Model has no publication context!");
+ return -EINVAL;
+ }
+
+ if (!val) {
+ mod->pub->addr = BT_MESH_ADDR_UNASSIGNED;
+ mod->pub->key = 0;
+ mod->pub->cred = 0;
+ mod->pub->ttl = 0;
+ mod->pub->period = 0;
+ mod->pub->retransmit = 0;
+ mod->pub->count = 0;
+
+ BT_DBG("Cleared publication for model");
+ return 0;
+ }
+
+ len = sizeof(pub);
+ err = settings_bytes_from_str(val, &pub, &len);
+ if (err) {
+ BT_ERR("Failed to decode value %s (err %d)", val, err);
+ return -EINVAL;
+ }
+
+ if (len != sizeof(pub)) {
+ BT_ERR("Invalid length for model publication");
+ return -EINVAL;
+ }
+
+ mod->pub->addr = pub.addr;
+ mod->pub->key = pub.key;
+ mod->pub->cred = pub.cred;
+ mod->pub->ttl = pub.ttl;
+ mod->pub->period = pub.period;
+ mod->pub->retransmit = pub.retransmit;
+ mod->pub->count = 0;
+
+ BT_DBG("Restored model publication, dst 0x%04x app_idx 0x%03x",
+ pub.addr, pub.key);
+
+ return 0;
+}
+
+static int mod_set(bool vnd, int argc, char **argv, char *val)
+{
+ struct bt_mesh_model *mod;
+ u8_t elem_idx, mod_idx;
+ u16_t mod_key;
+
+ if (argc < 2) {
+ BT_ERR("Too small argc (%d)", argc);
+ return -ENOENT;
+ }
+
+ mod_key = strtol(argv[0], NULL, 16);
+ elem_idx = mod_key >> 8;
+ mod_idx = mod_key;
+
+ BT_DBG("Decoded mod_key 0x%04x as elem_idx %u mod_idx %u",
+ mod_key, elem_idx, mod_idx);
+
+ mod = bt_mesh_model_get(vnd, elem_idx, mod_idx);
+ if (!mod) {
+ BT_ERR("Failed to get model for elem_idx %u mod_idx %u",
+ elem_idx, mod_idx);
+ return -ENOENT;
+ }
+
+ if (!strcmp(argv[1], "bind")) {
+ return mod_set_bind(mod, val);
+ }
+
+ if (!strcmp(argv[1], "sub")) {
+ return mod_set_sub(mod, val);
+ }
+
+ if (!strcmp(argv[1], "pub")) {
+ return mod_set_pub(mod, val);
+ }
+
+ if (!strcmp(argv[1], "data")) {
+ mod->flags |= BT_MESH_MOD_DATA_PRESENT;
+
+ if (mod->cb && mod->cb->settings_set) {
+ return mod->cb->settings_set(mod, val);
+ }
+ }
+
+ BT_WARN("Unknown module key %s", argv[1]);
+ return -ENOENT;
+}
+
+static int sig_mod_set(int argc, char **argv, char *val)
+{
+ return mod_set(false, argc, argv, val);
+}
+
+static int vnd_mod_set(int argc, char **argv, char *val)
+{
+ return mod_set(true, argc, argv, val);
+}
+
+#if CONFIG_BT_MESH_LABEL_COUNT > 0
+static int va_set(int argc, char **argv, char *val)
+{
+ struct va_val va;
+ struct label *lab;
+ u16_t index;
+ int len, err;
+
+ if (argc < 1) {
+ BT_ERR("Insufficient number of arguments");
+ return -ENOENT;
+ }
+
+ index = strtol(argv[0], NULL, 16);
+
+ if (val == NULL) {
+ BT_WARN("Mesh Virtual Address length = 0");
+ return 0;
+ }
+
+ err = settings_bytes_from_str(val, &va, &len);
+ if (err) {
+ BT_ERR("Failed to decode value %s (err %d)", val, err);
+ return -EINVAL;
+ }
+
+ if (len != sizeof(struct va_val)) {
+ BT_ERR("Invalid length for virtual address");
+ return -EINVAL;
+ }
+
+ if (va.ref == 0) {
+ BT_WARN("Ignore Mesh Virtual Address ref = 0");
+ return 0;
+ }
+
+ lab = get_label(index);
+ if (lab == NULL) {
+ BT_WARN("Out of labels buffers");
+ return -ENOBUFS;
+ }
+
+ memcpy(lab->uuid, va.uuid, 16);
+ lab->addr = va.addr;
+ lab->ref = va.ref;
+
+ BT_DBG("Restored Virtual Address, addr 0x%04x ref 0x%04x",
+ lab->addr, lab->ref);
+
+ return 0;
+}
+#endif
+
+#if MYNEWT_VAL(BLE_MESH_PROVISIONER)
+static int node_set(int argc, char **argv, char *str)
+{
+ struct bt_mesh_node *node;
+ struct node_val val;
+ u16_t addr;
+ int len, err;
+
+ if (argc < 1) {
+ BT_ERR("Insufficient number of arguments");
+ return -ENOENT;
+ }
+
+ addr = strtol(argv[0], NULL, 16);
+
+ if (str == NULL) {
+ BT_DBG("val (null)");
+ BT_DBG("Deleting node 0x%04x", addr);
+
+ node = bt_mesh_node_find(addr);
+ if (node) {
+ bt_mesh_node_del(node, false);
+ }
+
+ return 0;
+ }
+
+ err = settings_bytes_from_str(str, &val, &len);
+ if (err) {
+ BT_ERR("Failed to decode value %s (err %d)", val, err);
+ return -EINVAL;
+ }
+
+ if (len != sizeof(struct node_val)) {
+ BT_ERR("Invalid length for node_val");
+ return -EINVAL;
+ }
+
+ node = bt_mesh_node_find(addr);
+ if (!node) {
+ node = bt_mesh_node_alloc(addr, val.num_elem, val.net_idx);
+ }
+
+ if (!node) {
+ BT_ERR("No space for a new node");
+ return -ENOMEM;
+ }
+
+ memcpy(node->dev_key, &val.dev_key, 16);
+
+ BT_DBG("Node 0x%04x recovered from storage", addr);
+
+ return 0;
+}
+#endif
+
+const struct mesh_setting {
+ const char *name;
+ int (*func)(int argc, char **argv, char *val);
+} settings[] = {
+ { "Net", net_set },
+ { "IV", iv_set },
+ { "Seq", seq_set },
+ { "RPL", rpl_set },
+ { "NetKey", net_key_set },
+ { "AppKey", app_key_set },
+ { "HBPub", hb_pub_set },
+ { "Cfg", cfg_set },
+ { "s", sig_mod_set },
+ { "v", vnd_mod_set },
+#if CONFIG_BT_MESH_LABEL_COUNT > 0
+ { "Va", va_set },
+#endif
+#if MYNEWT_VAL(BLE_MESH_PROVISIONER)
+ { "Node", node_set },
+#endif
+};
+
+static int mesh_set(int argc, char **argv, char *val)
+{
+ int i;
+
+ if (argc < 1) {
+ BT_ERR("Insufficient number of arguments");
+ return -EINVAL;
+ }
+
+ BT_DBG("argv[0] %s val %s", argv[0], val ? val : "(null)");
+
+ for (i = 0; i < ARRAY_SIZE(settings); i++) {
+ if (!strcmp(settings[i].name, argv[0])) {
+ argc--;
+ argv++;
+
+ return settings[i].func(argc, argv, val);
+ }
+ }
+
+ BT_WARN("No matching handler for key %s", argv[0]);
+
+ return -ENOENT;
+}
+
+static int subnet_init(struct bt_mesh_subnet *sub)
+{
+ int err;
+
+ err = bt_mesh_net_keys_create(&sub->keys[0], sub->keys[0].net);
+ if (err) {
+ BT_ERR("Unable to generate keys for subnet");
+ return -EIO;
+ }
+
+ if (sub->kr_phase != BT_MESH_KR_NORMAL) {
+ err = bt_mesh_net_keys_create(&sub->keys[1], sub->keys[1].net);
+ if (err) {
+ BT_ERR("Unable to generate keys for subnet");
+ memset(&sub->keys[0], 0, sizeof(sub->keys[0]));
+ return -EIO;
+ }
+ }
+
+ if (IS_ENABLED(CONFIG_BT_MESH_GATT_PROXY)) {
+ sub->node_id = BT_MESH_NODE_IDENTITY_STOPPED;
+ } else {
+ sub->node_id = BT_MESH_NODE_IDENTITY_NOT_SUPPORTED;
+ }
+
+ /* Make sure we have valid beacon data to be sent */
+ bt_mesh_net_beacon_update(sub);
+
+ return 0;
+}
+
+static void commit_mod(struct bt_mesh_model *mod, struct bt_mesh_elem *elem,
+ bool vnd, bool primary, void *user_data)
+{
+ if (mod->pub && mod->pub->update &&
+ mod->pub->addr != BT_MESH_ADDR_UNASSIGNED) {
+ s32_t ms = bt_mesh_model_pub_period_get(mod);
+ if (ms) {
+ BT_DBG("Starting publish timer (period %u ms)",
+ (unsigned) ms);
+ k_delayed_work_submit(&mod->pub->timer, ms);
+ }
+ }
+
+ if (mod->cb && mod->cb->settings_commit) {
+ mod->cb->settings_commit(mod);
+ }
+}
+
+static int mesh_commit(void)
+{
+ struct bt_mesh_hb_pub *hb_pub;
+ struct bt_mesh_cfg_srv *cfg;
+ int i;
+
+ BT_DBG("sub[0].net_idx 0x%03x", bt_mesh.sub[0].net_idx);
+
+ if (bt_mesh.sub[0].net_idx == BT_MESH_KEY_UNUSED) {
+ /* Nothing to do since we're not yet provisioned */
+ return 0;
+ }
+
+ if (IS_ENABLED(CONFIG_BT_MESH_PB_GATT)) {
+ bt_mesh_proxy_prov_disable(true);
+ }
+
+ for (i = 0; i < ARRAY_SIZE(bt_mesh.sub); i++) {
+ struct bt_mesh_subnet *sub = &bt_mesh.sub[i];
+ int err;
+
+ if (sub->net_idx == BT_MESH_KEY_UNUSED) {
+ continue;
+ }
+
+ err = subnet_init(sub);
+ if (err) {
+ BT_ERR("Failed to init subnet 0x%03x", sub->net_idx);
+ }
+ }
+
+ if (bt_mesh.ivu_duration < BT_MESH_IVU_MIN_HOURS) {
+ k_delayed_work_submit(&bt_mesh.ivu_timer, BT_MESH_IVU_TIMEOUT);
+ }
+
+ bt_mesh_model_foreach(commit_mod, NULL);
+
+ hb_pub = bt_mesh_hb_pub_get();
+ if (hb_pub && hb_pub->dst != BT_MESH_ADDR_UNASSIGNED &&
+ hb_pub->count && hb_pub->period) {
+ BT_DBG("Starting heartbeat publication");
+ k_work_submit(&hb_pub->timer.work);
+ }
+
+ cfg = bt_mesh_cfg_get();
+ if (cfg && stored_cfg.valid) {
+ cfg->net_transmit = stored_cfg.cfg.net_transmit;
+ cfg->relay = stored_cfg.cfg.relay;
+ cfg->relay_retransmit = stored_cfg.cfg.relay_retransmit;
+ cfg->beacon = stored_cfg.cfg.beacon;
+ cfg->gatt_proxy = stored_cfg.cfg.gatt_proxy;
+ cfg->frnd = stored_cfg.cfg.frnd;
+ cfg->default_ttl = stored_cfg.cfg.default_ttl;
+ }
+
+ atomic_set_bit(bt_mesh.flags, BT_MESH_VALID);
+
+ bt_mesh_net_start();
+
+ return 0;
+}
+
+/* Pending flags that use K_NO_WAIT as the storage timeout */
+#define NO_WAIT_PENDING_BITS (BIT(BT_MESH_NET_PENDING) | \
+ BIT(BT_MESH_IV_PENDING) | \
+ BIT(BT_MESH_SEQ_PENDING))
+
+/* Pending flags that use CONFIG_BT_MESH_STORE_TIMEOUT */
+#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))
+
+static void schedule_store(int flag)
+{
+ s32_t timeout, remaining;
+
+ atomic_set_bit(bt_mesh.flags, flag);
+
+ if (atomic_get(bt_mesh.flags) & NO_WAIT_PENDING_BITS) {
+ timeout = K_NO_WAIT;
+ } else if (atomic_test_bit(bt_mesh.flags, BT_MESH_RPL_PENDING) &&
+ (!(atomic_get(bt_mesh.flags) & GENERIC_PENDING_BITS) ||
+ (CONFIG_BT_MESH_RPL_STORE_TIMEOUT <
+ CONFIG_BT_MESH_STORE_TIMEOUT))) {
+ timeout = K_SECONDS(CONFIG_BT_MESH_RPL_STORE_TIMEOUT);
+ } else {
+ timeout = K_SECONDS(CONFIG_BT_MESH_STORE_TIMEOUT);
+ }
+
+ remaining = k_delayed_work_remaining_get(&pending_store);
+ if (remaining && remaining < timeout) {
+ BT_DBG("Not rescheduling due to existing earlier deadline");
+ return;
+ }
+
+ BT_DBG("Waiting %d seconds", (int) (timeout / MSEC_PER_SEC));
+
+ k_delayed_work_submit(&pending_store, timeout);
+}
+
+static void clear_iv(void)
+{
+ int err;
+
+ err = settings_save_one("bt_mesh/IV", NULL);
+ if (err) {
+ BT_ERR("Failed to clear IV");
+ } else {
+ BT_DBG("Cleared IV");
+ }
+}
+
+static void clear_net(void)
+{
+ int err;
+
+ err = settings_save_one("bt_mesh/Net", NULL);
+ if (err) {
+ BT_ERR("Failed to clear Network");
+ } else {
+ BT_DBG("Cleared Network");
+ }
+}
+
+static void store_pending_net(void)
+{
+ char buf[BT_SETTINGS_SIZE(sizeof(struct net_val))];
+ struct net_val net;
+ char *str;
+ int err;
+
+ BT_DBG("addr 0x%04x DevKey %s", bt_mesh_primary_addr(),
+ bt_hex(bt_mesh.dev_key, 16));
+
+ net.primary_addr = bt_mesh_primary_addr();
+ memcpy(net.dev_key, bt_mesh.dev_key, 16);
+
+ str = settings_str_from_bytes(&net, sizeof(net), buf, sizeof(buf));
+ if (!str) {
+ BT_ERR("Unable to encode Network as value");
+ return;
+ }
+
+ BT_DBG("Saving Network as value %s", str);
+ err = settings_save_one("bt_mesh/Net", str);
+ if (err) {
+ BT_ERR("Failed to store Network");
+ } else {
+ BT_DBG("Stored Network");
+ }
+}
+
+void bt_mesh_store_net(void)
+{
+ schedule_store(BT_MESH_NET_PENDING);
+}
+
+static void store_pending_iv(void)
+{
+ char buf[BT_SETTINGS_SIZE(sizeof(struct iv_val))];
+ struct iv_val iv;
+ char *str;
+ int err;
+
+ iv.iv_index = bt_mesh.iv_index;
+ iv.iv_update = atomic_test_bit(bt_mesh.flags, BT_MESH_IVU_IN_PROGRESS);
+ iv.iv_duration = bt_mesh.ivu_duration;
+
+ str = settings_str_from_bytes(&iv, sizeof(iv), buf, sizeof(buf));
+ if (!str) {
+ BT_ERR("Unable to encode IV as value");
+ return;
+ }
+
+ BT_DBG("Saving IV as value %s", str);
+ err = settings_save_one("bt_mesh/IV", str);
+ if (err) {
+ BT_ERR("Failed to store IV");
+ } else {
+ BT_DBG("Stored IV");
+ }
+}
+
+void bt_mesh_store_iv(bool only_duration)
+{
+ schedule_store(BT_MESH_IV_PENDING);
+
+ if (!only_duration) {
+ /* Always update Seq whenever IV changes */
+ schedule_store(BT_MESH_SEQ_PENDING);
+ }
+}
+
+static void store_pending_seq(void)
+{
+ char buf[BT_SETTINGS_SIZE(sizeof(struct seq_val))];
+ struct seq_val seq;
+ char *str;
+ int err;
+
+ seq.val[0] = bt_mesh.seq;
+ seq.val[1] = bt_mesh.seq >> 8;
+ seq.val[2] = bt_mesh.seq >> 16;
+
+ str = settings_str_from_bytes(&seq, sizeof(seq), buf, sizeof(buf));
+ if (!str) {
+ BT_ERR("Unable to encode Seq as value");
+ return;
+ }
+
+ BT_DBG("Saving Seq as value %s", str);
+ err = settings_save_one("bt_mesh/Seq", str);
+ if (err) {
+ BT_ERR("Failed to store Seq");
+ } else {
+ BT_DBG("Stored Seq");
+ }
+}
+
+void bt_mesh_store_seq(void)
+{
+ if (CONFIG_BT_MESH_SEQ_STORE_RATE &&
+ (bt_mesh.seq % CONFIG_BT_MESH_SEQ_STORE_RATE)) {
+ return;
+ }
+
+ schedule_store(BT_MESH_SEQ_PENDING);
+}
+
+static void store_rpl(struct bt_mesh_rpl *entry)
+{
+ char buf[BT_SETTINGS_SIZE(sizeof(struct rpl_val))];
+ struct rpl_val rpl;
+ char path[18];
+ char *str;
+ int err;
+
+ BT_DBG("src 0x%04x seq 0x%06x old_iv %u", entry->src,
+ (unsigned) entry->seq, entry->old_iv);
+
+ rpl.seq = entry->seq;
+ rpl.old_iv = entry->old_iv;
+
+ str = settings_str_from_bytes(&rpl, sizeof(rpl), buf, sizeof(buf));
+ if (!str) {
+ BT_ERR("Unable to encode RPL as value");
+ return;
+ }
+
+ snprintk(path, sizeof(path), "bt_mesh/RPL/%x", entry->src);
+
+ BT_DBG("Saving RPL %s as value %s", path, str);
+ err = settings_save_one(path, str);
+ if (err) {
+ BT_ERR("Failed to store RPL");
+ } else {
+ BT_DBG("Stored RPL");
+ }
+}
+
+static void clear_rpl(void)
+{
+ int i, err;
+
+ BT_DBG("");
+
+ for (i = 0; i < ARRAY_SIZE(bt_mesh.rpl); i++) {
+ struct bt_mesh_rpl *rpl = &bt_mesh.rpl[i];
+ char path[18];
+
+ if (!rpl->src) {
+ continue;
+ }
+
+ snprintk(path, sizeof(path), "bt_mesh/RPL/%x", rpl->src);
+ err = settings_save_one(path, NULL);
+ if (err) {
+ BT_ERR("Failed to clear RPL");
+ } else {
+ BT_DBG("Cleared RPL");
+ }
+
+ memset(rpl, 0, sizeof(*rpl));
+ }
+}
+
+static void store_pending_rpl(void)
+{
+ int i;
+
+ BT_DBG("");
+
+ for (i = 0; i < ARRAY_SIZE(bt_mesh.rpl); i++) {
+ struct bt_mesh_rpl *rpl = &bt_mesh.rpl[i];
+
+ if (rpl->store) {
+ rpl->store = false;
+ store_rpl(rpl);
+ }
+ }
+}
+
+static void store_pending_hb_pub(void)
+{
+ char buf[BT_SETTINGS_SIZE(sizeof(struct hb_pub_val))];
+ struct bt_mesh_hb_pub *pub = bt_mesh_hb_pub_get();
+ struct hb_pub_val val;
+ char *str;
+ int err;
+
+ if (!pub) {
+ return;
+ }
+
+ if (pub->dst == BT_MESH_ADDR_UNASSIGNED) {
+ str = NULL;
+ } else {
+ val.indefinite = (pub->count == 0xffff);
+ val.dst = pub->dst;
+ val.period = pub->period;
+ val.ttl = pub->ttl;
+ val.feat = pub->feat;
+ val.net_idx = pub->net_idx;
+
+ str = settings_str_from_bytes(&val, sizeof(val),
+ buf, sizeof(buf));
+ if (!str) {
+ BT_ERR("Unable to encode hb pub as value");
+ return;
+ }
+ }
+
+ BT_DBG("Saving Heartbeat Publication as value %s",
+ str ? str : "(null)");
+ err = settings_save_one("bt_mesh/HBPub", str);
+ if (err) {
+ BT_ERR("Failed to store Heartbeat Publication");
+ } else {
+ BT_DBG("Stored Heartbeat Publication");
+ }
+}
+
+static void store_pending_cfg(void)
+{
+ char buf[BT_SETTINGS_SIZE(sizeof(struct cfg_val))];
+ struct bt_mesh_cfg_srv *cfg = bt_mesh_cfg_get();
+ struct cfg_val val;
+ char *str;
+ int err;
+
+ if (!cfg) {
+ return;
+ }
+
+ val.net_transmit = cfg->net_transmit;
+ val.relay = cfg->relay;
+ val.relay_retransmit = cfg->relay_retransmit;
+ val.beacon = cfg->beacon;
+ val.gatt_proxy = cfg->gatt_proxy;
+ val.frnd = cfg->frnd;
+ val.default_ttl = cfg->default_ttl;
+
+ str = settings_str_from_bytes(&val, sizeof(val), buf, sizeof(buf));
+ if (!str) {
+ BT_ERR("Unable to encode configuration as value");
+ return;
+ }
+
+ BT_DBG("Saving configuration as value %s", str);
+ err = settings_save_one("bt_mesh/Cfg", str);
+ if (err) {
+ BT_ERR("Failed to store configuration");
+ } else {
+ BT_DBG("Stored configuration");
+ }
+}
+
+static void clear_cfg(void)
+{
+ int err;
+
+ err = settings_save_one("bt_mesh/Cfg", NULL);
+ if (err) {
+ BT_ERR("Failed to clear configuration");
+ } else {
+ BT_DBG("Cleared configuration");
+ }
+}
+
+static void clear_app_key(u16_t app_idx)
+{
+ char path[20];
+ int err;
+
+ BT_DBG("AppKeyIndex 0x%03x", app_idx);
+
+ snprintk(path, sizeof(path), "bt_mesh/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 clear_net_key(u16_t net_idx)
+{
+ char path[20];
+ int err;
+
+ BT_DBG("NetKeyIndex 0x%03x", net_idx);
+
+ snprintk(path, sizeof(path), "bt_mesh/NetKey/%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_net_key(struct bt_mesh_subnet *sub)
+{
+ char buf[BT_SETTINGS_SIZE(sizeof(struct net_key_val))];
+ struct net_key_val key;
+ char path[20];
+ char *str;
+ int err;
+
+ BT_DBG("NetKeyIndex 0x%03x NetKey %s", sub->net_idx,
+ bt_hex(sub->keys[0].net, 16));
+
+ memcpy(&key.val[0], sub->keys[0].net, 16);
+ memcpy(&key.val[1], sub->keys[1].net, 16);
+ key.kr_flag = sub->kr_flag;
+ key.kr_phase = sub->kr_phase;
+
+ str = settings_str_from_bytes(&key, sizeof(key), buf, sizeof(buf));
+ if (!str) {
+ BT_ERR("Unable to encode NetKey as value");
+ return;
+ }
+
+ snprintk(path, sizeof(path), "bt_mesh/NetKey/%x", sub->net_idx);
+
+ BT_DBG("Saving NetKey %s as value %s", path, str);
+ err = settings_save_one(path, str);
+ if (err) {
+ BT_ERR("Failed to store NetKey");
+ } else {
+ BT_DBG("Stored NetKey");
+ }
+}
+
+static void store_app_key(struct bt_mesh_app_key *app)
+{
+ char buf[BT_SETTINGS_SIZE(sizeof(struct app_key_val))];
+ struct app_key_val key;
+ char path[20];
+ char *str;
+ int err;
+
+ key.net_idx = app->net_idx;
+ key.updated = app->updated;
+ memcpy(key.val[0], app->keys[0].val, 16);
+ memcpy(key.val[1], app->keys[1].val, 16);
+
+ str = settings_str_from_bytes(&key, sizeof(key), buf, sizeof(buf));
+ if (!str) {
+ BT_ERR("Unable to encode AppKey as value");
+ return;
+ }
+
+ snprintk(path, sizeof(path), "bt_mesh/AppKey/%x", app->app_idx);
+
+ BT_DBG("Saving AppKey %s as value %s", path, str);
+ err = settings_save_one(path, str);
+ if (err) {
+ BT_ERR("Failed to store AppKey");
+ } else {
+ BT_DBG("Stored AppKey");
+ }
+}
+
+static void store_pending_keys(void)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(key_updates); i++) {
+ struct key_update *update = &key_updates[i];
+
+ if (!update->valid) {
+ continue;
+ }
+
+ if (update->clear) {
+ if (update->app_key) {
+ clear_app_key(update->key_idx);
+ } else {
+ clear_net_key(update->key_idx);
+ }
+ } else {
+ if (update->app_key) {
+ struct bt_mesh_app_key *key;
+
+ key = bt_mesh_app_key_find(update->key_idx);
+ if (key) {
+ store_app_key(key);
+ } else {
+ BT_WARN("AppKeyIndex 0x%03x not found",
+ update->key_idx);
+ }
+
+ } else {
+ struct bt_mesh_subnet *sub;
+
+ sub = bt_mesh_subnet_get(update->key_idx);
+ if (sub) {
+ store_net_key(sub);
+ } else {
+ BT_WARN("NetKeyIndex 0x%03x not found",
+ update->key_idx);
+ }
+ }
+ }
+
+ update->valid = 0;
+ }
+}
+
+static void store_node(struct bt_mesh_node *node)
+{
+ char buf[BT_SETTINGS_SIZE(sizeof(struct node_val))];
+ struct node_val val;
+ char path[20];
+ char *str;
+ int err;
+
+ val.net_idx = node->net_idx;
+ val.num_elem = node->num_elem;
+ memcpy(val.dev_key, node->dev_key, 16);
+
+ snprintk(path, sizeof(path), "bt_mesh/Node/%x", node->addr);
+
+ str = settings_str_from_bytes(&val, sizeof(val), buf, sizeof(buf));
+ if (!str) {
+ BT_ERR("Unable to encode Node as value");
+ return;
+ }
+
+
+ err = settings_save_one(path, str);
+ if (err) {
+ BT_ERR("Failed to store Node %s value", path);
+ } else {
+ BT_DBG("Stored Node %s value", path);
+ }
+}
+
+static void clear_node(u16_t addr)
+{
+ char path[20];
+ int err;
+
+ BT_DBG("Node 0x%04x", addr);
+
+ snprintk(path, sizeof(path), "bt_mesh/Node/%x", addr);
+ err = settings_save_one(path, NULL);
+ if (err) {
+ BT_ERR("Failed to clear Node 0x%04x", addr);
+ } else {
+ BT_DBG("Cleared Node 0x%04x", addr);
+ }
+}
+
+static void store_pending_nodes(void)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(node_updates); ++i) {
+ struct node_update *update = &node_updates[i];
+
+ if (update->addr == BT_MESH_ADDR_UNASSIGNED) {
+ continue;
+ }
+
+ if (update->clear) {
+ clear_node(update->addr);
+ } else {
+ struct bt_mesh_node *node;
+
+ node = bt_mesh_node_find(update->addr);
+ if (node) {
+ store_node(node);
+ } else {
+ BT_WARN("Node 0x%04x not found", update->addr);
+ }
+ }
+
+ update->addr = BT_MESH_ADDR_UNASSIGNED;
+ }
+}
+
+static struct node_update *node_update_find(u16_t addr,
+ struct node_update **free_slot)
+{
+ struct node_update *match;
+ int i;
+
+ match = NULL;
+ *free_slot = NULL;
+
+ for (i = 0; i < ARRAY_SIZE(node_updates); i++) {
+ struct node_update *update = &node_updates[i];
+
+ if (update->addr == BT_MESH_ADDR_UNASSIGNED) {
+ *free_slot = update;
+ continue;
+ }
+
+ if (update->addr == addr) {
+ match = update;
+ }
+ }
+
+ return match;
+}
+
+static void encode_mod_path(struct bt_mesh_model *mod, bool vnd,
+ const char *key, char *path, size_t path_len)
+{
+ u16_t mod_key = (((u16_t)mod->elem_idx << 8) | mod->mod_idx);
+
+ if (vnd) {
+ snprintk(path, path_len, "bt_mesh/v/%x/%s", mod_key, key);
+ } else {
+ snprintk(path, path_len, "bt_mesh/s/%x/%s", mod_key, key);
+ }
+}
+
+static void store_pending_mod_bind(struct bt_mesh_model *mod, bool vnd)
+{
+ u16_t keys[CONFIG_BT_MESH_MODEL_KEY_COUNT];
+ char buf[BT_SETTINGS_SIZE(sizeof(keys))];
+ char path[20];
+ int i, count, err;
+ char *val;
+
+ for (i = 0, count = 0; i < ARRAY_SIZE(mod->keys); i++) {
+ if (mod->keys[i] != BT_MESH_KEY_UNUSED) {
+ keys[count++] = mod->keys[i];
+ }
+ }
+
+ if (count) {
+ val = settings_str_from_bytes(keys, count * sizeof(keys[0]),
+ buf, sizeof(buf));
+ if (!val) {
+ BT_ERR("Unable to encode model bindings as value");
+ return;
+ }
+ } else {
+ val = NULL;
+ }
+
+ encode_mod_path(mod, vnd, "bind", path, sizeof(path));
+
+ BT_DBG("Saving %s as %s", path, val ? val : "(null)");
+ err = settings_save_one(path, val);
+ if (err) {
+ BT_ERR("Failed to store bind");
+ } else {
+ BT_DBG("Stored bind");
+ }
+}
+
+static void store_pending_mod_sub(struct bt_mesh_model *mod, bool vnd)
+{
+ u16_t groups[CONFIG_BT_MESH_MODEL_GROUP_COUNT];
+ char buf[BT_SETTINGS_SIZE(sizeof(groups))];
+ char path[20];
+ int i, count, err;
+ char *val;
+
+ for (i = 0, count = 0; i < CONFIG_BT_MESH_MODEL_GROUP_COUNT; i++) {
+ if (mod->groups[i] != BT_MESH_ADDR_UNASSIGNED) {
+ groups[count++] = mod->groups[i];
+ }
+ }
+
+ if (count) {
+ val = settings_str_from_bytes(groups, count * sizeof(groups[0]),
+ buf, sizeof(buf));
+ if (!val) {
+ BT_ERR("Unable to encode model subscription as value");
+ return;
+ }
+ } else {
+ val = NULL;
+ }
+
+ encode_mod_path(mod, vnd, "sub", path, sizeof(path));
+
+ BT_DBG("Saving %s as %s", path, val ? val : "(null)");
+ err = settings_save_one(path, val);
+ if (err) {
+ BT_ERR("Failed to store sub");
+ } else {
+ BT_DBG("Stored sub");
+ }
+}
+
+static void store_pending_mod_pub(struct bt_mesh_model *mod, bool vnd)
+{
+ char buf[BT_SETTINGS_SIZE(sizeof(struct mod_pub_val))];
+ struct mod_pub_val pub;
+ char path[20];
+ char *val;
+ int err;
+
+ if (!mod->pub || mod->pub->addr == BT_MESH_ADDR_UNASSIGNED) {
+ val = NULL;
+ } else {
+ pub.addr = mod->pub->addr;
+ pub.key = mod->pub->key;
+ pub.ttl = mod->pub->ttl;
+ pub.retransmit = mod->pub->retransmit;
+ pub.period = mod->pub->period;
+ pub.period_div = mod->pub->period_div;
+ pub.cred = mod->pub->cred;
+
+ val = settings_str_from_bytes(&pub, sizeof(pub),
+ buf, sizeof(buf));
+ if (!val) {
+ BT_ERR("Unable to encode model publication as value");
+ return;
+ }
+ }
+
+ encode_mod_path(mod, vnd, "pub", path, sizeof(path));
+
+ BT_DBG("Saving %s as %s", path, val ? val : "(null)");
+ err = settings_save_one(path, val);
+ if (err) {
+ BT_ERR("Failed to store pub");
+ } else {
+ BT_DBG("Stored pub");
+ }
+}
+
+static void store_pending_mod(struct bt_mesh_model *mod,
+ struct bt_mesh_elem *elem, bool vnd,
+ bool primary, void *user_data)
+{
+ if (!mod->flags) {
+ return;
+ }
+
+ if (mod->flags & BT_MESH_MOD_BIND_PENDING) {
+ mod->flags &= ~BT_MESH_MOD_BIND_PENDING;
+ store_pending_mod_bind(mod, vnd);
+ }
+
+ if (mod->flags & BT_MESH_MOD_SUB_PENDING) {
+ mod->flags &= ~BT_MESH_MOD_SUB_PENDING;
+ store_pending_mod_sub(mod, vnd);
+ }
+
+ if (mod->flags & BT_MESH_MOD_PUB_PENDING) {
+ mod->flags &= ~BT_MESH_MOD_PUB_PENDING;
+ store_pending_mod_pub(mod, vnd);
+ }
+}
+
+#define IS_VA_DEL(_label) ((_label)->ref == 0)
+static void store_pending_va(void)
+{
+ char buf[BT_SETTINGS_SIZE(sizeof(struct va_val))];
+ struct label *lab;
+ struct va_val va;
+ char path[18];
+ char *val;
+ u16_t i;
+ int err = 0;
+
+ for (i = 0; (lab = get_label(i)) != NULL; i++) {
+ if (!atomic_test_and_clear_bit(lab->flags,
+ BT_MESH_VA_CHANGED)) {
+ continue;
+ }
+
+ snprintk(path, sizeof(path), "bt_mesh/Va/%x", i);
+
+ if (IS_VA_DEL(lab)) {
+ val = NULL;
+ } else {
+ va.ref = lab->ref;
+ va.addr = lab->addr;
+ memcpy(va.uuid, lab->uuid, 16);
+
+ val = settings_str_from_bytes(&va, sizeof(va),
+ buf, sizeof(buf));
+ if (!val) {
+ BT_ERR("Unable to encode model publication as value");
+ return;
+ }
+
+ err = settings_save_one(path, val);
+ }
+
+ if (err) {
+ BT_ERR("Failed to %s %s value (err %d)",
+ IS_VA_DEL(lab) ? "delete" : "store", path, err);
+ } else {
+ BT_DBG("%s %s value",
+ IS_VA_DEL(lab) ? "Deleted" : "Stored", path);
+ }
+ }
+}
+
+static void store_pending(struct ble_npl_event *work)
+{
+ BT_DBG("");
+
+ if (atomic_test_and_clear_bit(bt_mesh.flags, BT_MESH_RPL_PENDING)) {
+ if (atomic_test_bit(bt_mesh.flags, BT_MESH_VALID)) {
+ store_pending_rpl();
+ } else {
+ clear_rpl();
+ }
+ }
+
+ if (atomic_test_and_clear_bit(bt_mesh.flags, BT_MESH_KEYS_PENDING)) {
+ store_pending_keys();
+ }
+
+ if (atomic_test_and_clear_bit(bt_mesh.flags, BT_MESH_NET_PENDING)) {
+ if (atomic_test_bit(bt_mesh.flags, BT_MESH_VALID)) {
+ store_pending_net();
+ } else {
+ clear_net();
+ }
+ }
+
+ if (atomic_test_and_clear_bit(bt_mesh.flags, BT_MESH_IV_PENDING)) {
+ if (atomic_test_bit(bt_mesh.flags, BT_MESH_VALID)) {
+ store_pending_iv();
+ } else {
+ clear_iv();
+ }
+ }
+
+ if (atomic_test_and_clear_bit(bt_mesh.flags, BT_MESH_SEQ_PENDING)) {
+ store_pending_seq();
+ }
+
+ if (atomic_test_and_clear_bit(bt_mesh.flags, BT_MESH_HB_PUB_PENDING)) {
+ store_pending_hb_pub();
+ }
+
+ if (atomic_test_and_clear_bit(bt_mesh.flags, BT_MESH_CFG_PENDING)) {
+ if (atomic_test_bit(bt_mesh.flags, BT_MESH_VALID)) {
+ store_pending_cfg();
+ } else {
+ clear_cfg();
+ }
+ }
+
+ if (atomic_test_and_clear_bit(bt_mesh.flags, BT_MESH_MOD_PENDING)) {
+ bt_mesh_model_foreach(store_pending_mod, NULL);
+ }
+
+ if (atomic_test_and_clear_bit(bt_mesh.flags, BT_MESH_VA_PENDING)) {
+ 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();
+ }
+}
+
+void bt_mesh_store_rpl(struct bt_mesh_rpl *entry)
+{
+ entry->store = true;
+ schedule_store(BT_MESH_RPL_PENDING);
+}
+
+static struct key_update *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 = &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_subnet(struct bt_mesh_subnet *sub)
+{
+ struct key_update *update, *free_slot;
+
+ BT_DBG("NetKeyIndex 0x%03x", sub->net_idx);
+
+ update = key_update_find(false, sub->net_idx, &free_slot);
+ if (update) {
+ update->clear = 0;
+ schedule_store(BT_MESH_KEYS_PENDING);
+ return;
+ }
+
+ if (!free_slot) {
+ store_net_key(sub);
+ return;
+ }
+
+ free_slot->valid = 1;
+ free_slot->key_idx = sub->net_idx;
+ free_slot->app_key = 0;
+ free_slot->clear = 0;
+
+ schedule_store(BT_MESH_KEYS_PENDING);
+}
+
+void bt_mesh_store_app_key(struct bt_mesh_app_key *key)
+{
+ struct key_update *update, *free_slot;
+
+ BT_DBG("AppKeyIndex 0x%03x", key->app_idx);
+
+ update = key_update_find(true, key->app_idx, &free_slot);
+ if (update) {
+ update->clear = 0;
+ schedule_store(BT_MESH_KEYS_PENDING);
+ return;
+ }
+
+ if (!free_slot) {
+ store_app_key(key);
+ return;
+ }
+
+ free_slot->valid = 1;
+ free_slot->key_idx = key->app_idx;
+ free_slot->app_key = 1;
+ free_slot->clear = 0;
+
+ schedule_store(BT_MESH_KEYS_PENDING);
+}
+
+void bt_mesh_store_hb_pub(void)
+{
+ schedule_store(BT_MESH_HB_PUB_PENDING);
+}
+
+void bt_mesh_store_cfg(void)
+{
+ schedule_store(BT_MESH_CFG_PENDING);
+}
+
+void bt_mesh_clear_net(void)
+{
+ schedule_store(BT_MESH_NET_PENDING);
+ schedule_store(BT_MESH_IV_PENDING);
+ schedule_store(BT_MESH_CFG_PENDING);
+}
+
+void bt_mesh_clear_subnet(struct bt_mesh_subnet *sub)
+{
+ struct key_update *update, *free_slot;
+
+ BT_DBG("NetKeyIndex 0x%03x", sub->net_idx);
+
+ update = key_update_find(false, sub->net_idx, &free_slot);
+ if (update) {
+ update->clear = 1;
+ schedule_store(BT_MESH_KEYS_PENDING);
+ return;
+ }
+
+ if (!free_slot) {
+ clear_net_key(sub->net_idx);
+ return;
+ }
+
+ free_slot->valid = 1;
+ free_slot->key_idx = sub->net_idx;
+ free_slot->app_key = 0;
+ free_slot->clear = 1;
+
+ schedule_store(BT_MESH_KEYS_PENDING);
+}
+
+void bt_mesh_clear_app_key(struct bt_mesh_app_key *key)
+{
+ struct key_update *update, *free_slot;
+
+ BT_DBG("AppKeyIndex 0x%03x", key->app_idx);
+
+ update = key_update_find(true, key->app_idx, &free_slot);
+ if (update) {
+ update->clear = 1;
+ schedule_store(BT_MESH_KEYS_PENDING);
+ return;
+ }
+
+ if (!free_slot) {
+ clear_app_key(key->app_idx);
+ return;
+ }
+
+ free_slot->valid = 1;
+ free_slot->key_idx = key->app_idx;
+ free_slot->app_key = 1;
+ free_slot->clear = 1;
+
+ schedule_store(BT_MESH_KEYS_PENDING);
+}
+
+void bt_mesh_clear_rpl(void)
+{
+ schedule_store(BT_MESH_RPL_PENDING);
+}
+
+void bt_mesh_store_mod_bind(struct bt_mesh_model *mod)
+{
+ mod->flags |= BT_MESH_MOD_BIND_PENDING;
+ schedule_store(BT_MESH_MOD_PENDING);
+}
+
+void bt_mesh_store_mod_sub(struct bt_mesh_model *mod)
+{
+ mod->flags |= BT_MESH_MOD_SUB_PENDING;
+ schedule_store(BT_MESH_MOD_PENDING);
+}
+
+void bt_mesh_store_mod_pub(struct bt_mesh_model *mod)
+{
+ mod->flags |= BT_MESH_MOD_PUB_PENDING;
+ schedule_store(BT_MESH_MOD_PENDING);
+}
+
+
+void bt_mesh_store_label(void)
+{
+ schedule_store(BT_MESH_VA_PENDING);
+}
+
+void bt_mesh_store_node(struct bt_mesh_node *node)
+{
+ 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);
+}
+
+void bt_mesh_clear_node(struct bt_mesh_node *node)
+{
+ 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 = true;
+ schedule_store(BT_MESH_NODES_PENDING);
+ return;
+ }
+
+ if (!free_slot) {
+ clear_node(node->addr);
+ return;
+ }
+
+ free_slot->addr = node->addr;
+
+ schedule_store(BT_MESH_NODES_PENDING);
+}
+
+int bt_mesh_model_data_store(struct bt_mesh_model *mod, bool vnd,
+ const void *data, size_t data_len)
+{
+ char path[20];
+ char buf[BT_SETTINGS_SIZE(sizeof(struct mod_pub_val))];
+ char *val;
+ int err;
+
+ encode_mod_path(mod, vnd, "data", path, sizeof(path));
+
+ if (data_len) {
+ mod->flags |= BT_MESH_MOD_DATA_PRESENT;
+ val = settings_str_from_bytes(data, data_len,
+ buf, sizeof(buf));
+ if (!val) {
+ BT_ERR("Unable to encode model publication as value");
+ return -EINVAL;
+ }
+ err = settings_save_one(path, val);
+ } else if (mod->flags & BT_MESH_MOD_DATA_PRESENT) {
+ mod->flags &= ~BT_MESH_MOD_DATA_PRESENT;
+ err = settings_save_one(path, NULL);
+ } else {
+ /* Nothing to delete */
+ err = 0;
+ }
+
+ if (err) {
+ BT_ERR("Failed to store %s value", path);
+ } else {
+ BT_DBG("Stored %s value", path);
+ }
+ return err;
+}
+
+static struct conf_handler bt_mesh_settings_conf_handler = {
+ .ch_name = "bt_mesh",
+ .ch_get = NULL,
+ .ch_set = mesh_set,
+ .ch_commit = mesh_commit,
+ .ch_export = NULL,
+};
+
+void bt_mesh_settings_init(void)
+{
+ int rc;
+
+ rc = conf_register(&bt_mesh_settings_conf_handler);
+
+ SYSINIT_PANIC_ASSERT_MSG(rc == 0,
+ "Failed to register bt_mesh_settings conf");
+
+ k_delayed_work_init(&pending_store, store_pending);
+}
+
+#endif /* MYNEWT_VAL(BLE_MESH_SETTINGS) */
diff --git a/src/libs/mynewt-nimble/nimble/host/mesh/src/settings.h b/src/libs/mynewt-nimble/nimble/host/mesh/src/settings.h
new file mode 100644
index 00000000..c630814e
--- /dev/null
+++ b/src/libs/mynewt-nimble/nimble/host/mesh/src/settings.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2018 Intel Corporation
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+void bt_mesh_store_net(void);
+void bt_mesh_store_iv(bool only_duration);
+void bt_mesh_store_seq(void);
+void bt_mesh_store_rpl(struct bt_mesh_rpl *rpl);
+void bt_mesh_store_subnet(struct bt_mesh_subnet *sub);
+void bt_mesh_store_app_key(struct bt_mesh_app_key *key);
+void bt_mesh_store_hb_pub(void);
+void bt_mesh_store_cfg(void);
+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_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_settings_init(void);
diff --git a/src/libs/mynewt-nimble/nimble/host/mesh/src/shell.c b/src/libs/mynewt-nimble/nimble/host/mesh/src/shell.c
new file mode 100644
index 00000000..91fbd978
--- /dev/null
+++ b/src/libs/mynewt-nimble/nimble/host/mesh/src/shell.c
@@ -0,0 +1,2819 @@
+/** @file
+ * @brief Bluetooth Mesh shell
+ *
+ */
+
+/*
+ * Copyright (c) 2017 Intel Corporation
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include "syscfg/syscfg.h"
+
+#if MYNEWT_VAL(BLE_MESH_SHELL)
+
+#include <stdlib.h>
+#include <ctype.h>
+#include <errno.h>
+#include "shell/shell.h"
+#include "console/console.h"
+#include "mesh/mesh.h"
+#include "mesh/main.h"
+#include "mesh/glue.h"
+#include "mesh/testing.h"
+
+/* Private includes for raw Network & Transport layer access */
+#include "net.h"
+#include "access.h"
+#include "mesh_priv.h"
+#include "lpn.h"
+#include "transport.h"
+#include "foundation.h"
+#include "testing.h"
+#include "settings.h"
+
+#if MYNEWT_VAL(BLE_MESH_SHELL_MODELS)
+#include "mesh/model_srv.h"
+#include "mesh/model_cli.h"
+#include "light_model.h"
+#endif
+
+/* This should be higher priority (lower value) than main task priority */
+#define BLE_MESH_SHELL_TASK_PRIO 126
+#define BLE_MESH_SHELL_STACK_SIZE 768
+
+OS_TASK_STACK_DEFINE(g_blemesh_shell_stack, BLE_MESH_SHELL_STACK_SIZE);
+
+struct os_task mesh_shell_task;
+static struct os_eventq mesh_shell_queue;
+
+#define CID_NVAL 0xffff
+#define CID_VENDOR 0x05C3
+
+/* Vendor Model data */
+#define VND_MODEL_ID_1 0x1234
+
+/* Default net, app & dev key values, unless otherwise specified */
+static const u8_t default_key[16] = {
+ 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef,
+ 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef,
+};
+
+static struct {
+ u16_t local;
+ u16_t dst;
+ u16_t net_idx;
+ u16_t app_idx;
+} net = {
+ .local = BT_MESH_ADDR_UNASSIGNED,
+ .dst = BT_MESH_ADDR_UNASSIGNED,
+};
+
+static struct bt_mesh_cfg_srv cfg_srv = {
+ .relay = BT_MESH_RELAY_DISABLED,
+ .beacon = BT_MESH_BEACON_ENABLED,
+#if MYNEWT_VAL(BLE_MESH_FRIEND)
+ .frnd = BT_MESH_FRIEND_DISABLED,
+#else
+ .frnd = BT_MESH_FRIEND_NOT_SUPPORTED,
+#endif
+#if MYNEWT_VAL(BLE_MESH_GATT_PROXY)
+ .gatt_proxy = BT_MESH_GATT_PROXY_DISABLED,
+#else
+ .gatt_proxy = BT_MESH_GATT_PROXY_NOT_SUPPORTED,
+#endif
+
+ .default_ttl = 7,
+
+ /* 3 transmissions with 20ms interval */
+ .net_transmit = BT_MESH_TRANSMIT(2, 20),
+ .relay_retransmit = BT_MESH_TRANSMIT(2, 20),
+};
+
+#define CUR_FAULTS_MAX 4
+
+static u8_t cur_faults[CUR_FAULTS_MAX];
+static u8_t reg_faults[CUR_FAULTS_MAX * 2];
+
+static void get_faults(u8_t *faults, u8_t faults_size, u8_t *dst, u8_t *count)
+{
+ u8_t i, limit = *count;
+
+ for (i = 0, *count = 0; i < faults_size && *count < limit; i++) {
+ if (faults[i]) {
+ *dst++ = faults[i];
+ (*count)++;
+ }
+ }
+}
+
+static int fault_get_cur(struct bt_mesh_model *model, u8_t *test_id,
+ u16_t *company_id, u8_t *faults, u8_t *fault_count)
+{
+ printk("Sending current faults\n");
+
+ *test_id = 0x00;
+ *company_id = CID_VENDOR;
+
+ get_faults(cur_faults, sizeof(cur_faults), faults, fault_count);
+
+ return 0;
+}
+
+static int fault_get_reg(struct bt_mesh_model *model, u16_t cid,
+ u8_t *test_id, u8_t *faults, u8_t *fault_count)
+{
+ if (cid != CID_VENDOR) {
+ printk("Faults requested for unknown Company ID 0x%04x\n", cid);
+ return -EINVAL;
+ }
+
+ printk("Sending registered faults\n");
+
+ *test_id = 0x00;
+
+ get_faults(reg_faults, sizeof(reg_faults), faults, fault_count);
+
+ return 0;
+}
+
+static int fault_clear(struct bt_mesh_model *model, uint16_t cid)
+{
+ if (cid != CID_VENDOR) {
+ return -EINVAL;
+ }
+
+ memset(reg_faults, 0, sizeof(reg_faults));
+
+ return 0;
+}
+
+static int fault_test(struct bt_mesh_model *model, uint8_t test_id,
+ uint16_t cid)
+{
+ if (cid != CID_VENDOR) {
+ return -EINVAL;
+ }
+
+ if (test_id != 0x00) {
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const struct bt_mesh_health_srv_cb health_srv_cb = {
+ .fault_get_cur = fault_get_cur,
+ .fault_get_reg = fault_get_reg,
+ .fault_clear = fault_clear,
+ .fault_test = fault_test,
+};
+
+static struct bt_mesh_health_srv health_srv = {
+ .cb = &health_srv_cb,
+};
+
+static struct bt_mesh_model_pub health_pub;
+
+static void
+health_pub_init(void)
+{
+ health_pub.msg = BT_MESH_HEALTH_FAULT_MSG(CUR_FAULTS_MAX);
+}
+#if MYNEWT_VAL(BLE_MESH_CFG_CLI)
+
+static struct bt_mesh_cfg_cli cfg_cli = {
+};
+
+#endif /* MYNEWT_VAL(BLE_MESH_CFG_CLI) */
+
+#if MYNEWT_VAL(BLE_MESH_HEALTH_CLI)
+void show_faults(u8_t test_id, u16_t cid, u8_t *faults, size_t fault_count)
+{
+ size_t i;
+
+ if (!fault_count) {
+ printk("Health Test ID 0x%02x Company ID 0x%04x: no faults\n",
+ test_id, cid);
+ return;
+ }
+
+ printk("Health Test ID 0x%02x Company ID 0x%04x Fault Count %zu:\n",
+ test_id, cid, fault_count);
+
+ for (i = 0; i < fault_count; i++) {
+ printk("\t0x%02x\n", faults[i]);
+ }
+}
+
+static void health_current_status(struct bt_mesh_health_cli *cli, u16_t addr,
+ u8_t test_id, u16_t cid, u8_t *faults,
+ size_t fault_count)
+{
+ printk("Health Current Status from 0x%04x\n", addr);
+ show_faults(test_id, cid, faults, fault_count);
+}
+
+static struct bt_mesh_health_cli health_cli = {
+ .current_status = health_current_status,
+};
+
+#endif /* MYNEWT_VAL(BLE_MESH_HEALTH_CLI) */
+
+#if MYNEWT_VAL(BLE_MESH_SHELL_MODELS)
+static struct bt_mesh_gen_model_cli gen_onoff_cli;
+static struct bt_mesh_model_pub gen_onoff_cli_pub;
+static struct bt_mesh_model_pub gen_onoff_srv_pub;
+static struct bt_mesh_gen_model_cli gen_level_cli;
+static struct bt_mesh_model_pub gen_level_cli_pub;
+static struct bt_mesh_model_pub gen_level_srv_pub;
+static struct bt_mesh_model_pub light_lightness_pub;
+static struct bt_mesh_gen_onoff_srv gen_onoff_srv = {
+ .get = light_model_gen_onoff_get,
+ .set = light_model_gen_onoff_set,
+};
+static struct bt_mesh_gen_level_srv gen_level_srv = {
+ .get = light_model_gen_level_get,
+ .set = light_model_gen_level_set,
+};
+static struct bt_mesh_light_lightness_srv light_lightness_srv = {
+ .get = light_model_light_lightness_get,
+ .set = light_model_light_lightness_set,
+};
+
+void bt_mesh_set_gen_onoff_srv_cb(int (*get)(struct bt_mesh_model *model, u8_t *state),
+ int (*set)(struct bt_mesh_model *model, u8_t state))
+{
+ gen_onoff_srv.get = get;
+ gen_onoff_srv.set = set;
+}
+
+void bt_mesh_set_gen_level_srv_cb(int (*get)(struct bt_mesh_model *model, s16_t *level),
+ int (*set)(struct bt_mesh_model *model, s16_t level))
+{
+ gen_level_srv.get = get;
+ gen_level_srv.set = set;
+}
+
+void bt_mesh_set_light_lightness_srv_cb(int (*get)(struct bt_mesh_model *model, s16_t *level),
+ int (*set)(struct bt_mesh_model *model, s16_t level))
+{
+ light_lightness_srv.get = get;
+ light_lightness_srv.set = set;
+}
+#endif
+
+static struct bt_mesh_model root_models[] = {
+ BT_MESH_MODEL_CFG_SRV(&cfg_srv),
+ BT_MESH_MODEL_HEALTH_SRV(&health_srv, &health_pub),
+#if MYNEWT_VAL(BLE_MESH_CFG_CLI)
+ BT_MESH_MODEL_CFG_CLI(&cfg_cli),
+#endif
+#if MYNEWT_VAL(BLE_MESH_HEALTH_CLI)
+ BT_MESH_MODEL_HEALTH_CLI(&health_cli),
+#endif
+#if MYNEWT_VAL(BLE_MESH_SHELL_MODELS)
+ BT_MESH_MODEL_GEN_ONOFF_SRV(&gen_onoff_srv, &gen_onoff_srv_pub),
+ BT_MESH_MODEL_GEN_ONOFF_CLI(&gen_onoff_cli, &gen_onoff_cli_pub),
+ BT_MESH_MODEL_GEN_LEVEL_SRV(&gen_level_srv, &gen_level_srv_pub),
+ BT_MESH_MODEL_GEN_LEVEL_CLI(&gen_level_cli, &gen_level_cli_pub),
+ BT_MESH_MODEL_LIGHT_LIGHTNESS_SRV(&light_lightness_srv, &light_lightness_pub),
+#endif
+};
+
+static struct bt_mesh_model vnd_models[] = {
+ BT_MESH_MODEL_VND(CID_VENDOR, VND_MODEL_ID_1,
+ BT_MESH_MODEL_NO_OPS, NULL, NULL),
+};
+
+static struct bt_mesh_elem elements[] = {
+ BT_MESH_ELEM(0, root_models, vnd_models),
+};
+
+static const struct bt_mesh_comp comp = {
+ .cid = CID_VENDOR,
+ .elem = elements,
+ .elem_count = ARRAY_SIZE(elements),
+};
+
+static u8_t hex2val(char c)
+{
+ if (c >= '0' && c <= '9') {
+ return c - '0';
+ } else if (c >= 'a' && c <= 'f') {
+ return c - 'a' + 10;
+ } else if (c >= 'A' && c <= 'F') {
+ return c - 'A' + 10;
+ } else {
+ return 0;
+ }
+}
+
+static size_t hex2bin(const char *hex, u8_t *bin, size_t bin_len)
+{
+ size_t len = 0;
+
+ while (*hex && len < bin_len) {
+ bin[len] = hex2val(*hex++) << 4;
+
+ if (!*hex) {
+ len++;
+ break;
+ }
+
+ bin[len++] |= hex2val(*hex++);
+ }
+
+ return len;
+}
+
+static void prov_complete(u16_t net_idx, u16_t addr)
+{
+ printk("Local node provisioned, net_idx 0x%04x address 0x%04x\n",
+ net_idx, addr);
+ net.local = addr;
+ net.net_idx = net_idx,
+ net.dst = addr;
+}
+
+static void prov_node_added(u16_t net_idx, u16_t addr, u8_t num_elem)
+{
+ printk("Node provisioned, net_idx 0x%04x address "
+ "0x%04x elements %d", net_idx, addr, num_elem);
+
+ net.net_idx = net_idx,
+ net.dst = addr;
+}
+
+static void prov_input_complete(void)
+{
+ printk("Input complete");
+}
+
+static void prov_reset(void)
+{
+ printk("The local node has been reset and needs reprovisioning\n");
+}
+
+static int output_number(bt_mesh_output_action_t action, uint32_t number)
+{
+ printk("OOB Number: %lu\n", number);
+ return 0;
+}
+
+static int output_string(const char *str)
+{
+ printk("OOB String: %s\n", str);
+ return 0;
+}
+
+static bt_mesh_input_action_t input_act;
+static u8_t input_size;
+
+static int cmd_input_num(int argc, char *argv[])
+{
+ int err;
+
+ if (argc < 2) {
+ return -EINVAL;
+ }
+
+ if (input_act != BT_MESH_ENTER_NUMBER) {
+ printk("A number hasn't been requested!\n");
+ return 0;
+ }
+
+ if (strlen(argv[1]) < input_size) {
+ printk("Too short input (%u digits required)\n",
+ input_size);
+ return 0;
+ }
+
+ err = bt_mesh_input_number(strtoul(argv[1], NULL, 10));
+ if (err) {
+ printk("Numeric input failed (err %d)\n", err);
+ return 0;
+ }
+
+ input_act = BT_MESH_NO_INPUT;
+ return 0;
+}
+
+struct shell_cmd_help cmd_input_num_help = {
+ NULL, "<number>", NULL
+};
+
+static int cmd_input_str(int argc, char *argv[])
+{
+ int err;
+
+ if (argc < 2) {
+ return -EINVAL;
+ }
+
+ if (input_act != BT_MESH_ENTER_STRING) {
+ printk("A string hasn't been requested!\n");
+ return 0;
+ }
+
+ if (strlen(argv[1]) < input_size) {
+ printk("Too short input (%u characters required)\n",
+ input_size);
+ return 0;
+ }
+
+ err = bt_mesh_input_string(argv[1]);
+ if (err) {
+ printk("String input failed (err %d)\n", err);
+ return 0;
+ }
+
+ input_act = BT_MESH_NO_INPUT;
+ return 0;
+}
+
+struct shell_cmd_help cmd_input_str_help = {
+ NULL, "<string>", NULL
+};
+
+static int input(bt_mesh_input_action_t act, u8_t size)
+{
+ switch (act) {
+ case BT_MESH_ENTER_NUMBER:
+ printk("Enter a number (max %u digits) with: input-num <num>\n",
+ size);
+ break;
+ case BT_MESH_ENTER_STRING:
+ printk("Enter a string (max %u chars) with: input-str <str>\n",
+ size);
+ break;
+ default:
+ printk("Unknown input action %u (size %u) requested!\n",
+ act, size);
+ return -EINVAL;
+ }
+
+ input_act = act;
+ input_size = size;
+ return 0;
+}
+
+static const char *bearer2str(bt_mesh_prov_bearer_t bearer)
+{
+ switch (bearer) {
+ case BT_MESH_PROV_ADV:
+ return "PB-ADV";
+ case BT_MESH_PROV_GATT:
+ return "PB-GATT";
+ default:
+ return "unknown";
+ }
+}
+
+static void link_open(bt_mesh_prov_bearer_t bearer)
+{
+ printk("Provisioning link opened on %s\n", bearer2str(bearer));
+}
+
+static void link_close(bt_mesh_prov_bearer_t bearer)
+{
+ printk("Provisioning link closed on %s\n", bearer2str(bearer));
+}
+
+static u8_t dev_uuid[16] = MYNEWT_VAL(BLE_MESH_DEV_UUID);
+
+static u8_t static_val[16];
+
+static struct bt_mesh_prov prov = {
+ .uuid = dev_uuid,
+ .link_open = link_open,
+ .link_close = link_close,
+ .complete = prov_complete,
+ .node_added = prov_node_added,
+ .reset = prov_reset,
+ .static_val = NULL,
+ .static_val_len = 0,
+ .output_size = MYNEWT_VAL(BLE_MESH_OOB_OUTPUT_SIZE),
+ .output_actions = MYNEWT_VAL(BLE_MESH_OOB_OUTPUT_ACTIONS),
+ .output_number = output_number,
+ .output_string = output_string,
+ .input_size = MYNEWT_VAL(BLE_MESH_OOB_INPUT_SIZE),
+ .input_actions = MYNEWT_VAL(BLE_MESH_OOB_INPUT_ACTIONS),
+ .input = input,
+ .input_complete = prov_input_complete,
+};
+
+static int cmd_static_oob(int argc, char *argv[])
+{
+ if (argc < 2) {
+ prov.static_val = NULL;
+ prov.static_val_len = 0;
+ } else {
+ prov.static_val_len = hex2bin(argv[1], static_val, 16);
+ if (prov.static_val_len) {
+ prov.static_val = static_val;
+ } else {
+ prov.static_val = NULL;
+ }
+ }
+
+ if (prov.static_val) {
+ printk("Static OOB value set (length %u)\n",
+ prov.static_val_len);
+ } else {
+ printk("Static OOB value cleared\n");
+ }
+
+ return 0;
+}
+
+struct shell_cmd_help cmd_static_oob_help = {
+ NULL, "[val: 1-16 hex values]", NULL
+};
+
+static int cmd_uuid(int argc, char *argv[])
+{
+ u8_t uuid[16];
+ size_t len;
+
+ if (argc < 2) {
+ return -EINVAL;
+ }
+
+ len = hex2bin(argv[1], uuid, sizeof(uuid));
+ if (len < 1) {
+ return -EINVAL;
+ }
+
+ memcpy(dev_uuid, uuid, len);
+ memset(dev_uuid + len, 0, sizeof(dev_uuid) - len);
+
+ printk("Device UUID set\n");
+
+ return 0;
+}
+
+struct shell_cmd_help cmd_uuid_help = {
+ NULL, "<UUID: 1-16 hex values>", NULL
+};
+
+static int cmd_reset(int argc, char *argv[])
+{
+ bt_mesh_reset();
+ printk("Local node reset complete\n");
+ return 0;
+}
+
+static u8_t str2u8(const char *str)
+{
+ if (isdigit(str[0])) {
+ return strtoul(str, NULL, 0);
+ }
+
+ return (!strcmp(str, "on") || !strcmp(str, "enable"));
+}
+
+static bool str2bool(const char *str)
+{
+ return str2u8(str);
+}
+
+#if MYNEWT_VAL(BLE_MESH_LOW_POWER)
+static int cmd_lpn(int argc, char *argv[])
+{
+ static bool enabled;
+ int err;
+
+ if (argc < 2) {
+ printk("%s\n", enabled ? "enabled" : "disabled");
+ return 0;
+ }
+
+ if (str2bool(argv[1])) {
+ if (enabled) {
+ printk("LPN already enabled\n");
+ return 0;
+ }
+
+ err = bt_mesh_lpn_set(true);
+ if (err) {
+ printk("Enabling LPN failed (err %d)\n", err);
+ } else {
+ enabled = true;
+ }
+ } else {
+ if (!enabled) {
+ printk("LPN already disabled\n");
+ return 0;
+ }
+
+ err = bt_mesh_lpn_set(false);
+ if (err) {
+ printk("Enabling LPN failed (err %d)\n", err);
+ } else {
+ enabled = false;
+ }
+ }
+
+ return 0;
+}
+
+static int cmd_poll(int argc, char *argv[])
+{
+ int err;
+
+ err = bt_mesh_lpn_poll();
+ if (err) {
+ printk("Friend Poll failed (err %d)\n", err);
+ }
+
+ return 0;
+}
+
+static void lpn_cb(u16_t friend_addr, bool established)
+{
+ if (established) {
+ printk("Friendship (as LPN) established to Friend 0x%04x\n",
+ friend_addr);
+ } else {
+ printk("Friendship (as LPN) lost with Friend 0x%04x\n",
+ friend_addr);
+ }
+}
+
+struct shell_cmd_help cmd_lpn_help = {
+ NULL, "<value: off, on>", NULL
+};
+
+#endif /* MESH_LOW_POWER */
+
+static int check_pub_addr_unassigned(void)
+{
+#ifdef ARCH_sim
+ return 0;
+#else
+ uint8_t zero_addr[BLE_DEV_ADDR_LEN] = { 0 };
+
+ return memcmp(MYNEWT_VAL(BLE_PUBLIC_DEV_ADDR),
+ zero_addr, BLE_DEV_ADDR_LEN) == 0;
+#endif
+}
+
+int cmd_mesh_init(int argc, char *argv[])
+{
+ int err;
+ ble_addr_t addr;
+
+ if (check_pub_addr_unassigned()) {
+ /* Use NRPA */
+ err = ble_hs_id_gen_rnd(1, &addr);
+ assert(err == 0);
+ err = ble_hs_id_set_rnd(addr.val);
+ assert(err == 0);
+
+ err = bt_mesh_init(addr.type, &prov, &comp);
+ }
+ else {
+ err = bt_mesh_init(0, &prov, &comp);
+ }
+
+ if (err) {
+ printk("Mesh initialization failed (err %d)\n", err);
+ }
+
+ printk("Mesh initialized\n");
+
+ if (IS_ENABLED(CONFIG_SETTINGS)) {
+ settings_load();
+ }
+
+ if (bt_mesh_is_provisioned()) {
+ printk("Mesh network restored from flash\n");
+ } else {
+ printk("Use \"pb-adv on\" or \"pb-gatt on\" to enable"
+ " advertising\n");
+ }
+
+#if MYNEWT_VAL(BLE_MESH_LOW_POWER)
+ bt_mesh_lpn_set_cb(lpn_cb);
+#endif
+
+ return 0;
+}
+
+#if MYNEWT_VAL(BLE_MESH_GATT_PROXY)
+static int cmd_ident(int argc, char *argv[])
+{
+ int err;
+
+ err = bt_mesh_proxy_identity_enable();
+ if (err) {
+ printk("Failed advertise using Node Identity (err %d)\n", err);
+ }
+
+ return 0;
+}
+#endif /* MESH_GATT_PROXY */
+
+static int cmd_dst(int argc, char *argv[])
+{
+ if (argc < 2) {
+ printk("Destination address: 0x%04x%s\n", net.dst,
+ net.dst == net.local ? " (local)" : "");
+ return 0;
+ }
+
+ if (!strcmp(argv[1], "local")) {
+ net.dst = net.local;
+ } else {
+ net.dst = strtoul(argv[1], NULL, 0);
+ }
+
+ printk("Destination address set to 0x%04x%s\n", net.dst,
+ net.dst == net.local ? " (local)" : "");
+ return 0;
+}
+
+struct shell_cmd_help cmd_dst_help = {
+ NULL, "[destination address]", NULL
+};
+
+static int cmd_netidx(int argc, char *argv[])
+{
+ if (argc < 2) {
+ printk("NetIdx: 0x%04x\n", net.net_idx);
+ return 0;
+ }
+
+ net.net_idx = strtoul(argv[1], NULL, 0);
+ printk("NetIdx set to 0x%04x\n", net.net_idx);
+ return 0;
+}
+
+struct shell_cmd_help cmd_netidx_help = {
+ NULL, "[NetIdx]", NULL
+};
+
+static int cmd_appidx(int argc, char *argv[])
+{
+ if (argc < 2) {
+ printk("AppIdx: 0x%04x\n", net.app_idx);
+ return 0;
+ }
+
+ net.app_idx = strtoul(argv[1], NULL, 0);
+ printk("AppIdx set to 0x%04x\n", net.app_idx);
+ return 0;
+}
+
+struct shell_cmd_help cmd_appidx_help = {
+ NULL, "[AppIdx]", NULL
+};
+
+static int cmd_net_send(int argc, char *argv[])
+{
+ struct os_mbuf *msg = NET_BUF_SIMPLE(32);
+ struct bt_mesh_msg_ctx ctx = {
+ .send_ttl = BT_MESH_TTL_DEFAULT,
+ .net_idx = net.net_idx,
+ .addr = net.dst,
+ .app_idx = net.app_idx,
+
+ };
+ struct bt_mesh_net_tx tx = {
+ .ctx = &ctx,
+ .src = net.local,
+ .xmit = bt_mesh_net_transmit_get(),
+ .sub = bt_mesh_subnet_get(net.net_idx),
+ };
+ size_t len;
+ int err = 0;
+
+ if (argc < 2) {
+ err = -EINVAL;
+ goto done;
+ }
+
+ if (!tx.sub) {
+ printk("No matching subnet for NetKey Index 0x%04x\n",
+ net.net_idx);
+ goto done;
+ }
+
+ net_buf_simple_init(msg, 0);
+ len = hex2bin(argv[1], msg->om_data, net_buf_simple_tailroom(msg) - 4);
+ net_buf_simple_add(msg, len);
+
+ err = bt_mesh_trans_send(&tx, msg, NULL, NULL);
+ if (err) {
+ printk("Failed to send (err %d)\n", err);
+ }
+
+done:
+ os_mbuf_free_chain(msg);
+ return err;
+}
+
+struct shell_cmd_help cmd_net_send_help = {
+ NULL, "<hex string>", NULL
+};
+
+static int cmd_rpl_clear(int argc, char *argv[])
+{
+ bt_mesh_rpl_clear();
+ return 0;
+}
+
+#if MYNEWT_VAL(BLE_MESH_LOW_POWER)
+static int cmd_lpn_subscribe(int argc, char *argv[])
+{
+ u16_t address;
+
+ if (argc < 2) {
+ return -EINVAL;
+ }
+
+ address = strtoul(argv[1], NULL, 0);
+
+ printk("address 0x%04x", address);
+
+ bt_mesh_lpn_group_add(address);
+
+ return 0;
+}
+
+struct shell_cmd_help cmd_lpn_subscribe_help = {
+ NULL, "<addr>", NULL
+};
+
+static int cmd_lpn_unsubscribe(int argc, char *argv[])
+{
+ u16_t address;
+
+ if (argc < 2) {
+ return -EINVAL;
+ }
+
+ address = strtoul(argv[1], NULL, 0);
+
+ printk("address 0x%04x", address);
+
+ bt_mesh_lpn_group_del(&address, 1);
+
+ return 0;
+}
+
+struct shell_cmd_help cmd_lpn_unsubscribe_help = {
+ NULL, "<addr>", NULL
+};
+#endif
+
+#if MYNEWT_VAL(BLE_MESH_IV_UPDATE_TEST)
+static int cmd_iv_update(int argc, char *argv[])
+{
+ if (bt_mesh_iv_update()) {
+ printk("Transitioned to IV Update In Progress state\n");
+ } else {
+ printk("Transitioned to IV Update Normal state\n");
+ }
+
+ printk("IV Index is 0x%08lx\n", bt_mesh.iv_index);
+
+ return 0;
+}
+
+static int cmd_iv_update_test(int argc, char *argv[])
+{
+ bool enable;
+
+ if (argc < 2) {
+ return -EINVAL;
+ }
+
+ enable = str2bool(argv[1]);
+ if (enable) {
+ printk("Enabling IV Update test mode\n");
+ } else {
+ printk("Disabling IV Update test mode\n");
+ }
+
+ bt_mesh_iv_update_test(enable);
+
+ return 0;
+}
+
+struct shell_cmd_help cmd_iv_update_test_help = {
+ NULL, "<value: off, on>", NULL
+};
+#endif
+
+#if MYNEWT_VAL(BLE_MESH_CFG_CLI)
+
+int cmd_timeout(int argc, char *argv[])
+{
+ s32_t timeout;
+
+ if (argc < 2) {
+ timeout = bt_mesh_cfg_cli_timeout_get();
+ if (timeout == K_FOREVER) {
+ printk("Message timeout: forever\n");
+ } else {
+ printk("Message timeout: %lu seconds\n",
+ timeout / 1000);
+ }
+
+ return 0;
+ }
+
+ timeout = strtol(argv[1], NULL, 0);
+ if (timeout < 0 || timeout > (INT32_MAX / 1000)) {
+ timeout = K_FOREVER;
+ } else {
+ timeout = timeout * 1000;
+ }
+
+ bt_mesh_cfg_cli_timeout_set(timeout);
+ if (timeout == K_FOREVER) {
+ printk("Message timeout: forever\n");
+ } else {
+ printk("Message timeout: %lu seconds\n",
+ timeout / 1000);
+ }
+
+ return 0;
+}
+
+struct shell_cmd_help cmd_timeout_help = {
+ NULL, "[timeout in seconds]", NULL
+};
+
+
+static int cmd_get_comp(int argc, char *argv[])
+{
+ struct os_mbuf *comp = NET_BUF_SIMPLE(32);
+ u8_t status, page = 0x00;
+ int err = 0;
+
+ if (argc > 1) {
+ page = strtol(argv[1], NULL, 0);
+ }
+
+ net_buf_simple_init(comp, 0);
+ err = bt_mesh_cfg_comp_data_get(net.net_idx, net.dst, page,
+ &status, comp);
+ if (err) {
+ printk("Getting composition failed (err %d)\n", err);
+ goto done;
+ }
+
+ if (status != 0x00) {
+ printk("Got non-success status 0x%02x\n", status);
+ goto done;
+ }
+
+ printk("Got Composition Data for 0x%04x:\n", net.dst);
+ printk("\tCID 0x%04x\n", net_buf_simple_pull_le16(comp));
+ printk("\tPID 0x%04x\n", net_buf_simple_pull_le16(comp));
+ printk("\tVID 0x%04x\n", net_buf_simple_pull_le16(comp));
+ printk("\tCRPL 0x%04x\n", net_buf_simple_pull_le16(comp));
+ printk("\tFeatures 0x%04x\n", net_buf_simple_pull_le16(comp));
+
+ while (comp->om_len > 4) {
+ u8_t sig, vnd;
+ u16_t loc;
+ int i;
+
+ loc = net_buf_simple_pull_le16(comp);
+ sig = net_buf_simple_pull_u8(comp);
+ vnd = net_buf_simple_pull_u8(comp);
+
+ printk("\n\tElement @ 0x%04x:\n", loc);
+
+ if (comp->om_len < ((sig * 2) + (vnd * 4))) {
+ printk("\t\t...truncated data!\n");
+ break;
+ }
+
+ if (sig) {
+ printk("\t\tSIG Models:\n");
+ } else {
+ printk("\t\tNo SIG Models\n");
+ }
+
+ for (i = 0; i < sig; i++) {
+ u16_t mod_id = net_buf_simple_pull_le16(comp);
+
+ printk("\t\t\t0x%04x\n", mod_id);
+ }
+
+ if (vnd) {
+ printk("\t\tVendor Models:\n");
+ } else {
+ printk("\t\tNo Vendor Models\n");
+ }
+
+ for (i = 0; i < vnd; i++) {
+ u16_t cid = net_buf_simple_pull_le16(comp);
+ u16_t mod_id = net_buf_simple_pull_le16(comp);
+
+ printk("\t\t\tCompany 0x%04x: 0x%04x\n", cid, mod_id);
+ }
+ }
+
+done:
+ os_mbuf_free_chain(comp);
+ return err;
+}
+
+struct shell_cmd_help cmd_get_comp_help = {
+ NULL, "[page]", NULL
+};
+
+static int cmd_beacon(int argc, char *argv[])
+{
+ u8_t status;
+ int err;
+
+ if (argc < 2) {
+ err = bt_mesh_cfg_beacon_get(net.net_idx, net.dst, &status);
+ } else {
+ u8_t val = str2u8(argv[1]);
+
+ err = bt_mesh_cfg_beacon_set(net.net_idx, net.dst, val,
+ &status);
+ }
+
+ if (err) {
+ printk("Unable to send Beacon Get/Set message (err %d)\n", err);
+ return 0;
+ }
+
+ printk("Beacon state is 0x%02x\n", status);
+
+ return 0;
+}
+
+struct shell_cmd_help cmd_beacon_help = {
+ NULL, "[val: off, on]", NULL
+};
+
+static int cmd_ttl(int argc, char *argv[])
+{
+ u8_t ttl;
+ int err;
+
+ if (argc < 2) {
+ err = bt_mesh_cfg_ttl_get(net.net_idx, net.dst, &ttl);
+ } else {
+ u8_t val = strtoul(argv[1], NULL, 0);
+
+ err = bt_mesh_cfg_ttl_set(net.net_idx, net.dst, val, &ttl);
+ }
+
+ if (err) {
+ printk("Unable to send Default TTL Get/Set (err %d)\n", err);
+ return 0;
+ }
+
+ printk("Default TTL is 0x%02x\n", ttl);
+
+ return 0;
+}
+
+struct shell_cmd_help cmd_ttl_help = {
+ NULL, "[ttl: 0x00, 0x02-0x7f]", NULL
+};
+
+static int cmd_friend(int argc, char *argv[])
+{
+ u8_t frnd;
+ int err;
+
+ if (argc < 2) {
+ err = bt_mesh_cfg_friend_get(net.net_idx, net.dst, &frnd);
+ } else {
+ u8_t val = str2u8(argv[1]);
+
+ err = bt_mesh_cfg_friend_set(net.net_idx, net.dst, val, &frnd);
+ }
+
+ if (err) {
+ printk("Unable to send Friend Get/Set (err %d)\n", err);
+ return 0;
+ }
+
+ printk("Friend is set to 0x%02x\n", frnd);
+
+ return 0;
+}
+
+struct shell_cmd_help cmd_friend_help = {
+ NULL, "[val: off, on]", NULL
+};
+
+static int cmd_gatt_proxy(int argc, char *argv[])
+{
+ u8_t proxy;
+ int err;
+
+ if (argc < 2) {
+ err = bt_mesh_cfg_gatt_proxy_get(net.net_idx, net.dst, &proxy);
+ } else {
+ u8_t val = str2u8(argv[1]);
+
+ err = bt_mesh_cfg_gatt_proxy_set(net.net_idx, net.dst, val,
+ &proxy);
+ }
+
+ if (err) {
+ printk("Unable to send GATT Proxy Get/Set (err %d)\n", err);
+ return 0;
+ }
+
+ printk("GATT Proxy is set to 0x%02x\n", proxy);
+
+ return 0;
+}
+
+struct shell_cmd_help cmd_gatt_proxy_help = {
+ NULL, "[val: off, on]", NULL
+};
+
+static int cmd_relay(int argc, char *argv[])
+{
+ u8_t relay, transmit;
+ int err;
+
+ if (argc < 2) {
+ err = bt_mesh_cfg_relay_get(net.net_idx, net.dst, &relay,
+ &transmit);
+ } else {
+ u8_t val = str2u8(argv[1]);
+ u8_t count, interval, new_transmit;
+
+ if (val) {
+ if (argc > 2) {
+ count = strtoul(argv[2], NULL, 0);
+ } else {
+ count = 2;
+ }
+
+ if (argc > 3) {
+ interval = strtoul(argv[3], NULL, 0);
+ } else {
+ interval = 20;
+ }
+
+ new_transmit = BT_MESH_TRANSMIT(count, interval);
+ } else {
+ new_transmit = 0;
+ }
+
+ err = bt_mesh_cfg_relay_set(net.net_idx, net.dst, val,
+ new_transmit, &relay, &transmit);
+ }
+
+ if (err) {
+ printk("Unable to send Relay Get/Set (err %d)\n", err);
+ return 0;
+ }
+
+ printk("Relay is 0x%02x, Transmit 0x%02x (count %u interval %ums)\n",
+ relay, transmit, BT_MESH_TRANSMIT_COUNT(transmit),
+ BT_MESH_TRANSMIT_INT(transmit));
+
+ return 0;
+}
+
+struct shell_cmd_help cmd_relay_help = {
+ NULL, "[val: off, on] [count: 0-7] [interval: 0-32]", NULL
+};
+
+static int cmd_net_key_add(int argc, char *argv[])
+{
+ u8_t key_val[16];
+ u16_t key_net_idx;
+ u8_t status;
+ int err;
+
+ if (argc < 2) {
+ return -EINVAL;
+ }
+
+ key_net_idx = strtoul(argv[1], NULL, 0);
+
+ if (argc > 2) {
+ size_t len;
+
+ len = hex2bin(argv[3], key_val, sizeof(key_val));
+ memset(key_val, 0, sizeof(key_val) - len);
+ } else {
+ memcpy(key_val, default_key, sizeof(key_val));
+ }
+
+ err = bt_mesh_cfg_net_key_add(net.net_idx, net.dst, key_net_idx,
+ key_val, &status);
+ if (err) {
+ printk("Unable to send NetKey Add (err %d)\n", err);
+ return 0;
+ }
+
+ if (status) {
+ printk("NetKeyAdd failed with status 0x%02x\n", status);
+ } else {
+ printk("NetKey added with NetKey Index 0x%03x\n", key_net_idx);
+ }
+
+ return 0;
+}
+
+struct shell_cmd_help cmd_net_key_add_help = {
+ NULL, "<NetKeyIndex> [val]", NULL
+};
+
+static int cmd_app_key_add(int argc, char *argv[])
+{
+ u8_t key_val[16];
+ u16_t key_net_idx, key_app_idx;
+ u8_t status;
+ int err;
+
+ if (argc < 3) {
+ return -EINVAL;
+ }
+
+ key_net_idx = strtoul(argv[1], NULL, 0);
+ key_app_idx = strtoul(argv[2], NULL, 0);
+
+ if (argc > 3) {
+ size_t len;
+
+ len = hex2bin(argv[3], key_val, sizeof(key_val));
+ memset(key_val, 0, sizeof(key_val) - len);
+ } else {
+ memcpy(key_val, default_key, sizeof(key_val));
+ }
+
+ err = bt_mesh_cfg_app_key_add(net.net_idx, net.dst, key_net_idx,
+ key_app_idx, key_val, &status);
+ if (err) {
+ printk("Unable to send App Key Add (err %d)\n", err);
+ return 0;
+ }
+
+ if (status) {
+ printk("AppKeyAdd failed with status 0x%02x\n", status);
+ } else {
+ printk("AppKey added, NetKeyIndex 0x%04x AppKeyIndex 0x%04x\n",
+ key_net_idx, key_app_idx);
+ }
+
+ return 0;
+}
+
+struct shell_cmd_help cmd_app_key_add_help = {
+ NULL, "<NetKeyIndex> <AppKeyIndex> [val]", NULL
+};
+
+static int cmd_mod_app_bind(int argc, char *argv[])
+{
+ u16_t elem_addr, mod_app_idx, mod_id, cid;
+ u8_t status;
+ int err;
+
+ if (argc < 4) {
+ return -EINVAL;
+ }
+
+ elem_addr = strtoul(argv[1], NULL, 0);
+ mod_app_idx = strtoul(argv[2], NULL, 0);
+ mod_id = strtoul(argv[3], NULL, 0);
+
+ if (argc > 4) {
+ cid = strtoul(argv[4], NULL, 0);
+ err = bt_mesh_cfg_mod_app_bind_vnd(net.net_idx, net.dst,
+ elem_addr, mod_app_idx,
+ mod_id, cid, &status);
+ } else {
+ err = bt_mesh_cfg_mod_app_bind(net.net_idx, net.dst, elem_addr,
+ mod_app_idx, mod_id, &status);
+ }
+
+ if (err) {
+ printk("Unable to send Model App Bind (err %d)\n", err);
+ return 0;
+ }
+
+ if (status) {
+ printk("Model App Bind failed with status 0x%02x\n", status);
+ } else {
+ printk("AppKey successfully bound\n");
+ }
+
+ return 0;
+}
+
+struct shell_cmd_help cmd_mod_app_bind_help = {
+ NULL, "<addr> <AppIndex> <Model ID> [Company ID]", NULL
+};
+
+static int cmd_mod_sub_add(int argc, char *argv[])
+{
+ u16_t elem_addr, sub_addr, mod_id, cid;
+ u8_t status;
+ int err;
+
+ if (argc < 4) {
+ return -EINVAL;
+ }
+
+ elem_addr = strtoul(argv[1], NULL, 0);
+ sub_addr = strtoul(argv[2], NULL, 0);
+ mod_id = strtoul(argv[3], NULL, 0);
+
+ if (argc > 4) {
+ cid = strtoul(argv[4], NULL, 0);
+ err = bt_mesh_cfg_mod_sub_add_vnd(net.net_idx, net.dst,
+ elem_addr, sub_addr, mod_id,
+ cid, &status);
+ } else {
+ err = bt_mesh_cfg_mod_sub_add(net.net_idx, net.dst, elem_addr,
+ sub_addr, mod_id, &status);
+ }
+
+ if (err) {
+ printk("Unable to send Model Subscription Add (err %d)\n", err);
+ return 0;
+ }
+
+ if (status) {
+ printk("Model Subscription Add failed with status 0x%02x\n",
+ status);
+ } else {
+ printk("Model subscription was successful\n");
+ }
+
+ return 0;
+}
+
+struct shell_cmd_help cmd_mod_sub_add_help = {
+ NULL, "<elem addr> <sub addr> <Model ID> [Company ID]", NULL
+};
+
+static int cmd_mod_sub_del(int argc, char *argv[])
+{
+ u16_t elem_addr, sub_addr, mod_id, cid;
+ u8_t status;
+ int err;
+
+ if (argc < 4) {
+ return -EINVAL;
+ }
+
+ elem_addr = strtoul(argv[1], NULL, 0);
+ sub_addr = strtoul(argv[2], NULL, 0);
+ mod_id = strtoul(argv[3], NULL, 0);
+
+ if (argc > 4) {
+ cid = strtoul(argv[4], NULL, 0);
+ err = bt_mesh_cfg_mod_sub_del_vnd(net.net_idx, net.dst,
+ elem_addr, sub_addr, mod_id,
+ cid, &status);
+ } else {
+ err = bt_mesh_cfg_mod_sub_del(net.net_idx, net.dst, elem_addr,
+ sub_addr, mod_id, &status);
+ }
+
+ if (err) {
+ printk("Unable to send Model Subscription Delete (err %d)\n",
+ err);
+ return 0;
+ }
+
+ if (status) {
+ printk("Model Subscription Delete failed with status 0x%02x\n",
+ status);
+ } else {
+ printk("Model subscription deltion was successful\n");
+ }
+
+ return 0;
+}
+
+struct shell_cmd_help cmd_mod_sub_del_help = {
+ NULL, "<elem addr> <sub addr> <Model ID> [Company ID]", NULL
+};
+
+static int cmd_mod_sub_add_va(int argc, char *argv[])
+{
+ u16_t elem_addr, sub_addr, mod_id, cid;
+ u8_t label[16];
+ u8_t status;
+ size_t len;
+ int err;
+
+ if (argc < 4) {
+ return -EINVAL;
+ }
+
+ elem_addr = strtoul(argv[1], NULL, 0);
+
+ len = hex2bin(argv[2], label, sizeof(label));
+ memset(label + len, 0, sizeof(label) - len);
+
+ mod_id = strtoul(argv[3], NULL, 0);
+
+ if (argc > 4) {
+ cid = strtoul(argv[4], NULL, 0);
+ err = bt_mesh_cfg_mod_sub_va_add_vnd(net.net_idx, net.dst,
+ elem_addr, label, mod_id,
+ cid, &sub_addr, &status);
+ } else {
+ err = bt_mesh_cfg_mod_sub_va_add(net.net_idx, net.dst,
+ elem_addr, label, mod_id,
+ &sub_addr, &status);
+ }
+
+ if (err) {
+ printk("Unable to send Mod Sub VA Add (err %d)\n", err);
+ return 0;
+ }
+
+ if (status) {
+ printk("Mod Sub VA Add failed with status 0x%02x\n",
+ status);
+ } else {
+ printk("0x%04x subscribed to Label UUID %s (va 0x%04x)\n",
+ elem_addr, argv[2], sub_addr);
+ }
+
+ return 0;
+}
+
+struct shell_cmd_help cmd_mod_sub_add_va_help = {
+ NULL, "<elem addr> <Label UUID> <Model ID> [Company ID]", NULL
+};
+
+static int cmd_mod_sub_del_va(int argc, char *argv[])
+{
+ u16_t elem_addr, sub_addr, mod_id, cid;
+ u8_t label[16];
+ u8_t status;
+ size_t len;
+ int err;
+
+ if (argc < 4) {
+ return -EINVAL;
+ }
+
+ elem_addr = strtoul(argv[1], NULL, 0);
+
+ len = hex2bin(argv[2], label, sizeof(label));
+ memset(label + len, 0, sizeof(label) - len);
+
+ mod_id = strtoul(argv[3], NULL, 0);
+
+ if (argc > 4) {
+ cid = strtoul(argv[4], NULL, 0);
+ err = bt_mesh_cfg_mod_sub_va_del_vnd(net.net_idx, net.dst,
+ elem_addr, label, mod_id,
+ cid, &sub_addr, &status);
+ } else {
+ err = bt_mesh_cfg_mod_sub_va_del(net.net_idx, net.dst,
+ elem_addr, label, mod_id,
+ &sub_addr, &status);
+ }
+
+ if (err) {
+ printk("Unable to send Model Subscription Delete (err %d)\n",
+ err);
+ return 0;
+ }
+
+ if (status) {
+ printk("Model Subscription Delete failed with status 0x%02x\n",
+ status);
+ } else {
+ printk("0x%04x unsubscribed from Label UUID %s (va 0x%04x)\n",
+ elem_addr, argv[2], sub_addr);
+ }
+
+ return 0;
+}
+
+struct shell_cmd_help cmd_mod_sub_del_va_help = {
+ NULL, "<elem addr> <Label UUID> <Model ID> [Company ID]", NULL
+};
+
+static int mod_pub_get(u16_t addr, u16_t mod_id, u16_t cid)
+{
+ struct bt_mesh_cfg_mod_pub pub;
+ u8_t status;
+ int err;
+
+ if (cid == CID_NVAL) {
+ err = bt_mesh_cfg_mod_pub_get(net.net_idx, net.dst, addr,
+ mod_id, &pub, &status);
+ } else {
+ err = bt_mesh_cfg_mod_pub_get_vnd(net.net_idx, net.dst, addr,
+ mod_id, cid, &pub, &status);
+ }
+
+ if (err) {
+ printk("Model Publication Get failed (err %d)\n", err);
+ return 0;
+ }
+
+ if (status) {
+ printk("Model Publication Get failed (status 0x%02x)\n",
+ status);
+ return 0;
+ }
+
+ printk("Model Publication for Element 0x%04x, Model 0x%04x:\n"
+ "\tPublish Address: 0x%04x\n"
+ "\tAppKeyIndex: 0x%04x\n"
+ "\tCredential Flag: %u\n"
+ "\tPublishTTL: %u\n"
+ "\tPublishPeriod: 0x%02x\n"
+ "\tPublishRetransmitCount: %u\n"
+ "\tPublishRetransmitInterval: %ums\n",
+ addr, mod_id, pub.addr, pub.app_idx, pub.cred_flag, pub.ttl,
+ pub.period, BT_MESH_PUB_TRANSMIT_COUNT(pub.transmit),
+ BT_MESH_PUB_TRANSMIT_INT(pub.transmit));
+
+ return 0;
+}
+
+static int mod_pub_set(u16_t addr, u16_t mod_id, u16_t cid, char *argv[])
+{
+ struct bt_mesh_cfg_mod_pub pub;
+ u8_t status, count;
+ u16_t interval;
+ int err;
+
+ pub.addr = strtoul(argv[0], NULL, 0);
+ pub.app_idx = strtoul(argv[1], NULL, 0);
+ pub.cred_flag = str2bool(argv[2]);
+ pub.ttl = strtoul(argv[3], NULL, 0);
+ pub.period = strtoul(argv[4], NULL, 0);
+
+ count = strtoul(argv[5], NULL, 0);
+ if (count > 7) {
+ printk("Invalid retransmit count\n");
+ return -EINVAL;
+ }
+
+ interval = strtoul(argv[6], NULL, 0);
+ if (interval > (31 * 50) || (interval % 50)) {
+ printk("Invalid retransmit interval %u\n", interval);
+ return -EINVAL;
+ }
+
+ pub.transmit = BT_MESH_PUB_TRANSMIT(count, interval);
+
+ if (cid == CID_NVAL) {
+ err = bt_mesh_cfg_mod_pub_set(net.net_idx, net.dst, addr,
+ mod_id, &pub, &status);
+ } else {
+ err = bt_mesh_cfg_mod_pub_set_vnd(net.net_idx, net.dst, addr,
+ mod_id, cid, &pub, &status);
+ }
+
+ if (err) {
+ printk("Model Publication Set failed (err %d)\n", err);
+ return 0;
+ }
+
+ if (status) {
+ printk("Model Publication Set failed (status 0x%02x)\n",
+ status);
+ } else {
+ printk("Model Publication successfully set\n");
+ }
+
+ return 0;
+}
+
+static int cmd_mod_pub(int argc, char *argv[])
+{
+ u16_t addr, mod_id, cid;
+
+ if (argc < 3) {
+ return -EINVAL;
+ }
+
+ addr = strtoul(argv[1], NULL, 0);
+ mod_id = strtoul(argv[2], NULL, 0);
+
+ argc -= 3;
+ argv += 3;
+
+ if (argc == 1 || argc == 8) {
+ cid = strtoul(argv[0], NULL, 0);
+ argc--;
+ argv++;
+ } else {
+ cid = CID_NVAL;
+ }
+
+ if (argc > 0) {
+ if (argc < 7) {
+ return -EINVAL;
+ }
+
+ return mod_pub_set(addr, mod_id, cid, argv);
+ } else {
+ return mod_pub_get(addr, mod_id, cid);
+ }
+}
+
+struct shell_cmd_help cmd_mod_pub_help = {
+ NULL, "<addr> <mod id> [cid] [<PubAddr> "
+ "<AppKeyIndex> <cred> <ttl> <period> <count> <interval>]" , NULL
+};
+
+static void hb_sub_print(struct bt_mesh_cfg_hb_sub *sub)
+{
+ printk("Heartbeat Subscription:\n"
+ "\tSource: 0x%04x\n"
+ "\tDestination: 0x%04x\n"
+ "\tPeriodLog: 0x%02x\n"
+ "\tCountLog: 0x%02x\n"
+ "\tMinHops: %u\n"
+ "\tMaxHops: %u\n",
+ sub->src, sub->dst, sub->period, sub->count,
+ sub->min, sub->max);
+}
+
+static int hb_sub_get(int argc, char *argv[])
+{
+ struct bt_mesh_cfg_hb_sub sub;
+ u8_t status;
+ int err;
+
+ err = bt_mesh_cfg_hb_sub_get(net.net_idx, net.dst, &sub, &status);
+ if (err) {
+ printk("Heartbeat Subscription Get failed (err %d)\n", err);
+ return 0;
+ }
+
+ if (status) {
+ printk("Heartbeat Subscription Get failed (status 0x%02x)\n",
+ status);
+ } else {
+ hb_sub_print(&sub);
+ }
+
+ return 0;
+}
+
+static int hb_sub_set(int argc, char *argv[])
+{
+ struct bt_mesh_cfg_hb_sub sub;
+ u8_t status;
+ int err;
+
+ sub.src = strtoul(argv[1], NULL, 0);
+ sub.dst = strtoul(argv[2], NULL, 0);
+ sub.period = strtoul(argv[3], NULL, 0);
+
+ err = bt_mesh_cfg_hb_sub_set(net.net_idx, net.dst, &sub, &status);
+ if (err) {
+ printk("Heartbeat Subscription Set failed (err %d)\n", err);
+ return 0;
+ }
+
+ if (status) {
+ printk("Heartbeat Subscription Set failed (status 0x%02x)\n",
+ status);
+ } else {
+ hb_sub_print(&sub);
+ }
+
+ return 0;
+}
+
+static int cmd_hb_sub(int argc, char *argv[])
+{
+ if (argc > 1) {
+ if (argc < 4) {
+ return -EINVAL;
+ }
+
+ return hb_sub_set(argc, argv);
+ } else {
+ return hb_sub_get(argc, argv);
+ }
+}
+
+struct shell_cmd_help cmd_hb_sub_help = {
+ NULL, "<src> <dst> <period>", NULL
+};
+
+static int hb_pub_get(int argc, char *argv[])
+{
+ struct bt_mesh_cfg_hb_pub pub;
+ u8_t status;
+ int err;
+
+ err = bt_mesh_cfg_hb_pub_get(net.net_idx, net.dst, &pub, &status);
+ if (err) {
+ printk("Heartbeat Publication Get failed (err %d)\n", err);
+ return 0;
+ }
+
+ if (status) {
+ printk("Heartbeat Publication Get failed (status 0x%02x)\n",
+ status);
+ return 0;
+ }
+
+ printk("Heartbeat publication:\n");
+ printk("\tdst 0x%04x count 0x%02x period 0x%02x\n",
+ pub.dst, pub.count, pub.period);
+ printk("\tttl 0x%02x feat 0x%04x net_idx 0x%04x\n",
+ pub.ttl, pub.feat, pub.net_idx);
+
+ return 0;
+}
+
+static int hb_pub_set(int argc, char *argv[])
+{
+ struct bt_mesh_cfg_hb_pub pub;
+ u8_t status;
+ int err;
+
+ pub.dst = strtoul(argv[1], NULL, 0);
+ pub.count = strtoul(argv[2], NULL, 0);
+ pub.period = strtoul(argv[3], NULL, 0);
+ pub.ttl = strtoul(argv[4], NULL, 0);
+ pub.feat = strtoul(argv[5], NULL, 0);
+ pub.net_idx = strtoul(argv[5], NULL, 0);
+
+ err = bt_mesh_cfg_hb_pub_set(net.net_idx, net.dst, &pub, &status);
+ if (err) {
+ printk("Heartbeat Publication Set failed (err %d)\n", err);
+ return 0;
+ }
+
+ if (status) {
+ printk("Heartbeat Publication Set failed (status 0x%02x)\n",
+ status);
+ } else {
+ printk("Heartbeat publication successfully set\n");
+ }
+
+ return 0;
+}
+
+static int cmd_hb_pub(int argc, char *argv[])
+{
+ if (argc > 1) {
+ if (argc < 7) {
+ return -EINVAL;
+ }
+
+ return hb_pub_set(argc, argv);
+ } else {
+ return hb_pub_get(argc, argv);
+ }
+}
+
+struct shell_cmd_help cmd_hb_pub_help = {
+ NULL, "<dst> <count> <period> <ttl> <features> <NetKeyIndex>" , NULL
+};
+
+#endif /* MYNEWT_VAL(BLE_MESH_CFG_CLI) */
+
+#if MYNEWT_VAL(BLE_MESH_PROV)
+static int cmd_pb(bt_mesh_prov_bearer_t bearer, int argc, char *argv[])
+{
+ int err;
+
+ if (argc < 2) {
+ return -EINVAL;
+ }
+
+ if (str2bool(argv[1])) {
+ err = bt_mesh_prov_enable(bearer);
+ if (err) {
+ printk("Failed to enable %s (err %d)\n",
+ bearer2str(bearer), err);
+ } else {
+ printk("%s enabled\n", bearer2str(bearer));
+ }
+ } else {
+ err = bt_mesh_prov_disable(bearer);
+ if (err) {
+ printk("Failed to disable %s (err %d)\n",
+ bearer2str(bearer), err);
+ } else {
+ printk("%s disabled\n", bearer2str(bearer));
+ }
+ }
+
+ return 0;
+
+}
+
+struct shell_cmd_help cmd_pb_help = {
+ NULL, "<val: off, on>", NULL
+};
+
+#endif
+
+#if MYNEWT_VAL(BLE_MESH_PB_ADV)
+static int cmd_pb_adv(int argc, char *argv[])
+{
+ return cmd_pb(BT_MESH_PROV_ADV, argc, argv);
+}
+
+#if MYNEWT_VAL(BLE_MESH_PROVISIONER)
+static int cmd_provision_adv(int argc, char *argv[])
+{
+ u8_t uuid[16];
+ u8_t attention_duration;
+ u16_t net_idx;
+ u16_t addr;
+ size_t len;
+ int err;
+
+ len = hex2bin(argv[1], uuid, sizeof(uuid));
+ (void)memset(uuid + len, 0, sizeof(uuid) - len);
+
+ net_idx = strtoul(argv[2], NULL, 0);
+ addr = strtoul(argv[3], NULL, 0);
+ attention_duration = strtoul(argv[4], NULL, 0);
+
+ err = bt_mesh_provision_adv(uuid, net_idx, addr, attention_duration);
+ if (err) {
+ printk("Provisioning failed (err %d)", err);
+ }
+
+ return 0;
+}
+
+struct shell_cmd_help cmd_provision_adv_help = {
+ NULL, "<UUID> <NetKeyIndex> <addr> <AttentionDuration>" , NULL
+};
+#endif /* CONFIG_BT_MESH_PROVISIONER */
+
+#endif /* CONFIG_BT_MESH_PB_ADV */
+
+#if MYNEWT_VAL(BLE_MESH_PB_GATT)
+static int cmd_pb_gatt(int argc, char *argv[])
+{
+ return cmd_pb(BT_MESH_PROV_GATT, argc, argv);
+}
+#endif /* CONFIG_BT_MESH_PB_GATT */
+
+static int cmd_provision(int argc, char *argv[])
+{
+ u16_t net_idx, addr;
+ u32_t iv_index;
+ int err;
+
+ if (argc < 3) {
+ return -EINVAL;
+ }
+
+ net_idx = strtoul(argv[1], NULL, 0);
+ addr = strtoul(argv[2], NULL, 0);
+
+ if (argc > 3) {
+ iv_index = strtoul(argv[3], NULL, 0);
+ } else {
+ iv_index = 0;
+ }
+
+ err = bt_mesh_provision(default_key, net_idx, 0, iv_index, addr,
+ default_key);
+ if (err) {
+ printk("Provisioning failed (err %d)\n", err);
+ }
+
+ return 0;
+}
+
+struct shell_cmd_help cmd_provision_help = {
+ NULL, "<NetKeyIndex> <addr> [IVIndex]" , NULL
+};
+
+#if MYNEWT_VAL(BLE_MESH_HEALTH_CLI)
+
+static int cmd_fault_get(int argc, char *argv[])
+{
+ u8_t faults[32];
+ size_t fault_count;
+ u8_t test_id;
+ u16_t cid;
+ int err;
+
+ if (argc < 2) {
+ return -EINVAL;
+ }
+
+ cid = strtoul(argv[1], NULL, 0);
+ fault_count = sizeof(faults);
+
+ err = bt_mesh_health_fault_get(net.net_idx, net.dst, net.app_idx, cid,
+ &test_id, faults, &fault_count);
+ if (err) {
+ printk("Failed to send Health Fault Get (err %d)\n", err);
+ } else {
+ show_faults(test_id, cid, faults, fault_count);
+ }
+
+ return 0;
+}
+
+struct shell_cmd_help cmd_fault_get_help = {
+ NULL, "<Company ID>", NULL
+};
+
+static int cmd_fault_clear(int argc, char *argv[])
+{
+ u8_t faults[32];
+ size_t fault_count;
+ u8_t test_id;
+ u16_t cid;
+ int err;
+
+ if (argc < 2) {
+ return -EINVAL;
+ }
+
+ cid = strtoul(argv[1], NULL, 0);
+ fault_count = sizeof(faults);
+
+ err = bt_mesh_health_fault_clear(net.net_idx, net.dst, net.app_idx,
+ cid, &test_id, faults, &fault_count);
+ if (err) {
+ printk("Failed to send Health Fault Clear (err %d)\n", err);
+ } else {
+ show_faults(test_id, cid, faults, fault_count);
+ }
+
+ return 0;
+}
+
+struct shell_cmd_help cmd_fault_clear_help = {
+ NULL, "<Company ID>", NULL
+};
+
+static int cmd_fault_clear_unack(int argc, char *argv[])
+{
+ u16_t cid;
+ int err;
+
+ if (argc < 2) {
+ return -EINVAL;
+ }
+
+ cid = strtoul(argv[1], NULL, 0);
+
+ err = bt_mesh_health_fault_clear(net.net_idx, net.dst, net.app_idx,
+ cid, NULL, NULL, NULL);
+ if (err) {
+ printk("Health Fault Clear Unacknowledged failed (err %d)\n",
+ err);
+ }
+
+ return 0;
+}
+
+struct shell_cmd_help cmd_fault_clear_unack_help = {
+ NULL, "<Company ID>", NULL
+};
+
+static int cmd_fault_test(int argc, char *argv[])
+{
+ u8_t faults[32];
+ size_t fault_count;
+ u8_t test_id;
+ u16_t cid;
+ int err;
+
+ if (argc < 3) {
+ return -EINVAL;
+ }
+
+ cid = strtoul(argv[1], NULL, 0);
+ test_id = strtoul(argv[2], NULL, 0);
+ fault_count = sizeof(faults);
+
+ err = bt_mesh_health_fault_test(net.net_idx, net.dst, net.app_idx,
+ cid, test_id, faults, &fault_count);
+ if (err) {
+ printk("Failed to send Health Fault Test (err %d)\n", err);
+ } else {
+ show_faults(test_id, cid, faults, fault_count);
+ }
+
+ return 0;
+}
+
+struct shell_cmd_help cmd_fault_test_help = {
+ NULL, "<Company ID> <Test ID>", NULL
+};
+
+static int cmd_fault_test_unack(int argc, char *argv[])
+{
+ u16_t cid;
+ u8_t test_id;
+ int err;
+
+ if (argc < 3) {
+ return -EINVAL;
+ }
+
+ cid = strtoul(argv[1], NULL, 0);
+ test_id = strtoul(argv[2], NULL, 0);
+
+ err = bt_mesh_health_fault_test(net.net_idx, net.dst, net.app_idx,
+ cid, test_id, NULL, NULL);
+ if (err) {
+ printk("Health Fault Test Unacknowledged failed (err %d)\n",
+ err);
+ }
+
+ return 0;
+}
+
+struct shell_cmd_help cmd_fault_test_unack_help = {
+ NULL, "<Company ID> <Test ID>", NULL
+};
+
+static int cmd_period_get(int argc, char *argv[])
+{
+ u8_t divisor;
+ int err;
+
+ err = bt_mesh_health_period_get(net.net_idx, net.dst, net.app_idx,
+ &divisor);
+ if (err) {
+ printk("Failed to send Health Period Get (err %d)\n", err);
+ } else {
+ printk("Health FastPeriodDivisor: %u\n", divisor);
+ }
+
+ return 0;
+}
+
+static int cmd_period_set(int argc, char *argv[])
+{
+ u8_t divisor, updated_divisor;
+ int err;
+
+ if (argc < 2) {
+ return -EINVAL;
+ }
+
+ divisor = strtoul(argv[1], NULL, 0);
+
+ err = bt_mesh_health_period_set(net.net_idx, net.dst, net.app_idx,
+ divisor, &updated_divisor);
+ if (err) {
+ printk("Failed to send Health Period Set (err %d)\n", err);
+ } else {
+ printk("Health FastPeriodDivisor: %u\n", updated_divisor);
+ }
+
+ return 0;
+}
+
+struct shell_cmd_help cmd_period_set_help = {
+ NULL, "<divisor>", NULL
+};
+
+static int cmd_period_set_unack(int argc, char *argv[])
+{
+ u8_t divisor;
+ int err;
+
+ if (argc < 2) {
+ return -EINVAL;
+ }
+
+ divisor = strtoul(argv[1], NULL, 0);
+
+ err = bt_mesh_health_period_set(net.net_idx, net.dst, net.app_idx,
+ divisor, NULL);
+ if (err) {
+ printk("Failed to send Health Period Set (err %d)\n", err);
+ }
+
+ return 0;
+}
+
+struct shell_cmd_help cmd_period_set_unack_help = {
+ NULL, "<divisor>", NULL
+};
+
+static int cmd_attention_get(int argc, char *argv[])
+{
+ u8_t attention;
+ int err;
+
+ err = bt_mesh_health_attention_get(net.net_idx, net.dst, net.app_idx,
+ &attention);
+ if (err) {
+ printk("Failed to send Health Attention Get (err %d)\n", err);
+ } else {
+ printk("Health Attention Timer: %u\n", attention);
+ }
+
+ return 0;
+}
+
+static int cmd_attention_set(int argc, char *argv[])
+{
+ u8_t attention, updated_attention;
+ int err;
+
+ if (argc < 2) {
+ return -EINVAL;
+ }
+
+ attention = strtoul(argv[1], NULL, 0);
+
+ err = bt_mesh_health_attention_set(net.net_idx, net.dst, net.app_idx,
+ attention, &updated_attention);
+ if (err) {
+ printk("Failed to send Health Attention Set (err %d)\n", err);
+ } else {
+ printk("Health Attention Timer: %u\n", updated_attention);
+ }
+
+ return 0;
+}
+
+struct shell_cmd_help cmd_attention_set_help = {
+ NULL, "<timer>", NULL
+};
+
+static int cmd_attention_set_unack(int argc, char *argv[])
+{
+ u8_t attention;
+ int err;
+
+ if (argc < 2) {
+ return -EINVAL;
+ }
+
+ attention = strtoul(argv[1], NULL, 0);
+
+ err = bt_mesh_health_attention_set(net.net_idx, net.dst, net.app_idx,
+ attention, NULL);
+ if (err) {
+ printk("Failed to send Health Attention Set (err %d)\n", err);
+ }
+
+ return 0;
+}
+
+struct shell_cmd_help cmd_attention_set_unack_help = {
+ NULL, "<timer>", NULL
+};
+
+#endif /* MYNEWT_VAL(BLE_MESH_HEALTH_CLI) */
+
+static int cmd_add_fault(int argc, char *argv[])
+{
+ u8_t fault_id;
+ u8_t i;
+
+ if (argc < 2) {
+ return -EINVAL;
+ }
+
+ fault_id = strtoul(argv[1], NULL, 0);
+ if (!fault_id) {
+ printk("The Fault ID must be non-zero!\n");
+ return -EINVAL;
+ }
+
+ for (i = 0; i < sizeof(cur_faults); i++) {
+ if (!cur_faults[i]) {
+ cur_faults[i] = fault_id;
+ break;
+ }
+ }
+
+ if (i == sizeof(cur_faults)) {
+ printk("Fault array is full. Use \"del-fault\" to clear it\n");
+ return 0;
+ }
+
+ for (i = 0; i < sizeof(reg_faults); i++) {
+ if (!reg_faults[i]) {
+ reg_faults[i] = fault_id;
+ break;
+ }
+ }
+
+ if (i == sizeof(reg_faults)) {
+ printk("No space to store more registered faults\n");
+ }
+
+ bt_mesh_fault_update(&elements[0]);
+
+ return 0;
+}
+
+struct shell_cmd_help cmd_add_fault_help = {
+ NULL, "<Fault ID>", NULL
+};
+
+static int cmd_del_fault(int argc, char *argv[])
+{
+ u8_t fault_id;
+ u8_t i;
+
+ if (argc < 2) {
+ memset(cur_faults, 0, sizeof(cur_faults));
+ printk("All current faults cleared\n");
+ bt_mesh_fault_update(&elements[0]);
+ return 0;
+ }
+
+ fault_id = strtoul(argv[1], NULL, 0);
+ if (!fault_id) {
+ printk("The Fault ID must be non-zero!\n");
+ return -EINVAL;
+ }
+
+ for (i = 0; i < sizeof(cur_faults); i++) {
+ if (cur_faults[i] == fault_id) {
+ cur_faults[i] = 0;
+ printk("Fault cleared\n");
+ }
+ }
+
+ bt_mesh_fault_update(&elements[0]);
+
+ return 0;
+}
+
+struct shell_cmd_help cmd_del_fault_help = {
+ NULL, "[Fault ID]", NULL
+};
+
+#if MYNEWT_VAL(BLE_MESH_SHELL_MODELS)
+static int cmd_gen_onoff_get(int argc, char *argv[])
+{
+ u8_t state;
+ int err;
+
+ err = bt_mesh_gen_onoff_get(net.net_idx, net.dst, net.app_idx,
+ &state);
+ if (err) {
+ printk("Failed to send Generic OnOff Get (err %d)\n", err);
+ } else {
+ printk("Gen OnOff State %d\n", state);
+ }
+
+ return 0;
+}
+
+static int cmd_gen_onoff_set(int argc, char *argv[])
+{
+ u8_t state;
+ u8_t val;
+ int err;
+
+ if (argc < 2) {
+ return -EINVAL;
+ }
+
+ val = strtoul(argv[1], NULL, 0);
+
+ err = bt_mesh_gen_onoff_set(net.net_idx, net.dst, net.app_idx,
+ val, &state);
+ if (err) {
+ printk("Failed to send Generic OnOff Get (err %d)\n", err);
+ } else {
+ printk("Gen OnOff State %d\n", state);
+ }
+
+ return 0;
+}
+
+struct shell_cmd_help cmd_gen_onoff_set_help = {
+ NULL, "<0|1>", NULL
+};
+
+static int cmd_gen_onoff_set_unack(int argc, char *argv[])
+{
+ u8_t val;
+ int err;
+
+ if (argc < 2) {
+ return -EINVAL;
+ }
+
+ val = strtoul(argv[1], NULL, 0);
+
+ err = bt_mesh_gen_onoff_set(net.net_idx, net.dst, net.app_idx,
+ val, NULL);
+ if (err) {
+ printk("Failed to send Generic OnOff Get (err %d)\n", err);
+ }
+
+ return 0;
+}
+
+struct shell_cmd_help cmd_gen_onoff_set_unack_help = {
+ NULL, "<0|1>", NULL
+};
+
+static int cmd_gen_level_get(int argc, char *argv[])
+{
+ s16_t state;
+ int err;
+
+ err = bt_mesh_gen_level_get(net.net_idx, net.dst, net.app_idx,
+ &state);
+ if (err) {
+ printk("Failed to send Generic Level Get (err %d)\n", err);
+ } else {
+ printk("Gen Level State %d\n", state);
+ }
+
+ return 0;
+}
+
+static int cmd_gen_level_set(int argc, char *argv[])
+{
+ s16_t state;
+ s16_t val;
+ int err;
+
+ if (argc < 2) {
+ return -EINVAL;
+ }
+
+ val = (s16_t)strtoul(argv[1], NULL, 0);
+
+ err = bt_mesh_gen_level_set(net.net_idx, net.dst, net.app_idx,
+ val, &state);
+ if (err) {
+ printk("Failed to send Generic Level Get (err %d)\n", err);
+ } else {
+ printk("Gen Level State %d\n", state);
+ }
+
+ return 0;
+}
+
+struct shell_cmd_help cmd_gen_level_set_help = {
+ NULL, "<level>", NULL
+};
+
+static int cmd_gen_level_set_unack(int argc, char *argv[])
+{
+ s16_t val;
+ int err;
+
+ if (argc < 2) {
+ return -EINVAL;
+ }
+
+ val = (s16_t)strtoul(argv[1], NULL, 0);
+
+ err = bt_mesh_gen_level_set(net.net_idx, net.dst, net.app_idx,
+ val, NULL);
+ if (err) {
+ printk("Failed to send Generic Level Get (err %d)\n", err);
+ }
+
+ return 0;
+}
+
+struct shell_cmd_help cmd_gen_level_set_unack_help = {
+ NULL, "<level>", NULL
+};
+
+#endif /* MYNEWT_VAL(BLE_MESH_SHELL_MODELS) */
+
+static int cmd_print_credentials(int argc, char *argv[])
+{
+ bt_test_print_credentials();
+ return 0;
+}
+
+static void print_comp_elem(struct bt_mesh_elem *elem,
+ bool primary)
+{
+ struct bt_mesh_model *mod;
+ int i;
+
+ printk("Loc: %u\n", elem->loc);
+ printk("Model count: %u\n", elem->model_count);
+ printk("Vnd model count: %u\n", elem->vnd_model_count);
+
+ for (i = 0; i < elem->model_count; i++) {
+ mod = &elem->models[i];
+ printk(" Model: %u\n", i);
+ printk(" ID: 0x%04x\n", mod->id);
+ printk(" Opcode: 0x%08lx\n", mod->op->opcode);
+ }
+
+ for (i = 0; i < elem->vnd_model_count; i++) {
+ mod = &elem->vnd_models[i];
+ printk(" Vendor model: %u\n", i);
+ printk(" Company: 0x%04x\n", mod->vnd.company);
+ printk(" ID: 0x%04x\n", mod->vnd.id);
+ printk(" Opcode: 0x%08lx\n", mod->op->opcode);
+ }
+}
+
+static int cmd_print_composition_data(int argc, char *argv[])
+{
+ const struct bt_mesh_comp *comp;
+ int i;
+
+ comp = bt_mesh_comp_get();
+
+ printk("CID: %u\n", comp->cid);
+ printk("PID: %u\n", comp->pid);
+ printk("VID: %u\n", comp->vid);
+
+ for (i = 0; i < comp->elem_count; i++) {
+ print_comp_elem(&comp->elem[i], i == 0);
+ }
+
+ return 0;
+}
+
+
+static const struct shell_cmd mesh_commands[] = {
+ {
+ .sc_cmd = "init",
+ .sc_cmd_func = cmd_mesh_init,
+ .help = NULL,
+ },
+#if MYNEWT_VAL(BLE_MESH_PB_ADV)
+ {
+ .sc_cmd = "pb-adv",
+ .sc_cmd_func = cmd_pb_adv,
+ .help = &cmd_pb_help,
+ },
+#if MYNEWT_VAL(BLE_MESH_PROVISIONER)
+ {
+ .sc_cmd = "provision-adv",
+ .sc_cmd_func = cmd_provision_adv,
+ .help = &cmd_provision_adv_help,
+ },
+#endif
+#endif
+#if MYNEWT_VAL(BLE_MESH_PB_GATT)
+ {
+ .sc_cmd = "pb-gatt",
+ .sc_cmd_func = cmd_pb_gatt,
+ .help = &cmd_pb_help,
+ },
+#endif
+ {
+ .sc_cmd = "reset",
+ .sc_cmd_func = cmd_reset,
+ .help = NULL,
+ },
+ {
+ .sc_cmd = "uuid",
+ .sc_cmd_func = cmd_uuid,
+ .help = &cmd_uuid_help,
+ },
+ {
+ .sc_cmd = "input-num",
+ .sc_cmd_func = cmd_input_num,
+ .help = &cmd_input_num_help,
+ },
+ {
+ .sc_cmd = "input-str",
+ .sc_cmd_func = cmd_input_str,
+ .help = &cmd_input_str_help,
+ },
+ {
+ .sc_cmd = "static-oob",
+ .sc_cmd_func = cmd_static_oob,
+ .help = &cmd_static_oob_help,
+ },
+ {
+ .sc_cmd = "provision",
+ .sc_cmd_func = cmd_provision,
+ .help = &cmd_provision_help,
+ },
+#if MYNEWT_VAL(BLE_MESH_LOW_POWER)
+ {
+ .sc_cmd = "lpn",
+ .sc_cmd_func = cmd_lpn,
+ .help = &cmd_lpn_help,
+ },
+ {
+ .sc_cmd = "poll",
+ .sc_cmd_func = cmd_poll,
+ .help = NULL,
+ },
+#endif
+#if MYNEWT_VAL(BLE_MESH_GATT_PROXY)
+ {
+ .sc_cmd = "ident",
+ .sc_cmd_func = cmd_ident,
+ .help = NULL,
+ },
+#endif
+ {
+ .sc_cmd = "dst",
+ .sc_cmd_func = cmd_dst,
+ .help = &cmd_dst_help,
+ },
+ {
+ .sc_cmd = "netidx",
+ .sc_cmd_func = cmd_netidx,
+ .help = &cmd_netidx_help,
+ },
+ {
+ .sc_cmd = "appidx",
+ .sc_cmd_func = cmd_appidx,
+ .help = &cmd_appidx_help,
+ },
+
+ /* Commands which access internal APIs, for testing only */
+ {
+ .sc_cmd = "net-send",
+ .sc_cmd_func = cmd_net_send,
+ .help = &cmd_net_send_help,
+ },
+#if MYNEWT_VAL(BLE_MESH_IV_UPDATE_TEST)
+ {
+ .sc_cmd = "iv-update",
+ .sc_cmd_func = cmd_iv_update,
+ .help = NULL,
+ },
+ {
+ .sc_cmd = "iv-update-test",
+ .sc_cmd_func = cmd_iv_update_test,
+ .help = &cmd_iv_update_test_help,
+ },
+#endif
+ {
+ .sc_cmd = "rpl-clear",
+ .sc_cmd_func = cmd_rpl_clear,
+ .help = NULL,
+ },
+#if MYNEWT_VAL(BLE_MESH_LOW_POWER)
+ {
+ .sc_cmd = "lpn-subscribe",
+ .sc_cmd_func = cmd_lpn_subscribe,
+ .help = &cmd_lpn_subscribe_help,
+ },
+ {
+ .sc_cmd = "lpn-unsubscribe",
+ .sc_cmd_func = cmd_lpn_unsubscribe,
+ .help = &cmd_lpn_unsubscribe_help,
+ },
+#endif
+ {
+ .sc_cmd = "print-credentials",
+ .sc_cmd_func = cmd_print_credentials,
+ .help = NULL,
+ },
+ {
+ .sc_cmd = "print-composition-data",
+ .sc_cmd_func = cmd_print_composition_data,
+ .help = NULL,
+ },
+
+
+#if MYNEWT_VAL(BLE_MESH_CFG_CLI)
+ /* Configuration Client Model operations */
+ {
+ .sc_cmd = "timeout",
+ .sc_cmd_func = cmd_timeout,
+ .help = &cmd_timeout_help,
+ },
+ {
+ .sc_cmd = "get-comp",
+ .sc_cmd_func = cmd_get_comp,
+ .help = &cmd_get_comp_help,
+ },
+ {
+ .sc_cmd = "beacon",
+ .sc_cmd_func = cmd_beacon,
+ .help = &cmd_beacon_help,
+ },
+ {
+ .sc_cmd = "ttl",
+ .sc_cmd_func = cmd_ttl,
+ .help = &cmd_ttl_help,
+ },
+ {
+ .sc_cmd = "friend",
+ .sc_cmd_func = cmd_friend,
+ .help = &cmd_friend_help,
+ },
+ {
+ .sc_cmd = "gatt-proxy",
+ .sc_cmd_func = cmd_gatt_proxy,
+ .help = &cmd_gatt_proxy_help,
+ },
+ {
+ .sc_cmd = "relay",
+ .sc_cmd_func = cmd_relay,
+ .help = &cmd_relay_help,
+ },
+ {
+ .sc_cmd = "net-key-add",
+ .sc_cmd_func = cmd_net_key_add,
+ .help = &cmd_net_key_add_help,
+ },
+ {
+ .sc_cmd = "app-key-add",
+ .sc_cmd_func = cmd_app_key_add,
+ .help = &cmd_app_key_add_help,
+ },
+ {
+ .sc_cmd = "mod-app-bind",
+ .sc_cmd_func = cmd_mod_app_bind,
+ .help = &cmd_mod_app_bind_help,
+ },
+ {
+ .sc_cmd = "mod-pub",
+ .sc_cmd_func = cmd_mod_pub,
+ .help = &cmd_mod_pub_help,
+ },
+ {
+ .sc_cmd = "mod-sub-add",
+ .sc_cmd_func = cmd_mod_sub_add,
+ .help = &cmd_mod_sub_add_help,
+ },
+ {
+ .sc_cmd = "mod-sub-del",
+ .sc_cmd_func = cmd_mod_sub_del,
+ .help = &cmd_mod_sub_del_help,
+ },
+ {
+ .sc_cmd = "mod-sub-add-va",
+ .sc_cmd_func = cmd_mod_sub_add_va,
+ .help = &cmd_mod_sub_add_va_help,
+ },
+ {
+ .sc_cmd = "mod-sub-del-va",
+ .sc_cmd_func = cmd_mod_sub_del_va,
+ .help = &cmd_mod_sub_del_va_help,
+ },
+ {
+ .sc_cmd = "hb-sub",
+ .sc_cmd_func = cmd_hb_sub,
+ .help = &cmd_hb_sub_help,
+ },
+ {
+ .sc_cmd = "hb-pub",
+ .sc_cmd_func = cmd_hb_pub,
+ .help = &cmd_hb_pub_help,
+ },
+#endif
+
+#if MYNEWT_VAL(BLE_MESH_HEALTH_CLI)
+ /* Health Client Model Operations */
+ {
+ .sc_cmd = "fault-get",
+ .sc_cmd_func = cmd_fault_get,
+ .help = &cmd_fault_get_help,
+ },
+ {
+ .sc_cmd = "fault-clear",
+ .sc_cmd_func = cmd_fault_clear,
+ .help = &cmd_fault_clear_help,
+ },
+ {
+ .sc_cmd = "fault-clear-unack",
+ .sc_cmd_func = cmd_fault_clear_unack,
+ .help = &cmd_fault_clear_unack_help,
+ },
+ {
+ .sc_cmd = "fault-test",
+ .sc_cmd_func = cmd_fault_test,
+ .help = &cmd_fault_test_help,
+ },
+ {
+ .sc_cmd = "fault-test-unack",
+ .sc_cmd_func = cmd_fault_test_unack,
+ .help = &cmd_fault_test_unack_help,
+ },
+ {
+ .sc_cmd = "period-get",
+ .sc_cmd_func = cmd_period_get,
+ .help = NULL,
+ },
+ {
+ .sc_cmd = "period-set",
+ .sc_cmd_func = cmd_period_set,
+ .help = &cmd_period_set_help,
+ },
+ {
+ .sc_cmd = "period-set-unack",
+ .sc_cmd_func = cmd_period_set_unack,
+ .help = &cmd_period_set_unack_help,
+ },
+ {
+ .sc_cmd = "attention-get",
+ .sc_cmd_func = cmd_attention_get,
+ .help = NULL,
+ },
+ {
+ .sc_cmd = "attention-set",
+ .sc_cmd_func = cmd_attention_set,
+ .help = &cmd_attention_set_help,
+ },
+ {
+ .sc_cmd = "attention-set-unack",
+ .sc_cmd_func = cmd_attention_set_unack,
+ .help = &cmd_attention_set_unack_help,
+ },
+#endif
+
+ /* Health Server Model Operations */
+ {
+ .sc_cmd = "add-fault",
+ .sc_cmd_func = cmd_add_fault,
+ .help = &cmd_add_fault_help,
+ },
+ {
+ .sc_cmd = "del-fault",
+ .sc_cmd_func = cmd_del_fault,
+ .help = &cmd_del_fault_help,
+ },
+
+#if MYNEWT_VAL(BLE_MESH_SHELL_MODELS)
+ /* Generic Client Model Operations */
+ {
+ .sc_cmd = "gen-onoff-get",
+ .sc_cmd_func = cmd_gen_onoff_get,
+ .help = NULL,
+ },
+ {
+ .sc_cmd = "gen-onoff-set",
+ .sc_cmd_func = cmd_gen_onoff_set,
+ .help = &cmd_gen_onoff_set_help,
+ },
+ {
+ .sc_cmd = "gen-onoff-set-unack",
+ .sc_cmd_func = cmd_gen_onoff_set_unack,
+ .help = &cmd_gen_onoff_set_unack_help,
+ },
+ {
+ .sc_cmd = "gen-level-get",
+ .sc_cmd_func = cmd_gen_level_get,
+ .help = NULL,
+ },
+ {
+ .sc_cmd = "gen-level-set",
+ .sc_cmd_func = cmd_gen_level_set,
+ .help = &cmd_gen_level_set_help,
+ },
+ {
+ .sc_cmd = "gen-level-set-unack",
+ .sc_cmd_func = cmd_gen_level_set_unack,
+ .help = &cmd_gen_level_set_unack_help,
+ },
+#endif
+
+ { 0 },
+};
+
+static void mesh_shell_thread(void *args)
+{
+ while (1) {
+ os_eventq_run(&mesh_shell_queue);
+ }
+}
+
+static void bt_mesh_shell_task_init(void)
+{
+ os_eventq_init(&mesh_shell_queue);
+
+ os_task_init(&mesh_shell_task, "mesh_sh", mesh_shell_thread, NULL,
+ BLE_MESH_SHELL_TASK_PRIO, OS_WAIT_FOREVER, g_blemesh_shell_stack,
+ BLE_MESH_SHELL_STACK_SIZE);
+}
+#endif
+
+void ble_mesh_shell_init(void)
+{
+#if (MYNEWT_VAL(BLE_MESH_SHELL))
+
+ /* Initialize health pub message */
+ health_pub_init();
+
+ /* Shell and other mesh clients should use separate task to
+ avoid deadlocks with mesh message processing queue */
+ bt_mesh_shell_task_init();
+ shell_evq_set(&mesh_shell_queue);
+ shell_register("mesh", mesh_commands);
+
+#endif
+}
diff --git a/src/libs/mynewt-nimble/nimble/host/mesh/src/shell.h b/src/libs/mynewt-nimble/nimble/host/mesh/src/shell.h
new file mode 100644
index 00000000..53cc83a2
--- /dev/null
+++ b/src/libs/mynewt-nimble/nimble/host/mesh/src/shell.h
@@ -0,0 +1,6 @@
+#ifndef __SHELL_H__
+#define __SHELL_H__
+
+void ble_mesh_shell_init(void);
+
+#endif
diff --git a/src/libs/mynewt-nimble/nimble/host/mesh/src/testing.c b/src/libs/mynewt-nimble/nimble/host/mesh/src/testing.c
new file mode 100644
index 00000000..d0a05376
--- /dev/null
+++ b/src/libs/mynewt-nimble/nimble/host/mesh/src/testing.c
@@ -0,0 +1,200 @@
+/*
+ * Copyright (c) 2017 Intel Corporation
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include <stddef.h>
+
+#include "console/console.h"
+#include "mesh/testing.h"
+#include "mesh/slist.h"
+#include "mesh/glue.h"
+#include "mesh/access.h"
+
+#include "net.h"
+#include "testing.h"
+#include "access.h"
+#include "foundation.h"
+#include "lpn.h"
+#include "transport.h"
+
+static sys_slist_t cb_slist;
+
+void bt_test_cb_register(struct bt_test_cb *cb)
+{
+ sys_slist_append(&cb_slist, &cb->node);
+}
+
+void bt_test_cb_unregister(struct bt_test_cb *cb)
+{
+ sys_slist_find_and_remove(&cb_slist, &cb->node);
+}
+
+void bt_test_mesh_net_recv(u8_t ttl, u8_t ctl, u16_t src, u16_t dst,
+ const void *payload, size_t payload_len)
+{
+ struct bt_test_cb *cb;
+
+ SYS_SLIST_FOR_EACH_CONTAINER(&cb_slist, cb, node) {
+ if (cb->mesh_net_recv) {
+ cb->mesh_net_recv(ttl, ctl, src, dst, payload,
+ payload_len);
+ }
+ }
+}
+
+void bt_test_mesh_model_bound(u16_t addr, struct bt_mesh_model *model,
+ u16_t key_idx)
+{
+ struct bt_test_cb *cb;
+
+ SYS_SLIST_FOR_EACH_CONTAINER(&cb_slist, cb, node) {
+ if (cb->mesh_model_bound) {
+ cb->mesh_model_bound(addr, model, key_idx);
+ }
+ }
+}
+
+void bt_test_mesh_model_unbound(u16_t addr, struct bt_mesh_model *model,
+ u16_t key_idx)
+{
+ struct bt_test_cb *cb;
+
+ SYS_SLIST_FOR_EACH_CONTAINER(&cb_slist, cb, node) {
+ if (cb->mesh_model_unbound) {
+ cb->mesh_model_unbound(addr, model, key_idx);
+ }
+ }
+}
+
+void bt_test_mesh_prov_invalid_bearer(u8_t opcode)
+{
+ struct bt_test_cb *cb;
+
+ SYS_SLIST_FOR_EACH_CONTAINER(&cb_slist, cb, node) {
+ if (cb->mesh_prov_invalid_bearer) {
+ cb->mesh_prov_invalid_bearer(opcode);
+ }
+ }
+}
+
+void bt_test_mesh_trans_incomp_timer_exp(void)
+{
+ struct bt_test_cb *cb;
+
+ SYS_SLIST_FOR_EACH_CONTAINER(&cb_slist, cb, node) {
+ if (cb->mesh_trans_incomp_timer_exp) {
+ cb->mesh_trans_incomp_timer_exp();
+ }
+ }
+}
+
+int bt_test_mesh_lpn_group_add(u16_t group)
+{
+ bt_mesh_lpn_group_add(group);
+
+ return 0;
+}
+
+int bt_test_mesh_lpn_group_remove(u16_t *groups, size_t groups_count)
+{
+ bt_mesh_lpn_group_del(groups, groups_count);
+
+ return 0;
+}
+
+int bt_test_mesh_rpl_clear(void)
+{
+ bt_mesh_rpl_clear();
+
+ return 0;
+}
+
+void bt_test_print_credentials(void)
+{
+ int i;
+ u8_t nid;
+ const u8_t *enc;
+ const u8_t *priv;
+ struct bt_mesh_subnet *sub;
+ struct bt_mesh_app_key *app_key;
+
+ console_printf("IV Index: %08lx\n", (long) bt_mesh.iv_index);
+ console_printf("Dev key: %s\n", bt_hex(bt_mesh.dev_key, 16));
+
+ for (i = 0; i < MYNEWT_VAL(BLE_MESH_SUBNET_COUNT); ++i)
+ {
+ if (bt_mesh.app_keys[i].net_idx == BT_MESH_KEY_UNUSED) {
+ continue;
+ }
+
+ sub = &bt_mesh.sub[i];
+
+ console_printf("Subnet: %d\n", i);
+ console_printf("\tNetKeyIdx: %04x\n",
+ sub->net_idx);
+ console_printf("\tNetKey: %s\n",
+ bt_hex(sub->keys[sub->kr_flag].net, 16));
+ }
+
+ for (i = 0; i < MYNEWT_VAL(BLE_MESH_APP_KEY_COUNT); ++i)
+ {
+ if (bt_mesh.app_keys[i].net_idx == BT_MESH_KEY_UNUSED) {
+ continue;
+ }
+
+ sub = &bt_mesh.sub[i];
+ app_key = &bt_mesh.app_keys[i];
+
+ console_printf("AppKey: %d\n", i);
+ console_printf("\tNetKeyIdx: %04x\n",
+ app_key->net_idx);
+ console_printf("\tAppKeyIdx: %04x\n",
+ app_key->app_idx);
+ console_printf("\tAppKey: %s\n",
+ bt_hex(app_key->keys[sub->kr_flag].val, 16));
+ }
+
+ for (i = 0; i < MYNEWT_VAL(BLE_MESH_SUBNET_COUNT); ++i)
+ {
+ if (bt_mesh.sub[i].net_idx == BT_MESH_KEY_UNUSED) {
+ continue;
+ }
+
+ if (friend_cred_get(&bt_mesh.sub[i], BT_MESH_ADDR_UNASSIGNED,
+ &nid, &enc, &priv)) {
+ return;
+ }
+
+ console_printf("Friend cred: %d\n", i);
+ console_printf("\tNetKeyIdx: %04x\n",
+ bt_mesh.sub[i].net_idx);
+ console_printf("\tNID: %02x\n", nid);
+ console_printf("\tEncKey: %s\n",
+ bt_hex(enc, 16));
+ console_printf("\tPrivKey: %s\n",
+ bt_hex(priv, 16));
+ }
+}
+
+int bt_test_shell_init(void)
+{
+#if MYNEWT_VAL(BLE_MESH_SHELL)
+ return cmd_mesh_init(0, NULL);
+#else
+ return -ENOTSUP;
+#endif
+}
+
+int bt_test_bind_app_key_to_model(struct bt_mesh_model *model, u16_t key_idx, u16_t id)
+{
+ struct bt_mesh_model *found_model;
+
+ found_model = bt_mesh_model_find(bt_mesh_model_elem(model), id);
+ if (!found_model) {
+ return STATUS_INVALID_MODEL;
+ }
+
+ return mod_bind(found_model, key_idx);
+}
diff --git a/src/libs/mynewt-nimble/nimble/host/mesh/src/testing.h b/src/libs/mynewt-nimble/nimble/host/mesh/src/testing.h
new file mode 100644
index 00000000..166a9eea
--- /dev/null
+++ b/src/libs/mynewt-nimble/nimble/host/mesh/src/testing.h
@@ -0,0 +1,23 @@
+/**
+ * @file testing.h
+ * @brief Internal API for Bluetooth testing.
+ */
+
+/*
+ * Copyright (c) 2017 Intel Corporation
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include "mesh/glue.h"
+#include "mesh/access.h"
+
+void bt_test_mesh_model_bound(u16_t addr, struct bt_mesh_model *model,
+ u16_t key_idx);
+void bt_test_mesh_model_unbound(u16_t addr, struct bt_mesh_model *model,
+ u16_t key_idx);
+void bt_test_mesh_prov_invalid_bearer(u8_t opcode);
+void bt_test_mesh_net_recv(u8_t ttl, u8_t ctl, u16_t src, u16_t dst,
+ const void *payload, size_t payload_len);
+void bt_test_mesh_trans_incomp_timer_exp(void);
+void bt_test_print_credentials(void);
diff --git a/src/libs/mynewt-nimble/nimble/host/mesh/src/transport.c b/src/libs/mynewt-nimble/nimble/host/mesh/src/transport.c
new file mode 100644
index 00000000..caf1b4f1
--- /dev/null
+++ b/src/libs/mynewt-nimble/nimble/host/mesh/src/transport.c
@@ -0,0 +1,1668 @@
+/* Bluetooth Mesh */
+
+/*
+ * Copyright (c) 2017 Intel Corporation
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include "syscfg/syscfg.h"
+#define MESH_LOG_MODULE BLE_MESH_TRANS_LOG
+
+#include <errno.h>
+#include <string.h>
+
+#include "mesh/mesh.h"
+#include "mesh_priv.h"
+
+#include "crypto.h"
+#include "adv.h"
+#include "net.h"
+#include "lpn.h"
+#include "friend.h"
+#include "access.h"
+#include "foundation.h"
+#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
+ * buffers available compared to the maximum supported outgoing segment
+ * count.
+ */
+BUILD_ASSERT(CONFIG_BT_MESH_ADV_BUF_COUNT >= (CONFIG_BT_MESH_TX_SEG_MAX + 3));
+
+#define AID_MASK ((u8_t)(BIT_MASK(6)))
+
+#define SEG(data) ((data)[0] >> 7)
+#define AKF(data) (((data)[0] >> 6) & 0x01)
+#define AID(data) ((data)[0] & AID_MASK)
+#define ASZMIC(data) (((data)[1] >> 7) & 1)
+
+#define APP_MIC_LEN(aszmic) ((aszmic) ? 8 : 4)
+
+#define UNSEG_HDR(akf, aid) ((akf << 6) | (aid & AID_MASK))
+#define SEG_HDR(akf, aid) (UNSEG_HDR(akf, aid) | 0x80)
+
+#define BLOCK_COMPLETE(seg_n) (u32_t)(((u64_t)1 << (seg_n + 1)) - 1)
+
+#define SEQ_AUTH(iv_index, seq) (((u64_t)iv_index) << 24 | (u64_t)seq)
+
+/* Number of retransmit attempts (after the initial transmit) per segment */
+#define SEG_RETRANSMIT_ATTEMPTS (MYNEWT_VAL(BLE_MESH_SEG_RETRANSMIT_ATTEMPTS))
+
+/* "This timer shall be set to a minimum of 200 + 50 * TTL milliseconds.".
+ * We use 400 since 300 is a common send duration for standard HCI, and we
+ * need to have a timeout that's bigger than that.
+ */
+#define SEG_RETRANSMIT_TIMEOUT(tx) (K_MSEC(400) + 50 * (tx)->ttl)
+
+/* How long to wait for available buffers before giving up */
+#define BUF_TIMEOUT K_NO_WAIT
+
+static struct seg_tx {
+ struct bt_mesh_subnet *sub;
+ struct os_mbuf *seg[CONFIG_BT_MESH_TX_SEG_MAX];
+ u64_t seq_auth;
+ u16_t dst;
+ u8_t seg_n:5, /* Last segment index */
+ new_key:1; /* New/old key */
+ u8_t nack_count; /* Number of unacked segs */
+ u8_t ttl;
+ const struct bt_mesh_send_cb *cb;
+ void *cb_data;
+ struct k_delayed_work retransmit; /* Retransmit timer */
+} seg_tx[MYNEWT_VAL(BLE_MESH_TX_SEG_MSG_COUNT)];
+
+static struct seg_rx {
+ struct bt_mesh_subnet *sub;
+ u64_t seq_auth;
+ u8_t seg_n:5,
+ ctl:1,
+ in_use:1,
+ obo:1;
+ u8_t hdr;
+ u8_t ttl;
+ u16_t src;
+ u16_t dst;
+ u32_t block;
+ u32_t last;
+ struct k_delayed_work ack;
+ struct os_mbuf *buf;
+} seg_rx[MYNEWT_VAL(BLE_MESH_RX_SEG_MSG_COUNT)] = {
+ [0 ... (MYNEWT_VAL(BLE_MESH_RX_SEG_MSG_COUNT) - 1)] = { 0 },
+};
+
+static u16_t hb_sub_dst = BT_MESH_ADDR_UNASSIGNED;
+
+void bt_mesh_set_hb_sub_dst(u16_t addr)
+{
+ hb_sub_dst = addr;
+}
+
+static int send_unseg(struct bt_mesh_net_tx *tx, struct os_mbuf *sdu,
+ const struct bt_mesh_send_cb *cb, void *cb_data)
+{
+ struct os_mbuf *buf;
+
+ BT_DBG("src 0x%04x dst 0x%04x app_idx 0x%04x sdu_len %u",
+ tx->src, tx->ctx->addr, tx->ctx->app_idx, sdu->om_len);
+
+ buf = bt_mesh_adv_create(BT_MESH_ADV_DATA, tx->xmit, BUF_TIMEOUT);
+ if (!buf) {
+ BT_ERR("Out of network buffers");
+ return -ENOBUFS;
+ }
+
+ net_buf_reserve(buf, BT_MESH_NET_HDR_LEN);
+
+ if (BT_MESH_IS_DEV_KEY(tx->ctx->app_idx)) {
+ net_buf_add_u8(buf, UNSEG_HDR(0, 0));
+ } else {
+ net_buf_add_u8(buf, UNSEG_HDR(1, tx->aid));
+ }
+
+ net_buf_add_mem(buf, sdu->om_data, sdu->om_len);
+
+ if (IS_ENABLED(CONFIG_BT_MESH_FRIEND)) {
+ if (!bt_mesh_friend_queue_has_space(tx->sub->net_idx,
+ tx->src, tx->ctx->addr,
+ NULL, 1)) {
+ if (BT_MESH_ADDR_IS_UNICAST(tx->ctx->addr)) {
+ BT_ERR("Not enough space in Friend Queue");
+ net_buf_unref(buf);
+ return -ENOBUFS;
+ } else {
+ BT_WARN("No space in Friend Queue");
+ goto send;
+ }
+ }
+
+ if (bt_mesh_friend_enqueue_tx(tx, BT_MESH_FRIEND_PDU_SINGLE,
+ NULL, 1, buf) &&
+ BT_MESH_ADDR_IS_UNICAST(tx->ctx->addr)) {
+ /* PDUs for a specific Friend should only go
+ * out through the Friend Queue.
+ */
+ net_buf_unref(buf);
+ send_cb_finalize(cb, cb_data);
+ return 0;
+ }
+ }
+
+send:
+ return bt_mesh_net_send(tx, buf, cb, cb_data);
+}
+
+bool bt_mesh_tx_in_progress(void)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(seg_tx); i++) {
+ if (seg_tx[i].nack_count) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static void seg_tx_reset(struct seg_tx *tx)
+{
+ int i;
+
+ k_delayed_work_cancel(&tx->retransmit);
+
+ tx->cb = NULL;
+ tx->cb_data = NULL;
+ tx->seq_auth = 0;
+ tx->sub = NULL;
+ tx->dst = BT_MESH_ADDR_UNASSIGNED;
+
+ if (!tx->nack_count) {
+ return;
+ }
+
+ for (i = 0; i <= tx->seg_n; i++) {
+ if (!tx->seg[i]) {
+ continue;
+ }
+
+ net_buf_unref(tx->seg[i]);
+ tx->seg[i] = NULL;
+ }
+
+ tx->nack_count = 0U;
+
+ if (atomic_test_and_clear_bit(bt_mesh.flags, BT_MESH_IVU_PENDING)) {
+ BT_DBG("Proceding with pending IV Update");
+
+ /* bt_mesh_net_iv_update() will re-enable the flag if this
+ * wasn't the only transfer.
+ */
+ if (bt_mesh_net_iv_update(bt_mesh.iv_index, false)) {
+ bt_mesh_net_sec_update(NULL);
+ }
+ }
+}
+
+static inline void seg_tx_complete(struct seg_tx *tx, int err)
+{
+ if (tx->cb && tx->cb->end) {
+ tx->cb->end(err, tx->cb_data);
+ }
+
+ seg_tx_reset(tx);
+}
+
+static void seg_first_send_start(u16_t duration, int err, void *user_data)
+{
+ struct seg_tx *tx = user_data;
+
+ if (tx->cb && tx->cb->start) {
+ tx->cb->start(duration, err, tx->cb_data);
+ }
+}
+
+static void seg_send_start(u16_t duration, int err, void *user_data)
+{
+ struct seg_tx *tx = user_data;
+
+ /* If there's an error in transmitting the 'sent' callback will never
+ * be called. Make sure that we kick the retransmit timer also in this
+ * case since otherwise we risk the transmission of becoming stale.
+ */
+ if (err) {
+ k_delayed_work_submit(&tx->retransmit,
+ SEG_RETRANSMIT_TIMEOUT(tx));
+ }
+}
+
+static void seg_sent(int err, void *user_data)
+{
+ struct seg_tx *tx = user_data;
+
+ k_delayed_work_submit(&tx->retransmit,
+ SEG_RETRANSMIT_TIMEOUT(tx));
+}
+
+static const struct bt_mesh_send_cb first_sent_cb = {
+ .start = seg_first_send_start,
+ .end = seg_sent,
+};
+
+static const struct bt_mesh_send_cb seg_sent_cb = {
+ .start = seg_send_start,
+ .end = seg_sent,
+};
+
+static void seg_tx_send_unacked(struct seg_tx *tx)
+{
+ int i, err;
+
+ for (i = 0; i <= tx->seg_n; i++) {
+ struct os_mbuf *seg = tx->seg[i];
+
+ if (!seg) {
+ continue;
+ }
+
+ if (BT_MESH_ADV(seg)->busy) {
+ BT_DBG("Skipping segment that's still advertising");
+ continue;
+ }
+
+ if (!(BT_MESH_ADV(seg)->seg.attempts--)) {
+ BT_ERR("Ran out of retransmit attempts");
+ seg_tx_complete(tx, -ETIMEDOUT);
+ return;
+ }
+
+ BT_DBG("resending %u/%u", i, tx->seg_n);
+
+ err = bt_mesh_net_resend(tx->sub, seg, tx->new_key,
+ &seg_sent_cb, tx);
+ if (err) {
+ BT_ERR("Sending segment failed");
+ seg_tx_complete(tx, -EIO);
+ return;
+ }
+ }
+}
+
+static void seg_retransmit(struct ble_npl_event *work)
+{
+ struct seg_tx *tx = ble_npl_event_get_arg(work);
+ seg_tx_send_unacked(tx);
+}
+
+static int send_seg(struct bt_mesh_net_tx *net_tx, struct os_mbuf *sdu,
+ const struct bt_mesh_send_cb *cb, void *cb_data)
+{
+ u8_t seg_hdr, seg_o;
+ u16_t seq_zero;
+ struct seg_tx *tx;
+ int i;
+
+ BT_DBG("src 0x%04x dst 0x%04x app_idx 0x%04x aszmic %u sdu_len %u",
+ net_tx->src, net_tx->ctx->addr, net_tx->ctx->app_idx,
+ net_tx->aszmic, sdu->om_len);
+
+ if (sdu->om_len < 1) {
+ BT_ERR("Zero-length SDU not allowed");
+ return -EINVAL;
+ }
+
+ if (sdu->om_len > BT_MESH_TX_SDU_MAX) {
+ BT_ERR("Not enough segment buffers for length %u", sdu->om_len);
+ return -EMSGSIZE;
+ }
+
+ for (tx = NULL, i = 0; i < ARRAY_SIZE(seg_tx); i++) {
+ if (!seg_tx[i].nack_count) {
+ tx = &seg_tx[i];
+ break;
+ }
+ }
+
+ if (!tx) {
+ BT_ERR("No multi-segment message contexts available");
+ return -EBUSY;
+ }
+
+ if (BT_MESH_IS_DEV_KEY(net_tx->ctx->app_idx)) {
+ seg_hdr = SEG_HDR(0, 0);
+ } else {
+ seg_hdr = SEG_HDR(1, net_tx->aid);
+ }
+
+ seg_o = 0;
+ tx->dst = net_tx->ctx->addr;
+ tx->seg_n = (sdu->om_len - 1) / 12;
+ tx->nack_count = tx->seg_n + 1;
+ tx->seq_auth = SEQ_AUTH(BT_MESH_NET_IVI_TX, bt_mesh.seq);
+ tx->sub = net_tx->sub;
+ tx->new_key = net_tx->sub->kr_flag;
+ tx->cb = cb;
+ tx->cb_data = cb_data;
+
+ if (net_tx->ctx->send_ttl == BT_MESH_TTL_DEFAULT) {
+ tx->ttl = bt_mesh_default_ttl_get();
+ } else {
+ tx->ttl = net_tx->ctx->send_ttl;
+ }
+
+ seq_zero = tx->seq_auth & TRANS_SEQ_ZERO_MASK;
+
+ BT_DBG("SeqZero 0x%04x", seq_zero);
+
+ if (IS_ENABLED(CONFIG_BT_MESH_FRIEND) &&
+ !bt_mesh_friend_queue_has_space(tx->sub->net_idx, net_tx->src,
+ tx->dst, &tx->seq_auth,
+ tx->seg_n + 1) &&
+ BT_MESH_ADDR_IS_UNICAST(tx->dst)) {
+ BT_ERR("Not enough space in Friend Queue for %u segments",
+ tx->seg_n + 1);
+ seg_tx_reset(tx);
+ return -ENOBUFS;
+ }
+
+ for (seg_o = 0; sdu->om_len; seg_o++) {
+ struct os_mbuf *seg;
+ u16_t len;
+ int err;
+
+ seg = bt_mesh_adv_create(BT_MESH_ADV_DATA, net_tx->xmit,
+ BUF_TIMEOUT);
+ if (!seg) {
+ BT_ERR("Out of segment buffers");
+ seg_tx_reset(tx);
+ return -ENOBUFS;
+ }
+
+ BT_MESH_ADV(seg)->seg.attempts = SEG_RETRANSMIT_ATTEMPTS;
+
+ net_buf_reserve(seg, BT_MESH_NET_HDR_LEN);
+
+ net_buf_add_u8(seg, seg_hdr);
+ net_buf_add_u8(seg, (net_tx->aszmic << 7) | seq_zero >> 6);
+ net_buf_add_u8(seg, (((seq_zero & 0x3f) << 2) |
+ (seg_o >> 3)));
+ net_buf_add_u8(seg, ((seg_o & 0x07) << 5) | tx->seg_n);
+
+ len = min(sdu->om_len, 12);
+ net_buf_add_mem(seg, sdu->om_data, len);
+ net_buf_simple_pull(sdu, len);
+
+ if (IS_ENABLED(CONFIG_BT_MESH_FRIEND)) {
+ enum bt_mesh_friend_pdu_type type;
+
+ if (seg_o == tx->seg_n) {
+ type = BT_MESH_FRIEND_PDU_COMPLETE;
+ } else {
+ type = BT_MESH_FRIEND_PDU_PARTIAL;
+ }
+
+ if (bt_mesh_friend_enqueue_tx(net_tx, type,
+ &tx->seq_auth,
+ tx->seg_n + 1,
+ seg) &&
+ BT_MESH_ADDR_IS_UNICAST(net_tx->ctx->addr)) {
+ /* PDUs for a specific Friend should only go
+ * out through the Friend Queue.
+ */
+ net_buf_unref(seg);
+ continue;
+ }
+ }
+
+ tx->seg[seg_o] = net_buf_ref(seg);
+
+ BT_DBG("Sending %u/%u", seg_o, tx->seg_n);
+
+ err = bt_mesh_net_send(net_tx, seg,
+ seg_o ? &seg_sent_cb : &first_sent_cb,
+ tx);
+ if (err) {
+ BT_ERR("Sending segment failed");
+ seg_tx_reset(tx);
+ return err;
+ }
+ }
+
+ /* This can happen if segments only went into the Friend Queue */
+ if (IS_ENABLED(CONFIG_BT_MESH_FRIEND) && !tx->seg[0]) {
+ seg_tx_reset(tx);
+
+ /* If there was a callback notify sending immediately since
+ * there's no other way to track this (at least currently)
+ * with the Friend Queue.
+ */
+ send_cb_finalize(cb, cb_data);
+ }
+
+ if (IS_ENABLED(CONFIG_BT_MESH_LOW_POWER) &&
+ bt_mesh_lpn_established()) {
+ bt_mesh_lpn_poll();
+ }
+
+ return 0;
+}
+
+struct bt_mesh_app_key *bt_mesh_app_key_find(u16_t app_idx)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(bt_mesh.app_keys); i++) {
+ struct bt_mesh_app_key *key = &bt_mesh.app_keys[i];
+
+ if (key->net_idx != BT_MESH_KEY_UNUSED &&
+ key->app_idx == app_idx) {
+ return key;
+ }
+ }
+
+ return NULL;
+}
+
+int bt_mesh_trans_send(struct bt_mesh_net_tx *tx, struct os_mbuf *msg,
+ const struct bt_mesh_send_cb *cb, void *cb_data)
+{
+ const u8_t *key;
+ u8_t *ad;
+ u8_t aid;
+ int err;
+
+ if (net_buf_simple_tailroom(msg) < 4) {
+ BT_ERR("Insufficient tailroom for Transport MIC");
+ return -EINVAL;
+ }
+
+ if (msg->om_len > 11) {
+ tx->ctx->send_rel = 1;
+ tx->ctx->send_rel = true;
+ }
+
+ BT_DBG("net_idx 0x%04x app_idx 0x%04x dst 0x%04x", tx->sub->net_idx,
+ tx->ctx->app_idx, tx->ctx->addr);
+ BT_DBG("len %u: %s", msg->om_len, bt_hex(msg->om_data, msg->om_len));
+
+ err = bt_mesh_app_key_get(tx->sub, tx->ctx->app_idx,
+ tx->ctx->addr, &key, &aid);
+ if (err) {
+ return err;
+ }
+
+ tx->aid = aid;
+
+ if (!tx->ctx->send_rel || net_buf_simple_tailroom(msg) < 8) {
+ tx->aszmic = 0;
+ } else {
+ tx->aszmic = 1;
+ }
+
+ if (BT_MESH_ADDR_IS_VIRTUAL(tx->ctx->addr)) {
+ ad = bt_mesh_label_uuid_get(tx->ctx->addr);
+ } else {
+ ad = NULL;
+ }
+
+ err = bt_mesh_app_encrypt(key, BT_MESH_IS_DEV_KEY(tx->ctx->app_idx),
+ tx->aszmic, msg, ad, tx->src, tx->ctx->addr,
+ bt_mesh.seq, BT_MESH_NET_IVI_TX);
+ if (err) {
+ return err;
+ }
+
+ if (tx->ctx->send_rel) {
+ err = send_seg(tx, msg, cb, cb_data);
+ } else {
+ err = send_unseg(tx, msg, cb, cb_data);
+ }
+
+ return err;
+}
+
+static void update_rpl(struct bt_mesh_rpl *rpl, struct bt_mesh_net_rx *rx)
+{
+ rpl->src = rx->ctx.addr;
+ rpl->seq = rx->seq;
+ rpl->old_iv = rx->old_iv;
+
+ if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
+ bt_mesh_store_rpl(rpl);
+ }
+}
+
+/* Check the Replay Protection List for a replay attempt. If non-NULL match
+ * parameter is given the RPL slot is returned but it is not immediately
+ * updated (needed for segmented messages), whereas if a NULL match is given
+ * the RPL is immediately updated (used for unsegmented messages).
+ */
+static bool is_replay(struct bt_mesh_net_rx *rx, struct bt_mesh_rpl **match)
+{
+ int i;
+
+ /* Don't bother checking messages from ourselves */
+ if (rx->net_if == BT_MESH_NET_IF_LOCAL) {
+ return false;
+ }
+
+ /* The RPL is used only for the local node */
+ if (!rx->local_match) {
+ return false;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(bt_mesh.rpl); i++) {
+ struct bt_mesh_rpl *rpl = &bt_mesh.rpl[i];
+
+ /* Empty slot */
+ if (!rpl->src) {
+ if (match) {
+ *match = rpl;
+ } else {
+ update_rpl(rpl, rx);
+ }
+
+ return false;
+ }
+
+ /* Existing slot for given address */
+ if (rpl->src == rx->ctx.addr) {
+ if (rx->old_iv && !rpl->old_iv) {
+ return true;
+ }
+
+ if ((!rx->old_iv && rpl->old_iv) ||
+ rpl->seq < rx->seq) {
+ if (match) {
+ *match = rpl;
+ } else {
+ update_rpl(rpl, rx);
+ }
+
+ return false;
+ } else {
+ return true;
+ }
+ }
+ }
+
+ BT_ERR("RPL is full!");
+ return true;
+}
+
+static int sdu_recv(struct bt_mesh_net_rx *rx, u32_t seq, u8_t hdr,
+ u8_t aszmic, struct os_mbuf *buf)
+{
+ struct os_mbuf *sdu =
+ NET_BUF_SIMPLE(MYNEWT_VAL(BLE_MESH_RX_SDU_MAX) - 4);
+ u8_t *ad;
+ u16_t i;
+ int err = 0;
+
+ BT_DBG("ASZMIC %u AKF %u AID 0x%02x", aszmic, AKF(&hdr), AID(&hdr));
+ BT_DBG("len %u: %s", buf->om_len, bt_hex(buf->om_data, buf->om_len));
+
+ if (buf->om_len < 1 + APP_MIC_LEN(aszmic)) {
+ BT_ERR("Too short SDU + MIC");
+ err = -EINVAL;
+ goto done;
+ }
+
+ if (IS_ENABLED(CONFIG_BT_MESH_FRIEND) && !rx->local_match) {
+ BT_DBG("Ignoring PDU for LPN 0x%04x of this Friend",
+ rx->ctx.recv_dst);
+ goto done;
+ }
+
+ if (BT_MESH_ADDR_IS_VIRTUAL(rx->ctx.recv_dst)) {
+ ad = bt_mesh_label_uuid_get(rx->ctx.recv_dst);
+ } else {
+ ad = NULL;
+ }
+
+ /* Adjust the length to not contain the MIC at the end */
+ buf->om_len -= APP_MIC_LEN(aszmic);
+
+ if (!AKF(&hdr)) {
+ net_buf_simple_init(sdu, 0);
+ err = bt_mesh_app_decrypt(bt_mesh.dev_key, true, aszmic, buf,
+ sdu, ad, rx->ctx.addr,
+ rx->ctx.recv_dst, seq,
+ BT_MESH_NET_IVI_RX(rx));
+ if (err) {
+ BT_WARN("Unable to decrypt with local DevKey");
+ } else {
+ rx->ctx.app_idx = BT_MESH_KEY_DEV_LOCAL;
+ bt_mesh_model_recv(rx, sdu);
+ goto done;
+ }
+
+ if (IS_ENABLED(CONFIG_BT_MESH_PROVISIONER)) {
+ struct bt_mesh_node *node;
+
+ /*
+ * There is no way of knowing if we should use our
+ * local DevKey or the remote DevKey to decrypt the
+ * message so we must try both.
+ */
+
+ node = bt_mesh_node_find(rx->ctx.addr);
+ if (node == NULL) {
+ BT_ERR("No node found for addr 0x%04x",
+ rx->ctx.addr);
+ return -EINVAL;
+ }
+
+ net_buf_simple_init(sdu, 0);
+ err = bt_mesh_app_decrypt(node->dev_key, true, aszmic,
+ buf, sdu, ad, rx->ctx.addr,
+ rx->ctx.recv_dst, seq,
+ BT_MESH_NET_IVI_RX(rx));
+ if (err) {
+ BT_ERR("Unable to decrypt with node DevKey");
+ return -EINVAL;
+ }
+
+ rx->ctx.app_idx = BT_MESH_KEY_DEV_REMOTE;
+ bt_mesh_model_recv(rx, sdu);
+ return 0;
+ }
+
+ return -EINVAL;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(bt_mesh.app_keys); i++) {
+ struct bt_mesh_app_key *key = &bt_mesh.app_keys[i];
+ struct bt_mesh_app_keys *keys;
+
+ /* Check that this AppKey matches received net_idx */
+ if (key->net_idx != rx->sub->net_idx) {
+ continue;
+ }
+
+ if (rx->new_key && key->updated) {
+ keys = &key->keys[1];
+ } else {
+ keys = &key->keys[0];
+ }
+
+ /* Check that the AppKey ID matches */
+ if (AID(&hdr) != keys->id) {
+ continue;
+ }
+
+ net_buf_simple_init(sdu, 0);
+ err = bt_mesh_app_decrypt(keys->val, false, aszmic, buf,
+ sdu, ad, rx->ctx.addr,
+ rx->ctx.recv_dst, seq,
+ BT_MESH_NET_IVI_RX(rx));
+ if (err) {
+ BT_WARN("Unable to decrypt with AppKey 0x%03x",
+ key->app_idx);
+ continue;
+
+ }
+
+ rx->ctx.app_idx = key->app_idx;
+
+ bt_mesh_model_recv(rx, sdu);
+ goto done;
+ }
+
+ BT_WARN("No matching AppKey");
+
+ err = -EINVAL;
+done:
+ os_mbuf_free_chain(sdu);
+ return err;
+}
+
+static struct seg_tx *seg_tx_lookup(u16_t seq_zero, u8_t obo, u16_t addr)
+{
+ struct seg_tx *tx;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(seg_tx); i++) {
+ tx = &seg_tx[i];
+
+ if ((tx->seq_auth & TRANS_SEQ_ZERO_MASK) != seq_zero) {
+ continue;
+ }
+
+ if (tx->dst == addr) {
+ return tx;
+ }
+
+ /* If the expected remote address doesn't match,
+ * but the OBO flag is set and this is the first
+ * acknowledgement, assume it's a Friend that's
+ * responding and therefore accept the message.
+ */
+ if (obo && tx->nack_count == tx->seg_n + 1) {
+ tx->dst = addr;
+ return tx;
+ }
+ }
+
+ return NULL;
+}
+
+static int trans_ack(struct bt_mesh_net_rx *rx, u8_t hdr,
+ struct os_mbuf *buf, u64_t *seq_auth)
+{
+ struct seg_tx *tx;
+ unsigned int bit;
+ u32_t ack;
+ u16_t seq_zero;
+ u8_t obo;
+
+ if (buf->om_len < 6) {
+ BT_ERR("Too short ack message");
+ return -EINVAL;
+ }
+
+ seq_zero = net_buf_simple_pull_be16(buf);
+ obo = seq_zero >> 15;
+ seq_zero = (seq_zero >> 2) & TRANS_SEQ_ZERO_MASK;
+
+ if (IS_ENABLED(CONFIG_BT_MESH_FRIEND) && rx->friend_match) {
+ BT_DBG("Ack for LPN 0x%04x of this Friend", rx->ctx.recv_dst);
+ /* Best effort - we don't have enough info for true SeqAuth */
+ *seq_auth = SEQ_AUTH(BT_MESH_NET_IVI_RX(rx), seq_zero);
+ return 0;
+ }
+
+ ack = net_buf_simple_pull_be32(buf);
+
+ BT_DBG("OBO %u seq_zero 0x%04x ack 0x%08x", obo, seq_zero,
+ (unsigned) ack);
+
+ tx = seg_tx_lookup(seq_zero, obo, rx->ctx.addr);
+ if (!tx) {
+ BT_WARN("No matching TX context for ack");
+ return -EINVAL;
+ }
+
+ *seq_auth = tx->seq_auth;
+
+ if (!ack) {
+ BT_WARN("SDU canceled");
+ seg_tx_complete(tx, -ECANCELED);
+ return 0;
+ }
+
+ if (find_msb_set(ack) - 1 > tx->seg_n) {
+ BT_ERR("Too large segment number in ack");
+ return -EINVAL;
+ }
+
+ k_delayed_work_cancel(&tx->retransmit);
+
+ while ((bit = find_lsb_set(ack))) {
+ if (tx->seg[bit - 1]) {
+ BT_DBG("seg %u/%u acked", bit - 1, tx->seg_n);
+ net_buf_unref(tx->seg[bit - 1]);
+ tx->seg[bit - 1] = NULL;
+ tx->nack_count--;
+ }
+
+ ack &= ~BIT(bit - 1);
+ }
+
+ if (tx->nack_count) {
+ seg_tx_send_unacked(tx);
+ } else {
+ BT_DBG("SDU TX complete");
+ seg_tx_complete(tx, 0);
+ }
+
+ return 0;
+}
+
+static int trans_heartbeat(struct bt_mesh_net_rx *rx,
+ struct os_mbuf *buf)
+{
+ u8_t init_ttl, hops;
+ u16_t feat;
+
+ if (buf->om_len < 3) {
+ BT_ERR("Too short heartbeat message");
+ return -EINVAL;
+ }
+
+ if (rx->ctx.recv_dst != hb_sub_dst) {
+ BT_WARN("Ignoring heartbeat to non-subscribed destination");
+ return 0;
+ }
+
+ init_ttl = (net_buf_simple_pull_u8(buf) & 0x7f);
+ feat = net_buf_simple_pull_be16(buf);
+
+ hops = (init_ttl - rx->ctx.recv_ttl + 1);
+
+ BT_DBG("src 0x%04x TTL %u InitTTL %u (%u hop%s) feat 0x%04x",
+ rx->ctx.addr, rx->ctx.recv_ttl, init_ttl, hops,
+ (hops == 1) ? "" : "s", feat);
+
+ bt_mesh_heartbeat(rx->ctx.addr, rx->ctx.recv_dst, hops, feat);
+
+ return 0;
+}
+
+static int ctl_recv(struct bt_mesh_net_rx *rx, u8_t hdr,
+ struct os_mbuf *buf, u64_t *seq_auth)
+{
+ u8_t ctl_op = TRANS_CTL_OP(&hdr);
+
+ BT_DBG("OpCode 0x%02x len %u", ctl_op, buf->om_len);
+
+ switch (ctl_op) {
+ case TRANS_CTL_OP_ACK:
+ return trans_ack(rx, hdr, buf, seq_auth);
+ case TRANS_CTL_OP_HEARTBEAT:
+ return trans_heartbeat(rx, buf);
+ }
+
+ /* Only acks and heartbeats may need processing without local_match */
+ if (!rx->local_match) {
+ return 0;
+ }
+
+ if (IS_ENABLED(CONFIG_BT_MESH_FRIEND) && !bt_mesh_lpn_established()) {
+ switch (ctl_op) {
+ case TRANS_CTL_OP_FRIEND_POLL:
+ return bt_mesh_friend_poll(rx, buf);
+ case TRANS_CTL_OP_FRIEND_REQ:
+ return bt_mesh_friend_req(rx, buf);
+ case TRANS_CTL_OP_FRIEND_CLEAR:
+ return bt_mesh_friend_clear(rx, buf);
+ case TRANS_CTL_OP_FRIEND_CLEAR_CFM:
+ return bt_mesh_friend_clear_cfm(rx, buf);
+ case TRANS_CTL_OP_FRIEND_SUB_ADD:
+ return bt_mesh_friend_sub_add(rx, buf);
+ case TRANS_CTL_OP_FRIEND_SUB_REM:
+ return bt_mesh_friend_sub_rem(rx, buf);
+ }
+ }
+
+#if (MYNEWT_VAL(BLE_MESH_LOW_POWER))
+ if (ctl_op == TRANS_CTL_OP_FRIEND_OFFER) {
+ return bt_mesh_lpn_friend_offer(rx, buf);
+ }
+
+ if (rx->ctx.addr == bt_mesh.lpn.frnd) {
+ if (ctl_op == TRANS_CTL_OP_FRIEND_CLEAR_CFM) {
+ return bt_mesh_lpn_friend_clear_cfm(rx, buf);
+ }
+
+ if (!rx->friend_cred) {
+ BT_WARN("Message from friend with wrong credentials");
+ return -EINVAL;
+ }
+
+ switch (ctl_op) {
+ case TRANS_CTL_OP_FRIEND_UPDATE:
+ return bt_mesh_lpn_friend_update(rx, buf);
+ case TRANS_CTL_OP_FRIEND_SUB_CFM:
+ return bt_mesh_lpn_friend_sub_cfm(rx, buf);
+ }
+ }
+#endif /* MYNEWT_VAL(BLE_MESH_LOW_POWER) */
+
+ BT_WARN("Unhandled TransOpCode 0x%02x", ctl_op);
+
+ return -ENOENT;
+}
+
+static int trans_unseg(struct os_mbuf *buf, struct bt_mesh_net_rx *rx,
+ u64_t *seq_auth)
+{
+ u8_t hdr;
+
+ BT_DBG("AFK %u AID 0x%02x", AKF(buf->om_data), AID(buf->om_data));
+
+ if (buf->om_len < 1) {
+ BT_ERR("Too small unsegmented PDU");
+ return -EINVAL;
+ }
+
+ if (is_replay(rx, NULL)) {
+ BT_WARN("Replay: src 0x%04x dst 0x%04x seq 0x%06x",
+ rx->ctx.addr, rx->ctx.recv_dst, (unsigned) rx->seq);
+ return -EINVAL;
+ }
+
+ hdr = net_buf_simple_pull_u8(buf);
+
+ if (rx->ctl) {
+ return ctl_recv(rx, hdr, buf, seq_auth);
+ } else {
+ /* SDUs must match a local element or an LPN of this Friend. */
+ if (!rx->local_match && !rx->friend_match) {
+ return 0;
+ }
+
+ return sdu_recv(rx, rx->seq, hdr, 0, buf);
+ }
+}
+
+static inline s32_t ack_timeout(struct seg_rx *rx)
+{
+ s32_t to;
+ u8_t ttl;
+
+ if (rx->ttl == BT_MESH_TTL_DEFAULT) {
+ ttl = bt_mesh_default_ttl_get();
+ } else {
+ ttl = rx->ttl;
+ }
+
+ /* The acknowledgment timer shall be set to a minimum of
+ * 150 + 50 * TTL milliseconds.
+ */
+ to = K_MSEC(150 + (50 * ttl));
+
+ /* 100 ms for every not yet received segment */
+ to += K_MSEC(((rx->seg_n + 1) - popcount(rx->block)) * 100);
+
+ /* Make sure we don't send more frequently than the duration for
+ * each packet (default is 300ms).
+ */
+ return max(to, K_MSEC(400));
+}
+
+int bt_mesh_ctl_send(struct bt_mesh_net_tx *tx, u8_t ctl_op, void *data,
+ size_t data_len, u64_t *seq_auth,
+ const struct bt_mesh_send_cb *cb, void *cb_data)
+{
+ struct os_mbuf *buf;
+
+ BT_DBG("src 0x%04x dst 0x%04x ttl 0x%02x ctl 0x%02x", tx->src,
+ tx->ctx->addr, tx->ctx->send_ttl, ctl_op);
+ BT_DBG("len %zu: %s", data_len, bt_hex(data, data_len));
+
+ buf = bt_mesh_adv_create(BT_MESH_ADV_DATA, tx->xmit, BUF_TIMEOUT);
+ if (!buf) {
+ BT_ERR("Out of transport buffers");
+ return -ENOBUFS;
+ }
+
+ net_buf_reserve(buf, BT_MESH_NET_HDR_LEN);
+
+ net_buf_add_u8(buf, TRANS_CTL_HDR(ctl_op, 0));
+
+ net_buf_add_mem(buf, data, data_len);
+
+ if (IS_ENABLED(CONFIG_BT_MESH_FRIEND)) {
+ if (bt_mesh_friend_enqueue_tx(tx, BT_MESH_FRIEND_PDU_SINGLE,
+ seq_auth, 1, buf) &&
+ BT_MESH_ADDR_IS_UNICAST(tx->ctx->addr)) {
+ /* PDUs for a specific Friend should only go
+ * out through the Friend Queue.
+ */
+ net_buf_unref(buf);
+ return 0;
+ }
+ }
+
+ return bt_mesh_net_send(tx, buf, cb, cb_data);
+}
+
+static int send_ack(struct bt_mesh_subnet *sub, u16_t src, u16_t dst,
+ u8_t ttl, u64_t *seq_auth, u32_t block, u8_t obo)
+{
+ struct bt_mesh_msg_ctx ctx = {
+ .net_idx = sub->net_idx,
+ .app_idx = BT_MESH_KEY_UNUSED,
+ .addr = dst,
+ .send_ttl = ttl,
+ };
+ struct bt_mesh_net_tx tx = {
+ .sub = sub,
+ .ctx = &ctx,
+ .src = obo ? bt_mesh_primary_addr() : src,
+ .xmit = bt_mesh_net_transmit_get(),
+ };
+ u16_t seq_zero = *seq_auth & TRANS_SEQ_ZERO_MASK;
+ u8_t buf[6];
+
+ BT_DBG("SeqZero 0x%04x Block 0x%08x OBO %u", seq_zero,
+ (unsigned) block, obo);
+
+ if (bt_mesh_lpn_established()) {
+ BT_WARN("Not sending ack when LPN is enabled");
+ return 0;
+ }
+
+ /* This can happen if the segmented message was destined for a group
+ * or virtual address.
+ */
+ if (!BT_MESH_ADDR_IS_UNICAST(src)) {
+ BT_WARN("Not sending ack for non-unicast address");
+ return 0;
+ }
+
+ sys_put_be16(((seq_zero << 2) & 0x7ffc) | (obo << 15), buf);
+ sys_put_be32(block, &buf[2]);
+
+ return bt_mesh_ctl_send(&tx, TRANS_CTL_OP_ACK, buf, sizeof(buf),
+ NULL, NULL, NULL);
+}
+
+static void seg_rx_reset(struct seg_rx *rx, bool full_reset)
+{
+ BT_DBG("rx %p", rx);
+
+ k_delayed_work_cancel(&rx->ack);
+
+ if (IS_ENABLED(CONFIG_BT_MESH_FRIEND) && rx->obo &&
+ rx->block != BLOCK_COMPLETE(rx->seg_n)) {
+ BT_WARN("Clearing incomplete buffers from Friend queue");
+ bt_mesh_friend_clear_incomplete(rx->sub, rx->src, rx->dst,
+ &rx->seq_auth);
+ }
+
+ rx->in_use = 0;
+
+ /* We don't always reset these values since we need to be able to
+ * send an ack if we receive a segment after we've already received
+ * the full SDU.
+ */
+ if (full_reset) {
+ rx->seq_auth = 0;
+ rx->sub = NULL;
+ rx->src = BT_MESH_ADDR_UNASSIGNED;
+ rx->dst = BT_MESH_ADDR_UNASSIGNED;
+ }
+}
+
+static void seg_ack(struct ble_npl_event *work)
+{
+ struct seg_rx *rx = ble_npl_event_get_arg(work);
+
+ BT_DBG("rx %p", rx);
+
+ if (k_uptime_get_32() - rx->last > K_SECONDS(60)) {
+ BT_WARN("Incomplete timer expired");
+ seg_rx_reset(rx, false);
+
+ if (IS_ENABLED(CONFIG_BT_TESTING)) {
+ bt_test_mesh_trans_incomp_timer_exp();
+ }
+
+ return;
+ }
+
+ send_ack(rx->sub, rx->dst, rx->src, rx->ttl, &rx->seq_auth,
+ rx->block, rx->obo);
+
+ k_delayed_work_submit(&rx->ack, ack_timeout(rx));
+}
+
+static inline u8_t seg_len(bool ctl)
+{
+ if (ctl) {
+ return 8;
+ } else {
+ return 12;
+ }
+}
+
+static inline bool sdu_len_is_ok(bool ctl, u8_t seg_n)
+{
+ return ((seg_n * seg_len(ctl) + 1) <= MYNEWT_VAL(BLE_MESH_RX_SDU_MAX));
+}
+
+static struct seg_rx *seg_rx_find(struct bt_mesh_net_rx *net_rx,
+ const u64_t *seq_auth)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(seg_rx); i++) {
+ struct seg_rx *rx = &seg_rx[i];
+
+ if (rx->src != net_rx->ctx.addr ||
+ rx->dst != net_rx->ctx.recv_dst) {
+ continue;
+ }
+
+ /* Return newer RX context in addition to an exact match, so
+ * the calling function can properly discard an old SeqAuth.
+ */
+ if (rx->seq_auth >= *seq_auth) {
+ return rx;
+ }
+
+ if (rx->in_use) {
+ BT_WARN("Duplicate SDU from src 0x%04x",
+ net_rx->ctx.addr);
+
+ /* Clear out the old context since the sender
+ * has apparently started sending a new SDU.
+ */
+ seg_rx_reset(rx, true);
+
+ /* Return non-match so caller can re-allocate */
+ return NULL;
+ }
+ }
+
+ return NULL;
+}
+
+static bool seg_rx_is_valid(struct seg_rx *rx, struct bt_mesh_net_rx *net_rx,
+ const u8_t *hdr, u8_t seg_n)
+{
+ if (rx->hdr != *hdr || rx->seg_n != seg_n) {
+ BT_ERR("Invalid segment for ongoing session");
+ return false;
+ }
+
+ if (rx->src != net_rx->ctx.addr || rx->dst != net_rx->ctx.recv_dst) {
+ BT_ERR("Invalid source or destination for segment");
+ return false;
+ }
+
+ if (rx->ctl != net_rx->ctl) {
+ BT_ERR("Inconsistent CTL in segment");
+ return false;
+ }
+
+ return true;
+}
+
+static struct seg_rx *seg_rx_alloc(struct bt_mesh_net_rx *net_rx,
+ const u8_t *hdr, const u64_t *seq_auth,
+ u8_t seg_n)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(seg_rx); i++) {
+ struct seg_rx *rx = &seg_rx[i];
+
+ if (rx->in_use) {
+ continue;
+ }
+
+ rx->in_use = 1;
+ net_buf_simple_init(rx->buf, 0);
+ rx->sub = net_rx->sub;
+ rx->ctl = net_rx->ctl;
+ rx->seq_auth = *seq_auth;
+ rx->seg_n = seg_n;
+ rx->hdr = *hdr;
+ rx->ttl = net_rx->ctx.send_ttl;
+ rx->src = net_rx->ctx.addr;
+ rx->dst = net_rx->ctx.recv_dst;
+ rx->block = 0;
+
+ BT_DBG("New RX context. Block Complete 0x%08x",
+ (unsigned) BLOCK_COMPLETE(seg_n));
+
+ return rx;
+ }
+
+ return NULL;
+}
+
+static int trans_seg(struct os_mbuf *buf, struct bt_mesh_net_rx *net_rx,
+ enum bt_mesh_friend_pdu_type *pdu_type, u64_t *seq_auth,
+ u8_t *seg_count)
+{
+ struct bt_mesh_rpl *rpl = NULL;
+ struct seg_rx *rx;
+ u8_t *hdr = buf->om_data;
+ u16_t seq_zero;
+ u8_t seg_n;
+ u8_t seg_o;
+ int err;
+
+ if (buf->om_len < 5) {
+ BT_ERR("Too short segmented message (len %u)", buf->om_len);
+ return -EINVAL;
+ }
+
+ if (is_replay(net_rx, &rpl)) {
+ BT_WARN("Replay: src 0x%04x dst 0x%04x seq 0x%06x",
+ net_rx->ctx.addr, net_rx->ctx.recv_dst, net_rx->seq);
+ return -EINVAL;
+ }
+
+ BT_DBG("ASZMIC %u AKF %u AID 0x%02x", ASZMIC(hdr), AKF(hdr), AID(hdr));
+
+ net_buf_simple_pull(buf, 1);
+
+ seq_zero = net_buf_simple_pull_be16(buf);
+ seg_o = (seq_zero & 0x03) << 3;
+ seq_zero = (seq_zero >> 2) & TRANS_SEQ_ZERO_MASK;
+ seg_n = net_buf_simple_pull_u8(buf);
+ seg_o |= seg_n >> 5;
+ seg_n &= 0x1f;
+
+ BT_DBG("SeqZero 0x%04x SegO %u SegN %u", seq_zero, seg_o, seg_n);
+
+ if (seg_o > seg_n) {
+ BT_ERR("SegO greater than SegN (%u > %u)", seg_o, seg_n);
+ return -EINVAL;
+ }
+
+ /* According to Mesh 1.0 specification:
+ * "The SeqAuth is composed of the IV Index and the sequence number
+ * (SEQ) of the first segment"
+ *
+ * Therefore we need to calculate very first SEQ in order to find
+ * seqAuth. We can calculate as below:
+ *
+ * SEQ(0) = SEQ(n) - (delta between seqZero and SEQ(n) by looking into
+ * 14 least significant bits of SEQ(n))
+ *
+ * Mentioned delta shall be >= 0, if it is not then seq_auth will
+ * be broken and it will be verified by the code below.
+ */
+ *seq_auth = SEQ_AUTH(BT_MESH_NET_IVI_RX(net_rx),
+ (net_rx->seq -
+ ((((net_rx->seq & BIT_MASK(14)) - seq_zero)) &
+ BIT_MASK(13))));
+
+ *seg_count = seg_n + 1;
+
+ /* Look for old RX sessions */
+ rx = seg_rx_find(net_rx, seq_auth);
+ if (rx) {
+ /* Discard old SeqAuth packet */
+ if (rx->seq_auth > *seq_auth) {
+ BT_WARN("Ignoring old SeqAuth");
+ return -EINVAL;
+ }
+
+ if (!seg_rx_is_valid(rx, net_rx, hdr, seg_n)) {
+ return -EINVAL;
+ }
+
+ if (rx->in_use) {
+ BT_DBG("Existing RX context. Block 0x%08x",
+ (unsigned) rx->block);
+ goto found_rx;
+ }
+
+ if (rx->block == BLOCK_COMPLETE(rx->seg_n)) {
+ BT_WARN("Got segment for already complete SDU");
+
+ send_ack(net_rx->sub, net_rx->ctx.recv_dst,
+ net_rx->ctx.addr, net_rx->ctx.send_ttl,
+ seq_auth, rx->block, rx->obo);
+
+ if (rpl) {
+ update_rpl(rpl, net_rx);
+ }
+
+ return -EALREADY;
+ }
+
+ /* We ignore instead of sending block ack 0 since the
+ * ack timer is always smaller than the incomplete
+ * timer, i.e. the sender is misbehaving.
+ */
+ BT_WARN("Got segment for canceled SDU");
+ return -EINVAL;
+ }
+
+ /* Bail out early if we're not ready to receive such a large SDU */
+ if (!sdu_len_is_ok(net_rx->ctl, seg_n)) {
+ BT_ERR("Too big incoming SDU length");
+ send_ack(net_rx->sub, net_rx->ctx.recv_dst, net_rx->ctx.addr,
+ net_rx->ctx.send_ttl, seq_auth, 0,
+ net_rx->friend_match);
+ return -EMSGSIZE;
+ }
+
+ /* Verify early that there will be space in the Friend Queue(s) in
+ * case this message is destined to an LPN of ours.
+ */
+ if (IS_ENABLED(CONFIG_BT_MESH_FRIEND) &&
+ net_rx->friend_match && !net_rx->local_match &&
+ !bt_mesh_friend_queue_has_space(net_rx->sub->net_idx,
+ net_rx->ctx.addr,
+ net_rx->ctx.recv_dst, seq_auth,
+ *seg_count)) {
+ BT_ERR("No space in Friend Queue for %u segments", *seg_count);
+ send_ack(net_rx->sub, net_rx->ctx.recv_dst, net_rx->ctx.addr,
+ net_rx->ctx.send_ttl, seq_auth, 0,
+ net_rx->friend_match);
+ return -ENOBUFS;
+ }
+
+ /* Look for free slot for a new RX session */
+ rx = seg_rx_alloc(net_rx, hdr, seq_auth, seg_n);
+ if (!rx) {
+ /* Warn but don't cancel since the existing slots willl
+ * eventually be freed up and we'll be able to process
+ * this one.
+ */
+ BT_WARN("No free slots for new incoming segmented messages");
+ return -ENOMEM;
+ }
+
+ rx->obo = net_rx->friend_match;
+
+found_rx:
+ if (BIT(seg_o) & rx->block) {
+ BT_WARN("Received already received fragment");
+ return -EALREADY;
+ }
+
+ /* All segments, except the last one, must either have 8 bytes of
+ * payload (for 64bit Net MIC) or 12 bytes of payload (for 32bit
+ * Net MIC).
+ */
+ if (seg_o == seg_n) {
+ /* Set the expected final buffer length */
+ rx->buf->om_len = seg_n * seg_len(rx->ctl) + buf->om_len;
+ BT_DBG("Target len %u * %u + %u = %u", seg_n, seg_len(rx->ctl),
+ buf->om_len, rx->buf->om_len);
+
+ if (rx->buf->om_len > MYNEWT_VAL(BLE_MESH_RX_SDU_MAX)) {
+ BT_ERR("Too large SDU len");
+ send_ack(net_rx->sub, net_rx->ctx.recv_dst,
+ net_rx->ctx.addr, net_rx->ctx.send_ttl,
+ seq_auth, 0, rx->obo);
+ seg_rx_reset(rx, true);
+ return -EMSGSIZE;
+ }
+ } else {
+ if (buf->om_len != seg_len(rx->ctl)) {
+ BT_ERR("Incorrect segment size for message type");
+ return -EINVAL;
+ }
+ }
+
+ /* Reset the Incomplete Timer */
+ rx->last = k_uptime_get_32();
+
+ if (!k_delayed_work_remaining_get(&rx->ack) &&
+ !bt_mesh_lpn_established()) {
+ k_delayed_work_submit(&rx->ack, ack_timeout(rx));
+ }
+
+ /* Location in buffer can be calculated based on seg_o & rx->ctl */
+ memcpy(rx->buf->om_data + (seg_o * seg_len(rx->ctl)), buf->om_data, buf->om_len);
+
+ BT_DBG("Received %u/%u", seg_o, seg_n);
+
+ /* Mark segment as received */
+ rx->block |= BIT(seg_o);
+
+ if (rx->block != BLOCK_COMPLETE(seg_n)) {
+ *pdu_type = BT_MESH_FRIEND_PDU_PARTIAL;
+ return 0;
+ }
+
+ BT_DBG("Complete SDU");
+
+ if (rpl) {
+ update_rpl(rpl, net_rx);
+ }
+
+ *pdu_type = BT_MESH_FRIEND_PDU_COMPLETE;
+
+ k_delayed_work_cancel(&rx->ack);
+ send_ack(net_rx->sub, net_rx->ctx.recv_dst, net_rx->ctx.addr,
+ net_rx->ctx.send_ttl, seq_auth, rx->block, rx->obo);
+
+ if (net_rx->ctl) {
+ err = ctl_recv(net_rx, *hdr, rx->buf, seq_auth);
+ } else {
+ err = sdu_recv(net_rx, (rx->seq_auth & 0xffffff), *hdr,
+ ASZMIC(hdr), rx->buf);
+ }
+
+ seg_rx_reset(rx, false);
+
+ return err;
+}
+
+int bt_mesh_trans_recv(struct os_mbuf *buf, struct bt_mesh_net_rx *rx)
+{
+ u64_t seq_auth = TRANS_SEQ_AUTH_NVAL;
+ enum bt_mesh_friend_pdu_type pdu_type = BT_MESH_FRIEND_PDU_SINGLE;
+ struct net_buf_simple_state state;
+ u8_t seg_count = 0;
+ int err;
+
+ if (IS_ENABLED(CONFIG_BT_MESH_FRIEND)) {
+ rx->friend_match = bt_mesh_friend_match(rx->sub->net_idx,
+ rx->ctx.recv_dst);
+ } else {
+ rx->friend_match = false;
+ }
+
+ BT_DBG("src 0x%04x dst 0x%04x seq 0x%08x friend_match %u",
+ rx->ctx.addr, rx->ctx.recv_dst, (unsigned) rx->seq,
+ rx->friend_match);
+
+ /* Remove network headers */
+ net_buf_simple_pull(buf, BT_MESH_NET_HDR_LEN);
+
+ BT_DBG("Payload %s", bt_hex(buf->om_data, buf->om_len));
+
+ if (IS_ENABLED(CONFIG_BT_TESTING)) {
+ bt_test_mesh_net_recv(rx->ctx.recv_ttl, rx->ctl, rx->ctx.addr,
+ rx->ctx.recv_dst, buf->om_data, buf->om_len);
+ }
+
+ /* If LPN mode is enabled messages are only accepted when we've
+ * requested the Friend to send them. The messages must also
+ * be encrypted using the Friend Credentials.
+ */
+ if ((MYNEWT_VAL(BLE_MESH_LOW_POWER)) &&
+ bt_mesh_lpn_established() && rx->net_if == BT_MESH_NET_IF_ADV &&
+ (!bt_mesh_lpn_waiting_update() || !rx->friend_cred)) {
+ BT_WARN("Ignoring unexpected message in Low Power mode");
+ return -EAGAIN;
+ }
+
+ /* Save the app-level state so the buffer can later be placed in
+ * the Friend Queue.
+ */
+ net_buf_simple_save(buf, &state);
+
+ if (SEG(buf->om_data)) {
+ /* Segmented messages must match a local element or an
+ * LPN of this Friend.
+ */
+ if (!rx->local_match && !rx->friend_match) {
+ return 0;
+ }
+
+ err = trans_seg(buf, rx, &pdu_type, &seq_auth, &seg_count);
+ } else {
+ seg_count = 1;
+ err = trans_unseg(buf, rx, &seq_auth);
+ }
+
+ /* Notify LPN state machine so a Friend Poll will be sent. If the
+ * message was a Friend Update it's possible that a Poll was already
+ * queued for sending, however that's fine since then the
+ * bt_mesh_lpn_waiting_update() function will return false:
+ * we still need to go through the actual sending to the bearer and
+ * wait for ReceiveDelay before transitioning to WAIT_UPDATE state.
+ * Another situation where we want to notify the LPN state machine
+ * is if it's configured to use an automatic Friendship establishment
+ * timer, in which case we want to reset the timer at this point.
+ *
+ */
+ if (IS_ENABLED(CONFIG_BT_MESH_LOW_POWER) &&
+ (bt_mesh_lpn_timer() ||
+ (bt_mesh_lpn_established() && bt_mesh_lpn_waiting_update()))) {
+ bt_mesh_lpn_msg_received(rx);
+ }
+
+ net_buf_simple_restore(buf, &state);
+
+ if (IS_ENABLED(CONFIG_BT_MESH_FRIEND) && rx->friend_match && !err) {
+ if (seq_auth == TRANS_SEQ_AUTH_NVAL) {
+ bt_mesh_friend_enqueue_rx(rx, pdu_type, NULL,
+ seg_count, buf);
+ } else {
+ bt_mesh_friend_enqueue_rx(rx, pdu_type, &seq_auth,
+ seg_count, buf);
+ }
+ }
+
+ return err;
+}
+
+void bt_mesh_rx_reset(void)
+{
+ int i;
+
+ BT_DBG("");
+
+ for (i = 0; i < ARRAY_SIZE(seg_rx); i++) {
+ seg_rx_reset(&seg_rx[i], true);
+ }
+
+ if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
+ bt_mesh_clear_rpl();
+ } else {
+ memset(bt_mesh.rpl, 0, sizeof(bt_mesh.rpl));
+ }
+}
+
+void bt_mesh_tx_reset(void)
+{
+ int i;
+
+ BT_DBG("");
+
+ for (i = 0; i < ARRAY_SIZE(seg_tx); i++) {
+ seg_tx_reset(&seg_tx[i]);
+ }
+}
+
+void bt_mesh_trans_init(void)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(seg_tx); i++) {
+ k_delayed_work_init(&seg_tx[i].retransmit, seg_retransmit);
+ k_delayed_work_add_arg(&seg_tx[i].retransmit, &seg_tx[i]);
+ }
+
+ /* XXX Probably we need mempool for that.
+ * For now we increase MSYS_1_BLOCK_COUNT
+ */
+ for (i = 0; i < ARRAY_SIZE(seg_rx); i++) {
+ seg_rx[i].buf = NET_BUF_SIMPLE(MYNEWT_VAL(BLE_MESH_RX_SDU_MAX));
+ k_delayed_work_init(&seg_rx[i].ack, seg_ack);
+ k_delayed_work_add_arg(&seg_rx[i].ack, &seg_rx[i]);
+ }
+}
+
+void bt_mesh_rpl_clear(void)
+{
+ BT_DBG("");
+ memset(bt_mesh.rpl, 0, sizeof(bt_mesh.rpl));
+}
+
+void bt_mesh_heartbeat_send(void)
+{
+ struct bt_mesh_cfg_srv *cfg = bt_mesh_cfg_get();
+ u16_t feat = 0U;
+ struct __packed {
+ u8_t init_ttl;
+ u16_t feat;
+ } hb;
+ struct bt_mesh_msg_ctx ctx = {
+ .net_idx = cfg->hb_pub.net_idx,
+ .app_idx = BT_MESH_KEY_UNUSED,
+ .addr = cfg->hb_pub.dst,
+ .send_ttl = cfg->hb_pub.ttl,
+ };
+ struct bt_mesh_net_tx tx = {
+ .sub = bt_mesh_subnet_get(cfg->hb_pub.net_idx),
+ .ctx = &ctx,
+ .src = bt_mesh_model_elem(cfg->model)->addr,
+ .xmit = bt_mesh_net_transmit_get(),
+ };
+
+ /* Do nothing if heartbeat publication is not enabled */
+ if (cfg->hb_pub.dst == BT_MESH_ADDR_UNASSIGNED) {
+ return;
+ }
+
+ hb.init_ttl = cfg->hb_pub.ttl;
+
+ if (bt_mesh_relay_get() == BT_MESH_RELAY_ENABLED) {
+ feat |= BT_MESH_FEAT_RELAY;
+ }
+
+ if (bt_mesh_gatt_proxy_get() == BT_MESH_GATT_PROXY_ENABLED) {
+ feat |= BT_MESH_FEAT_PROXY;
+ }
+
+ if (bt_mesh_friend_get() == BT_MESH_FRIEND_ENABLED) {
+ feat |= BT_MESH_FEAT_FRIEND;
+ }
+
+ if (bt_mesh_lpn_established()) {
+ feat |= BT_MESH_FEAT_LOW_POWER;
+ }
+
+ hb.feat = sys_cpu_to_be16(feat);
+
+ BT_DBG("InitTTL %u feat 0x%04x", cfg->hb_pub.ttl, feat);
+
+ bt_mesh_ctl_send(&tx, TRANS_CTL_OP_HEARTBEAT, &hb, sizeof(hb),
+ NULL, NULL, NULL);
+}
+
+int bt_mesh_app_key_get(const struct bt_mesh_subnet *subnet, u16_t app_idx,
+ u16_t addr, const u8_t **key, u8_t *aid)
+{
+ 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)) {
+ *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)) {
+ return -EINVAL;
+ }
+
+ struct bt_mesh_node *node = bt_mesh_node_find(addr);
+ if (!node) {
+ return -EINVAL;
+ }
+
+ *key = node->dev_key;
+ *aid = 0;
+ return 0;
+ }
+
+ if (!subnet) {
+ return -EINVAL;
+ }
+
+ app_key = bt_mesh_app_key_find(app_idx);
+ if (!app_key) {
+ return -ENOENT;
+ }
+
+ if (subnet->kr_phase == BT_MESH_KR_PHASE_2 && app_key->updated) {
+ *key = app_key->keys[1].val;
+ *aid = app_key->keys[1].id;
+ } else {
+ *key = app_key->keys[0].val;
+ *aid = app_key->keys[0].id;
+ }
+
+ return 0;
+}
diff --git a/src/libs/mynewt-nimble/nimble/host/mesh/src/transport.h b/src/libs/mynewt-nimble/nimble/host/mesh/src/transport.h
new file mode 100644
index 00000000..eff768e9
--- /dev/null
+++ b/src/libs/mynewt-nimble/nimble/host/mesh/src/transport.h
@@ -0,0 +1,105 @@
+/* Bluetooth Mesh */
+
+/*
+ * Copyright (c) 2017 Intel Corporation
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include "syscfg/syscfg.h"
+#include "mesh/mesh.h"
+
+#define TRANS_SEQ_AUTH_NVAL 0xffffffffffffffff
+
+#define BT_MESH_TX_SDU_MAX (CONFIG_BT_MESH_TX_SEG_MAX * 12)
+
+#define TRANS_SEQ_ZERO_MASK ((u16_t)BIT_MASK(13))
+#define TRANS_CTL_OP_MASK ((u8_t)BIT_MASK(7))
+#define TRANS_CTL_OP(data) ((data)[0] & TRANS_CTL_OP_MASK)
+#define TRANS_CTL_HDR(op, seg) ((op & TRANS_CTL_OP_MASK) | (seg << 7))
+
+#define TRANS_CTL_OP_ACK 0x00
+#define TRANS_CTL_OP_FRIEND_POLL 0x01
+#define TRANS_CTL_OP_FRIEND_UPDATE 0x02
+#define TRANS_CTL_OP_FRIEND_REQ 0x03
+#define TRANS_CTL_OP_FRIEND_OFFER 0x04
+#define TRANS_CTL_OP_FRIEND_CLEAR 0x05
+#define TRANS_CTL_OP_FRIEND_CLEAR_CFM 0x06
+#define TRANS_CTL_OP_FRIEND_SUB_ADD 0x07
+#define TRANS_CTL_OP_FRIEND_SUB_REM 0x08
+#define TRANS_CTL_OP_FRIEND_SUB_CFM 0x09
+#define TRANS_CTL_OP_HEARTBEAT 0x0a
+
+struct bt_mesh_ctl_friend_poll {
+ u8_t fsn;
+}__attribute__((__packed__));
+
+struct bt_mesh_ctl_friend_update {
+ u8_t flags;
+ u32_t iv_index;
+ u8_t md;
+}__attribute__((__packed__));
+
+struct bt_mesh_ctl_friend_req {
+ u8_t criteria;
+ u8_t recv_delay;
+ u8_t poll_to[3];
+ u16_t prev_addr;
+ u8_t num_elem;
+ u16_t lpn_counter;
+}__attribute__((__packed__));
+
+struct bt_mesh_ctl_friend_offer {
+ u8_t recv_win;
+ u8_t queue_size;
+ u8_t sub_list_size;
+ s8_t rssi;
+ u16_t frnd_counter;
+}__attribute__((__packed__));
+
+struct bt_mesh_ctl_friend_clear {
+ u16_t lpn_addr;
+ u16_t lpn_counter;
+}__attribute__((__packed__));
+
+struct bt_mesh_ctl_friend_clear_confirm {
+ u16_t lpn_addr;
+ u16_t lpn_counter;
+}__attribute__((__packed__));
+
+#define BT_MESH_FRIEND_SUB_MIN_LEN (1 + 2)
+struct bt_mesh_ctl_friend_sub {
+ u8_t xact;
+ u16_t addr_list[5];
+}__attribute__((__packed__));
+
+struct bt_mesh_ctl_friend_sub_confirm {
+ u8_t xact;
+}__attribute__((__packed__));
+
+void bt_mesh_set_hb_sub_dst(u16_t addr);
+
+struct bt_mesh_app_key *bt_mesh_app_key_find(u16_t app_idx);
+
+bool bt_mesh_tx_in_progress(void);
+
+void bt_mesh_rx_reset(void);
+void bt_mesh_tx_reset(void);
+
+int bt_mesh_ctl_send(struct bt_mesh_net_tx *tx, u8_t ctl_op, void *data,
+ size_t data_len, u64_t *seq_auth,
+ const struct bt_mesh_send_cb *cb, void *cb_data);
+
+int bt_mesh_trans_send(struct bt_mesh_net_tx *tx, struct os_mbuf *msg,
+ const struct bt_mesh_send_cb *cb, void *cb_data);
+
+int bt_mesh_trans_recv(struct os_mbuf *buf, struct bt_mesh_net_rx *rx);
+
+void bt_mesh_trans_init(void);
+
+void bt_mesh_rpl_clear(void);
+
+void bt_mesh_heartbeat_send(void);
+
+int bt_mesh_app_key_get(const struct bt_mesh_subnet *subnet, u16_t app_idx,
+ u16_t addr, const u8_t **key, u8_t *aid);
diff --git a/src/libs/mynewt-nimble/nimble/host/mesh/syscfg.yml b/src/libs/mynewt-nimble/nimble/host/mesh/syscfg.yml
new file mode 100644
index 00000000..98632232
--- /dev/null
+++ b/src/libs/mynewt-nimble/nimble/host/mesh/syscfg.yml
@@ -0,0 +1,661 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+syscfg.defs:
+ BLE_MESH_PROV:
+ description: >
+ Enable provisioning. It is automatically enabled whenever
+ BLE_MESH_PB_ADV or BLE_MESH_PB_GATT is set.
+ value: 1
+
+ BLE_MESH_PB_ADV:
+ description: >
+ Enable this option to allow the device to be provisioned over
+ the advertising bearer.
+ value: 1
+
+ BLE_MESH_PROVISIONER:
+ description: >
+ Enable this option to have support for provisioning remote devices.
+ value: 0
+ restrictions:
+ - (BLE_MESH_PROV)
+
+ BLE_MESH_NODE_COUNT:
+ description: >
+ This option specifies how many nodes each network can at most
+ save in the provisioning database. Range 1-4096
+ value: 1
+
+ BLE_MESH_PROXY:
+ description: >
+ Enable proxy. This is automatically set whenever BLE_MESH_PB_GATT or
+ BLE_MESH_GATT_PROXY is set.
+ value: 0
+
+ BLE_MESH_PB_GATT:
+ description: >
+ Enable this option to allow the device to be provisioned over
+ the GATT bearer.
+ value: 1
+
+ BLE_MESH_GATT_PROXY:
+ description: >
+ This option enables support for the Mesh GATT Proxy Service,
+ i.e. the ability to act as a proxy between a Mesh GATT Client
+ and a Mesh network.
+ value: 1
+
+ BLE_MESH_NODE_ID_TIMEOUT:
+ description: >
+ This option determines for how long the local node advertises
+ using Node Identity. The given value is in seconds. The
+ specification limits this to 60 seconds, and implies that to
+ be the appropriate value as well, so just leaving this as the
+ default is the safest option.
+ value: 60
+
+ BLE_MESH_PROXY_FILTER_SIZE:
+ descryption: >
+ This option specifies how many Proxy Filter entries the local
+ node supports.
+ value: 1
+
+ BLE_MESH_SUBNET_COUNT:
+ description: >
+ This option specifies how many subnets a Mesh network can
+ participate in at the same time.
+ value: 1
+
+ BLE_MESH_APP_KEY_COUNT:
+ description: >
+ This option specifies how many application keys the device can
+ store per network.
+ value: 1
+
+ BLE_MESH_MODEL_KEY_COUNT:
+ description: >
+ This option specifies how many application keys each model can
+ at most be bound to.
+ value: 1
+
+ BLE_MESH_MODEL_GROUP_COUNT:
+ description: >
+ This option specifies how many group addresses each model can
+ at most be subscribed to.
+ value: 1
+
+ BLE_MESH_LABEL_COUNT:
+ description: >
+ This option specifies how many Label UUIDs can be stored.
+ value: 1
+
+ BLE_MESH_CRPL:
+ description: >
+ This options specifies the maximum capacity of the replay
+ protection list. This option is similar to the network message
+ cache size, but has a different purpose.
+ value: 10
+
+ BLE_MESH_ADV_TASK_PRIO:
+ description: >
+ Advertising task prio (FIXME)
+ type: task_priority
+ value: 9
+
+ BLE_MESH_MSG_CACHE_SIZE:
+ description: >
+ Number of messages that are cached for the network. This description
+ prevent unnecessary decryption operations and unnecessary
+ relays. This option is similar to the replay protection list,
+ but has a different purpose.
+ value: 10
+
+ BLE_MESH_ADV_BUF_COUNT:
+ description: >
+ Number of advertising buffers available. This should be chosen
+ based on what kind of features the local node shoule have. E.g.
+ a relay will perform better the more buffers it has. Another
+ thing to consider is outgoing segmented messages. There must
+ be at least three more advertising buffers than the maximum
+ supported outgoing segment count (BT_MESH_TX_SEG_MAX).
+ value: 6
+
+ BLE_MESH_IVU_DIVIDER:
+ description: >
+ When the IV Update state enters Normal operation or IV Update
+ in Progress, we need to keep track of how many hours has passed
+ in the state, since the specification requires us to remain in
+ the state at least for 96 hours (Update in Progress has an
+ additional upper limit of 144 hours).
+
+ In order to fulfil the above requirement, even if the node might
+ be powered off once in a while, we need to store persistently
+ how many hours the node has been in the state. This doesn't
+ necessarily need to happen every hour (thanks to the flexible
+ duration range). The exact cadence will depend a lot on the
+ ways that the node will be used and what kind of power source it
+ has.
+
+ Since there is no single optimal answer, this configuration
+ option allows specifying a divider, i.e. how many intervals
+ the 96 hour minimum gets split into. After each interval the
+ duration that the node has been in the current state gets
+ stored to flash. E.g. the default value of 4 means that the
+ state is saved every 24 hours (96 / 4).
+ value: 4
+
+ BLE_MESH_TX_SEG_MSG_COUNT:
+ description: >
+ Maximum number of simultaneous outgoing multi-segment and/or
+ reliable messages.
+ value: 4
+
+ BLE_MESH_RX_SEG_MSG_COUNT:
+ description: >
+ Maximum number of simultaneous incoming multi-segment and/or
+ reliable messages.
+ value: 2
+
+ BLE_MESH_RX_SDU_MAX:
+ description: >
+ Maximum incoming Upper Transport Access PDU length. This
+ determines also how many segments incoming segmented messages
+ can have. Each segment can contain 12 bytes, so this value should
+ be set to a multiple of 12 to avoid wasted memory. The minimum
+ requirement is 2 segments (24 bytes) whereas the maximum supported
+ by the Mesh specification is 32 segments (384 bytes).
+ value: 72
+
+ BLE_MESH_TX_SEG_MAX:
+ description: >
+ Maximum number of segments supported for outgoing messages.
+ This value should typically be fine-tuned based on what
+ models the local node supports, i.e. what's the largest
+ message payload that the node needs to be able to send.
+ This value affects memory and call stack consumption, which
+ is why the default is lower than the maximum that the
+ specification would allow (32 segments).
+
+ The maximum outgoing SDU size is 12 times this number (out of
+ which 4 or 8 bytes is used for the Transport Layer MIC). For
+ example, 5 segments means the maximum SDU size is 60 bytes,
+ which leaves 56 bytes for application layer data using a
+ 4-byte MIC and 52 bytes using an 8-byte MIC.
+
+ Be sure to specify a sufficient number of advertising buffers
+ when setting this option to a higher value. There must be at
+ least three more advertising buffers (BT_MESH_ADV_BUF_COUNT)
+ as there are outgoing segments.
+ value: 3
+
+ BLE_MESH_SEG_RETRANSMIT_ATTEMPTS:
+ description: >
+ Number of retransmit attempts (after the initial transmit) per segment
+ value: 4
+ retrictions: 'BLE_MESH_SEG_RETRANSMIT_ATTEMPTS > 1'
+
+ BLE_MESH_RELAY:
+ description: >
+ Support for acting as a Mesh Relay Node.
+ value: 0
+
+ BLE_MESH_LOW_POWER:
+ description: >
+ Enable this option to be able to act as a Low Power Node.
+ value: 0
+
+ BLE_MESH_LPN_ESTABLISHMENT:
+ description: >
+ Perform the Friendship establishment using low power, with
+ the help of a reduced scan duty cycle. The downside of this
+ is that the node may miss out on messages intended for it
+ until it has successfully set up Friendship with a Friend
+ node.
+ value: 1
+
+ BLE_MESH_LPN_AUTO:
+ description: >
+ Automatically enable LPN functionality once provisioned and start
+ looking for Friend nodes. If this option is disabled LPN mode
+ needs to be manually enabled by calling bt_mesh_lpn_set(true).
+ node.
+ value: 1
+
+ BLE_MESH_LPN_AUTO_TIMEOUT:
+ description: >
+ Time in seconds from the last received message, that the node
+ will wait before starting to look for Friend nodes.
+ value: 15
+
+ BLE_MESH_LPN_RETRY_TIMEOUT:
+ description: >
+ Time in seconds between Friend Requests, if a previous Friend
+ Request did not receive any acceptable Friend Offers.
+ value: 8
+
+ BLE_MESH_LPN_RSSI_FACTOR:
+ description: >
+ The contribution of the RSSI measured by the Friend node used
+ in Friend Offer Delay calculations. 0 = 1, 1 = 1.5, 2 = 2, 3 = 2.5.
+ value: 0
+
+ BLE_MESH_LPN_RECV_WIN_FACTOR:
+ description: >
+ The contribution of the supported Receive Window used in
+ Friend Offer Delay calculations. 0 = 1, 1 = 1.5, 2 = 2, 3 = 2.5.
+ value: 0
+
+ BLE_MESH_LPN_MIN_QUEUE_SIZE:
+ description: >
+ The MinQueueSizeLog field is defined as log_2(N), where N is
+ the minimum number of maximum size Lower Transport PDUs that
+ the Friend node can store in its Friend Queue. As an example,
+ MinQueueSizeLog value 1 gives N = 2, and value 7 gives N = 128.
+ value: 1
+
+ BLE_MESH_LPN_RECV_DELAY:
+ description: >
+ The ReceiveDelay is the time between the Low Power node
+ sending a request and listening for a response. This delay
+ allows the Friend node time to prepare the response. The value
+ is in units of milliseconds.
+ value: 100
+
+ BLE_MESH_LPN_POLL_TIMEOUT:
+ description: >
+ PollTimeout timer is used to measure time between two
+ consecutive requests sent by the Low Power node. If no
+ requests are received by the Friend node before the
+ PollTimeout timer expires, then the friendship is considered
+ terminated. The value is in units of 100 milliseconds, so e.g.
+ a value of 300 means 30 seconds.
+ value: 300
+
+ BLE_MESH_LPN_INIT_POLL_TIMEOUT:
+ description: >
+ The initial value of the PollTimeout timer when Friendship
+ gets established for the first time. After this the timeout
+ will gradually grow toward the actual PollTimeout, doubling
+ in value for each iteration. The value is in units of 100
+ milliseconds, so e.g. a value of 300 means 3 seconds.
+ value: MYNEWT_VAL_BLE_MESH_LPN_POLL_TIMEOUT
+
+ BLE_MESH_LPN_SCAN_LATENCY:
+ description: >
+ Latency in milliseconds that it takes to enable scanning. This
+ is in practice how much time in advance before the Receive Window
+ that scanning is requested to be enabled.
+ value: 10
+
+ BLE_MESH_LPN_GROUPS:
+ description: >
+ Maximum number of groups that the LPN can subscribe to.
+ value: 10
+
+ BLE_MESH_FRIEND:
+ description: >
+ Enable this option to be able to act as a Friend Node.
+ value: 0
+
+ BLE_MESH_FRIEND_RECV_WIN:
+ description: >
+ Receive Window in milliseconds supported by the Friend node.
+ value: 255
+
+ BLE_MESH_FRIEND_QUEUE_SIZE:
+ description: >
+ Minimum number of buffers available to be stored for each
+ local Friend Queue.
+ value: 16
+
+ BLE_MESH_FRIEND_SUB_LIST_SIZE:
+ description: >
+ Size of the Subscription List that can be supported by a
+ Friend node for a Low Power node.
+ value: 3
+
+ BLE_MESH_FRIEND_LPN_COUNT:
+ description: >
+ Number of Low Power Nodes the Friend can have a Friendship
+ with simultaneously.
+ value: 2
+
+ BLE_MESH_FRIEND_SEG_RX:
+ description: >
+ Number of incomplete segment lists that we track for each LPN
+ that we are Friends for. In other words, this determines how
+ many elements we can simultaneously be receiving segmented
+ messages from when the messages are going into the Friend queue.
+ value: 1
+
+ BLE_MESH_CFG_CLI:
+ description: >
+ Enable support for the configuration client model.
+ value: 0
+
+ BLE_MESH_HEALTH_CLI:
+ description: >
+ Enable support for the health client model.
+ value: 0
+
+ BLE_MESH_SHELL:
+ description: >
+ Activate shell module that provides Bluetooth Mesh commands to
+ the console.
+ value: 0
+
+ BLE_MESH_MODEL_EXTENSIONS:
+ description: >
+ Enable support for the model extension concept, allowing the Access
+ layer to know about Mesh model relationships.
+ value: 0
+
+ BLE_MESH_IV_UPDATE_TEST:
+ description: >
+ This option removes the 96 hour limit of the IV Update
+ Procedure and lets the state be changed at any time.
+ value: 0
+
+ BLE_MESH_TESTING:
+ description: >
+ This option enables testing API.
+ value: 0
+
+ BLE_MESH_DEV_UUID:
+ description: >
+ Device UUID
+ value: ((uint8_t[16]){0x11, 0x22, 0})
+
+ BLE_MESH_SHELL_MODELS:
+ description: >
+ Include implementation of some demo models.
+ value: 0
+
+ BLE_MESH_OOB_OUTPUT_ACTIONS:
+ description: >
+ Supported Output OOB Actions
+ BT_MESH_NO_OUTPUT = 0,
+ BT_MESH_BLINK = BIT(0)
+ BT_MESH_BEEP = BIT(1)
+ BT_MESH_VIBRATE = BIT(2)
+ BT_MESH_DISPLAY_NUMBER = BIT(3)
+ BT_MESH_DISPLAY_STRING = BIT(4)
+ value: ((BT_MESH_DISPLAY_NUMBER))
+
+ BLE_MESH_OOB_OUTPUT_SIZE:
+ description: >
+ Output OOB size
+ value: 4
+
+ BLE_MESH_OOB_INPUT_ACTIONS:
+ description: >
+ Supported Input OOB Actions
+ BT_MESH_NO_INPUT = 0,
+ BT_MESH_PUSH = BIT(0)
+ BT_MESH_TWIST = BIT(1)
+ BT_MESH_ENTER_NUMBER = BIT(2)
+ BT_MESH_ENTER_STRING = BIT(3)
+ value: ((BT_MESH_NO_INPUT))
+
+ BLE_MESH_OOB_INPUT_SIZE:
+ description: >
+ Input OOB size
+ value: 4
+
+ BLE_MESH_SETTINGS:
+ description: >
+ This option enables Mesh settings storage.
+ value: 1
+
+ BLE_MESH_STORE_TIMEOUT:
+ description: >
+ This value defines in seconds how soon any pending changes
+ are actually written into persistent storage (flash) after
+ a change occurs.
+ value: 2
+
+ BLE_MESH_SEQ_STORE_RATE:
+ description: >
+ This value defines how often the local sequence number gets
+ updated in persistent storage (i.e. flash). E.g. a value of 100
+ means that the sequence number will be stored to flash on every
+ 100th increment. If the node sends messages very frequently a
+ higher value makes more sense, whereas if the node sends
+ infrequently a value as low as 0 (update storage for every
+ increment) can make sense. When the stack gets initialized it
+ will add this number to the last stored one, so that it starts
+ off with a value that's guaranteed to be larger than the last
+ one used before power off.
+ value: 128
+
+ BLE_MESH_RPL_STORE_TIMEOUT:
+ description: >
+ This value defines in seconds how soon the RPL gets written to
+ persistent storage after a change occurs. If the node receives
+ messages frequently it may make sense to have this set to a
+ large value, whereas if the RPL gets updated infrequently a
+ value as low as 0 (write immediately) may make sense. Note that
+ if the node operates a security sensitive use case, and there's
+ a risk of sudden power loss, it may be a security vulnerability
+ to set this value to anything else than 0 (a power loss before
+ writing to storage exposes the node to potential message
+ replay attacks).
+ value: 5
+
+ BLE_MESH_DEVICE_NAME:
+ description: >
+ This value defines BLE Mesh device/node name.
+ value: '"nimble-mesh-node"'
+
+ BLE_MESH_SYSINIT_STAGE:
+ description: >
+ Primary sysinit stage for BLE mesh functionality.
+ value: 500
+
+ BLE_MESH_SYSINIT_STAGE_SHELL:
+ description: >
+ Secondary sysinit stage for BLE mesh functionality.
+ value: 1000
+
+ ### Log settings.
+
+ BLE_MESH_LOG_MOD:
+ description: >
+ Numeric module ID to use for BLE Mesh log messages.
+ value: 9
+ BLE_MESH_LOG_LVL:
+ description: >
+ Minimum level for the BLE Mesh log.
+ value: 1
+
+ BLE_MESH_ACCESS_LOG_MOD:
+ description: >
+ Numeric module ID to use for BLE Mesh Access-related log messages.
+ value: 10
+ BLE_MESH_ACCESS_LOG_LVL:
+ description: >
+ Minimum level for the BLE Mesh Access-related log.
+ value: 1
+
+ BLE_MESH_ADV_LOG_MOD:
+ description: >
+ Numeric module ID to use for BLE Mesh advertising log messages.
+ value: 11
+ BLE_MESH_ADV_LOG_LVL:
+ description: >
+ Minimum level for the BLE Mesh log.
+ value: 1
+
+ BLE_MESH_BEACON_LOG_MOD:
+ description: >
+ Numeric module ID to use for BLE Mesh Beacon-related log messages.
+ value: 12
+ BLE_MESH_BEACON_LOG_LVL:
+ description: >
+ Minimum level for the BLE Mesh Beacon-related log.
+ value: 1
+
+ BLE_MESH_CRYPTO_LOG_MOD:
+ description: >
+ Numeric module ID to use for BLE Mesh cryptographic log messages.
+ value: 13
+ BLE_MESH_CRYPTO_LOG_LVL:
+ description: >
+ Minimum level for the BLE Mesh cryptographic log.
+ value: 1
+
+ BLE_MESH_FRIEND_LOG_MOD:
+ description: >
+ Numeric module ID to use for BLE Mesh Friend log messages.
+ value: 14
+ BLE_MESH_FRIEND_LOG_LVL:
+ description: >
+ Minimum level for the BLE Mesh Friend log.
+ value: 1
+
+ BLE_MESH_LOW_POWER_LOG_MOD:
+ description: >
+ Numeric module ID to use for BLE Mesh Low Power log messages.
+ value: 15
+ BLE_MESH_LOW_POWER_LOG_LVL:
+ description: >
+ Minimum level for the BLE Mesh Low Power log.
+ value: 1
+
+ BLE_MESH_MODEL_LOG_MOD:
+ description: >
+ Numeric module ID to use for BLE Mesh Foundation Models log messages.
+ value: 16
+ BLE_MESH_MODEL_LOG_LVL:
+ description: >
+ Minimum level for the BLE Mesh Foundation Models log.
+ value: 1
+
+ BLE_MESH_NET_LOG_MOD:
+ description: >
+ Numeric module ID to use for BLE Mesh Network layer log messages.
+ value: 17
+ BLE_MESH_NET_LOG_LVL:
+ description: >
+ Minimum level for the BLE Mesh Network layer log.
+ value: 1
+
+ BLE_MESH_PROV_LOG_MOD:
+ description: >
+ Numeric module ID to use for BLE Mesh Provisioning log messages.
+ value: 18
+ BLE_MESH_PROV_LOG_LVL:
+ description: >
+ Minimum level for the BLE Mesh Provisioning log.
+ value: 1
+
+ BLE_MESH_PROXY_LOG_MOD:
+ description: >
+ Numeric module ID to use for BLE Mesh Proxy protocol log messages.
+ value: 19
+ BLE_MESH_PROXY_LOG_LVL:
+ description: >
+ Minimum level for the BLE Mesh Proxy protocol log.
+ value: 1
+
+ BLE_MESH_SETTINGS_LOG_MOD:
+ description: >
+ Numeric module ID to use for BLE Mesh persistent settings log messages.
+ value: 20
+ BLE_MESH_SETTINGS_LOG_LVL:
+ description: >
+ Minimum level for the BLE Mesh persistent settings log.
+ value: 1
+
+ BLE_MESH_TRANS_LOG_MOD:
+ description: >
+ Numeric module ID to use for BLE Mesh Transport Layer log messages.
+ value: 21
+ BLE_MESH_TRANS_LOG_LVL:
+ description: >
+ Minimum level for the BLE Mesh Transport Layer log.
+ value: 1
+
+syscfg.logs:
+ BLE_MESH_LOG:
+ module: MYNEWT_VAL(BLE_MESH_LOG_MOD)
+ level: MYNEWT_VAL(BLE_MESH_LOG_LVL)
+
+ BLE_MESH_ACCESS_LOG:
+ module: MYNEWT_VAL(BLE_MESH_ACCESS_LOG_MOD)
+ level: MYNEWT_VAL(BLE_MESH_ACCESS_LOG_LVL)
+
+ BLE_MESH_ADV_LOG:
+ module: MYNEWT_VAL(BLE_MESH_ADV_LOG_MOD)
+ level: MYNEWT_VAL(BLE_MESH_ADV_LOG_LVL)
+
+ BLE_MESH_BEACON_LOG:
+ module: MYNEWT_VAL(BLE_MESH_BEACON_LOG_MOD)
+ level: MYNEWT_VAL(BLE_MESH_BEACON_LOG_LVL)
+
+ BLE_MESH_CRYPTO_LOG:
+ module: MYNEWT_VAL(BLE_MESH_CRYPTO_LOG_MOD)
+ level: MYNEWT_VAL(BLE_MESH_CRYPTO_LOG_LVL)
+
+ BLE_MESH_FRIEND_LOG:
+ module: MYNEWT_VAL(BLE_MESH_FRIEND_LOG_MOD)
+ level: MYNEWT_VAL(BLE_MESH_FRIEND_LOG_LVL)
+
+ BLE_MESH_LOW_POWER_LOG:
+ module: MYNEWT_VAL(BLE_MESH_LOW_POWER_LOG_MOD)
+ level: MYNEWT_VAL(BLE_MESH_LOW_POWER_LOG_LVL)
+
+ BLE_MESH_MODEL_LOG:
+ module: MYNEWT_VAL(BLE_MESH_MODEL_LOG_MOD)
+ level: MYNEWT_VAL(BLE_MESH_MODEL_LOG_LVL)
+
+ BLE_MESH_NET_LOG:
+ module: MYNEWT_VAL(BLE_MESH_NET_LOG_MOD)
+ level: MYNEWT_VAL(BLE_MESH_NET_LOG_LVL)
+
+ BLE_MESH_PROV_LOG:
+ module: MYNEWT_VAL(BLE_MESH_PROV_LOG_MOD)
+ level: MYNEWT_VAL(BLE_MESH_PROV_LOG_LVL)
+
+ BLE_MESH_PROXY_LOG:
+ module: MYNEWT_VAL(BLE_MESH_PROXY_LOG_MOD)
+ level: MYNEWT_VAL(BLE_MESH_PROXY_LOG_LVL)
+
+ BLE_MESH_SETTINGS_LOG:
+ module: MYNEWT_VAL(BLE_MESH_SETTINGS_LOG_MOD)
+ level: MYNEWT_VAL(BLE_MESH_SETTINGS_LOG_LVL)
+
+ BLE_MESH_TRANS_LOG:
+ module: MYNEWT_VAL(BLE_MESH_TRANS_LOG_MOD)
+ level: MYNEWT_VAL(BLE_MESH_TRANS_LOG_LVL)
+
+syscfg.vals.BLE_MESH_SHELL:
+ BLE_MESH_CFG_CLI: 1
+ BLE_MESH_HEALTH_CLI: 1
+ BLE_MESH_IV_UPDATE_TEST: 1
+
+syscfg.vals.BLE_MESH_GATT_PROXY:
+ BLE_MESH_PROXY: 1
+
+syscfg.vals.BLE_MESH_PB_GATT:
+ BLE_MESH_PROXY: 1
+ BLE_MESH_PROV: 1
+
+syscfg.vals.BLE_MESH_PB_ADV:
+ BLE_MESH_PROV: 1