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