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