1 /*  Bluetooth Mesh */
2 
3 /*
4  * SPDX-FileCopyrightText: 2017 Intel Corporation
5  * SPDX-FileContributor: 2018-2021 Espressif Systems (Shanghai) CO 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