From da5195ea971dc0fe2cb420af43fc15ee5f03b00c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Narajowski?= Date: Thu, 18 Feb 2021 14:09:18 +0100 Subject: [PATCH] Bluetooth: Mesh: Add support for Opcodes Aggregator models MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Includes Opcodes Aggregator Server and Client. To use Opcodes Aggregator client features with some client model, that client should support async API. Co-authored-by: Ludvig Samuelsen Jordet Co-authored-by: Mia Koen Signed-off-by: MichaƂ Narajowski Signed-off-by: Pavel Vasilyev --- .../bluetooth/api/mesh/models.rst | 4 +- .../bluetooth/api/mesh/op_agg_cli.rst | 30 +++ .../bluetooth/api/mesh/op_agg_srv.rst | 32 +++ include/zephyr/bluetooth/mesh.h | 2 + include/zephyr/bluetooth/mesh/access.h | 2 + include/zephyr/bluetooth/mesh/op_agg_cli.h | 90 ++++++++ include/zephyr/bluetooth/mesh/op_agg_srv.h | 47 ++++ subsys/bluetooth/mesh/CMakeLists.txt | 6 + subsys/bluetooth/mesh/Kconfig | 27 +++ subsys/bluetooth/mesh/access.c | 98 +++++--- subsys/bluetooth/mesh/access.h | 3 +- subsys/bluetooth/mesh/cfg_srv.c | 1 + subsys/bluetooth/mesh/foundation.h | 9 + subsys/bluetooth/mesh/op_agg.c | 132 +++++++++++ subsys/bluetooth/mesh/op_agg.h | 51 +++++ subsys/bluetooth/mesh/op_agg_cli.c | 210 ++++++++++++++++++ subsys/bluetooth/mesh/op_agg_srv.c | 127 +++++++++++ subsys/bluetooth/mesh/transport.c | 2 +- 18 files changed, 837 insertions(+), 36 deletions(-) create mode 100644 doc/connectivity/bluetooth/api/mesh/op_agg_cli.rst create mode 100644 doc/connectivity/bluetooth/api/mesh/op_agg_srv.rst create mode 100644 include/zephyr/bluetooth/mesh/op_agg_cli.h create mode 100644 include/zephyr/bluetooth/mesh/op_agg_srv.h create mode 100644 subsys/bluetooth/mesh/op_agg.c create mode 100644 subsys/bluetooth/mesh/op_agg.h create mode 100644 subsys/bluetooth/mesh/op_agg_cli.c create mode 100644 subsys/bluetooth/mesh/op_agg_srv.c diff --git a/doc/connectivity/bluetooth/api/mesh/models.rst b/doc/connectivity/bluetooth/api/mesh/models.rst index 2b9cab610a1..8be4a3c36ec 100644 --- a/doc/connectivity/bluetooth/api/mesh/models.rst +++ b/doc/connectivity/bluetooth/api/mesh/models.rst @@ -6,7 +6,7 @@ Mesh models Foundation models ***************** -The Bluetooth mesh specification defines six foundation models that can be +The Bluetooth mesh specification defines foundation models that can be used by network administrators to configure and diagnose mesh nodes. .. toctree:: @@ -18,6 +18,8 @@ used by network administrators to configure and diagnose mesh nodes. health_cli priv_beacon_srv priv_beacon_cli + op_agg_cli + op_agg_srv Model specification models ************************** diff --git a/doc/connectivity/bluetooth/api/mesh/op_agg_cli.rst b/doc/connectivity/bluetooth/api/mesh/op_agg_cli.rst new file mode 100644 index 00000000000..23eb608391f --- /dev/null +++ b/doc/connectivity/bluetooth/api/mesh/op_agg_cli.rst @@ -0,0 +1,30 @@ +.. _bluetooth_mesh_models_op_agg_cli: + +Opcodes Aggregator Client +######################### + +The Opcodes Aggregator Client model is a foundation model defined by the Bluetooth +mesh specification. It is an optional model, enabled with the :kconfig:option:`CONFIG_BT_MESH_OP_AGG_CLI` option. + +The Opcodes Aggregator Client model is introduced in the Bluetooth Mesh Profile +Specification version 1.1, and is used to support the functionality of dispatching +a sequence of access layer messages to nodes supporting the :ref:`bluetooth_mesh_models_op_agg_srv` model. + +The Opcodes Aggregator Client model communicates with an Opcodes Aggregator Server model +using the device key of the target node or the application keys configured by the Configuration Client. + +The Opcodes Aggregator Client model must only be instantiated on the primary +element, and it is implicitly bound to the device key on initialization. + +The Opcodes Aggregator Client model should be bound to the same application keys that the client models, +used to produce the sequence of messages, are bound to. + +To be able to aggregate a message from a client model, it should support an asynchronous +API, for example through callbacks. + +API reference +************* + +.. doxygengroup:: bt_mesh_op_agg_cli + :project: Zephyr + :members: diff --git a/doc/connectivity/bluetooth/api/mesh/op_agg_srv.rst b/doc/connectivity/bluetooth/api/mesh/op_agg_srv.rst new file mode 100644 index 00000000000..5d493cb4590 --- /dev/null +++ b/doc/connectivity/bluetooth/api/mesh/op_agg_srv.rst @@ -0,0 +1,32 @@ +.. _bluetooth_mesh_models_op_agg_srv: + +Opcodes Aggregator Server +######################### + +The Opcodes Aggregator Server model is a foundation model defined by the Bluetooth +mesh specification. It is an optional model, enabled with the :kconfig:option:`CONFIG_BT_MESH_OP_AGG_SRV` option. + +The Opcodes Aggregator Server model is introduced in the Bluetooth Mesh Profile +Specification version 1.1, and is used to support the functionality of processing +a sequence of access layer messages. + +The Opcodes Aggregator Server model accepts messages encrypted with the node's device key +or the application keys. + +The Opcodes Aggregator Server model can only be instantiated on the +node's primary element. + +The targeted server models should be bound to the same application key that is used +to encrypt the sequence of access layer messages sent to the Opcodes Aggregator Server. + +The Opcodes Aggregator Server handles aggregated messages and dispatches them to the +respective models and their message handlers. Current implementation assumes that +responses are sent from the same execution context as the received message and +doesn't allow to send a postponed response, for example from a work queue. + +API reference +************* + +.. doxygengroup:: bt_mesh_op_agg_srv + :project: Zephyr + :members: diff --git a/include/zephyr/bluetooth/mesh.h b/include/zephyr/bluetooth/mesh.h index 8ca67c8d2fc..d2e4ca634ab 100644 --- a/include/zephyr/bluetooth/mesh.h +++ b/include/zephyr/bluetooth/mesh.h @@ -38,6 +38,8 @@ #include #include #include +#include +#include #include #include diff --git a/include/zephyr/bluetooth/mesh/access.h b/include/zephyr/bluetooth/mesh/access.h index 7915d338f48..b58f0646758 100644 --- a/include/zephyr/bluetooth/mesh/access.h +++ b/include/zephyr/bluetooth/mesh/access.h @@ -132,6 +132,8 @@ struct bt_mesh_elem { #define BT_MESH_MODEL_ID_PRIV_BEACON_CLI 0x000b #define BT_MESH_MODEL_ID_SAR_CFG_SRV 0x000e #define BT_MESH_MODEL_ID_SAR_CFG_CLI 0x000f +#define BT_MESH_MODEL_ID_OP_AGG_SRV 0x0010 +#define BT_MESH_MODEL_ID_OP_AGG_CLI 0x0011 #define BT_MESH_MODEL_ID_LARGE_COMP_DATA_SRV 0x0012 #define BT_MESH_MODEL_ID_LARGE_COMP_DATA_CLI 0x0013 diff --git a/include/zephyr/bluetooth/mesh/op_agg_cli.h b/include/zephyr/bluetooth/mesh/op_agg_cli.h new file mode 100644 index 00000000000..8ca45492bf3 --- /dev/null +++ b/include/zephyr/bluetooth/mesh/op_agg_cli.h @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2022 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file + * @defgroup bt_mesh_op_agg_cli Opcodes Aggregator Client model + * @{ + * @brief API for the Opcodes Aggregator Client model. + */ +#ifndef BT_MESH_OP_AGG_CLI_H__ +#define BT_MESH_OP_AGG_CLI_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * + * @brief Opcodes Aggregator Client model composition data entry. + */ +#define BT_MESH_MODEL_OP_AGG_CLI \ + BT_MESH_MODEL_CB(BT_MESH_MODEL_ID_OP_AGG_CLI, _bt_mesh_op_agg_cli_op, \ + NULL, NULL, &_bt_mesh_op_agg_cli_cb) + +/** @brief Configure Opcodes Aggregator context. + * + * @param net_idx NetKey index to encrypt with. + * @param app_idx AppKey index to encrypt with. + * @param dst Target Opcodes Aggregator Server address. + * @param elem_addr Target node element address for the sequence message. + * + * @return 0 on success, or (negative) error code on failure. + */ +int bt_mesh_op_agg_cli_seq_start(uint16_t net_idx, uint16_t app_idx, uint16_t dst, + uint16_t elem_addr); + +/** @brief Opcodes Aggregator message send. + * + * Uses previously configured context and sends aggregated message + * to target node. + * + * @return 0 on success, or (negative) error code on failure. + */ +int bt_mesh_op_agg_cli_seq_send(void); + +/** @brief Abort Opcodes Aggregator context. + */ +void bt_mesh_op_agg_cli_seq_abort(void); + +/** @brief Check if Opcodes Aggregator Sequence context is started. + * + * @return true if it is started, otherwise false. + */ +bool bt_mesh_op_agg_cli_seq_is_started(void); + +/** @brief Get Opcodes Aggregator context tailroom. + * + * @return Remaning tailroom of Opcodes Aggregator SDU. + */ +size_t bt_mesh_op_agg_cli_seq_tailroom(void); + +/** @brief Get the current transmission timeout value. + * + * @return The configured transmission timeout in milliseconds. + */ +int32_t bt_mesh_op_agg_cli_timeout_get(void); + +/** @brief Set the transmission timeout value. + * + * @param timeout The new transmission timeout. + */ +void bt_mesh_op_agg_cli_timeout_set(int32_t timeout); + +/** @cond INTERNAL_HIDDEN */ +extern const struct bt_mesh_model_op _bt_mesh_op_agg_cli_op[]; +extern const struct bt_mesh_model_cb _bt_mesh_op_agg_cli_cb; +/** @endcond */ + +#ifdef __cplusplus +} +#endif + +#endif /* BT_MESH_OP_AGG_CLI_H__ */ + +/** + * @} + */ diff --git a/include/zephyr/bluetooth/mesh/op_agg_srv.h b/include/zephyr/bluetooth/mesh/op_agg_srv.h new file mode 100644 index 00000000000..c9d7013f829 --- /dev/null +++ b/include/zephyr/bluetooth/mesh/op_agg_srv.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2022 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file + * @defgroup bt_mesh_op_agg_srv Opcodes Aggregator Server model + * @{ + * @brief API for the Opcodes Aggregator Server model. + */ +#ifndef BT_MESH_OP_AGG_SRV_H__ +#define BT_MESH_OP_AGG_SRV_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * + * @brief Opcodes Aggretator Server model composition data entry. + * + * @note The Opcodes Aggregator Server handles aggregated messages + * and dispatches them to the respective models and their message + * handlers. Current implementation assumes that responses are sent + * from the same execution context as the received message and + * doesn't allow to send a postponed response, e.g. from workqueue. + */ +#define BT_MESH_MODEL_OP_AGG_SRV \ + BT_MESH_MODEL_CB(BT_MESH_MODEL_ID_OP_AGG_SRV, _bt_mesh_op_agg_srv_op, \ + NULL, NULL, &_bt_mesh_op_agg_srv_cb) + +/** @cond INTERNAL_HIDDEN */ +extern const struct bt_mesh_model_op _bt_mesh_op_agg_srv_op[]; +extern const struct bt_mesh_model_cb _bt_mesh_op_agg_srv_cb; +/** @endcond */ + +#ifdef __cplusplus +} +#endif + +#endif /* BT_MESH_OP_AGG_SRV_H__ */ + +/** + * @} + */ diff --git a/subsys/bluetooth/mesh/CMakeLists.txt b/subsys/bluetooth/mesh/CMakeLists.txt index 184afb0d57e..129e7b1133f 100644 --- a/subsys/bluetooth/mesh/CMakeLists.txt +++ b/subsys/bluetooth/mesh/CMakeLists.txt @@ -69,6 +69,12 @@ zephyr_library_sources_ifdef(CONFIG_BT_MESH_SAR_CFG_SRV sar_cfg_srv.c) zephyr_library_sources_ifdef(CONFIG_BT_MESH_SAR_CFG sar_cfg.c) +zephyr_library_sources_ifdef(CONFIG_BT_MESH_OP_AGG_SRV op_agg_srv.c) + +zephyr_library_sources_ifdef(CONFIG_BT_MESH_OP_AGG_CLI op_agg_cli.c) + +zephyr_library_sources_ifdef(CONFIG_BT_MESH_OP_AGG op_agg.c) + zephyr_library_sources_ifdef(CONFIG_BT_MESH_LARGE_COMP_DATA_SRV large_comp_data_srv.c) zephyr_library_sources_ifdef(CONFIG_BT_MESH_LARGE_COMP_DATA_CLI large_comp_data_cli.c) diff --git a/subsys/bluetooth/mesh/Kconfig b/subsys/bluetooth/mesh/Kconfig index 79df427237f..c6e2794c7e8 100644 --- a/subsys/bluetooth/mesh/Kconfig +++ b/subsys/bluetooth/mesh/Kconfig @@ -1203,6 +1203,33 @@ config BT_MESH_SAR_CFG_CLI help Enable support for the SAR configuration client model. +config BT_MESH_OP_AGG + bool + +config BT_MESH_OP_AGG_SRV + bool "Support for Opcode Aggregator Server Model" + select BT_MESH_OP_AGG + help + Enable support for the Opcode Aggregator Server model. + +config BT_MESH_OP_AGG_CLI + bool "Support for Opcode Aggregator Client Model" + select BT_MESH_OP_AGG + help + Enable support for the Opcode Aggregator Client model. + +if BT_MESH_OP_AGG_CLI + +config BT_MESH_OP_AGG_CLI_TIMEOUT + int "Opcodes Aggregator Client model timeout in milliseconds" + default 10000 + help + This timeout controls how long Opcodes Aggregator Client waits + for a response message to arrive. This value can be changed at + runtime using @ref bt_mesh_op_agg_cli_timeout_set. + +endif # BT_MESH_OP_AGG_CLI + config BT_MESH_LARGE_COMP_DATA_SRV bool "Support for Large Composition Data Server Model" help diff --git a/subsys/bluetooth/mesh/access.c b/subsys/bluetooth/mesh/access.c index 33f94386058..f36f87cd822 100644 --- a/subsys/bluetooth/mesh/access.c +++ b/subsys/bluetooth/mesh/access.c @@ -25,6 +25,7 @@ #include "transport.h" #include "access.h" #include "foundation.h" +#include "op_agg.h" #include "settings.h" #define LOG_LEVEL CONFIG_BT_MESH_ACCESS_LOG_LEVEL @@ -1017,12 +1018,54 @@ static int get_opcode(struct net_buf_simple *buf, uint32_t *opcode) CODE_UNREACHABLE; } -void bt_mesh_model_recv(struct bt_mesh_msg_ctx *ctx, struct net_buf_simple *buf) +static int element_model_recv(struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf, uint16_t addr, + struct bt_mesh_elem *elem, uint32_t opcode) { - struct bt_mesh_model *model; const struct bt_mesh_model_op *op; + struct bt_mesh_model *model; + struct net_buf_simple_state state; + int err; + + op = find_op(elem, opcode, &model); + if (!op) { + LOG_ERR("No OpCode 0x%08x for elem 0x%02x", opcode, elem->addr); + return ACCESS_STATUS_WRONG_OPCODE; + } + + if (!bt_mesh_model_has_key(model, ctx->app_idx)) { + LOG_ERR("Wrong key"); + return ACCESS_STATUS_WRONG_KEY; + } + + if (!model_has_dst(model, addr)) { + LOG_ERR("Invalid address 0x%02x", addr); + return ACCESS_STATUS_INVALID_ADDRESS; + } + + if ((op->len >= 0) && (buf->len < (size_t)op->len)) { + LOG_ERR("Too short message for OpCode 0x%08x", opcode); + return ACCESS_STATUS_MESSAGE_NOT_UNDERSTOOD; + } else if ((op->len < 0) && (buf->len != (size_t)(-op->len))) { + LOG_ERR("Invalid message size for OpCode 0x%08x", opcode); + return ACCESS_STATUS_MESSAGE_NOT_UNDERSTOOD; + } + + net_buf_simple_save(buf, &state); + err = op->func(model, ctx, buf); + net_buf_simple_restore(buf, &state); + + if (err) { + return ACCESS_STATUS_MESSAGE_NOT_UNDERSTOOD; + } + return ACCESS_STATUS_SUCCESS; +} + +int bt_mesh_model_recv(struct bt_mesh_msg_ctx *ctx, struct net_buf_simple *buf) +{ + int err = ACCESS_STATUS_SUCCESS; uint32_t opcode; - int i; + uint16_t index; LOG_DBG("app_idx 0x%04x src 0x%04x dst 0x%04x", ctx->app_idx, ctx->addr, ctx->recv_dst); @@ -1035,54 +1078,45 @@ void bt_mesh_model_recv(struct bt_mesh_msg_ctx *ctx, struct net_buf_simple *buf) if (get_opcode(buf, &opcode) < 0) { LOG_WRN("Unable to decode OpCode"); - return; + return ACCESS_STATUS_WRONG_OPCODE; } LOG_DBG("OpCode 0x%08x", opcode); - for (i = 0; i < dev_comp->elem_count; i++) { - struct net_buf_simple_state state; + if (BT_MESH_ADDR_IS_UNICAST(ctx->recv_dst)) { + index = ctx->recv_dst - dev_comp->elem[0].addr; - op = find_op(&dev_comp->elem[i], opcode, &model); - if (!op) { - LOG_DBG("No OpCode 0x%08x for elem %d", opcode, i); - continue; + if (index >= dev_comp->elem_count) { + LOG_ERR("Invalid address 0x%02x", ctx->recv_dst); + err = ACCESS_STATUS_INVALID_ADDRESS; + } else { + struct bt_mesh_elem *elem = &dev_comp->elem[index]; + + err = element_model_recv(ctx, buf, ctx->recv_dst, elem, opcode); } + } else { + for (index = 0; index < dev_comp->elem_count; index++) { + struct bt_mesh_elem *elem = &dev_comp->elem[index]; - if (!bt_mesh_model_has_key(model, ctx->app_idx)) { - continue; + (void)element_model_recv(ctx, buf, ctx->recv_dst, elem, opcode); } - - if (!model_has_dst(model, ctx->recv_dst)) { - continue; - } - - if ((op->len >= 0) && (buf->len < (size_t)op->len)) { - LOG_ERR("Too short message for OpCode 0x%08x", opcode); - continue; - } else if ((op->len < 0) && (buf->len != (size_t)(-op->len))) { - LOG_ERR("Invalid message size 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); - (void)op->func(model, ctx, buf); - net_buf_simple_restore(buf, &state); } if (IS_ENABLED(CONFIG_BT_MESH_ACCESS_LAYER_MSG) && msg_cb) { msg_cb(opcode, ctx, buf); } + + return err; } int bt_mesh_model_send(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, struct net_buf_simple *msg, const struct bt_mesh_send_cb *cb, void *cb_data) { + if (IS_ENABLED(CONFIG_BT_MESH_OP_AGG) && bt_mesh_op_agg_accept(ctx)) { + return bt_mesh_op_agg_send(model, ctx, msg, cb); + } + if (!bt_mesh_model_has_key(model, ctx->app_idx)) { LOG_ERR("Model not bound to AppKey 0x%04x", ctx->app_idx); return -EINVAL; diff --git a/subsys/bluetooth/mesh/access.h b/subsys/bluetooth/mesh/access.h index c97d4edf145..c3cc5d07c8e 100644 --- a/subsys/bluetooth/mesh/access.h +++ b/subsys/bluetooth/mesh/access.h @@ -57,8 +57,7 @@ const struct bt_mesh_comp *bt_mesh_comp_get(void); struct bt_mesh_model *bt_mesh_model_get(bool vnd, uint8_t elem_idx, uint8_t mod_idx); -void bt_mesh_model_recv(struct bt_mesh_msg_ctx *ctx, - struct net_buf_simple *buf); +int bt_mesh_model_recv(struct bt_mesh_msg_ctx *ctx, struct net_buf_simple *buf); int bt_mesh_comp_register(const struct bt_mesh_comp *comp); int bt_mesh_comp_store(void); diff --git a/subsys/bluetooth/mesh/cfg_srv.c b/subsys/bluetooth/mesh/cfg_srv.c index b00145d79d3..15d4b43d46c 100644 --- a/subsys/bluetooth/mesh/cfg_srv.c +++ b/subsys/bluetooth/mesh/cfg_srv.c @@ -221,6 +221,7 @@ static uint8_t mod_bind(struct bt_mesh_model *model, uint16_t key_idx) } for (i = 0; i < model->keys_cnt; i++) { + LOG_DBG("model %p id 0x%04x i %d key 0x%03x", model, model->id, i, model->keys[i]); /* Treat existing binding as success */ if (model->keys[i] == key_idx) { return STATUS_SUCCESS; diff --git a/subsys/bluetooth/mesh/foundation.h b/subsys/bluetooth/mesh/foundation.h index 0be665c4d5c..c24c8945b01 100644 --- a/subsys/bluetooth/mesh/foundation.h +++ b/subsys/bluetooth/mesh/foundation.h @@ -97,6 +97,8 @@ #define OP_SAR_CFG_RX_GET BT_MESH_MODEL_OP_2(0x80, 0x6f) #define OP_SAR_CFG_RX_SET BT_MESH_MODEL_OP_2(0x80, 0x70) #define OP_SAR_CFG_RX_STATUS BT_MESH_MODEL_OP_2(0x80, 0x71) +#define OP_OPCODES_AGGREGATOR_SEQUENCE BT_MESH_MODEL_OP_2(0x80, 0x72) +#define OP_OPCODES_AGGREGATOR_STATUS BT_MESH_MODEL_OP_2(0x80, 0x73) #define OP_LARGE_COMP_DATA_GET BT_MESH_MODEL_OP_2(0x80, 0x74) #define OP_LARGE_COMP_DATA_STATUS BT_MESH_MODEL_OP_2(0x80, 0x75) #define OP_MODELS_METADATA_GET BT_MESH_MODEL_OP_2(0x80, 0x76) @@ -131,6 +133,13 @@ #define STATUS_UNSPECIFIED 0x10 #define STATUS_INVALID_BINDING 0x11 +#define ACCESS_STATUS_SUCCESS 0x00 +#define ACCESS_STATUS_INVALID_ADDRESS 0x01 +#define ACCESS_STATUS_WRONG_KEY 0x02 +#define ACCESS_STATUS_WRONG_OPCODE 0x03 +#define ACCESS_STATUS_MESSAGE_NOT_UNDERSTOOD 0x04 +#define ACCESS_STATUS_RESPONSE_OVERFLOW 0x05 + void bt_mesh_model_reset(void); void bt_mesh_attention(struct bt_mesh_model *model, uint8_t time); diff --git a/subsys/bluetooth/mesh/op_agg.c b/subsys/bluetooth/mesh/op_agg.c new file mode 100644 index 00000000000..d3b7238128a --- /dev/null +++ b/subsys/bluetooth/mesh/op_agg.c @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2022 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#include "foundation.h" +#include "op_agg.h" + +#define LOG_LEVEL CONFIG_BT_MESH_MODEL_LOG_LEVEL +#include +LOG_MODULE_REGISTER(bt_mesh_op_agg); + +#define IS_LENGTH_LONG(buf) ((buf)->data[0] & 1) +#define LENGTH_SHORT_MAX BIT_MASK(7) + +NET_BUF_SIMPLE_DEFINE_STATIC(sdu, BT_MESH_TX_SDU_MAX); + +#ifdef CONFIG_BT_MESH_OP_AGG_CLI +NET_BUF_SIMPLE_DEFINE_STATIC(srcs, BT_MESH_TX_SDU_MAX); +#endif + +static struct op_agg_ctx agg_ctx = { + .sdu = &sdu, +#ifdef CONFIG_BT_MESH_OP_AGG_CLI + .srcs = &srcs, +#endif +}; + +struct op_agg_ctx *bt_mesh_op_agg_ctx_get(void) +{ + return &agg_ctx; +} + +static bool ctx_match(struct bt_mesh_msg_ctx *ctx) +{ + return (ctx->net_idx == agg_ctx.net_idx) && (ctx->addr == agg_ctx.addr) && + (ctx->app_idx == agg_ctx.app_idx); +} + +int bt_mesh_op_agg_accept(struct bt_mesh_msg_ctx *msg_ctx) +{ +#ifdef CONFIG_BT_MESH_OP_AGG_CLI + return agg_ctx.initialized && ctx_match(msg_ctx); +#else + return 0; +#endif +} + +void bt_mesh_op_agg_ctx_reinit(void) +{ + agg_ctx.initialized = true; +} + +int bt_mesh_op_agg_send(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, struct net_buf_simple *msg, + const struct bt_mesh_send_cb *cb) +{ + uint16_t src; + int err; + + /* Model responded so mark this message as acknowledged */ + agg_ctx.ack = true; + + /* Store source address so that Opcodes Aggregator Client can match + * response with source model + */ + src = bt_mesh_model_elem(model)->addr; + + if (net_buf_simple_tailroom(&srcs) < 2) { + return -ENOMEM; + } + + net_buf_simple_add_le16(&srcs, src); + + err = bt_mesh_op_agg_encode_msg(msg); + if (err) { + agg_ctx.rsp_err = ACCESS_STATUS_RESPONSE_OVERFLOW; + } + + return err; +} + +int bt_mesh_op_agg_encode_msg(struct net_buf_simple *msg) +{ + if (msg->len > LENGTH_SHORT_MAX) { + if (net_buf_simple_tailroom(agg_ctx.sdu) < (msg->len + 2)) { + return -ENOMEM; + } + + net_buf_simple_add_le16(agg_ctx.sdu, (msg->len << 1) | 1); + } else { + if (net_buf_simple_tailroom(agg_ctx.sdu) < (msg->len + 1)) { + return -ENOMEM; + } + + net_buf_simple_add_u8(agg_ctx.sdu, msg->len << 1); + } + net_buf_simple_add_mem(agg_ctx.sdu, msg->data, msg->len); + + return 0; +} + +int bt_mesh_op_agg_decode_msg(struct net_buf_simple *msg, + struct net_buf_simple *buf) +{ + uint16_t len; + + if (IS_LENGTH_LONG(buf)) { + if (buf->len < 2) { + return -EINVAL; + } + + len = net_buf_simple_pull_le16(buf) >> 1; + } else { + if (buf->len < 1) { + return -EINVAL; + } + + len = net_buf_simple_pull_u8(buf) >> 1; + } + + if (buf->len < len) { + return -EINVAL; + } + + net_buf_simple_init_with_data(msg, net_buf_simple_pull_mem(buf, len), len); + + return 0; +} diff --git a/subsys/bluetooth/mesh/op_agg.h b/subsys/bluetooth/mesh/op_agg.h new file mode 100644 index 00000000000..a267fc737f7 --- /dev/null +++ b/subsys/bluetooth/mesh/op_agg.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2021 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +struct op_agg_ctx { + /** Context is initialized. */ + bool initialized; + + /** NetKey Index of the subnet to send the message on. */ + uint16_t net_idx; + + /** AppKey Index to encrypt the message with. */ + uint16_t app_idx; + + /** Remote element address. */ + uint16_t addr; + + /** List of source element addresses. + * Used by Client to match aggregated responses + * with local source client models. + */ + struct net_buf_simple *srcs; + + /** Response error code. */ + int rsp_err; + + /** Aggregated message buffer. */ + struct net_buf_simple *sdu; + + /** Used only by the Opcodes Aggregator Server. + * + * Indicates that the received aggregated message + * was acknowledged by local server model. + */ + bool ack; +}; + +struct op_agg_ctx *bt_mesh_op_agg_ctx_get(void); +void bt_mesh_op_agg_ctx_reinit(void); + +int bt_mesh_op_agg_encode_msg(struct net_buf_simple *msg); +int bt_mesh_op_agg_decode_msg(struct net_buf_simple *msg, + struct net_buf_simple *buf); + +int bt_mesh_op_agg_accept(struct bt_mesh_msg_ctx *ctx); + +int bt_mesh_op_agg_send(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, struct net_buf_simple *msg, + const struct bt_mesh_send_cb *cb); diff --git a/subsys/bluetooth/mesh/op_agg_cli.c b/subsys/bluetooth/mesh/op_agg_cli.c new file mode 100644 index 00000000000..d3371306a8b --- /dev/null +++ b/subsys/bluetooth/mesh/op_agg_cli.c @@ -0,0 +1,210 @@ +/* + * Copyright (c) 2022 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#include + +#include "access.h" +#include "foundation.h" +#include "net.h" +#include "mesh.h" +#include "op_agg.h" + +#define LOG_LEVEL CONFIG_BT_MESH_MODEL_LOG_LEVEL +#include +LOG_MODULE_REGISTER(bt_mesh_op_agg_cli); + +/** Mesh Opcodes Aggregator Client Model Context */ +static struct bt_mesh_op_agg_cli { + /** Composition data model entry pointer. */ + struct bt_mesh_model *model; + + /* Internal parameters for tracking message responses. */ + struct bt_mesh_msg_ack_ctx ack_ctx; +} cli; + +static int32_t msg_timeout; + +static int handle_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + struct op_agg_ctx *agg = bt_mesh_op_agg_ctx_get(); + struct net_buf_simple msg; + uint8_t status; + uint16_t elem_addr, addr; + int err; + + LOG_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s", + ctx->net_idx, ctx->app_idx, ctx->addr, buf->len, + bt_hex(buf->data, buf->len)); + + if (!bt_mesh_msg_ack_ctx_match(&cli.ack_ctx, + OP_OPCODES_AGGREGATOR_STATUS, ctx->addr, + NULL)) { + LOG_WRN("Unexpected Opcodes Aggregator Status"); + return -ENOENT; + } + + status = net_buf_simple_pull_u8(buf); + elem_addr = net_buf_simple_pull_le16(buf); + + while (buf->len > 0) { + err = bt_mesh_op_agg_decode_msg(&msg, buf); + if (err) { + LOG_ERR("Cannot decode aggregated message %d", err); + bt_mesh_op_agg_ctx_reinit(); + return -EINVAL; + } + + if (agg->srcs->len < 2) { + LOG_ERR("Mismatch in sources address buffer"); + bt_mesh_op_agg_ctx_reinit(); + return -ENOENT; + } + + addr = net_buf_simple_pull_le16(agg->srcs); + + /* Empty item means unacked msg. */ + if (!msg.len) { + continue; + } + + ctx->recv_dst = addr; + err = bt_mesh_model_recv(ctx, &msg); + if (err) { + LOG_ERR("Opcodes Aggregator receive error %d", err); + bt_mesh_op_agg_ctx_reinit(); + return err; + } + } + + bt_mesh_msg_ack_ctx_rx(&cli.ack_ctx); + + return 0; +} + +const struct bt_mesh_model_op _bt_mesh_op_agg_cli_op[] = { + { OP_OPCODES_AGGREGATOR_STATUS, BT_MESH_LEN_MIN(3), handle_status }, + BT_MESH_MODEL_OP_END, +}; + +static int op_agg_cli_init(struct bt_mesh_model *model) +{ + if (!bt_mesh_model_in_primary(model)) { + LOG_ERR("Opcodes Aggregator Client only allowed in primary element"); + return -EINVAL; + } + + /* Opcodes Aggregator Client model shall use the device key and + * application keys. + */ + model->keys[0] = BT_MESH_KEY_DEV_ANY; + + msg_timeout = CONFIG_BT_MESH_OP_AGG_CLI_TIMEOUT; + cli.model = model; + bt_mesh_msg_ack_ctx_init(&cli.ack_ctx); + + return 0; +} + +int bt_mesh_op_agg_cli_seq_start(uint16_t net_idx, uint16_t app_idx, uint16_t dst, + uint16_t elem_addr) +{ + struct op_agg_ctx *agg = bt_mesh_op_agg_ctx_get(); + + if (!BT_MESH_ADDR_IS_UNICAST(elem_addr)) { + LOG_ERR("Element address shall be a unicast address"); + return -EINVAL; + } + + if (agg->initialized) { + LOG_ERR("Opcodes Aggregator is already configured"); + return -EALREADY; + } + + agg->net_idx = net_idx; + agg->app_idx = app_idx; + agg->addr = dst; + agg->ack = false; + agg->rsp_err = 0; + agg->initialized = true; + + net_buf_simple_init(agg->srcs, 0); + bt_mesh_model_msg_init(agg->sdu, OP_OPCODES_AGGREGATOR_SEQUENCE); + net_buf_simple_add_le16(agg->sdu, elem_addr); + + return 0; +} + +int bt_mesh_op_agg_cli_seq_send(void) +{ + struct op_agg_ctx *agg = bt_mesh_op_agg_ctx_get(); + struct bt_mesh_msg_ctx ctx = { + .net_idx = agg->net_idx, + .app_idx = agg->app_idx, + .addr = agg->addr, + }; + int err; + + if (!agg->initialized) { + LOG_ERR("Opcodes Aggregator not initialized"); + return -EINVAL; + } + + err = bt_mesh_msg_ack_ctx_prepare(&cli.ack_ctx, OP_OPCODES_AGGREGATOR_STATUS, agg->addr, + NULL); + if (err) { + return err; + } + + agg->initialized = false; + + err = bt_mesh_model_send(cli.model, &ctx, agg->sdu, NULL, NULL); + if (err) { + LOG_ERR("model_send() failed (err %d)", err); + bt_mesh_msg_ack_ctx_clear(&cli.ack_ctx); + return err; + } + + return bt_mesh_msg_ack_ctx_wait(&cli.ack_ctx, K_MSEC(msg_timeout)); +} + +void bt_mesh_op_agg_cli_seq_abort(void) +{ + struct op_agg_ctx *agg = bt_mesh_op_agg_ctx_get(); + + agg->initialized = false; +} + +bool bt_mesh_op_agg_cli_seq_is_started(void) +{ + struct op_agg_ctx *agg = bt_mesh_op_agg_ctx_get(); + + return agg->initialized; +} + +size_t bt_mesh_op_agg_cli_seq_tailroom(void) +{ + struct op_agg_ctx *agg = bt_mesh_op_agg_ctx_get(); + + return net_buf_simple_tailroom(agg->sdu); +} + +int32_t bt_mesh_op_agg_cli_timeout_get(void) +{ + return msg_timeout; +} + +void bt_mesh_op_agg_cli_timeout_set(int32_t timeout) +{ + msg_timeout = timeout; +} + +const struct bt_mesh_model_cb _bt_mesh_op_agg_cli_cb = { + .init = op_agg_cli_init, +}; diff --git a/subsys/bluetooth/mesh/op_agg_srv.c b/subsys/bluetooth/mesh/op_agg_srv.c new file mode 100644 index 00000000000..bed3b54c946 --- /dev/null +++ b/subsys/bluetooth/mesh/op_agg_srv.c @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2022 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#include "access.h" +#include "foundation.h" +#include "net.h" +#include "mesh.h" +#include "transport.h" +#include "op_agg.h" + +#define LOG_LEVEL CONFIG_BT_MESH_MODEL_LOG_LEVEL +#include +LOG_MODULE_REGISTER(bt_mesh_op_agg_srv); + +/** Mesh Opcodes Aggragator Server Model Context */ +static struct bt_mesh_op_agg_srv { + /** Composition data model entry pointer. */ + struct bt_mesh_model *model; +} srv; + +static int handle_sequence(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + struct op_agg_ctx *agg = bt_mesh_op_agg_ctx_get(); + struct net_buf_simple_state state; + struct net_buf_simple msg; + uint16_t elem; + uint8_t *status; + int err; + + elem = net_buf_simple_pull_le16(buf); + ctx->recv_dst = elem; + + bt_mesh_model_msg_init(agg->sdu, OP_OPCODES_AGGREGATOR_STATUS); + status = net_buf_simple_add_u8(agg->sdu, 0); + net_buf_simple_add_le16(agg->sdu, elem); + + agg->net_idx = ctx->net_idx; + agg->app_idx = ctx->app_idx; + agg->addr = ctx->addr; + agg->initialized = true; + + if (!BT_MESH_ADDR_IS_UNICAST(elem)) { + LOG_WRN("Address is not unicast, ignoring."); + return -EINVAL; + } + + net_buf_simple_save(buf, &state); + while (buf->len > 0) { + err = bt_mesh_op_agg_decode_msg(&msg, buf); + if (err) { + LOG_ERR("Unable to parse Opcodes Aggregator Sequence message (err %d)", + err); + return err; + } + } + net_buf_simple_restore(buf, &state); + + if (!bt_mesh_elem_find(elem)) { + *status = ACCESS_STATUS_INVALID_ADDRESS; + goto send; + } + + while (buf->len > 0) { + (void) bt_mesh_op_agg_decode_msg(&msg, buf); + + agg->ack = false; + agg->rsp_err = 0; + err = bt_mesh_model_recv(ctx, &msg); + + if (agg->rsp_err) { + *status = agg->rsp_err; + break; + } + + if (err) { + *status = err; + break; + } + + if (!agg->ack) { + net_buf_simple_add_u8(agg->sdu, 0); + } + } + +send: + agg->initialized = false; + err = bt_mesh_model_send(model, ctx, agg->sdu, NULL, NULL); + if (err) { + LOG_ERR("Unable to send Opcodes Aggregator Status"); + return err; + } + + return 0; +} + +const struct bt_mesh_model_op _bt_mesh_op_agg_srv_op[] = { + { OP_OPCODES_AGGREGATOR_SEQUENCE, BT_MESH_LEN_MIN(2), handle_sequence }, + BT_MESH_MODEL_OP_END, +}; + +static int op_agg_srv_init(struct bt_mesh_model *model) +{ + if (!bt_mesh_model_in_primary(model)) { + LOG_ERR("Opcodes Aggregator Server only allowed in primary element"); + return -EINVAL; + } + + /* Opcodes Aggregator Server model shall use the device key and + * application keys. + */ + model->keys[0] = BT_MESH_KEY_DEV_ANY; + + srv.model = model; + + return 0; +} + +const struct bt_mesh_model_cb _bt_mesh_op_agg_srv_cb = { + .init = op_agg_srv_init, +}; diff --git a/subsys/bluetooth/mesh/transport.c b/subsys/bluetooth/mesh/transport.c index fc7328e23b1..b4b0f917904 100644 --- a/subsys/bluetooth/mesh/transport.c +++ b/subsys/bluetooth/mesh/transport.c @@ -807,7 +807,7 @@ static int sdu_recv(struct bt_mesh_net_rx *rx, uint8_t hdr, uint8_t aszmic, LOG_DBG("Decrypted (AppIdx: 0x%03x)", rx->ctx.app_idx); - bt_mesh_model_recv(&rx->ctx, sdu); + (void)bt_mesh_model_recv(&rx->ctx, sdu); return 0; }