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 "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_sol_pdu_rpl_cli);
15 
16 static struct bt_mesh_sol_pdu_rpl_cli *cli;
17 
18 static int32_t msg_timeout;
19 
20 struct sol_rpl_param {
21 	uint16_t *start;
22 	uint8_t *len;
23 };
24 
handle_status(const struct bt_mesh_model * mod,struct bt_mesh_msg_ctx * ctx,struct net_buf_simple * buf)25 static int handle_status(const struct bt_mesh_model *mod,
26 			 struct bt_mesh_msg_ctx *ctx,
27 			 struct net_buf_simple *buf)
28 {
29 	struct sol_rpl_param *param;
30 	uint16_t primary, range;
31 	uint8_t len = 0;
32 
33 	LOG_DBG("");
34 
35 	if (buf->len > 3) {
36 		return -EMSGSIZE;
37 	}
38 
39 	range = net_buf_simple_pull_le16(buf);
40 	primary = range >> 1;
41 	if (primary == 0) {
42 		return -EINVAL;
43 	}
44 
45 	if (range & BIT(0)) {
46 		if (buf->len == 0) {
47 			return -EMSGSIZE;
48 		}
49 
50 		len = net_buf_simple_pull_u8(buf);
51 
52 		if (len < 2) {
53 			return -EINVAL;
54 		}
55 	}
56 
57 	LOG_DBG("SRPL clear status received: range start: %u, range len: %u",
58 	       primary, len);
59 
60 	if (bt_mesh_msg_ack_ctx_match(&cli->ack_ctx, OP_SOL_PDU_RPL_ITEM_STATUS,
61 				       ctx->addr, (void **)&param)) {
62 		if (param->start) {
63 			*param->start = primary;
64 		}
65 
66 		if (param->len) {
67 			*param->len = len;
68 		}
69 
70 		bt_mesh_msg_ack_ctx_rx(&cli->ack_ctx);
71 	}
72 
73 	if (cli->srpl_status) {
74 		cli->srpl_status(cli, ctx->addr, primary, len);
75 	}
76 
77 	return 0;
78 }
79 
sol_pdu_rpl_clear_pdu_create(uint16_t range_start,uint8_t range_len,struct net_buf_simple * msg)80 static void sol_pdu_rpl_clear_pdu_create(uint16_t range_start, uint8_t range_len,
81 					 struct net_buf_simple *msg)
82 {
83 	uint16_t range;
84 
85 	range = range_start << 1 | (range_len >= 2 ? 1U : 0);
86 	net_buf_simple_add_le16(msg, range);
87 	if (range_len >= 2) {
88 		net_buf_simple_add_u8(msg, range_len);
89 	}
90 }
91 
bt_mesh_sol_pdu_rpl_clear(struct bt_mesh_msg_ctx * ctx,uint16_t range_start,uint8_t range_len,uint16_t * start_rsp,uint8_t * len_rsp)92 int bt_mesh_sol_pdu_rpl_clear(struct bt_mesh_msg_ctx *ctx, uint16_t range_start,
93 			      uint8_t range_len, uint16_t *start_rsp, uint8_t *len_rsp)
94 {
95 	struct sol_rpl_param param = {
96 		.start = start_rsp,
97 		.len = len_rsp,
98 	};
99 
100 	const struct bt_mesh_msg_rsp_ctx rsp = {
101 		.ack = &cli->ack_ctx,
102 		.op = OP_SOL_PDU_RPL_ITEM_STATUS,
103 		.user_data = &param,
104 		.timeout = msg_timeout,
105 	};
106 
107 	if (range_len == 1) {
108 		LOG_ERR("Invalid range length");
109 		return -EINVAL;
110 	}
111 
112 	if ((range_start + (range_len > 1 ? range_len : 0)) > 0x8000 || range_start == 0) {
113 		LOG_ERR("Range outside unicast address range");
114 		return -EINVAL;
115 	}
116 
117 	BT_MESH_MODEL_BUF_DEFINE(msg, OP_SOL_PDU_RPL_ITEM_CLEAR,
118 				 range_len >= 2 ? 3 : 2);
119 
120 	bt_mesh_model_msg_init(&msg, OP_SOL_PDU_RPL_ITEM_CLEAR);
121 
122 	sol_pdu_rpl_clear_pdu_create(range_start, range_len, &msg);
123 
124 	return bt_mesh_msg_ackd_send(cli->model, ctx, &msg,
125 				    (start_rsp && len_rsp) ? &rsp : NULL);
126 }
127 
bt_mesh_sol_pdu_rpl_clear_unack(struct bt_mesh_msg_ctx * ctx,uint16_t range_start,uint8_t range_len)128 int bt_mesh_sol_pdu_rpl_clear_unack(struct bt_mesh_msg_ctx *ctx, uint16_t range_start,
129 				    uint8_t range_len)
130 {
131 	if (range_len == 1) {
132 		LOG_ERR("Invalid range length");
133 		return -EINVAL;
134 	}
135 
136 	if ((range_start + (range_len > 1 ? range_len : 0)) > 0x8000 || range_start == 0) {
137 		LOG_ERR("Range outside unicast address range");
138 		return -EINVAL;
139 	}
140 
141 	BT_MESH_MODEL_BUF_DEFINE(msg, OP_SOL_PDU_RPL_ITEM_CLEAR,
142 				 range_len >= 2 ? 3 : 2);
143 
144 	bt_mesh_model_msg_init(&msg, OP_SOL_PDU_RPL_ITEM_CLEAR_UNACKED);
145 
146 	sol_pdu_rpl_clear_pdu_create(range_start, range_len, &msg);
147 
148 	return bt_mesh_msg_send(cli->model, ctx, &msg);
149 
150 }
151 
152 const struct bt_mesh_model_op _bt_mesh_sol_pdu_rpl_cli_op[] = {
153 	{ OP_SOL_PDU_RPL_ITEM_STATUS, BT_MESH_LEN_MIN(2), handle_status },
154 
155 	BT_MESH_MODEL_OP_END
156 };
157 
bt_mesh_sol_pdu_rpl_cli_timeout_set(int32_t timeout)158 void bt_mesh_sol_pdu_rpl_cli_timeout_set(int32_t timeout)
159 {
160 	msg_timeout = timeout;
161 }
162 
sol_pdu_rpl_cli_init(const struct bt_mesh_model * mod)163 static int sol_pdu_rpl_cli_init(const struct bt_mesh_model *mod)
164 {
165 	if (!bt_mesh_model_in_primary(mod)) {
166 		LOG_ERR("Solicitation PDU RPL Configuration client not in primary element");
167 		return -EINVAL;
168 	}
169 
170 	msg_timeout = CONFIG_BT_MESH_SOL_PDU_RPL_CLI_TIMEOUT;
171 
172 	cli = mod->rt->user_data;
173 	cli->model = mod;
174 	bt_mesh_msg_ack_ctx_init(&cli->ack_ctx);
175 	return 0;
176 }
177 
178 const struct bt_mesh_model_cb _bt_mesh_sol_pdu_rpl_cli_cb = {
179 	.init = sol_pdu_rpl_cli_init,
180 };
181