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(¶m, 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(¶m, 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(¶m, 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(¶m, 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(¶m, 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(¶m, 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(¶m, 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