1 /*
2  * Copyright (c) 2017 Intel Corporation
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/kernel.h>
8 #include <string.h>
9 #include <errno.h>
10 #include <stdbool.h>
11 #include <zephyr/types.h>
12 #include <zephyr/sys/util.h>
13 #include <zephyr/sys/byteorder.h>
14 
15 #include <zephyr/bluetooth/bluetooth.h>
16 #include <zephyr/bluetooth/conn.h>
17 #include <zephyr/bluetooth/mesh.h>
18 
19 #include "common/bt_str.h"
20 
21 #include "net.h"
22 #include "foundation.h"
23 #include "msg.h"
24 
25 #define LOG_LEVEL CONFIG_BT_MESH_MODEL_LOG_LEVEL
26 #include <zephyr/logging/log.h>
27 LOG_MODULE_REGISTER(bt_mesh_health_cli);
28 
29 static int32_t msg_timeout;
30 
31 struct health_fault_param {
32 	uint16_t   cid;
33 	uint8_t   *expect_test_id;
34 	uint8_t   *test_id;
35 	uint8_t   *faults;
36 	size_t *fault_count;
37 };
38 
health_fault_status(struct bt_mesh_model * model,struct bt_mesh_msg_ctx * ctx,struct net_buf_simple * buf)39 static int health_fault_status(struct bt_mesh_model *model,
40 			       struct bt_mesh_msg_ctx *ctx,
41 			       struct net_buf_simple *buf)
42 {
43 	struct bt_mesh_health_cli *cli = model->user_data;
44 	struct health_fault_param *param;
45 	uint8_t test_id;
46 	uint16_t cid;
47 
48 	LOG_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s", ctx->net_idx, ctx->app_idx,
49 		ctx->addr, buf->len, bt_hex(buf->data, buf->len));
50 
51 	test_id = net_buf_simple_pull_u8(buf);
52 	cid = net_buf_simple_pull_le16(buf);
53 
54 	if (bt_mesh_msg_ack_ctx_match(&cli->ack_ctx,
55 				      OP_HEALTH_FAULT_STATUS, ctx->addr,
56 				      (void **)&param)) {
57 		if (param->expect_test_id &&
58 		    (test_id != *param->expect_test_id)) {
59 			goto done;
60 		}
61 
62 		if (cid != param->cid) {
63 			goto done;
64 		}
65 
66 		if (param->test_id) {
67 			*param->test_id = test_id;
68 		}
69 
70 		if (param->faults && param->fault_count) {
71 			if (buf->len > *param->fault_count) {
72 				LOG_WRN("Got more faults than there's space for");
73 			} else {
74 				*param->fault_count = buf->len;
75 			}
76 
77 			memcpy(param->faults, buf->data, *param->fault_count);
78 		}
79 
80 		bt_mesh_msg_ack_ctx_rx(&cli->ack_ctx);
81 	}
82 
83 done:
84 	if (cli->fault_status) {
85 		cli->fault_status(cli, ctx->addr, test_id, cid,
86 					 buf->data, buf->len);
87 	}
88 
89 	return 0;
90 }
91 
health_current_status(struct bt_mesh_model * model,struct bt_mesh_msg_ctx * ctx,struct net_buf_simple * buf)92 static int health_current_status(struct bt_mesh_model *model,
93 				 struct bt_mesh_msg_ctx *ctx,
94 				 struct net_buf_simple *buf)
95 {
96 	struct bt_mesh_health_cli *cli = model->user_data;
97 	uint8_t test_id;
98 	uint16_t cid;
99 
100 	LOG_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s", ctx->net_idx, ctx->app_idx,
101 		ctx->addr, buf->len, bt_hex(buf->data, buf->len));
102 
103 	test_id = net_buf_simple_pull_u8(buf);
104 	cid = net_buf_simple_pull_le16(buf);
105 
106 	LOG_DBG("Test ID 0x%02x Company ID 0x%04x Fault Count %u", test_id, cid, buf->len);
107 
108 	if (cli->current_status) {
109 		cli->current_status(cli, ctx->addr, test_id, cid,
110 					   buf->data, buf->len);
111 	}
112 
113 	return 0;
114 }
115 
116 struct health_period_param {
117 	uint8_t *divisor;
118 };
119 
health_period_status(struct bt_mesh_model * model,struct bt_mesh_msg_ctx * ctx,struct net_buf_simple * buf)120 static int health_period_status(struct bt_mesh_model *model,
121 				struct bt_mesh_msg_ctx *ctx,
122 				struct net_buf_simple *buf)
123 {
124 	struct bt_mesh_health_cli *cli = model->user_data;
125 	struct health_period_param *param;
126 	uint8_t divisor;
127 
128 	LOG_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s", ctx->net_idx, ctx->app_idx,
129 		ctx->addr, buf->len, bt_hex(buf->data, buf->len));
130 
131 	divisor = net_buf_simple_pull_u8(buf);
132 
133 	if (bt_mesh_msg_ack_ctx_match(&cli->ack_ctx,
134 				      OP_HEALTH_PERIOD_STATUS, ctx->addr,
135 				      (void **)&param)) {
136 		if (param->divisor) {
137 			*param->divisor = divisor;
138 		}
139 
140 		bt_mesh_msg_ack_ctx_rx(&cli->ack_ctx);
141 	}
142 
143 	if (cli->period_status) {
144 		cli->period_status(cli, ctx->addr, divisor);
145 	}
146 
147 	return 0;
148 }
149 
150 struct health_attention_param {
151 	uint8_t *attention;
152 };
153 
health_attention_status(struct bt_mesh_model * model,struct bt_mesh_msg_ctx * ctx,struct net_buf_simple * buf)154 static int health_attention_status(struct bt_mesh_model *model,
155 				   struct bt_mesh_msg_ctx *ctx,
156 				   struct net_buf_simple *buf)
157 {
158 	struct bt_mesh_health_cli *cli = model->user_data;
159 	struct health_attention_param *param;
160 	uint8_t attention;
161 
162 	LOG_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s", ctx->net_idx, ctx->app_idx,
163 		ctx->addr, buf->len, bt_hex(buf->data, buf->len));
164 
165 	attention = net_buf_simple_pull_u8(buf);
166 
167 	if (bt_mesh_msg_ack_ctx_match(&cli->ack_ctx, OP_ATTENTION_STATUS,
168 				      ctx->addr, (void **)&param)) {
169 		if (param->attention) {
170 			*param->attention = attention;
171 		}
172 
173 		bt_mesh_msg_ack_ctx_rx(&cli->ack_ctx);
174 	}
175 
176 	if (cli->attention_status) {
177 		cli->attention_status(cli, ctx->addr, attention);
178 	}
179 	return 0;
180 }
181 
182 const struct bt_mesh_model_op bt_mesh_health_cli_op[] = {
183 	{ OP_HEALTH_FAULT_STATUS,     BT_MESH_LEN_MIN(3),    health_fault_status },
184 	{ OP_HEALTH_CURRENT_STATUS,   BT_MESH_LEN_MIN(3),    health_current_status },
185 	{ OP_HEALTH_PERIOD_STATUS,    BT_MESH_LEN_EXACT(1),  health_period_status },
186 	{ OP_ATTENTION_STATUS,        BT_MESH_LEN_EXACT(1),  health_attention_status },
187 	BT_MESH_MODEL_OP_END,
188 };
189 
bt_mesh_health_cli_attention_get(struct bt_mesh_health_cli * cli,struct bt_mesh_msg_ctx * ctx,uint8_t * attention)190 int bt_mesh_health_cli_attention_get(struct bt_mesh_health_cli *cli, struct bt_mesh_msg_ctx *ctx,
191 				     uint8_t *attention)
192 {
193 	BT_MESH_MODEL_BUF_DEFINE(msg, OP_ATTENTION_GET, 0);
194 	struct health_attention_param param = {
195 		.attention = attention,
196 	};
197 
198 	bt_mesh_model_msg_init(&msg, OP_ATTENTION_GET);
199 
200 	const struct bt_mesh_msg_rsp_ctx rsp = {
201 		.ack = &cli->ack_ctx,
202 		.op = OP_ATTENTION_STATUS,
203 		.user_data = &param,
204 		.timeout = msg_timeout,
205 	};
206 
207 	return bt_mesh_msg_ackd_send(cli->model, ctx, &msg, attention ? &rsp : NULL);
208 }
209 
bt_mesh_health_cli_attention_set(struct bt_mesh_health_cli * cli,struct bt_mesh_msg_ctx * ctx,uint8_t attention,uint8_t * updated_attention)210 int bt_mesh_health_cli_attention_set(struct bt_mesh_health_cli *cli, struct bt_mesh_msg_ctx *ctx,
211 				     uint8_t attention, uint8_t *updated_attention)
212 {
213 	BT_MESH_MODEL_BUF_DEFINE(msg, OP_ATTENTION_SET, 1);
214 	struct health_attention_param param = {
215 		.attention = updated_attention,
216 	};
217 
218 	bt_mesh_model_msg_init(&msg, OP_ATTENTION_SET);
219 	net_buf_simple_add_u8(&msg, attention);
220 
221 	const struct bt_mesh_msg_rsp_ctx rsp = {
222 		.ack = &cli->ack_ctx,
223 		.op = OP_ATTENTION_STATUS,
224 		.user_data = &param,
225 		.timeout = msg_timeout,
226 	};
227 
228 	return bt_mesh_msg_ackd_send(cli->model, ctx, &msg, updated_attention ? &rsp : NULL);
229 }
230 
bt_mesh_health_cli_attention_set_unack(struct bt_mesh_health_cli * cli,struct bt_mesh_msg_ctx * ctx,uint8_t attention)231 int bt_mesh_health_cli_attention_set_unack(struct bt_mesh_health_cli *cli,
232 					   struct bt_mesh_msg_ctx *ctx, uint8_t attention)
233 {
234 	BT_MESH_MODEL_BUF_DEFINE(msg, OP_ATTENTION_SET_UNREL, 1);
235 
236 	bt_mesh_model_msg_init(&msg, OP_ATTENTION_SET_UNREL);
237 	net_buf_simple_add_u8(&msg, attention);
238 
239 	return bt_mesh_msg_send(cli->model, ctx, &msg);
240 }
241 
bt_mesh_health_cli_period_get(struct bt_mesh_health_cli * cli,struct bt_mesh_msg_ctx * ctx,uint8_t * divisor)242 int bt_mesh_health_cli_period_get(struct bt_mesh_health_cli *cli, struct bt_mesh_msg_ctx *ctx,
243 				  uint8_t *divisor)
244 {
245 	BT_MESH_MODEL_BUF_DEFINE(msg, OP_HEALTH_PERIOD_GET, 0);
246 	struct health_period_param param = {
247 		.divisor = divisor,
248 	};
249 
250 	bt_mesh_model_msg_init(&msg, OP_HEALTH_PERIOD_GET);
251 
252 	const struct bt_mesh_msg_rsp_ctx rsp = {
253 		.ack = &cli->ack_ctx,
254 		.op = OP_HEALTH_PERIOD_STATUS,
255 		.user_data = &param,
256 		.timeout = msg_timeout,
257 	};
258 
259 	return bt_mesh_msg_ackd_send(cli->model, ctx, &msg, divisor ? &rsp : NULL);
260 }
261 
bt_mesh_health_cli_period_set(struct bt_mesh_health_cli * cli,struct bt_mesh_msg_ctx * ctx,uint8_t divisor,uint8_t * updated_divisor)262 int bt_mesh_health_cli_period_set(struct bt_mesh_health_cli *cli, struct bt_mesh_msg_ctx *ctx,
263 				  uint8_t divisor, uint8_t *updated_divisor)
264 {
265 	BT_MESH_MODEL_BUF_DEFINE(msg, OP_HEALTH_PERIOD_SET, 1);
266 	struct health_period_param param = {
267 		.divisor = updated_divisor,
268 	};
269 
270 	bt_mesh_model_msg_init(&msg, OP_HEALTH_PERIOD_SET);
271 	net_buf_simple_add_u8(&msg, divisor);
272 
273 	const struct bt_mesh_msg_rsp_ctx rsp = {
274 		.ack = &cli->ack_ctx,
275 		.op = OP_HEALTH_PERIOD_STATUS,
276 		.user_data = &param,
277 		.timeout = msg_timeout,
278 	};
279 
280 	return bt_mesh_msg_ackd_send(cli->model, ctx, &msg, updated_divisor ? &rsp : NULL);
281 }
282 
bt_mesh_health_cli_period_set_unack(struct bt_mesh_health_cli * cli,struct bt_mesh_msg_ctx * ctx,uint8_t divisor)283 int bt_mesh_health_cli_period_set_unack(struct bt_mesh_health_cli *cli,
284 					struct bt_mesh_msg_ctx *ctx, uint8_t divisor)
285 {
286 	BT_MESH_MODEL_BUF_DEFINE(msg, OP_HEALTH_PERIOD_SET_UNREL, 1);
287 
288 	bt_mesh_model_msg_init(&msg, OP_HEALTH_PERIOD_SET_UNREL);
289 	net_buf_simple_add_u8(&msg, divisor);
290 
291 	return bt_mesh_msg_send(cli->model, ctx, &msg);
292 }
293 
bt_mesh_health_cli_fault_test(struct bt_mesh_health_cli * cli,struct bt_mesh_msg_ctx * ctx,uint16_t cid,uint8_t test_id,uint8_t * faults,size_t * fault_count)294 int bt_mesh_health_cli_fault_test(struct bt_mesh_health_cli *cli, struct bt_mesh_msg_ctx *ctx,
295 				  uint16_t cid, uint8_t test_id, uint8_t *faults,
296 				  size_t *fault_count)
297 {
298 	BT_MESH_MODEL_BUF_DEFINE(msg, OP_HEALTH_FAULT_TEST, 3);
299 	struct health_fault_param param = {
300 		.cid = cid,
301 		.expect_test_id = &test_id,
302 		.faults = faults,
303 		.fault_count = fault_count,
304 	};
305 
306 	bt_mesh_model_msg_init(&msg, OP_HEALTH_FAULT_TEST);
307 	net_buf_simple_add_u8(&msg, test_id);
308 	net_buf_simple_add_le16(&msg, cid);
309 
310 	const struct bt_mesh_msg_rsp_ctx rsp = {
311 		.ack = &cli->ack_ctx,
312 		.op = OP_HEALTH_FAULT_STATUS,
313 		.user_data = &param,
314 		.timeout = msg_timeout,
315 	};
316 
317 	return bt_mesh_msg_ackd_send(cli->model, ctx, &msg, &rsp);
318 }
319 
bt_mesh_health_cli_fault_test_unack(struct bt_mesh_health_cli * cli,struct bt_mesh_msg_ctx * ctx,uint16_t cid,uint8_t test_id)320 int bt_mesh_health_cli_fault_test_unack(struct bt_mesh_health_cli *cli,
321 					struct bt_mesh_msg_ctx *ctx, uint16_t cid, uint8_t test_id)
322 {
323 	BT_MESH_MODEL_BUF_DEFINE(msg, OP_HEALTH_FAULT_TEST_UNREL, 3);
324 
325 	bt_mesh_model_msg_init(&msg, OP_HEALTH_FAULT_TEST_UNREL);
326 	net_buf_simple_add_u8(&msg, test_id);
327 	net_buf_simple_add_le16(&msg, cid);
328 
329 	return bt_mesh_msg_send(cli->model, ctx, &msg);
330 }
331 
bt_mesh_health_cli_fault_clear(struct bt_mesh_health_cli * cli,struct bt_mesh_msg_ctx * ctx,uint16_t cid,uint8_t * test_id,uint8_t * faults,size_t * fault_count)332 int bt_mesh_health_cli_fault_clear(struct bt_mesh_health_cli *cli, struct bt_mesh_msg_ctx *ctx,
333 				   uint16_t cid, uint8_t *test_id, uint8_t *faults,
334 				   size_t *fault_count)
335 {
336 	BT_MESH_MODEL_BUF_DEFINE(msg, OP_HEALTH_FAULT_CLEAR, 2);
337 	struct health_fault_param param = {
338 		.cid = cid,
339 		.test_id = test_id,
340 		.faults = faults,
341 		.fault_count = fault_count,
342 	};
343 
344 	bt_mesh_model_msg_init(&msg, OP_HEALTH_FAULT_CLEAR);
345 	net_buf_simple_add_le16(&msg, cid);
346 
347 	const struct bt_mesh_msg_rsp_ctx rsp = {
348 		.ack = &cli->ack_ctx,
349 		.op = OP_HEALTH_FAULT_STATUS,
350 		.user_data = &param,
351 		.timeout = msg_timeout,
352 	};
353 
354 	return bt_mesh_msg_ackd_send(cli->model, ctx, &msg,
355 				     (!test_id && (!faults || !fault_count)) ? NULL : &rsp);
356 }
357 
bt_mesh_health_cli_fault_clear_unack(struct bt_mesh_health_cli * cli,struct bt_mesh_msg_ctx * ctx,uint16_t cid)358 int bt_mesh_health_cli_fault_clear_unack(struct bt_mesh_health_cli *cli,
359 					 struct bt_mesh_msg_ctx *ctx, uint16_t cid)
360 {
361 	BT_MESH_MODEL_BUF_DEFINE(msg, OP_HEALTH_FAULT_CLEAR_UNREL, 2);
362 
363 	bt_mesh_model_msg_init(&msg, OP_HEALTH_FAULT_CLEAR_UNREL);
364 	net_buf_simple_add_le16(&msg, cid);
365 
366 	return bt_mesh_msg_send(cli->model, ctx, &msg);
367 }
368 
bt_mesh_health_cli_fault_get(struct bt_mesh_health_cli * cli,struct bt_mesh_msg_ctx * ctx,uint16_t cid,uint8_t * test_id,uint8_t * faults,size_t * fault_count)369 int bt_mesh_health_cli_fault_get(struct bt_mesh_health_cli *cli, struct bt_mesh_msg_ctx *ctx,
370 				 uint16_t cid, uint8_t *test_id, uint8_t *faults,
371 				 size_t *fault_count)
372 {
373 	BT_MESH_MODEL_BUF_DEFINE(msg, OP_HEALTH_FAULT_GET, 2);
374 	struct health_fault_param param = {
375 		.cid = cid,
376 		.test_id = test_id,
377 		.faults = faults,
378 		.fault_count = fault_count,
379 	};
380 
381 	bt_mesh_model_msg_init(&msg, OP_HEALTH_FAULT_GET);
382 	net_buf_simple_add_le16(&msg, cid);
383 
384 	const struct bt_mesh_msg_rsp_ctx rsp = {
385 		.ack = &cli->ack_ctx,
386 		.op = OP_HEALTH_FAULT_STATUS,
387 		.user_data = &param,
388 		.timeout = msg_timeout,
389 	};
390 
391 	return bt_mesh_msg_ackd_send(cli->model, ctx, &msg,
392 				     (!test_id && (!faults || !fault_count)) ? NULL : &rsp);
393 }
394 
bt_mesh_health_cli_timeout_get(void)395 int32_t bt_mesh_health_cli_timeout_get(void)
396 {
397 	return msg_timeout;
398 }
399 
bt_mesh_health_cli_timeout_set(int32_t timeout)400 void bt_mesh_health_cli_timeout_set(int32_t timeout)
401 {
402 	msg_timeout = timeout;
403 }
404 
health_cli_init(struct bt_mesh_model * model)405 static int health_cli_init(struct bt_mesh_model *model)
406 {
407 	struct bt_mesh_health_cli *cli = model->user_data;
408 
409 	LOG_DBG("primary %u", bt_mesh_model_in_primary(model));
410 
411 	if (!cli) {
412 		LOG_ERR("No Health Client context provided");
413 		return -EINVAL;
414 	}
415 
416 	cli->model = model;
417 	msg_timeout = CONFIG_BT_MESH_HEALTH_CLI_TIMEOUT;
418 
419 	cli->pub.msg = &cli->pub_buf;
420 	net_buf_simple_init_with_data(&cli->pub_buf, cli->pub_data, sizeof(cli->pub_data));
421 
422 	bt_mesh_msg_ack_ctx_init(&cli->ack_ctx);
423 	return 0;
424 }
425 
health_cli_reset(struct bt_mesh_model * model)426 static void health_cli_reset(struct bt_mesh_model *model)
427 {
428 	struct bt_mesh_health_cli *cli = model->user_data;
429 
430 	net_buf_simple_reset(cli->pub.msg);
431 }
432 
433 const struct bt_mesh_model_cb bt_mesh_health_cli_cb = {
434 	.init = health_cli_init,
435 	.reset = health_cli_reset,
436 };
437