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