Bluetooth: Mesh: Add support for Opcodes Aggregator models

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 <ludvig.jordet@nordicsemi.no>
Co-authored-by: Mia Koen <mia.koen@nordicsemi.no>
Signed-off-by: Michał Narajowski <michal.narajowski@codecoup.pl>
Signed-off-by: Pavel Vasilyev <pavel.vasilyev@nordicsemi.no>
This commit is contained in:
Michał Narajowski 2021-02-18 14:09:18 +01:00 committed by Carles Cufí
commit da5195ea97
18 changed files with 837 additions and 36 deletions

View file

@ -6,7 +6,7 @@ Mesh models
Foundation 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. used by network administrators to configure and diagnose mesh nodes.
.. toctree:: .. toctree::
@ -18,6 +18,8 @@ used by network administrators to configure and diagnose mesh nodes.
health_cli health_cli
priv_beacon_srv priv_beacon_srv
priv_beacon_cli priv_beacon_cli
op_agg_cli
op_agg_srv
Model specification models Model specification models
************************** **************************

View file

@ -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:

View file

@ -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:

View file

@ -38,6 +38,8 @@
#include <zephyr/bluetooth/mesh/rpr_srv.h> #include <zephyr/bluetooth/mesh/rpr_srv.h>
#include <zephyr/bluetooth/mesh/sar_cfg_srv.h> #include <zephyr/bluetooth/mesh/sar_cfg_srv.h>
#include <zephyr/bluetooth/mesh/sar_cfg_cli.h> #include <zephyr/bluetooth/mesh/sar_cfg_cli.h>
#include <zephyr/bluetooth/mesh/op_agg_srv.h>
#include <zephyr/bluetooth/mesh/op_agg_cli.h>
#include <zephyr/bluetooth/mesh/large_comp_data_srv.h> #include <zephyr/bluetooth/mesh/large_comp_data_srv.h>
#include <zephyr/bluetooth/mesh/large_comp_data_cli.h> #include <zephyr/bluetooth/mesh/large_comp_data_cli.h>

View file

@ -132,6 +132,8 @@ struct bt_mesh_elem {
#define BT_MESH_MODEL_ID_PRIV_BEACON_CLI 0x000b #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_SRV 0x000e
#define BT_MESH_MODEL_ID_SAR_CFG_CLI 0x000f #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_SRV 0x0012
#define BT_MESH_MODEL_ID_LARGE_COMP_DATA_CLI 0x0013 #define BT_MESH_MODEL_ID_LARGE_COMP_DATA_CLI 0x0013

View file

@ -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__ */
/**
* @}
*/

View file

@ -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__ */
/**
* @}
*/

View file

@ -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_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_SRV large_comp_data_srv.c)
zephyr_library_sources_ifdef(CONFIG_BT_MESH_LARGE_COMP_DATA_CLI large_comp_data_cli.c) zephyr_library_sources_ifdef(CONFIG_BT_MESH_LARGE_COMP_DATA_CLI large_comp_data_cli.c)

View file

@ -1203,6 +1203,33 @@ config BT_MESH_SAR_CFG_CLI
help help
Enable support for the SAR configuration client model. 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 config BT_MESH_LARGE_COMP_DATA_SRV
bool "Support for Large Composition Data Server Model" bool "Support for Large Composition Data Server Model"
help help

View file

@ -25,6 +25,7 @@
#include "transport.h" #include "transport.h"
#include "access.h" #include "access.h"
#include "foundation.h" #include "foundation.h"
#include "op_agg.h"
#include "settings.h" #include "settings.h"
#define LOG_LEVEL CONFIG_BT_MESH_ACCESS_LOG_LEVEL #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; 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; 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; 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, LOG_DBG("app_idx 0x%04x src 0x%04x dst 0x%04x", ctx->app_idx, ctx->addr,
ctx->recv_dst); 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) { if (get_opcode(buf, &opcode) < 0) {
LOG_WRN("Unable to decode OpCode"); LOG_WRN("Unable to decode OpCode");
return; return ACCESS_STATUS_WRONG_OPCODE;
} }
LOG_DBG("OpCode 0x%08x", opcode); LOG_DBG("OpCode 0x%08x", opcode);
for (i = 0; i < dev_comp->elem_count; i++) { if (BT_MESH_ADDR_IS_UNICAST(ctx->recv_dst)) {
struct net_buf_simple_state state; index = ctx->recv_dst - dev_comp->elem[0].addr;
op = find_op(&dev_comp->elem[i], opcode, &model); if (index >= dev_comp->elem_count) {
if (!op) { LOG_ERR("Invalid address 0x%02x", ctx->recv_dst);
LOG_DBG("No OpCode 0x%08x for elem %d", opcode, i); err = ACCESS_STATUS_INVALID_ADDRESS;
continue; } 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)) { (void)element_model_recv(ctx, buf, ctx->recv_dst, elem, opcode);
continue;
} }
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) { if (IS_ENABLED(CONFIG_BT_MESH_ACCESS_LAYER_MSG) && msg_cb) {
msg_cb(opcode, ctx, buf); msg_cb(opcode, ctx, buf);
} }
return err;
} }
int bt_mesh_model_send(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, int bt_mesh_model_send(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx,
struct net_buf_simple *msg, struct net_buf_simple *msg,
const struct bt_mesh_send_cb *cb, void *cb_data) 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)) { if (!bt_mesh_model_has_key(model, ctx->app_idx)) {
LOG_ERR("Model not bound to AppKey 0x%04x", ctx->app_idx); LOG_ERR("Model not bound to AppKey 0x%04x", ctx->app_idx);
return -EINVAL; return -EINVAL;

View file

@ -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); 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, int bt_mesh_model_recv(struct bt_mesh_msg_ctx *ctx, struct net_buf_simple *buf);
struct net_buf_simple *buf);
int bt_mesh_comp_register(const struct bt_mesh_comp *comp); int bt_mesh_comp_register(const struct bt_mesh_comp *comp);
int bt_mesh_comp_store(void); int bt_mesh_comp_store(void);

View file

@ -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++) { 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 */ /* Treat existing binding as success */
if (model->keys[i] == key_idx) { if (model->keys[i] == key_idx) {
return STATUS_SUCCESS; return STATUS_SUCCESS;

View file

@ -97,6 +97,8 @@
#define OP_SAR_CFG_RX_GET BT_MESH_MODEL_OP_2(0x80, 0x6f) #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_SET BT_MESH_MODEL_OP_2(0x80, 0x70)
#define OP_SAR_CFG_RX_STATUS BT_MESH_MODEL_OP_2(0x80, 0x71) #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_GET BT_MESH_MODEL_OP_2(0x80, 0x74)
#define OP_LARGE_COMP_DATA_STATUS BT_MESH_MODEL_OP_2(0x80, 0x75) #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) #define OP_MODELS_METADATA_GET BT_MESH_MODEL_OP_2(0x80, 0x76)
@ -131,6 +133,13 @@
#define STATUS_UNSPECIFIED 0x10 #define STATUS_UNSPECIFIED 0x10
#define STATUS_INVALID_BINDING 0x11 #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_model_reset(void);
void bt_mesh_attention(struct bt_mesh_model *model, uint8_t time); void bt_mesh_attention(struct bt_mesh_model *model, uint8_t time);

View file

@ -0,0 +1,132 @@
/*
* Copyright (c) 2022 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/bluetooth/mesh.h>
#include "foundation.h"
#include "op_agg.h"
#define LOG_LEVEL CONFIG_BT_MESH_MODEL_LOG_LEVEL
#include <zephyr/logging/log.h>
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;
}

View file

@ -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);

View file

@ -0,0 +1,210 @@
/*
* Copyright (c) 2022 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/bluetooth/mesh.h>
#include <common/bt_str.h>
#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 <zephyr/logging/log.h>
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,
};

View file

@ -0,0 +1,127 @@
/*
* Copyright (c) 2022 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/bluetooth/mesh.h>
#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 <zephyr/logging/log.h>
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,
};

View file

@ -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); 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; return 0;
} }