1 /*
2 * Copyright (c) 2022 Nordic Semiconductor ASA
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <zephyr/bluetooth/mesh.h>
8
9 #include <common/bt_str.h>
10
11 #include "access.h"
12 #include "foundation.h"
13 #include "net.h"
14 #include "mesh.h"
15 #include "op_agg.h"
16
17 #define LOG_LEVEL CONFIG_BT_MESH_MODEL_LOG_LEVEL
18 #include <zephyr/logging/log.h>
19 LOG_MODULE_REGISTER(bt_mesh_op_agg_cli);
20
21 NET_BUF_SIMPLE_DEFINE_STATIC(srcs, BT_MESH_TX_SDU_MAX);
22 NET_BUF_SIMPLE_DEFINE_STATIC(sdu, BT_MESH_TX_SDU_MAX);
23
24 /** Mesh Opcodes Aggregator Client Model Context */
25 static struct bt_mesh_op_agg_cli {
26 /** Composition data model entry pointer. */
27 const struct bt_mesh_model *model;
28 /** Acknowledge context. */
29 struct bt_mesh_msg_ack_ctx ack_ctx;
30 /** List of source element addresses.
31 * Used by Client to match aggregated responses
32 * with local source client models.
33 */
34 struct net_buf_simple *srcs;
35 /** Aggregator context. */
36 struct op_agg_ctx ctx;
37
38 } cli = {.srcs = &srcs, .ctx.sdu = &sdu};
39
40 static int32_t msg_timeout;
41
handle_status(const struct bt_mesh_model * model,struct bt_mesh_msg_ctx * ctx,struct net_buf_simple * buf)42 static int handle_status(const struct bt_mesh_model *model,
43 struct bt_mesh_msg_ctx *ctx,
44 struct net_buf_simple *buf)
45 {
46 struct net_buf_simple msg;
47 uint8_t status;
48 uint16_t elem_addr, addr;
49 int err;
50
51 LOG_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s",
52 ctx->net_idx, ctx->app_idx, ctx->addr, buf->len,
53 bt_hex(buf->data, buf->len));
54
55 if (!bt_mesh_msg_ack_ctx_match(&cli.ack_ctx,
56 OP_OPCODES_AGGREGATOR_STATUS, ctx->addr,
57 NULL)) {
58 LOG_WRN("Unexpected Opcodes Aggregator Status");
59 return -ENOENT;
60 }
61
62 status = net_buf_simple_pull_u8(buf);
63 elem_addr = net_buf_simple_pull_le16(buf);
64
65 while (buf->len > 0) {
66 err = bt_mesh_op_agg_decode_msg(&msg, buf);
67 if (err) {
68 LOG_ERR("Cannot decode aggregated message %d", err);
69 cli.ctx.initialized = true;
70 return -EINVAL;
71 }
72
73 if (cli.srcs->len < 2) {
74 LOG_ERR("Mismatch in sources address buffer");
75 cli.ctx.initialized = true;
76 return -ENOENT;
77 }
78
79 addr = net_buf_simple_pull_le16(cli.srcs);
80
81 /* Empty item means unacked msg. */
82 if (!msg.len) {
83 continue;
84 }
85
86 ctx->recv_dst = addr;
87 err = bt_mesh_model_recv(ctx, &msg);
88 if (err) {
89 LOG_ERR("Opcodes Aggregator receive error %d", err);
90 cli.ctx.initialized = true;
91 return err;
92 }
93 }
94
95 bt_mesh_msg_ack_ctx_rx(&cli.ack_ctx);
96
97 return 0;
98 }
99
100 const struct bt_mesh_model_op _bt_mesh_op_agg_cli_op[] = {
101 { OP_OPCODES_AGGREGATOR_STATUS, BT_MESH_LEN_MIN(3), handle_status },
102 BT_MESH_MODEL_OP_END,
103 };
104
op_agg_cli_init(const struct bt_mesh_model * model)105 static int op_agg_cli_init(const struct bt_mesh_model *model)
106 {
107 if (!bt_mesh_model_in_primary(model)) {
108 LOG_ERR("Opcodes Aggregator Client only allowed in primary element");
109 return -EINVAL;
110 }
111
112 /* Opcodes Aggregator Client model shall use the device key and
113 * application keys.
114 */
115 model->keys[0] = BT_MESH_KEY_DEV_ANY;
116
117 msg_timeout = CONFIG_BT_MESH_OP_AGG_CLI_TIMEOUT;
118 cli.model = model;
119 bt_mesh_msg_ack_ctx_init(&cli.ack_ctx);
120
121 return 0;
122 }
123
bt_mesh_op_agg_cli_seq_start(uint16_t net_idx,uint16_t app_idx,uint16_t dst,uint16_t elem_addr)124 int bt_mesh_op_agg_cli_seq_start(uint16_t net_idx, uint16_t app_idx, uint16_t dst,
125 uint16_t elem_addr)
126 {
127 if (!BT_MESH_ADDR_IS_UNICAST(elem_addr)) {
128 LOG_ERR("Element address shall be a unicast address");
129 return -EINVAL;
130 }
131
132 if (cli.ctx.initialized) {
133 LOG_ERR("Opcodes Aggregator is already configured");
134 return -EALREADY;
135 }
136
137 cli.ctx.net_idx = net_idx;
138 cli.ctx.app_idx = app_idx;
139 cli.ctx.addr = dst;
140 cli.ctx.initialized = true;
141
142 net_buf_simple_init(cli.srcs, 0);
143 bt_mesh_model_msg_init(cli.ctx.sdu, OP_OPCODES_AGGREGATOR_SEQUENCE);
144 net_buf_simple_add_le16(cli.ctx.sdu, elem_addr);
145
146 return 0;
147 }
148
bt_mesh_op_agg_cli_seq_send(void)149 int bt_mesh_op_agg_cli_seq_send(void)
150 {
151 struct bt_mesh_msg_ctx ctx = {
152 .net_idx = cli.ctx.net_idx,
153 .app_idx = cli.ctx.app_idx,
154 .addr = cli.ctx.addr,
155 };
156 int err;
157
158 if (!cli.ctx.initialized) {
159 LOG_ERR("Opcodes Aggregator not initialized");
160 return -EINVAL;
161 }
162
163 err = bt_mesh_msg_ack_ctx_prepare(&cli.ack_ctx, OP_OPCODES_AGGREGATOR_STATUS,
164 cli.ctx.addr, NULL);
165 if (err) {
166 return err;
167 }
168
169 cli.ctx.initialized = false;
170
171 err = bt_mesh_model_send(cli.model, &ctx, cli.ctx.sdu, NULL, NULL);
172 if (err) {
173 LOG_ERR("model_send() failed (err %d)", err);
174 bt_mesh_msg_ack_ctx_clear(&cli.ack_ctx);
175 return err;
176 }
177
178 return bt_mesh_msg_ack_ctx_wait(&cli.ack_ctx, K_MSEC(msg_timeout));
179 }
180
bt_mesh_op_agg_cli_seq_abort(void)181 void bt_mesh_op_agg_cli_seq_abort(void)
182 {
183 cli.ctx.initialized = false;
184 }
185
bt_mesh_op_agg_cli_seq_is_started(void)186 bool bt_mesh_op_agg_cli_seq_is_started(void)
187 {
188 return cli.ctx.initialized;
189 }
190
bt_mesh_op_agg_cli_seq_tailroom(void)191 size_t bt_mesh_op_agg_cli_seq_tailroom(void)
192 {
193 return net_buf_simple_tailroom(cli.ctx.sdu);
194 }
195
bt_mesh_op_agg_cli_timeout_get(void)196 int32_t bt_mesh_op_agg_cli_timeout_get(void)
197 {
198 return msg_timeout;
199 }
200
bt_mesh_op_agg_cli_timeout_set(int32_t timeout)201 void bt_mesh_op_agg_cli_timeout_set(int32_t timeout)
202 {
203 msg_timeout = timeout;
204 }
205
bt_mesh_op_agg_cli_send(const struct bt_mesh_model * model,struct net_buf_simple * msg)206 int bt_mesh_op_agg_cli_send(const struct bt_mesh_model *model, struct net_buf_simple *msg)
207 {
208 uint16_t src = bt_mesh_model_elem(model)->rt->addr;
209
210 if (net_buf_simple_tailroom(&srcs) < 2) {
211 return -ENOMEM;
212 }
213
214 net_buf_simple_add_le16(&srcs, src);
215 return bt_mesh_op_agg_encode_msg(msg, cli.ctx.sdu);
216 }
217
bt_mesh_op_agg_cli_accept(struct bt_mesh_msg_ctx * ctx,struct net_buf_simple * buf)218 int bt_mesh_op_agg_cli_accept(struct bt_mesh_msg_ctx *ctx, struct net_buf_simple *buf)
219 {
220
221 return cli.ctx.initialized && (ctx->net_idx == cli.ctx.net_idx) &&
222 (ctx->addr == cli.ctx.addr) && (ctx->app_idx == cli.ctx.app_idx) &&
223 !bt_mesh_op_agg_is_op_agg_msg(buf);
224 }
225
226 const struct bt_mesh_model_cb _bt_mesh_op_agg_cli_cb = {
227 .init = op_agg_cli_init,
228 };
229