/* * 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, };