From bdc10744fb338ae197692713a0b48a7ccc36f566 Mon Sep 17 00:00:00 2001 From: JF Date: Sun, 26 Apr 2020 10:25:59 +0200 Subject: Add Nimble in libs directory --- .../nimble/host/mesh/include/mesh/access.h | 656 ++++ .../nimble/host/mesh/include/mesh/cfg_cli.h | 234 ++ .../nimble/host/mesh/include/mesh/cfg_srv.h | 78 + .../nimble/host/mesh/include/mesh/glue.h | 502 +++ .../nimble/host/mesh/include/mesh/health_cli.h | 81 + .../nimble/host/mesh/include/mesh/health_srv.h | 100 + .../nimble/host/mesh/include/mesh/main.h | 441 +++ .../nimble/host/mesh/include/mesh/mesh.h | 26 + .../nimble/host/mesh/include/mesh/model_cli.h | 49 + .../nimble/host/mesh/include/mesh/model_srv.h | 67 + .../nimble/host/mesh/include/mesh/porting.h | 27 + .../nimble/host/mesh/include/mesh/proxy.h | 43 + .../nimble/host/mesh/include/mesh/slist.h | 468 +++ .../nimble/host/mesh/include/mesh/testing.h | 105 + src/libs/mynewt-nimble/nimble/host/mesh/pkg.yml | 49 + .../mynewt-nimble/nimble/host/mesh/src/access.c | 856 +++++ .../mynewt-nimble/nimble/host/mesh/src/access.h | 67 + src/libs/mynewt-nimble/nimble/host/mesh/src/adv.c | 439 +++ src/libs/mynewt-nimble/nimble/host/mesh/src/adv.h | 79 + .../mynewt-nimble/nimble/host/mesh/src/atomic.h | 409 +++ .../mynewt-nimble/nimble/host/mesh/src/beacon.c | 441 +++ .../mynewt-nimble/nimble/host/mesh/src/beacon.h | 26 + .../mynewt-nimble/nimble/host/mesh/src/cfg_cli.c | 1498 ++++++++ .../mynewt-nimble/nimble/host/mesh/src/cfg_srv.c | 3619 ++++++++++++++++++++ .../mynewt-nimble/nimble/host/mesh/src/crypto.c | 911 +++++ .../mynewt-nimble/nimble/host/mesh/src/crypto.h | 170 + .../nimble/host/mesh/src/foundation.h | 171 + .../mynewt-nimble/nimble/host/mesh/src/friend.c | 1651 +++++++++ .../mynewt-nimble/nimble/host/mesh/src/friend.h | 56 + src/libs/mynewt-nimble/nimble/host/mesh/src/glue.c | 870 +++++ .../nimble/host/mesh/src/health_cli.c | 556 +++ .../nimble/host/mesh/src/health_srv.c | 453 +++ .../nimble/host/mesh/src/light_model.c | 58 + .../nimble/host/mesh/src/light_model.h | 19 + src/libs/mynewt-nimble/nimble/host/mesh/src/lpn.c | 1056 ++++++ src/libs/mynewt-nimble/nimble/host/mesh/src/lpn.h | 68 + src/libs/mynewt-nimble/nimble/host/mesh/src/mesh.c | 361 ++ .../mynewt-nimble/nimble/host/mesh/src/mesh_priv.h | 39 + .../mynewt-nimble/nimble/host/mesh/src/model_cli.c | 301 ++ .../mynewt-nimble/nimble/host/mesh/src/model_srv.c | 266 ++ src/libs/mynewt-nimble/nimble/host/mesh/src/net.c | 1433 ++++++++ src/libs/mynewt-nimble/nimble/host/mesh/src/net.h | 412 +++ .../mynewt-nimble/nimble/host/mesh/src/nodes.c | 161 + .../mynewt-nimble/nimble/host/mesh/src/nodes.h | 10 + src/libs/mynewt-nimble/nimble/host/mesh/src/prov.c | 2008 +++++++++++ src/libs/mynewt-nimble/nimble/host/mesh/src/prov.h | 37 + .../mynewt-nimble/nimble/host/mesh/src/proxy.c | 1499 ++++++++ .../mynewt-nimble/nimble/host/mesh/src/proxy.h | 45 + .../mynewt-nimble/nimble/host/mesh/src/settings.c | 2083 +++++++++++ .../mynewt-nimble/nimble/host/mesh/src/settings.h | 27 + .../mynewt-nimble/nimble/host/mesh/src/shell.c | 2819 +++++++++++++++ .../mynewt-nimble/nimble/host/mesh/src/shell.h | 6 + .../mynewt-nimble/nimble/host/mesh/src/testing.c | 200 ++ .../mynewt-nimble/nimble/host/mesh/src/testing.h | 23 + .../mynewt-nimble/nimble/host/mesh/src/transport.c | 1668 +++++++++ .../mynewt-nimble/nimble/host/mesh/src/transport.h | 105 + src/libs/mynewt-nimble/nimble/host/mesh/syscfg.yml | 661 ++++ 57 files changed, 30563 insertions(+) create mode 100644 src/libs/mynewt-nimble/nimble/host/mesh/include/mesh/access.h create mode 100644 src/libs/mynewt-nimble/nimble/host/mesh/include/mesh/cfg_cli.h create mode 100644 src/libs/mynewt-nimble/nimble/host/mesh/include/mesh/cfg_srv.h create mode 100644 src/libs/mynewt-nimble/nimble/host/mesh/include/mesh/glue.h create mode 100644 src/libs/mynewt-nimble/nimble/host/mesh/include/mesh/health_cli.h create mode 100644 src/libs/mynewt-nimble/nimble/host/mesh/include/mesh/health_srv.h create mode 100644 src/libs/mynewt-nimble/nimble/host/mesh/include/mesh/main.h create mode 100644 src/libs/mynewt-nimble/nimble/host/mesh/include/mesh/mesh.h create mode 100644 src/libs/mynewt-nimble/nimble/host/mesh/include/mesh/model_cli.h create mode 100644 src/libs/mynewt-nimble/nimble/host/mesh/include/mesh/model_srv.h create mode 100644 src/libs/mynewt-nimble/nimble/host/mesh/include/mesh/porting.h create mode 100644 src/libs/mynewt-nimble/nimble/host/mesh/include/mesh/proxy.h create mode 100644 src/libs/mynewt-nimble/nimble/host/mesh/include/mesh/slist.h create mode 100644 src/libs/mynewt-nimble/nimble/host/mesh/include/mesh/testing.h create mode 100644 src/libs/mynewt-nimble/nimble/host/mesh/pkg.yml create mode 100644 src/libs/mynewt-nimble/nimble/host/mesh/src/access.c create mode 100644 src/libs/mynewt-nimble/nimble/host/mesh/src/access.h create mode 100644 src/libs/mynewt-nimble/nimble/host/mesh/src/adv.c create mode 100644 src/libs/mynewt-nimble/nimble/host/mesh/src/adv.h create mode 100644 src/libs/mynewt-nimble/nimble/host/mesh/src/atomic.h create mode 100644 src/libs/mynewt-nimble/nimble/host/mesh/src/beacon.c create mode 100644 src/libs/mynewt-nimble/nimble/host/mesh/src/beacon.h create mode 100644 src/libs/mynewt-nimble/nimble/host/mesh/src/cfg_cli.c create mode 100644 src/libs/mynewt-nimble/nimble/host/mesh/src/cfg_srv.c create mode 100644 src/libs/mynewt-nimble/nimble/host/mesh/src/crypto.c create mode 100644 src/libs/mynewt-nimble/nimble/host/mesh/src/crypto.h create mode 100644 src/libs/mynewt-nimble/nimble/host/mesh/src/foundation.h create mode 100644 src/libs/mynewt-nimble/nimble/host/mesh/src/friend.c create mode 100644 src/libs/mynewt-nimble/nimble/host/mesh/src/friend.h create mode 100644 src/libs/mynewt-nimble/nimble/host/mesh/src/glue.c create mode 100644 src/libs/mynewt-nimble/nimble/host/mesh/src/health_cli.c create mode 100644 src/libs/mynewt-nimble/nimble/host/mesh/src/health_srv.c create mode 100644 src/libs/mynewt-nimble/nimble/host/mesh/src/light_model.c create mode 100644 src/libs/mynewt-nimble/nimble/host/mesh/src/light_model.h create mode 100644 src/libs/mynewt-nimble/nimble/host/mesh/src/lpn.c create mode 100644 src/libs/mynewt-nimble/nimble/host/mesh/src/lpn.h create mode 100644 src/libs/mynewt-nimble/nimble/host/mesh/src/mesh.c create mode 100644 src/libs/mynewt-nimble/nimble/host/mesh/src/mesh_priv.h create mode 100644 src/libs/mynewt-nimble/nimble/host/mesh/src/model_cli.c create mode 100644 src/libs/mynewt-nimble/nimble/host/mesh/src/model_srv.c create mode 100644 src/libs/mynewt-nimble/nimble/host/mesh/src/net.c create mode 100644 src/libs/mynewt-nimble/nimble/host/mesh/src/net.h create mode 100644 src/libs/mynewt-nimble/nimble/host/mesh/src/nodes.c create mode 100644 src/libs/mynewt-nimble/nimble/host/mesh/src/nodes.h create mode 100644 src/libs/mynewt-nimble/nimble/host/mesh/src/prov.c create mode 100644 src/libs/mynewt-nimble/nimble/host/mesh/src/prov.h create mode 100644 src/libs/mynewt-nimble/nimble/host/mesh/src/proxy.c create mode 100644 src/libs/mynewt-nimble/nimble/host/mesh/src/proxy.h create mode 100644 src/libs/mynewt-nimble/nimble/host/mesh/src/settings.c create mode 100644 src/libs/mynewt-nimble/nimble/host/mesh/src/settings.h create mode 100644 src/libs/mynewt-nimble/nimble/host/mesh/src/shell.c create mode 100644 src/libs/mynewt-nimble/nimble/host/mesh/src/shell.h create mode 100644 src/libs/mynewt-nimble/nimble/host/mesh/src/testing.c create mode 100644 src/libs/mynewt-nimble/nimble/host/mesh/src/testing.h create mode 100644 src/libs/mynewt-nimble/nimble/host/mesh/src/transport.c create mode 100644 src/libs/mynewt-nimble/nimble/host/mesh/src/transport.h create mode 100644 src/libs/mynewt-nimble/nimble/host/mesh/syscfg.yml (limited to 'src/libs/mynewt-nimble/nimble/host/mesh') 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 +#include + +#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 +#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 +#include + +#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) { + * + * } + * + * 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) { + * + * } + * + * 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) { + * + * } + * + * 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) { + * + * } + * + * @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) { + * + * } + * + * @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 " +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 +#include + +#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(¶m, &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 +#include +#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 +#include +#include + +#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(¶m, 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(¶m, 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(¶m, 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(¶m, 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(¶m, 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(¶m, 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(¶m, 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(¶m, 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(¶m, 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(¶m, 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(¶m, 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(¶m, 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(¶m, 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(¶m, 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 +#include +#include + +#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 +#include +#include + +#include +#include +#include +#include +#include + +#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 +#include +#include + +#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 +#include +#include + +#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(¶m, 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(¶m, 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(¶m, 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(¶m, 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(¶m, 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(¶m, 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(¶m, 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 +#include +#include + +#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 + +#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 +#include + +#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, ¶m, 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, ¶m, 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, ¶m, 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, ¶m, 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 +#include +#include + +#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 +#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 + +#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 +#include +#include +#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, "", 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, "", 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 \n", + size); + break; + case BT_MESH_ENTER_STRING: + printk("Enter a string (max %u chars) with: input-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, "", 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, "", 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, "", 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, "", 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, "", 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, "", 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, " [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, " [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, " [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, " [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, " [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, "