1 /*
2 * Copyright (c) 2024 Nordic Semiconductor ASA
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <zephyr/bluetooth/mesh.h>
8 #include "access.h"
9 #include "foundation.h"
10 #include "msg.h"
11
12 #define LOG_LEVEL CONFIG_BT_MESH_MODEL_LOG_LEVEL
13 #include <zephyr/logging/log.h>
14 LOG_MODULE_REGISTER(bt_mesh_brg_cfg_cli);
15
16 static int32_t msg_timeout;
17
18 static struct bt_mesh_brg_cfg_cli *cli;
19
bridge_status(const struct bt_mesh_model * model,struct bt_mesh_msg_ctx * ctx,struct net_buf_simple * buf)20 static int bridge_status(const struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx,
21 struct net_buf_simple *buf)
22 {
23 enum bt_mesh_brg_cfg_state status = (enum bt_mesh_brg_cfg_state)net_buf_simple_pull_u8(buf);
24 enum bt_mesh_brg_cfg_state *rsp;
25
26 if (bt_mesh_msg_ack_ctx_match(&cli->ack_ctx, OP_SUBNET_BRIDGE_STATUS, ctx->addr,
27 (void **)&rsp)) {
28 *rsp = status;
29 bt_mesh_msg_ack_ctx_rx(&cli->ack_ctx);
30 }
31
32 if (cli->cb && cli->cb->bridge_status) {
33 cli->cb->bridge_status(cli, ctx->addr, status);
34 }
35 return 0;
36 }
37
table_status(const struct bt_mesh_model * model,struct bt_mesh_msg_ctx * ctx,struct net_buf_simple * buf)38 static int table_status(const struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx,
39 struct net_buf_simple *buf)
40 {
41 struct bt_mesh_brg_cfg_table_status table_status;
42 struct bt_mesh_brg_cfg_table_status *rsp;
43
44 table_status.status = net_buf_simple_pull_u8(buf);
45 table_status.entry.directions = net_buf_simple_pull_u8(buf);
46 key_idx_unpack_pair(buf, &table_status.entry.net_idx1, &table_status.entry.net_idx2);
47 table_status.entry.addr1 = net_buf_simple_pull_le16(buf);
48 table_status.entry.addr2 = net_buf_simple_pull_le16(buf);
49
50 if (!(table_status.entry.addr1 == BT_MESH_ADDR_UNASSIGNED ||
51 BT_MESH_ADDR_IS_UNICAST(table_status.entry.addr1))) {
52 LOG_ERR("addr1 shall be a unicast address or unassigned.");
53 return -EINVAL;
54 } else if (table_status.entry.addr2 == BT_MESH_ADDR_ALL_NODES) {
55 LOG_ERR("addr2 shall not be the all-nodes fixed group address.");
56 return -EINVAL;
57 }
58
59 if (bt_mesh_msg_ack_ctx_match(&cli->ack_ctx, OP_BRIDGING_TABLE_STATUS, ctx->addr,
60 (void **)&rsp)) {
61 *rsp = table_status;
62 bt_mesh_msg_ack_ctx_rx(&cli->ack_ctx);
63 }
64
65 if (cli->cb && cli->cb->table_status) {
66 cli->cb->table_status(cli, ctx->addr, &table_status);
67 }
68 return 0;
69 }
70
subnets_list(const struct bt_mesh_model * model,struct bt_mesh_msg_ctx * ctx,struct net_buf_simple * buf)71 static int subnets_list(const struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx,
72 struct net_buf_simple *buf)
73 {
74 struct bt_mesh_brg_cfg_subnets_list subnets_list;
75 struct bt_mesh_brg_cfg_subnets_list *rsp;
76 uint16_t net_idx_filter;
77
78 net_idx_filter = net_buf_simple_pull_le16(buf);
79 subnets_list.net_idx_filter.filter = net_idx_filter & BIT_MASK(2);
80 subnets_list.net_idx_filter.prohibited = (net_idx_filter >> 2) & BIT_MASK(2);
81 subnets_list.net_idx_filter.net_idx = (net_idx_filter >> 4) & BIT_MASK(12);
82 subnets_list.start_idx = net_buf_simple_pull_u8(buf);
83
84 if (buf->len && !(buf->len % 3)) {
85 subnets_list.list = buf;
86 } else {
87 subnets_list.list = NULL;
88 }
89
90 if (bt_mesh_msg_ack_ctx_match(&cli->ack_ctx, OP_BRIDGED_SUBNETS_LIST, ctx->addr,
91 (void **)&rsp)) {
92 rsp->net_idx_filter = subnets_list.net_idx_filter;
93 rsp->start_idx = subnets_list.start_idx;
94
95 if (rsp->list) {
96 size_t to_copy;
97
98 to_copy = MIN(net_buf_simple_tailroom(rsp->list), buf->len);
99 net_buf_simple_add_mem(rsp->list, buf->data, to_copy);
100 }
101
102 bt_mesh_msg_ack_ctx_rx(&cli->ack_ctx);
103 }
104
105 if (cli->cb && cli->cb->subnets_list) {
106 cli->cb->subnets_list(cli, ctx->addr, &subnets_list);
107 }
108 return 0;
109 }
110
table_list(const struct bt_mesh_model * model,struct bt_mesh_msg_ctx * ctx,struct net_buf_simple * buf)111 static int table_list(const struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx,
112 struct net_buf_simple *buf)
113 {
114 struct bt_mesh_brg_cfg_table_list table_list;
115 struct bt_mesh_brg_cfg_table_list *rsp;
116
117 table_list.status = net_buf_simple_pull_u8(buf);
118 key_idx_unpack_pair(buf, &table_list.net_idx1, &table_list.net_idx2);
119 table_list.start_idx = net_buf_simple_pull_le16(buf);
120
121 if ((table_list.status == STATUS_SUCCESS) && buf->len && !(buf->len % 5)) {
122 table_list.list = buf;
123 } else {
124 table_list.list = NULL;
125 }
126
127 if (bt_mesh_msg_ack_ctx_match(&cli->ack_ctx, OP_BRIDGING_TABLE_LIST, ctx->addr,
128 (void **)&rsp)) {
129 rsp->status = table_list.status;
130 rsp->net_idx1 = table_list.net_idx1;
131 rsp->net_idx2 = table_list.net_idx2;
132 rsp->start_idx = table_list.start_idx;
133
134 if (rsp->list) {
135 size_t to_copy;
136
137 to_copy = MIN(net_buf_simple_tailroom(rsp->list), (buf->len / 5) * 5);
138 net_buf_simple_add_mem(rsp->list, buf->data, to_copy);
139 }
140
141 bt_mesh_msg_ack_ctx_rx(&cli->ack_ctx);
142 }
143
144 if (cli->cb && cli->cb->table_list) {
145 cli->cb->table_list(cli, ctx->addr, &table_list);
146 }
147 return 0;
148 }
149
table_size_status(const struct bt_mesh_model * model,struct bt_mesh_msg_ctx * ctx,struct net_buf_simple * buf)150 static int table_size_status(const struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx,
151 struct net_buf_simple *buf)
152 {
153 uint16_t size = net_buf_simple_pull_le16(buf);
154 uint16_t *rsp;
155
156 if (bt_mesh_msg_ack_ctx_match(&cli->ack_ctx, OP_BRIDGING_TABLE_SIZE_STATUS, ctx->addr,
157 (void **)&rsp)) {
158 *rsp = size;
159 bt_mesh_msg_ack_ctx_rx(&cli->ack_ctx);
160 }
161
162 if (cli->cb && cli->cb->table_size_status) {
163 cli->cb->table_size_status(cli, ctx->addr, size);
164 }
165 return 0;
166 }
167
168 const struct bt_mesh_model_op _bt_mesh_brg_cfg_cli_op[] = {
169 {OP_SUBNET_BRIDGE_STATUS, BT_MESH_LEN_EXACT(1), bridge_status},
170 {OP_BRIDGING_TABLE_STATUS, BT_MESH_LEN_EXACT(9), table_status},
171 {OP_BRIDGED_SUBNETS_LIST, BT_MESH_LEN_MIN(3), subnets_list},
172 {OP_BRIDGING_TABLE_LIST, BT_MESH_LEN_MIN(6), table_list},
173 {OP_BRIDGING_TABLE_SIZE_STATUS, BT_MESH_LEN_EXACT(2), table_size_status},
174 BT_MESH_MODEL_OP_END,
175 };
176
brg_cfg_cli_init(const struct bt_mesh_model * model)177 static int brg_cfg_cli_init(const struct bt_mesh_model *model)
178 {
179 if (!bt_mesh_model_in_primary(model)) {
180 LOG_ERR("Bridge Configuration Client only allowed in primary element");
181 return -EINVAL;
182 }
183
184 if (!model->rt->user_data) {
185 LOG_ERR("No Bridge Configuration Client context provided");
186 return -EINVAL;
187 }
188
189 cli = model->rt->user_data;
190 cli->model = model;
191 msg_timeout = CONFIG_BT_MESH_BRG_CFG_CLI_TIMEOUT;
192
193 model->keys[0] = BT_MESH_KEY_DEV_ANY;
194 model->rt->flags |= BT_MESH_MOD_DEVKEY_ONLY;
195
196 bt_mesh_msg_ack_ctx_init(&cli->ack_ctx);
197
198 return 0;
199 }
200
201 const struct bt_mesh_model_cb _bt_mesh_brg_cfg_cli_cb = {
202 .init = brg_cfg_cli_init,
203 };
204
bt_mesh_brg_cfg_cli_get(uint16_t net_idx,uint16_t addr,enum bt_mesh_brg_cfg_state * status)205 int bt_mesh_brg_cfg_cli_get(uint16_t net_idx, uint16_t addr, enum bt_mesh_brg_cfg_state *status)
206 {
207 BT_MESH_MODEL_BUF_DEFINE(msg, OP_SUBNET_BRIDGE_GET, 0);
208 struct bt_mesh_msg_ctx ctx = BT_MESH_MSG_CTX_INIT_DEV(net_idx, addr);
209 const struct bt_mesh_msg_rsp_ctx rsp_ctx = {
210 .ack = &cli->ack_ctx,
211 .op = OP_SUBNET_BRIDGE_STATUS,
212 .user_data = status,
213 .timeout = msg_timeout,
214 };
215
216 bt_mesh_model_msg_init(&msg, OP_SUBNET_BRIDGE_GET);
217
218 return bt_mesh_msg_ackd_send(cli->model, &ctx, &msg, !status ? NULL : &rsp_ctx);
219 }
220
bt_mesh_brg_cfg_cli_set(uint16_t net_idx,uint16_t addr,enum bt_mesh_brg_cfg_state val,enum bt_mesh_brg_cfg_state * status)221 int bt_mesh_brg_cfg_cli_set(uint16_t net_idx, uint16_t addr, enum bt_mesh_brg_cfg_state val,
222 enum bt_mesh_brg_cfg_state *status)
223 {
224 BT_MESH_MODEL_BUF_DEFINE(msg, OP_SUBNET_BRIDGE_SET, 1);
225 struct bt_mesh_msg_ctx ctx = BT_MESH_MSG_CTX_INIT_DEV(net_idx, addr);
226 const struct bt_mesh_msg_rsp_ctx rsp_ctx = {
227 .ack = &cli->ack_ctx,
228 .op = OP_SUBNET_BRIDGE_STATUS,
229 .user_data = status,
230 .timeout = msg_timeout,
231 };
232
233 bt_mesh_model_msg_init(&msg, OP_SUBNET_BRIDGE_SET);
234 net_buf_simple_add_u8(&msg, (uint8_t)val);
235
236 return bt_mesh_msg_ackd_send(cli->model, &ctx, &msg, !status ? NULL : &rsp_ctx);
237 }
238
bt_mesh_brg_cfg_cli_table_size_get(uint16_t net_idx,uint16_t addr,uint16_t * size)239 int bt_mesh_brg_cfg_cli_table_size_get(uint16_t net_idx, uint16_t addr, uint16_t *size)
240 {
241 BT_MESH_MODEL_BUF_DEFINE(msg, OP_BRIDGING_TABLE_SIZE_GET, 0);
242 struct bt_mesh_msg_ctx ctx = BT_MESH_MSG_CTX_INIT_DEV(net_idx, addr);
243 const struct bt_mesh_msg_rsp_ctx rsp_ctx = {
244 .ack = &cli->ack_ctx,
245 .op = OP_BRIDGING_TABLE_SIZE_STATUS,
246 .user_data = size,
247 .timeout = msg_timeout,
248 };
249
250 bt_mesh_model_msg_init(&msg, OP_BRIDGING_TABLE_SIZE_GET);
251
252 return bt_mesh_msg_ackd_send(cli->model, &ctx, &msg, !size ? NULL : &rsp_ctx);
253 }
254
bt_mesh_brg_cfg_cli_table_add(uint16_t net_idx,uint16_t addr,struct bt_mesh_brg_cfg_table_entry * entry,struct bt_mesh_brg_cfg_table_status * rsp)255 int bt_mesh_brg_cfg_cli_table_add(uint16_t net_idx, uint16_t addr,
256 struct bt_mesh_brg_cfg_table_entry *entry,
257 struct bt_mesh_brg_cfg_table_status *rsp)
258 {
259 BT_MESH_MODEL_BUF_DEFINE(msg, OP_BRIDGING_TABLE_ADD, 8);
260 struct bt_mesh_msg_ctx ctx = BT_MESH_MSG_CTX_INIT_DEV(net_idx, addr);
261 const struct bt_mesh_msg_rsp_ctx rsp_ctx = {
262 .ack = &cli->ack_ctx,
263 .op = OP_BRIDGING_TABLE_STATUS,
264 .user_data = rsp,
265 .timeout = msg_timeout,
266 };
267
268 if (entry->addr1 == entry->addr2) {
269 LOG_ERR("addr1 and addr2 shall have different values.");
270 return -EINVAL;
271 } else if (!BT_MESH_ADDR_IS_UNICAST(entry->addr1)) {
272 LOG_ERR("addr1 shall be a unicast address.");
273 return -EINVAL;
274 } else if (entry->directions == 0x01 && (entry->addr2 == BT_MESH_ADDR_UNASSIGNED ||
275 entry->addr2 == BT_MESH_ADDR_ALL_NODES)) {
276 LOG_ERR("For direction 0x01: addr2 shall not be unassigned or the all-nodes fixed "
277 "group address.");
278 return -EINVAL;
279 } else if (entry->directions == 0x02 && !BT_MESH_ADDR_IS_UNICAST(entry->addr2)) {
280 LOG_ERR("For direction 0x02: addr2 shall be a unicast address.");
281 return -EINVAL;
282 }
283
284 bt_mesh_model_msg_init(&msg, OP_BRIDGING_TABLE_ADD);
285 net_buf_simple_add_u8(&msg, entry->directions);
286 key_idx_pack_pair(&msg, entry->net_idx1, entry->net_idx2);
287 net_buf_simple_add_le16(&msg, entry->addr1);
288 net_buf_simple_add_le16(&msg, entry->addr2);
289
290 return bt_mesh_msg_ackd_send(cli->model, &ctx, &msg, !rsp ? NULL : &rsp_ctx);
291 }
292
bt_mesh_brg_cfg_cli_table_remove(uint16_t net_idx,uint16_t addr,uint16_t net_idx1,uint16_t net_idx2,uint16_t addr1,uint16_t addr2,struct bt_mesh_brg_cfg_table_status * rsp)293 int bt_mesh_brg_cfg_cli_table_remove(uint16_t net_idx, uint16_t addr, uint16_t net_idx1,
294 uint16_t net_idx2, uint16_t addr1, uint16_t addr2,
295 struct bt_mesh_brg_cfg_table_status *rsp)
296 {
297 BT_MESH_MODEL_BUF_DEFINE(msg, OP_BRIDGING_TABLE_REMOVE, 7);
298 struct bt_mesh_msg_ctx ctx = BT_MESH_MSG_CTX_INIT_DEV(net_idx, addr);
299 const struct bt_mesh_msg_rsp_ctx rsp_ctx = {
300 .ack = &cli->ack_ctx,
301 .op = OP_BRIDGING_TABLE_STATUS,
302 .user_data = rsp,
303 .timeout = msg_timeout,
304 };
305
306 if (!(addr1 == BT_MESH_ADDR_UNASSIGNED || BT_MESH_ADDR_IS_UNICAST(addr1))) {
307 LOG_ERR("addr1 shall be a unicast address or unassigned.");
308 return -EINVAL;
309 } else if (addr2 == BT_MESH_ADDR_ALL_NODES) {
310 LOG_ERR("addr2 shall not be the all-nodes fixed group address.");
311 return -EINVAL;
312 }
313
314 bt_mesh_model_msg_init(&msg, OP_BRIDGING_TABLE_REMOVE);
315 key_idx_pack_pair(&msg, net_idx1, net_idx2);
316 net_buf_simple_add_le16(&msg, addr1);
317 net_buf_simple_add_le16(&msg, addr2);
318
319 return bt_mesh_msg_ackd_send(cli->model, &ctx, &msg, !rsp ? NULL : &rsp_ctx);
320 }
321
bt_mesh_brg_cfg_cli_subnets_get(uint16_t net_idx,uint16_t addr,struct bt_mesh_brg_cfg_filter_netkey filter_net_idx,uint8_t start_idx,struct bt_mesh_brg_cfg_subnets_list * rsp)322 int bt_mesh_brg_cfg_cli_subnets_get(uint16_t net_idx, uint16_t addr,
323 struct bt_mesh_brg_cfg_filter_netkey filter_net_idx,
324 uint8_t start_idx, struct bt_mesh_brg_cfg_subnets_list *rsp)
325 {
326 BT_MESH_MODEL_BUF_DEFINE(msg, OP_BRIDGED_SUBNETS_GET, 3);
327 struct bt_mesh_msg_ctx ctx = BT_MESH_MSG_CTX_INIT_DEV(net_idx, addr);
328 const struct bt_mesh_msg_rsp_ctx rsp_ctx = {
329 .ack = &cli->ack_ctx,
330 .op = OP_BRIDGED_SUBNETS_LIST,
331 .user_data = rsp,
332 .timeout = msg_timeout,
333 };
334
335 bt_mesh_model_msg_init(&msg, OP_BRIDGED_SUBNETS_GET);
336 net_buf_simple_add_le16(&msg, (filter_net_idx.filter | filter_net_idx.net_idx << 4));
337 net_buf_simple_add_u8(&msg, start_idx);
338
339 return bt_mesh_msg_ackd_send(cli->model, &ctx, &msg, !rsp ? NULL : &rsp_ctx);
340 }
341
bt_mesh_brg_cfg_cli_table_get(uint16_t net_idx,uint16_t addr,uint16_t net_idx1,uint16_t net_idx2,uint16_t start_idx,struct bt_mesh_brg_cfg_table_list * rsp)342 int bt_mesh_brg_cfg_cli_table_get(uint16_t net_idx, uint16_t addr, uint16_t net_idx1,
343 uint16_t net_idx2, uint16_t start_idx,
344 struct bt_mesh_brg_cfg_table_list *rsp)
345 {
346 BT_MESH_MODEL_BUF_DEFINE(msg, OP_BRIDGING_TABLE_GET, 5);
347 struct bt_mesh_msg_ctx ctx = BT_MESH_MSG_CTX_INIT_DEV(net_idx, addr);
348 const struct bt_mesh_msg_rsp_ctx rsp_ctx = {
349 .ack = &cli->ack_ctx,
350 .op = OP_BRIDGING_TABLE_LIST,
351 .user_data = rsp,
352 .timeout = msg_timeout,
353 };
354
355 bt_mesh_model_msg_init(&msg, OP_BRIDGING_TABLE_GET);
356 key_idx_pack_pair(&msg, net_idx1, net_idx2);
357 net_buf_simple_add_le16(&msg, start_idx);
358
359 return bt_mesh_msg_ackd_send(cli->model, &ctx, &msg, !rsp ? NULL : &rsp_ctx);
360 }
361
bt_mesh_brg_cfg_cli_timeout_get(void)362 int32_t bt_mesh_brg_cfg_cli_timeout_get(void)
363 {
364 return msg_timeout;
365 }
366
bt_mesh_brg_cfg_cli_timeout_set(int32_t timeout)367 void bt_mesh_brg_cfg_cli_timeout_set(int32_t timeout)
368 {
369 msg_timeout = timeout;
370 }
371