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