1 /* Bluetooth Mesh */
2
3 /*
4 * Copyright (c) 2017 Intel Corporation
5 * Additional Copyright (c) 2018 Espressif Systems (Shanghai) PTE LTD
6 *
7 * SPDX-License-Identifier: Apache-2.0
8 */
9
10 #include <string.h>
11 #include <errno.h>
12
13 #include "btc_ble_mesh_health_model.h"
14
15 #include "access.h"
16 #include "foundation.h"
17 #include "mesh_common.h"
18
19 #if CONFIG_BLE_MESH_HEALTH_SRV
20 #include "health_srv.h"
21
22 #define HEALTH_TEST_STANDARD 0x00
23
24 #define HEALTH_NO_FAULT 0x00
25
26 /* Health Server context of the primary element */
27 struct bt_mesh_health_srv *health_srv;
28
29 /**
30 * When an Element receives a Health Fault Get, or a Health Fault Test, or
31 * a Health Fault Test Unacknowledged, or a Health Fault Clear, or a Health
32 * Fault Clear Unacknowledged message that is not successfully processed
33 * (i.e. the Company ID field that does not identify any Health Fault state
34 * present in the node), it shall ignore the message.
35 * The Health Fault state is identified by Company ID and may be present in
36 * the node for more than one Company ID.
37 */
38
health_get_curr_fault_count(struct bt_mesh_model * model)39 static uint8_t health_get_curr_fault_count(struct bt_mesh_model *model)
40 {
41 struct bt_mesh_health_srv *srv = model->user_data;
42 uint8_t count = 0U;
43 size_t i = 0U;
44
45 for (i = 0U; i < ARRAY_SIZE(srv->test.curr_faults); i++) {
46 if (srv->test.curr_faults[i] != HEALTH_NO_FAULT) {
47 count++;
48 }
49 }
50
51 return count;
52 }
53
health_get_fault_value(struct bt_mesh_model * model,struct net_buf_simple * msg,bool current)54 static void health_get_fault_value(struct bt_mesh_model *model,
55 struct net_buf_simple *msg,
56 bool current)
57 {
58 struct bt_mesh_health_srv *srv = model->user_data;
59 size_t array_size = 0U;
60 size_t i = 0U;
61
62 array_size = current ? ARRAY_SIZE(srv->test.curr_faults) : ARRAY_SIZE(srv->test.reg_faults);
63
64 for (i = 0U; i < array_size; i++) {
65 if (net_buf_simple_tailroom(msg) == 0) {
66 return;
67 }
68
69 uint8_t fault = current ? srv->test.curr_faults[i] : srv->test.reg_faults[i];
70 if (fault != HEALTH_NO_FAULT) {
71 net_buf_simple_add_u8(msg, fault);
72 }
73 }
74 }
75
health_is_test_id_exist(struct bt_mesh_model * model,uint8_t test_id)76 static bool health_is_test_id_exist(struct bt_mesh_model *model, uint8_t test_id)
77 {
78 struct bt_mesh_health_srv *srv = model->user_data;
79 int i;
80
81 for (i = 0; i < srv->test.id_count; i++) {
82 if (srv->test.test_ids[i] == test_id) {
83 return true;
84 }
85 }
86
87 return false;
88 }
89
health_send_fault_status(struct bt_mesh_model * model,struct bt_mesh_msg_ctx * ctx)90 static int health_send_fault_status(struct bt_mesh_model *model,
91 struct bt_mesh_msg_ctx *ctx)
92 {
93 struct bt_mesh_health_srv *srv = model->user_data;
94 struct net_buf_simple *msg = NULL;
95 int err = 0;
96
97 msg = bt_mesh_alloc_buf(4 + ARRAY_SIZE(srv->test.reg_faults) + 4);
98 if (!msg) {
99 BT_ERR("%s, Out of memory", __func__);
100 return -ENOMEM;
101 }
102
103 bt_mesh_model_msg_init(msg, OP_HEALTH_FAULT_STATUS);
104 net_buf_simple_add_u8(msg, srv->test.prev_test_id);
105 net_buf_simple_add_le16(msg, srv->test.company_id);
106 if (ctx->recv_op != OP_HEALTH_FAULT_CLEAR) {
107 /**
108 * For Health Fault Clear, the FaultArray field in Health Fault Status
109 * shall be empty.
110 */
111 health_get_fault_value(model, msg, false);
112 }
113
114 err = bt_mesh_model_send(model, ctx, msg, NULL, NULL);
115 if (err) {
116 BT_ERR("Failed to send Health Fault Status response");
117 }
118
119 bt_mesh_free_buf(msg);
120 return err;
121 }
122
health_fault_get(struct bt_mesh_model * model,struct bt_mesh_msg_ctx * ctx,struct net_buf_simple * buf)123 static void health_fault_get(struct bt_mesh_model *model,
124 struct bt_mesh_msg_ctx *ctx,
125 struct net_buf_simple *buf)
126 {
127 struct bt_mesh_health_srv *srv = model->user_data;
128 uint16_t company_id = 0U;
129
130 if (!srv) {
131 BT_ERR("No Health Server context provided");
132 return;
133 }
134
135 company_id = net_buf_simple_pull_le16(buf);
136 if (company_id != srv->test.company_id) {
137 BT_ERR("Unknown Company ID 0x%04x", company_id);
138 return;
139 }
140
141 BT_DBG("company_id 0x%04x", company_id);
142
143 health_send_fault_status(model, ctx);
144 }
145
health_fault_clear(struct bt_mesh_model * model,struct bt_mesh_msg_ctx * ctx,struct net_buf_simple * buf)146 static void health_fault_clear(struct bt_mesh_model *model,
147 struct bt_mesh_msg_ctx *ctx,
148 struct net_buf_simple *buf)
149 {
150 struct bt_mesh_health_srv *srv = model->user_data;
151 uint16_t company_id = 0U;
152
153 if (!srv) {
154 BT_ERR("No Health Server context provided");
155 return;
156 }
157
158 company_id = net_buf_simple_pull_le16(buf);
159 if (company_id != srv->test.company_id) {
160 BT_ERR("Unknown Company ID 0x%04x", company_id);
161 return;
162 }
163
164 BT_DBG("company_id 0x%04x", company_id);
165
166 memset(srv->test.reg_faults, HEALTH_NO_FAULT, ARRAY_SIZE(srv->test.reg_faults));
167
168 if (srv->cb.fault_clear) {
169 srv->cb.fault_clear(model, company_id);
170 }
171
172 if (ctx->recv_op == OP_HEALTH_FAULT_CLEAR) {
173 health_send_fault_status(model, ctx);
174 }
175 }
176
health_fault_test(struct bt_mesh_model * model,struct bt_mesh_msg_ctx * ctx,struct net_buf_simple * buf)177 static void health_fault_test(struct bt_mesh_model *model,
178 struct bt_mesh_msg_ctx *ctx,
179 struct net_buf_simple *buf)
180 {
181 struct bt_mesh_health_srv *srv = model->user_data;
182 uint16_t company_id = 0U;
183 uint8_t test_id = 0U;
184
185 BT_DBG("%s", __func__);
186
187 if (!srv) {
188 BT_ERR("No Health Server context provided");
189 return;
190 }
191
192 test_id = net_buf_simple_pull_u8(buf);
193 if (health_is_test_id_exist(model, test_id) == false) {
194 BT_ERR("Unknown Test ID 0x%02x", test_id);
195 return;
196 }
197
198 company_id = net_buf_simple_pull_le16(buf);
199 if (company_id != srv->test.company_id) {
200 BT_ERR("Unknown Company ID 0x%04x", company_id);
201 return;
202 }
203
204 BT_DBG("test 0x%02x company 0x%04x", test_id, company_id);
205
206 srv->test.prev_test_id = test_id;
207
208 if (srv->cb.fault_test) {
209 srv->cb.fault_test(model, test_id, company_id);
210 }
211
212 if (ctx->recv_op == OP_HEALTH_FAULT_TEST) {
213 health_send_fault_status(model, ctx);
214 }
215 }
216
send_attention_status(struct bt_mesh_model * model,struct bt_mesh_msg_ctx * ctx)217 static void send_attention_status(struct bt_mesh_model *model,
218 struct bt_mesh_msg_ctx *ctx)
219 {
220 BLE_MESH_MODEL_BUF_DEFINE(msg, OP_ATTENTION_STATUS, 1);
221 struct bt_mesh_health_srv *srv = model->user_data;
222 uint8_t time = 0U;
223
224 if (!srv) {
225 BT_ERR("No Health Server context provided");
226 return;
227 }
228
229 time = k_delayed_work_remaining_get(&srv->attn_timer) / 1000;
230 BT_DBG("%u second%s", time, (time == 1U) ? "" : "s");
231
232 bt_mesh_model_msg_init(&msg, OP_ATTENTION_STATUS);
233 net_buf_simple_add_u8(&msg, time);
234
235 if (bt_mesh_model_send(model, ctx, &msg, NULL, NULL)) {
236 BT_ERR("Unable to send Health Attention Status");
237 }
238 }
239
attention_get(struct bt_mesh_model * model,struct bt_mesh_msg_ctx * ctx,struct net_buf_simple * buf)240 static void attention_get(struct bt_mesh_model *model,
241 struct bt_mesh_msg_ctx *ctx,
242 struct net_buf_simple *buf)
243 {
244 BT_DBG("%s", __func__);
245
246 send_attention_status(model, ctx);
247 }
248
health_set_attention(struct bt_mesh_model * model,struct bt_mesh_msg_ctx * ctx,struct net_buf_simple * buf)249 static void health_set_attention(struct bt_mesh_model *model,
250 struct bt_mesh_msg_ctx *ctx,
251 struct net_buf_simple *buf)
252 {
253 uint8_t time = 0U;
254
255 time = net_buf_simple_pull_u8(buf);
256
257 BT_DBG("%u second%s", time, (time == 1U) ? "" : "s");
258
259 bt_mesh_attention(model, time);
260 }
261
attention_set(struct bt_mesh_model * model,struct bt_mesh_msg_ctx * ctx,struct net_buf_simple * buf)262 static void attention_set(struct bt_mesh_model *model,
263 struct bt_mesh_msg_ctx *ctx,
264 struct net_buf_simple *buf)
265 {
266 BT_DBG("%s", __func__);
267
268 health_set_attention(model, ctx, buf);
269
270 if (ctx->recv_op == OP_ATTENTION_SET) {
271 send_attention_status(model, ctx);
272 }
273 }
274
send_health_period_status(struct bt_mesh_model * model,struct bt_mesh_msg_ctx * ctx)275 static void send_health_period_status(struct bt_mesh_model *model,
276 struct bt_mesh_msg_ctx *ctx)
277 {
278 BLE_MESH_MODEL_BUF_DEFINE(msg, OP_HEALTH_PERIOD_STATUS, 1);
279
280 bt_mesh_model_msg_init(&msg, OP_HEALTH_PERIOD_STATUS);
281 net_buf_simple_add_u8(&msg, model->pub->period_div);
282
283 if (bt_mesh_model_send(model, ctx, &msg, NULL, NULL)) {
284 BT_ERR("Unable to send Health Period Status");
285 }
286 }
287
health_period_get(struct bt_mesh_model * model,struct bt_mesh_msg_ctx * ctx,struct net_buf_simple * buf)288 static void health_period_get(struct bt_mesh_model *model,
289 struct bt_mesh_msg_ctx *ctx,
290 struct net_buf_simple *buf)
291 {
292 BT_DBG("%s", __func__);
293
294 send_health_period_status(model, ctx);
295 }
296
health_set_period(struct bt_mesh_model * model,struct bt_mesh_msg_ctx * ctx,struct net_buf_simple * buf)297 static void health_set_period(struct bt_mesh_model *model,
298 struct bt_mesh_msg_ctx *ctx,
299 struct net_buf_simple *buf)
300 {
301 uint8_t period = 0U;
302
303 period = net_buf_simple_pull_u8(buf);
304 if (period > 15) {
305 BT_WARN("Prohibited period value %u", period);
306 return;
307 }
308
309 BT_DBG("period %u", period);
310
311 model->pub->period_div = period;
312 }
313
health_period_set(struct bt_mesh_model * model,struct bt_mesh_msg_ctx * ctx,struct net_buf_simple * buf)314 static void health_period_set(struct bt_mesh_model *model,
315 struct bt_mesh_msg_ctx *ctx,
316 struct net_buf_simple *buf)
317 {
318 BT_DBG("%s", __func__);
319
320 health_set_period(model, ctx, buf);
321
322 if (ctx->recv_op == OP_HEALTH_PERIOD_SET) {
323 send_health_period_status(model, ctx);
324 }
325 }
326
327 const struct bt_mesh_model_op bt_mesh_health_srv_op[] = {
328 { OP_HEALTH_FAULT_GET, 2, health_fault_get },
329 { OP_HEALTH_FAULT_CLEAR, 2, health_fault_clear },
330 { OP_HEALTH_FAULT_CLEAR_UNREL, 2, health_fault_clear },
331 { OP_HEALTH_FAULT_TEST, 3, health_fault_test },
332 { OP_HEALTH_FAULT_TEST_UNREL, 3, health_fault_test },
333 { OP_HEALTH_PERIOD_GET, 0, health_period_get },
334 { OP_HEALTH_PERIOD_SET, 1, health_period_set },
335 { OP_HEALTH_PERIOD_SET_UNREL, 1, health_period_set },
336 { OP_ATTENTION_GET, 0, attention_get },
337 { OP_ATTENTION_SET, 1, attention_set },
338 { OP_ATTENTION_SET_UNREL, 1, attention_set },
339 BLE_MESH_MODEL_OP_END,
340 };
341
health_get_current(struct bt_mesh_model * model,struct net_buf_simple * msg)342 static size_t health_get_current(struct bt_mesh_model *model,
343 struct net_buf_simple *msg)
344 {
345 struct bt_mesh_health_srv *srv = model->user_data;
346
347 if (!srv) {
348 BT_ERR("No Health Server context provided");
349 return 0;
350 }
351
352 if (msg->size < 4) {
353 BT_ERR("Too small health publication msg size %d", msg->size);
354 return 0;
355 }
356
357 bt_mesh_model_msg_init(msg, OP_HEALTH_CURRENT_STATUS);
358 net_buf_simple_add_u8(msg, srv->test.prev_test_id);
359 net_buf_simple_add_le16(msg, srv->test.company_id);
360 health_get_fault_value(model, msg, true);
361
362 return health_get_curr_fault_count(model);
363 }
364
health_pub_update(struct bt_mesh_model * model)365 static int health_pub_update(struct bt_mesh_model *model)
366 {
367 struct bt_mesh_model_pub *pub = model->pub;
368 size_t count = 0U;
369
370 BT_DBG("%s", __func__);
371
372 if (!pub || !pub->msg) {
373 BT_ERR("Invalid health publication context");
374 return -EINVAL;
375 }
376
377 count = health_get_current(model, pub->msg);
378 if (count) {
379 pub->fast_period = 1U;
380 } else {
381 pub->fast_period = 0U;
382 }
383
384 return 0;
385 }
386
bt_mesh_fault_update(struct bt_mesh_elem * elem)387 int bt_mesh_fault_update(struct bt_mesh_elem *elem)
388 {
389 struct bt_mesh_model *model = NULL;
390
391 model = bt_mesh_model_find(elem, BLE_MESH_MODEL_ID_HEALTH_SRV);
392 if (!model) {
393 BT_ERR("Health Server not exists");
394 return -EINVAL;
395 }
396
397 if (!model->pub) {
398 BT_ERR("Health Server has no publication support");
399 return -EINVAL;
400 }
401
402 /* Let periodic publishing, if enabled, take care of sending the
403 * Health Current Status.
404 */
405 if (bt_mesh_model_pub_period_get(model)) {
406 return 0;
407 }
408
409 health_pub_update(model);
410
411 return bt_mesh_model_publish(model);
412 }
413
attention_off(struct k_work * work)414 static void attention_off(struct k_work *work)
415 {
416 struct bt_mesh_health_srv *srv = CONTAINER_OF(work,
417 struct bt_mesh_health_srv,
418 attn_timer.work);
419 BT_DBG("%s", __func__);
420
421 if (!srv) {
422 BT_ERR("No Health Server context provided");
423 return;
424 }
425
426 if (srv->cb.attn_off) {
427 srv->cb.attn_off(srv->model);
428 }
429 srv->attn_timer_start = false;
430 }
431
health_srv_init(struct bt_mesh_model * model)432 static int health_srv_init(struct bt_mesh_model *model)
433 {
434 struct bt_mesh_health_srv *srv = model->user_data;
435
436 /* Health Server Model shall be supported by a primary element and may be
437 * supported by any secondary elements.
438 */
439
440 if (!srv) {
441 BT_ERR("No Health Server context provided");
442 return -EINVAL;
443 }
444
445 if (srv->test.id_count == 0 || !srv->test.test_ids) {
446 BT_ERR("No Health Test ID provided");
447 return -EINVAL;
448 }
449
450 if (!model->pub) {
451 BT_ERR("Health Server has no publication support");
452 return -EINVAL;
453 }
454
455 model->pub->update = health_pub_update;
456
457 k_delayed_work_init(&srv->attn_timer, attention_off);
458
459 srv->model = model;
460 srv->attn_timer_start = false;
461
462 memset(srv->test.curr_faults, HEALTH_NO_FAULT, ARRAY_SIZE(srv->test.curr_faults));
463 memset(srv->test.reg_faults, HEALTH_NO_FAULT, ARRAY_SIZE(srv->test.reg_faults));
464
465 if (bt_mesh_model_in_primary(model)) {
466 health_srv = srv;
467 }
468
469 return 0;
470 }
471
472 #if CONFIG_BLE_MESH_DEINIT
health_srv_deinit(struct bt_mesh_model * model)473 static int health_srv_deinit(struct bt_mesh_model *model)
474 {
475 struct bt_mesh_health_srv *srv = model->user_data;
476
477 if (!srv) {
478 BT_ERR("No Health Server context provided");
479 return -EINVAL;
480 }
481
482 if (srv->test.id_count == 0 || !srv->test.test_ids) {
483 BT_ERR("No Health Test ID provided");
484 return -EINVAL;
485 }
486
487 if (!model->pub) {
488 BT_ERR("Health Server has no publication support");
489 return -EINVAL;
490 }
491
492 model->pub->addr = BLE_MESH_ADDR_UNASSIGNED;
493 model->pub->update = NULL;
494
495 k_delayed_work_free(&srv->attn_timer);
496
497 if (bt_mesh_model_in_primary(model)) {
498 health_srv = NULL;
499 }
500
501 return 0;
502 }
503 #endif /* CONFIG_BLE_MESH_DEINIT */
504
505 const struct bt_mesh_model_cb bt_mesh_health_srv_cb = {
506 .init = health_srv_init,
507 #if CONFIG_BLE_MESH_DEINIT
508 .deinit = health_srv_deinit,
509 #endif /* CONFIG_BLE_MESH_DEINIT */
510 };
511
bt_mesh_attention(struct bt_mesh_model * model,uint8_t time)512 void bt_mesh_attention(struct bt_mesh_model *model, uint8_t time)
513 {
514 struct bt_mesh_health_srv *srv = NULL;
515
516 if (!model) {
517 srv = health_srv;
518 if (!srv) {
519 BT_WARN("No Health Server context provided");
520 return;
521 }
522
523 model = srv->model;
524 } else {
525 srv = model->user_data;
526 if (!srv) {
527 BT_WARN("No Health Server context provided");
528 return;
529 }
530 }
531
532 if (time) {
533 if (srv->cb.attn_on) {
534 srv->cb.attn_on(model, time);
535 }
536
537 k_delayed_work_submit(&srv->attn_timer, time * 1000U);
538 srv->attn_timer_start = true;
539 } else {
540 k_delayed_work_cancel(&srv->attn_timer);
541
542 if (srv->attn_timer_start == true) {
543 if (srv->cb.attn_off) {
544 srv->cb.attn_off(model);
545 }
546 srv->attn_timer_start = false;
547 }
548 }
549 }
550 #else /* CONFIG_BLE_MESH_HEALTH_SRV */
bt_mesh_attention(struct bt_mesh_model * model,uint8_t time)551 void bt_mesh_attention(struct bt_mesh_model *model, uint8_t time)
552 {
553 return;
554 }
555 #endif /* CONFIG_BLE_MESH_HEALTH_SRV */
556