Bluetooth: Mesh: Op agg mdl coex
Refactors the implementation of the Opcode Aggregator models to allow them to coexist on the same device. Signed-off-by: Anders Storrø <anders.storro@nordicsemi.no>
This commit is contained in:
parent
31081d8411
commit
0a25a61c7d
5 changed files with 141 additions and 157 deletions
|
@ -1506,8 +1506,10 @@ 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 (IS_ENABLED(CONFIG_BT_MESH_OP_AGG_SRV) && bt_mesh_op_agg_srv_accept(ctx, msg)) {
|
||||
return bt_mesh_op_agg_srv_send(model, msg);
|
||||
} else if (IS_ENABLED(CONFIG_BT_MESH_OP_AGG_CLI) && bt_mesh_op_agg_cli_accept(ctx, msg)) {
|
||||
return bt_mesh_op_agg_cli_send(model, msg);
|
||||
}
|
||||
|
||||
if (!bt_mesh_model_has_key(model, ctx->app_idx)) {
|
||||
|
|
|
@ -16,83 +16,22 @@ 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);
|
||||
NET_BUF_SIMPLE_DEFINE_STATIC(srcs, BT_MESH_TX_SDU_MAX);
|
||||
|
||||
static struct op_agg_ctx agg_ctx = {
|
||||
.sdu = &sdu,
|
||||
#if IS_ENABLED(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)
|
||||
{
|
||||
return agg_ctx.initialized && ctx_match(msg_ctx);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
int err;
|
||||
|
||||
/* Model responded so mark this message as acknowledged */
|
||||
agg_ctx.ack = true;
|
||||
|
||||
if (IS_ENABLED(CONFIG_BT_MESH_OP_AGG_CLI)) {
|
||||
/* Store source address so that Opcodes Aggregator Client can
|
||||
* match response with source model
|
||||
*/
|
||||
uint16_t 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)
|
||||
int bt_mesh_op_agg_encode_msg(struct net_buf_simple *msg, struct net_buf_simple *buf)
|
||||
{
|
||||
if (msg->len > LENGTH_SHORT_MAX) {
|
||||
if (net_buf_simple_tailroom(agg_ctx.sdu) < (msg->len + 2)) {
|
||||
if (net_buf_simple_tailroom(buf) < (msg->len + 2)) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
net_buf_simple_add_le16(agg_ctx.sdu, (msg->len << 1) | 1);
|
||||
net_buf_simple_add_le16(buf, (msg->len << 1) | 1);
|
||||
} else {
|
||||
if (net_buf_simple_tailroom(agg_ctx.sdu) < (msg->len + 1)) {
|
||||
if (net_buf_simple_tailroom(buf) < (msg->len + 1)) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
net_buf_simple_add_u8(agg_ctx.sdu, msg->len << 1);
|
||||
net_buf_simple_add_u8(buf, msg->len << 1);
|
||||
}
|
||||
net_buf_simple_add_mem(agg_ctx.sdu, msg->data, msg->len);
|
||||
net_buf_simple_add_mem(buf, msg->data, msg->len);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -124,3 +63,21 @@ int bt_mesh_op_agg_decode_msg(struct net_buf_simple *msg,
|
|||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool bt_mesh_op_agg_is_op_agg_msg(struct net_buf_simple *buf)
|
||||
{
|
||||
if (buf->len >= 2 && (buf->data[0] >> 6) == 2) {
|
||||
uint16_t opcode;
|
||||
struct net_buf_simple_state state;
|
||||
|
||||
net_buf_simple_save(buf, &state);
|
||||
opcode = net_buf_simple_pull_be16(buf);
|
||||
net_buf_simple_restore(buf, &state);
|
||||
|
||||
if ((opcode == OP_OPCODES_AGGREGATOR_STATUS) ||
|
||||
(opcode == OP_OPCODES_AGGREGATOR_SEQUENCE)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -7,45 +7,20 @@
|
|||
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);
|
||||
int bt_mesh_op_agg_encode_msg(struct net_buf_simple *msg, struct net_buf_simple *buf);
|
||||
int bt_mesh_op_agg_decode_msg(struct net_buf_simple *msg, struct net_buf_simple *buf);
|
||||
int bt_mesh_op_agg_cli_send(struct bt_mesh_model *model, struct net_buf_simple *msg);
|
||||
int bt_mesh_op_agg_cli_accept(struct bt_mesh_msg_ctx *ctx, struct net_buf_simple *buf);
|
||||
int bt_mesh_op_agg_srv_send(struct bt_mesh_model *model, struct net_buf_simple *msg);
|
||||
int bt_mesh_op_agg_srv_accept(struct bt_mesh_msg_ctx *ctx, struct net_buf_simple *buf);
|
||||
bool bt_mesh_op_agg_is_op_agg_msg(struct net_buf_simple *buf);
|
||||
|
|
|
@ -18,14 +18,24 @@
|
|||
#include <zephyr/logging/log.h>
|
||||
LOG_MODULE_REGISTER(bt_mesh_op_agg_cli);
|
||||
|
||||
NET_BUF_SIMPLE_DEFINE_STATIC(srcs, BT_MESH_TX_SDU_MAX);
|
||||
NET_BUF_SIMPLE_DEFINE_STATIC(sdu, BT_MESH_TX_SDU_MAX);
|
||||
|
||||
/** 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. */
|
||||
/** Acknowledge context. */
|
||||
struct bt_mesh_msg_ack_ctx ack_ctx;
|
||||
} cli;
|
||||
/** List of source element addresses.
|
||||
* Used by Client to match aggregated responses
|
||||
* with local source client models.
|
||||
*/
|
||||
struct net_buf_simple *srcs;
|
||||
/** Aggregator context. */
|
||||
struct op_agg_ctx ctx;
|
||||
|
||||
} cli = {.srcs = &srcs, .ctx.sdu = &sdu};
|
||||
|
||||
static int32_t msg_timeout;
|
||||
|
||||
|
@ -33,7 +43,6 @@ 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;
|
||||
|
@ -57,17 +66,17 @@ static int handle_status(struct bt_mesh_model *model,
|
|||
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();
|
||||
cli.ctx.initialized = true;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (agg->srcs->len < 2) {
|
||||
if (cli.srcs->len < 2) {
|
||||
LOG_ERR("Mismatch in sources address buffer");
|
||||
bt_mesh_op_agg_ctx_reinit();
|
||||
cli.ctx.initialized = true;
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
addr = net_buf_simple_pull_le16(agg->srcs);
|
||||
addr = net_buf_simple_pull_le16(cli.srcs);
|
||||
|
||||
/* Empty item means unacked msg. */
|
||||
if (!msg.len) {
|
||||
|
@ -78,7 +87,7 @@ static int handle_status(struct bt_mesh_model *model,
|
|||
err = bt_mesh_model_recv(ctx, &msg);
|
||||
if (err) {
|
||||
LOG_ERR("Opcodes Aggregator receive error %d", err);
|
||||
bt_mesh_op_agg_ctx_reinit();
|
||||
cli.ctx.initialized = true;
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
@ -115,56 +124,51 @@ static int op_agg_cli_init(struct bt_mesh_model *model)
|
|||
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) {
|
||||
if (cli.ctx.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;
|
||||
cli.ctx.net_idx = net_idx;
|
||||
cli.ctx.app_idx = app_idx;
|
||||
cli.ctx.addr = dst;
|
||||
cli.ctx.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);
|
||||
net_buf_simple_init(cli.srcs, 0);
|
||||
bt_mesh_model_msg_init(cli.ctx.sdu, OP_OPCODES_AGGREGATOR_SEQUENCE);
|
||||
net_buf_simple_add_le16(cli.ctx.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,
|
||||
.net_idx = cli.ctx.net_idx,
|
||||
.app_idx = cli.ctx.app_idx,
|
||||
.addr = cli.ctx.addr,
|
||||
};
|
||||
int err;
|
||||
|
||||
if (!agg->initialized) {
|
||||
if (!cli.ctx.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);
|
||||
err = bt_mesh_msg_ack_ctx_prepare(&cli.ack_ctx, OP_OPCODES_AGGREGATOR_STATUS,
|
||||
cli.ctx.addr, NULL);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
agg->initialized = false;
|
||||
cli.ctx.initialized = false;
|
||||
|
||||
err = bt_mesh_model_send(cli.model, &ctx, agg->sdu, NULL, NULL);
|
||||
err = bt_mesh_model_send(cli.model, &ctx, cli.ctx.sdu, NULL, NULL);
|
||||
if (err) {
|
||||
LOG_ERR("model_send() failed (err %d)", err);
|
||||
bt_mesh_msg_ack_ctx_clear(&cli.ack_ctx);
|
||||
|
@ -176,23 +180,17 @@ int bt_mesh_op_agg_cli_seq_send(void)
|
|||
|
||||
void bt_mesh_op_agg_cli_seq_abort(void)
|
||||
{
|
||||
struct op_agg_ctx *agg = bt_mesh_op_agg_ctx_get();
|
||||
|
||||
agg->initialized = false;
|
||||
cli.ctx.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;
|
||||
return cli.ctx.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);
|
||||
return net_buf_simple_tailroom(cli.ctx.sdu);
|
||||
}
|
||||
|
||||
int32_t bt_mesh_op_agg_cli_timeout_get(void)
|
||||
|
@ -205,6 +203,26 @@ void bt_mesh_op_agg_cli_timeout_set(int32_t timeout)
|
|||
msg_timeout = timeout;
|
||||
}
|
||||
|
||||
int bt_mesh_op_agg_cli_send(struct bt_mesh_model *model, struct net_buf_simple *msg)
|
||||
{
|
||||
uint16_t src = bt_mesh_model_elem(model)->addr;
|
||||
|
||||
if (net_buf_simple_tailroom(&srcs) < 2) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
net_buf_simple_add_le16(&srcs, src);
|
||||
return bt_mesh_op_agg_encode_msg(msg, cli.ctx.sdu);
|
||||
}
|
||||
|
||||
int bt_mesh_op_agg_cli_accept(struct bt_mesh_msg_ctx *ctx, struct net_buf_simple *buf)
|
||||
{
|
||||
|
||||
return cli.ctx.initialized && (ctx->net_idx == cli.ctx.net_idx) &&
|
||||
(ctx->addr == cli.ctx.addr) && (ctx->app_idx == cli.ctx.app_idx) &&
|
||||
!bt_mesh_op_agg_is_op_agg_msg(buf);
|
||||
}
|
||||
|
||||
const struct bt_mesh_model_cb _bt_mesh_op_agg_cli_cb = {
|
||||
.init = op_agg_cli_init,
|
||||
};
|
||||
|
|
|
@ -17,17 +17,26 @@
|
|||
#include <zephyr/logging/log.h>
|
||||
LOG_MODULE_REGISTER(bt_mesh_op_agg_srv);
|
||||
|
||||
NET_BUF_SIMPLE_DEFINE_STATIC(sdu, BT_MESH_TX_SDU_MAX);
|
||||
|
||||
/** Mesh Opcodes Aggragator Server Model Context */
|
||||
static struct bt_mesh_op_agg_srv {
|
||||
/** Composition data model entry pointer. */
|
||||
struct bt_mesh_model *model;
|
||||
} srv;
|
||||
/** Response error code. */
|
||||
int rsp_err;
|
||||
/** Indicates that the received aggregated message
|
||||
* was acknowledged by local server model.
|
||||
*/
|
||||
bool ack;
|
||||
/** Aggregator context. */
|
||||
struct op_agg_ctx ctx;
|
||||
} srv = {.ctx.sdu = &sdu};
|
||||
|
||||
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;
|
||||
|
@ -37,14 +46,14 @@ static int handle_sequence(struct bt_mesh_model *model,
|
|||
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);
|
||||
bt_mesh_model_msg_init(srv.ctx.sdu, OP_OPCODES_AGGREGATOR_STATUS);
|
||||
status = net_buf_simple_add_u8(srv.ctx.sdu, 0);
|
||||
net_buf_simple_add_le16(srv.ctx.sdu, elem);
|
||||
|
||||
agg->net_idx = ctx->net_idx;
|
||||
agg->app_idx = ctx->app_idx;
|
||||
agg->addr = ctx->addr;
|
||||
agg->initialized = true;
|
||||
srv.ctx.net_idx = ctx->net_idx;
|
||||
srv.ctx.app_idx = ctx->app_idx;
|
||||
srv.ctx.addr = ctx->addr;
|
||||
srv.ctx.initialized = true;
|
||||
|
||||
if (!BT_MESH_ADDR_IS_UNICAST(elem)) {
|
||||
LOG_WRN("Address is not unicast, ignoring.");
|
||||
|
@ -70,12 +79,12 @@ static int handle_sequence(struct bt_mesh_model *model,
|
|||
while (buf->len > 0) {
|
||||
(void) bt_mesh_op_agg_decode_msg(&msg, buf);
|
||||
|
||||
agg->ack = false;
|
||||
agg->rsp_err = 0;
|
||||
srv.ack = false;
|
||||
srv.rsp_err = 0;
|
||||
err = bt_mesh_model_recv(ctx, &msg);
|
||||
|
||||
if (agg->rsp_err) {
|
||||
*status = agg->rsp_err;
|
||||
if (srv.rsp_err) {
|
||||
*status = srv.rsp_err;
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -84,14 +93,14 @@ static int handle_sequence(struct bt_mesh_model *model,
|
|||
break;
|
||||
}
|
||||
|
||||
if (!agg->ack) {
|
||||
net_buf_simple_add_u8(agg->sdu, 0);
|
||||
if (!srv.ack) {
|
||||
net_buf_simple_add_u8(srv.ctx.sdu, 0);
|
||||
}
|
||||
}
|
||||
|
||||
send:
|
||||
agg->initialized = false;
|
||||
err = bt_mesh_model_send(model, ctx, agg->sdu, NULL, NULL);
|
||||
srv.ctx.initialized = false;
|
||||
err = bt_mesh_model_send(model, ctx, srv.ctx.sdu, NULL, NULL);
|
||||
if (err) {
|
||||
LOG_ERR("Unable to send Opcodes Aggregator Status");
|
||||
return err;
|
||||
|
@ -122,6 +131,29 @@ static int op_agg_srv_init(struct bt_mesh_model *model)
|
|||
return 0;
|
||||
}
|
||||
|
||||
int bt_mesh_op_agg_srv_send(struct bt_mesh_model *model, struct net_buf_simple *msg)
|
||||
{
|
||||
int err;
|
||||
|
||||
/* Model responded so mark this message as acknowledged */
|
||||
srv.ack = true;
|
||||
|
||||
err = bt_mesh_op_agg_encode_msg(msg, srv.ctx.sdu);
|
||||
if (err) {
|
||||
srv.rsp_err = ACCESS_STATUS_RESPONSE_OVERFLOW;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int bt_mesh_op_agg_srv_accept(struct bt_mesh_msg_ctx *ctx, struct net_buf_simple *buf)
|
||||
{
|
||||
|
||||
return srv.ctx.initialized && (ctx->net_idx == srv.ctx.net_idx) &&
|
||||
(ctx->addr == srv.ctx.addr) && (ctx->app_idx == srv.ctx.app_idx) &&
|
||||
!bt_mesh_op_agg_is_op_agg_msg(buf);
|
||||
}
|
||||
|
||||
const struct bt_mesh_model_cb _bt_mesh_op_agg_srv_cb = {
|
||||
.init = op_agg_srv_init,
|
||||
};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue