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:
parent
d46480c36b
commit
da5195ea97
18 changed files with 837 additions and 36 deletions
|
@ -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
|
||||
**************************
|
||||
|
|
30
doc/connectivity/bluetooth/api/mesh/op_agg_cli.rst
Normal file
30
doc/connectivity/bluetooth/api/mesh/op_agg_cli.rst
Normal 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:
|
32
doc/connectivity/bluetooth/api/mesh/op_agg_srv.rst
Normal file
32
doc/connectivity/bluetooth/api/mesh/op_agg_srv.rst
Normal 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:
|
|
@ -38,6 +38,8 @@
|
|||
#include <zephyr/bluetooth/mesh/rpr_srv.h>
|
||||
#include <zephyr/bluetooth/mesh/sar_cfg_srv.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_cli.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
|
||||
|
||||
|
|
90
include/zephyr/bluetooth/mesh/op_agg_cli.h
Normal file
90
include/zephyr/bluetooth/mesh/op_agg_cli.h
Normal 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__ */
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
47
include/zephyr/bluetooth/mesh/op_agg_srv.h
Normal file
47
include/zephyr/bluetooth/mesh/op_agg_srv.h
Normal 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__ */
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
132
subsys/bluetooth/mesh/op_agg.c
Normal file
132
subsys/bluetooth/mesh/op_agg.c
Normal 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;
|
||||
}
|
51
subsys/bluetooth/mesh/op_agg.h
Normal file
51
subsys/bluetooth/mesh/op_agg.h
Normal 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);
|
210
subsys/bluetooth/mesh/op_agg_cli.c
Normal file
210
subsys/bluetooth/mesh/op_agg_cli.c
Normal 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,
|
||||
};
|
127
subsys/bluetooth/mesh/op_agg_srv.c
Normal file
127
subsys/bluetooth/mesh/op_agg_srv.c
Normal 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,
|
||||
};
|
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue