1 /*
2  * Copyright (c) 2017 Intel Corporation
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr.h>
8 #include <string.h>
9 #include <errno.h>
10 #include <stdbool.h>
11 #include <zephyr/types.h>
12 #include <sys/util.h>
13 #include <sys/byteorder.h>
14 
15 #include <bluetooth/bluetooth.h>
16 #include <bluetooth/conn.h>
17 #include <bluetooth/mesh.h>
18 
19 #define BT_DBG_ENABLED IS_ENABLED(CONFIG_BT_MESH_DEBUG_MODEL)
20 #define LOG_MODULE_NAME bt_mesh_health_cli
21 #include "common/log.h"
22 
23 #include "net.h"
24 #include "foundation.h"
25 
26 static int32_t msg_timeout;
27 
28 static struct bt_mesh_health_cli *health_cli;
29 
30 struct health_fault_param {
31 	uint16_t   cid;
32 	uint8_t   *expect_test_id;
33 	uint8_t   *test_id;
34 	uint8_t   *faults;
35 	size_t *fault_count;
36 };
37 
health_fault_status(struct bt_mesh_model * model,struct bt_mesh_msg_ctx * ctx,struct net_buf_simple * buf)38 static int health_fault_status(struct bt_mesh_model *model,
39 			       struct bt_mesh_msg_ctx *ctx,
40 			       struct net_buf_simple *buf)
41 {
42 	struct health_fault_param *param;
43 	uint8_t test_id;
44 	uint16_t cid;
45 
46 	BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s",
47 	       ctx->net_idx, ctx->app_idx, ctx->addr, buf->len,
48 	       bt_hex(buf->data, buf->len));
49 
50 	if (!bt_mesh_msg_ack_ctx_match(&health_cli->ack_ctx, OP_HEALTH_FAULT_STATUS, ctx->addr,
51 				       (void **)&param)) {
52 		return -ENOENT;
53 	}
54 
55 	test_id = net_buf_simple_pull_u8(buf);
56 	if (param->expect_test_id && test_id != *param->expect_test_id) {
57 		BT_WARN("Health fault with unexpected Test ID");
58 		return -ENOENT;
59 	}
60 
61 	cid = net_buf_simple_pull_le16(buf);
62 	if (cid != param->cid) {
63 		BT_WARN("Health fault with unexpected Company ID");
64 		return -ENOENT;
65 	}
66 
67 	if (param->test_id) {
68 		*param->test_id = test_id;
69 	}
70 
71 	if (buf->len > *param->fault_count) {
72 		BT_WARN("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 	bt_mesh_msg_ack_ctx_rx(&health_cli->ack_ctx);
80 
81 	return 0;
82 }
83 
health_current_status(struct bt_mesh_model * model,struct bt_mesh_msg_ctx * ctx,struct net_buf_simple * buf)84 static int health_current_status(struct bt_mesh_model *model,
85 				 struct bt_mesh_msg_ctx *ctx,
86 				 struct net_buf_simple *buf)
87 {
88 	struct bt_mesh_health_cli *cli = model->user_data;
89 	uint8_t test_id;
90 	uint16_t cid;
91 
92 	BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s",
93 	       ctx->net_idx, ctx->app_idx, ctx->addr, buf->len,
94 	       bt_hex(buf->data, buf->len));
95 
96 	test_id = net_buf_simple_pull_u8(buf);
97 	cid = net_buf_simple_pull_le16(buf);
98 
99 	BT_DBG("Test ID 0x%02x Company ID 0x%04x Fault Count %u",
100 	       test_id, cid, buf->len);
101 
102 	if (!cli->current_status) {
103 		BT_WARN("No Current Status callback available");
104 		return 0;
105 	}
106 
107 	cli->current_status(cli, ctx->addr, test_id, cid, buf->data, buf->len);
108 
109 	return 0;
110 }
111 
112 struct health_period_param {
113 	uint8_t *divisor;
114 };
115 
health_period_status(struct bt_mesh_model * model,struct bt_mesh_msg_ctx * ctx,struct net_buf_simple * buf)116 static int health_period_status(struct bt_mesh_model *model,
117 				struct bt_mesh_msg_ctx *ctx,
118 				struct net_buf_simple *buf)
119 {
120 	struct health_period_param *param;
121 
122 	BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s",
123 	       ctx->net_idx, ctx->app_idx, ctx->addr, buf->len,
124 	       bt_hex(buf->data, buf->len));
125 
126 	if (!bt_mesh_msg_ack_ctx_match(&health_cli->ack_ctx, OP_HEALTH_PERIOD_STATUS, ctx->addr,
127 				       (void **)&param)) {
128 		return -ENOENT;
129 	}
130 
131 	*param->divisor = net_buf_simple_pull_u8(buf);
132 
133 	bt_mesh_msg_ack_ctx_rx(&health_cli->ack_ctx);
134 
135 	return 0;
136 }
137 
138 struct health_attention_param {
139 	uint8_t *attention;
140 };
141 
health_attention_status(struct bt_mesh_model * model,struct bt_mesh_msg_ctx * ctx,struct net_buf_simple * buf)142 static int health_attention_status(struct bt_mesh_model *model,
143 				   struct bt_mesh_msg_ctx *ctx,
144 				   struct net_buf_simple *buf)
145 {
146 	struct health_attention_param *param;
147 
148 	BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s",
149 	       ctx->net_idx, ctx->app_idx, ctx->addr, buf->len,
150 	       bt_hex(buf->data, buf->len));
151 
152 	if (!bt_mesh_msg_ack_ctx_match(&health_cli->ack_ctx, OP_ATTENTION_STATUS, ctx->addr,
153 				       (void **)&param)) {
154 		return -ENOENT;
155 	}
156 
157 	if (param->attention) {
158 		*param->attention = net_buf_simple_pull_u8(buf);
159 	}
160 
161 	bt_mesh_msg_ack_ctx_rx(&health_cli->ack_ctx);
162 
163 	return 0;
164 }
165 
166 const struct bt_mesh_model_op bt_mesh_health_cli_op[] = {
167 	{ OP_HEALTH_FAULT_STATUS,     BT_MESH_LEN_MIN(3),    health_fault_status },
168 	{ OP_HEALTH_CURRENT_STATUS,   BT_MESH_LEN_MIN(3),    health_current_status },
169 	{ OP_HEALTH_PERIOD_STATUS,    BT_MESH_LEN_EXACT(1),  health_period_status },
170 	{ OP_ATTENTION_STATUS,        BT_MESH_LEN_EXACT(1),  health_attention_status },
171 	BT_MESH_MODEL_OP_END,
172 };
173 
cli_prepare(void * param,uint32_t op,uint16_t addr)174 static int cli_prepare(void *param, uint32_t op, uint16_t addr)
175 {
176 	if (!health_cli) {
177 		BT_ERR("No available Health Client context!");
178 		return -EINVAL;
179 	}
180 
181 	return bt_mesh_msg_ack_ctx_prepare(&health_cli->ack_ctx, op, addr, param);
182 }
183 
bt_mesh_health_attention_get(uint16_t addr,uint16_t app_idx,uint8_t * attention)184 int bt_mesh_health_attention_get(uint16_t addr, uint16_t app_idx, uint8_t *attention)
185 {
186 	BT_MESH_MODEL_BUF_DEFINE(msg, OP_ATTENTION_GET, 0);
187 	struct bt_mesh_msg_ctx ctx = {
188 		.app_idx = app_idx,
189 		.addr = addr,
190 		.send_ttl = BT_MESH_TTL_DEFAULT,
191 	};
192 	struct health_attention_param param = {
193 		.attention = attention,
194 	};
195 	int err;
196 
197 	err = cli_prepare(&param, OP_ATTENTION_STATUS, addr);
198 	if (err) {
199 		return err;
200 	}
201 
202 	bt_mesh_model_msg_init(&msg, OP_ATTENTION_GET);
203 
204 	err = bt_mesh_model_send(health_cli->model, &ctx, &msg, NULL, NULL);
205 	if (err) {
206 		BT_ERR("model_send() failed (err %d)", err);
207 		bt_mesh_msg_ack_ctx_clear(&health_cli->ack_ctx);
208 		return err;
209 	}
210 
211 	return bt_mesh_msg_ack_ctx_wait(&health_cli->ack_ctx, K_MSEC(msg_timeout));
212 }
213 
bt_mesh_health_attention_set(uint16_t addr,uint16_t app_idx,uint8_t attention,uint8_t * updated_attention)214 int bt_mesh_health_attention_set(uint16_t addr, uint16_t app_idx, uint8_t attention,
215 				 uint8_t *updated_attention)
216 {
217 	BT_MESH_MODEL_BUF_DEFINE(msg, OP_ATTENTION_SET, 1);
218 	struct bt_mesh_msg_ctx ctx = {
219 		.app_idx = app_idx,
220 		.addr = addr,
221 		.send_ttl = BT_MESH_TTL_DEFAULT,
222 	};
223 	struct health_attention_param param = {
224 		.attention = updated_attention,
225 	};
226 	int err;
227 
228 	err = cli_prepare(&param, OP_ATTENTION_STATUS, addr);
229 	if (err) {
230 		return err;
231 	}
232 
233 	if (updated_attention) {
234 		bt_mesh_model_msg_init(&msg, OP_ATTENTION_SET);
235 	} else {
236 		bt_mesh_model_msg_init(&msg, OP_ATTENTION_SET_UNREL);
237 	}
238 
239 	net_buf_simple_add_u8(&msg, attention);
240 
241 	err = bt_mesh_model_send(health_cli->model, &ctx, &msg, NULL, NULL);
242 	if (err) {
243 		BT_ERR("model_send() failed (err %d)", err);
244 		bt_mesh_msg_ack_ctx_clear(&health_cli->ack_ctx);
245 		return err;
246 	}
247 
248 	if (!updated_attention) {
249 		bt_mesh_msg_ack_ctx_clear(&health_cli->ack_ctx);
250 		return 0;
251 	}
252 
253 	return bt_mesh_msg_ack_ctx_wait(&health_cli->ack_ctx, K_MSEC(msg_timeout));
254 }
255 
bt_mesh_health_period_get(uint16_t addr,uint16_t app_idx,uint8_t * divisor)256 int bt_mesh_health_period_get(uint16_t addr, uint16_t app_idx, uint8_t *divisor)
257 {
258 	BT_MESH_MODEL_BUF_DEFINE(msg, OP_HEALTH_PERIOD_GET, 0);
259 	struct bt_mesh_msg_ctx ctx = {
260 		.app_idx = app_idx,
261 		.addr = addr,
262 		.send_ttl = BT_MESH_TTL_DEFAULT,
263 	};
264 	struct health_period_param param = {
265 		.divisor = divisor,
266 	};
267 	int err;
268 
269 	err = cli_prepare(&param, OP_HEALTH_PERIOD_STATUS, addr);
270 	if (err) {
271 		return err;
272 	}
273 
274 	bt_mesh_model_msg_init(&msg, OP_HEALTH_PERIOD_GET);
275 
276 	err = bt_mesh_model_send(health_cli->model, &ctx, &msg, NULL, NULL);
277 	if (err) {
278 		BT_ERR("model_send() failed (err %d)", err);
279 		bt_mesh_msg_ack_ctx_clear(&health_cli->ack_ctx);
280 		return err;
281 	}
282 
283 	return bt_mesh_msg_ack_ctx_wait(&health_cli->ack_ctx, K_MSEC(msg_timeout));
284 }
285 
bt_mesh_health_period_set(uint16_t addr,uint16_t app_idx,uint8_t divisor,uint8_t * updated_divisor)286 int bt_mesh_health_period_set(uint16_t addr, uint16_t app_idx, uint8_t divisor,
287 				 uint8_t *updated_divisor)
288 {
289 	BT_MESH_MODEL_BUF_DEFINE(msg, OP_HEALTH_PERIOD_SET, 1);
290 	struct bt_mesh_msg_ctx ctx = {
291 		.app_idx = app_idx,
292 		.addr = addr,
293 		.send_ttl = BT_MESH_TTL_DEFAULT,
294 	};
295 	struct health_period_param param = {
296 		.divisor = updated_divisor,
297 	};
298 	int err;
299 
300 	err = cli_prepare(&param, OP_HEALTH_PERIOD_STATUS, addr);
301 	if (err) {
302 		return err;
303 	}
304 
305 	if (updated_divisor) {
306 		bt_mesh_model_msg_init(&msg, OP_HEALTH_PERIOD_SET);
307 	} else {
308 		bt_mesh_model_msg_init(&msg, OP_HEALTH_PERIOD_SET_UNREL);
309 	}
310 
311 	net_buf_simple_add_u8(&msg, divisor);
312 
313 	err = bt_mesh_model_send(health_cli->model, &ctx, &msg, NULL, NULL);
314 	if (err) {
315 		BT_ERR("model_send() failed (err %d)", err);
316 		bt_mesh_msg_ack_ctx_clear(&health_cli->ack_ctx);
317 		return err;
318 	}
319 
320 	if (!updated_divisor) {
321 		bt_mesh_msg_ack_ctx_clear(&health_cli->ack_ctx);
322 		return 0;
323 	}
324 
325 	return bt_mesh_msg_ack_ctx_wait(&health_cli->ack_ctx, K_MSEC(msg_timeout));
326 }
327 
bt_mesh_health_fault_test(uint16_t addr,uint16_t app_idx,uint16_t cid,uint8_t test_id,uint8_t * faults,size_t * fault_count)328 int bt_mesh_health_fault_test(uint16_t addr, uint16_t app_idx, uint16_t cid,
329 				 uint8_t test_id, uint8_t *faults,
330 				 size_t *fault_count)
331 {
332 	BT_MESH_MODEL_BUF_DEFINE(msg, OP_HEALTH_FAULT_TEST, 3);
333 	struct bt_mesh_msg_ctx ctx = {
334 		.app_idx = app_idx,
335 		.addr = addr,
336 		.send_ttl = BT_MESH_TTL_DEFAULT,
337 	};
338 	struct health_fault_param param = {
339 		.cid = cid,
340 		.expect_test_id = &test_id,
341 		.faults = faults,
342 		.fault_count = fault_count,
343 	};
344 	int err;
345 
346 	err = cli_prepare(&param, OP_HEALTH_FAULT_STATUS, addr);
347 	if (err) {
348 		return err;
349 	}
350 
351 	if (faults) {
352 		bt_mesh_model_msg_init(&msg, OP_HEALTH_FAULT_TEST);
353 	} else {
354 		bt_mesh_model_msg_init(&msg, OP_HEALTH_FAULT_TEST_UNREL);
355 	}
356 
357 	net_buf_simple_add_u8(&msg, test_id);
358 	net_buf_simple_add_le16(&msg, cid);
359 
360 	err = bt_mesh_model_send(health_cli->model, &ctx, &msg, NULL, NULL);
361 	if (err) {
362 		BT_ERR("model_send() failed (err %d)", err);
363 		bt_mesh_msg_ack_ctx_clear(&health_cli->ack_ctx);
364 		return err;
365 	}
366 
367 	if (!faults) {
368 		bt_mesh_msg_ack_ctx_clear(&health_cli->ack_ctx);
369 		return 0;
370 	}
371 
372 	return bt_mesh_msg_ack_ctx_wait(&health_cli->ack_ctx, K_MSEC(msg_timeout));
373 }
374 
bt_mesh_health_fault_clear(uint16_t addr,uint16_t app_idx,uint16_t cid,uint8_t * test_id,uint8_t * faults,size_t * fault_count)375 int bt_mesh_health_fault_clear(uint16_t addr, uint16_t app_idx, uint16_t cid,
376 				 uint8_t *test_id, uint8_t *faults,
377 				 size_t *fault_count)
378 {
379 	BT_MESH_MODEL_BUF_DEFINE(msg, OP_HEALTH_FAULT_CLEAR, 2);
380 	struct bt_mesh_msg_ctx ctx = {
381 		.app_idx = app_idx,
382 		.addr = addr,
383 		.send_ttl = BT_MESH_TTL_DEFAULT,
384 	};
385 	struct health_fault_param param = {
386 		.cid = cid,
387 		.test_id = test_id,
388 		.faults = faults,
389 		.fault_count = fault_count,
390 	};
391 	int err;
392 
393 	err = cli_prepare(&param, OP_HEALTH_FAULT_STATUS, addr);
394 	if (err) {
395 		return err;
396 	}
397 
398 	if (test_id) {
399 		bt_mesh_model_msg_init(&msg, OP_HEALTH_FAULT_CLEAR);
400 	} else {
401 		bt_mesh_model_msg_init(&msg, OP_HEALTH_FAULT_CLEAR_UNREL);
402 	}
403 
404 	net_buf_simple_add_le16(&msg, cid);
405 
406 	err = bt_mesh_model_send(health_cli->model, &ctx, &msg, NULL, NULL);
407 	if (err) {
408 		BT_ERR("model_send() failed (err %d)", err);
409 		bt_mesh_msg_ack_ctx_clear(&health_cli->ack_ctx);
410 		return err;
411 	}
412 
413 	if (!test_id) {
414 		bt_mesh_msg_ack_ctx_clear(&health_cli->ack_ctx);
415 		return 0;
416 	}
417 
418 	return bt_mesh_msg_ack_ctx_wait(&health_cli->ack_ctx, K_MSEC(msg_timeout));
419 }
420 
bt_mesh_health_fault_get(uint16_t addr,uint16_t app_idx,uint16_t cid,uint8_t * test_id,uint8_t * faults,size_t * fault_count)421 int bt_mesh_health_fault_get(uint16_t addr, uint16_t app_idx, uint16_t cid,
422 				 uint8_t *test_id, uint8_t *faults,
423 				 size_t *fault_count)
424 {
425 	BT_MESH_MODEL_BUF_DEFINE(msg, OP_HEALTH_FAULT_GET, 2);
426 	struct bt_mesh_msg_ctx ctx = {
427 		.app_idx = app_idx,
428 		.addr = addr,
429 		.send_ttl = BT_MESH_TTL_DEFAULT,
430 	};
431 	struct health_fault_param param = {
432 		.cid = cid,
433 		.test_id = test_id,
434 		.faults = faults,
435 		.fault_count = fault_count,
436 	};
437 	int err;
438 
439 	err = cli_prepare(&param, OP_HEALTH_FAULT_STATUS, addr);
440 	if (err) {
441 		return err;
442 	}
443 
444 	bt_mesh_model_msg_init(&msg, OP_HEALTH_FAULT_GET);
445 	net_buf_simple_add_le16(&msg, cid);
446 
447 	err = bt_mesh_model_send(health_cli->model, &ctx, &msg, NULL, NULL);
448 	if (err) {
449 		BT_ERR("model_send() failed (err %d)", err);
450 		bt_mesh_msg_ack_ctx_clear(&health_cli->ack_ctx);
451 		return err;
452 	}
453 
454 	return bt_mesh_msg_ack_ctx_wait(&health_cli->ack_ctx, K_MSEC(msg_timeout));
455 }
456 
bt_mesh_health_cli_timeout_get(void)457 int32_t bt_mesh_health_cli_timeout_get(void)
458 {
459 	return msg_timeout;
460 }
461 
bt_mesh_health_cli_timeout_set(int32_t timeout)462 void bt_mesh_health_cli_timeout_set(int32_t timeout)
463 {
464 	msg_timeout = timeout;
465 }
466 
bt_mesh_health_cli_set(struct bt_mesh_model * model)467 int bt_mesh_health_cli_set(struct bt_mesh_model *model)
468 {
469 	if (!model->user_data) {
470 		BT_ERR("No Health Client context for given model");
471 		return -EINVAL;
472 	}
473 
474 	health_cli = model->user_data;
475 	msg_timeout = 2 * MSEC_PER_SEC;
476 
477 	return 0;
478 }
479 
health_cli_init(struct bt_mesh_model * model)480 static int health_cli_init(struct bt_mesh_model *model)
481 {
482 	struct bt_mesh_health_cli *cli = model->user_data;
483 
484 	BT_DBG("primary %u", bt_mesh_model_in_primary(model));
485 
486 	if (!cli) {
487 		BT_ERR("No Health Client context provided");
488 		return -EINVAL;
489 	}
490 
491 	cli = model->user_data;
492 	cli->model = model;
493 	msg_timeout = 2 * MSEC_PER_SEC;
494 
495 	/* Set the default health client pointer */
496 	if (!health_cli) {
497 		health_cli = cli;
498 	}
499 
500 	bt_mesh_msg_ack_ctx_init(&health_cli->ack_ctx);
501 	return 0;
502 }
503 
504 const struct bt_mesh_model_cb bt_mesh_health_cli_cb = {
505 	.init = health_cli_init,
506 };
507