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