1 /*
2 * Copyright (c) 2017 Intel Corporation
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <zephyr/kernel.h>
8 #include <string.h>
9 #include <errno.h>
10 #include <stdbool.h>
11 #include <zephyr/types.h>
12 #include <zephyr/sys/byteorder.h>
13 #include <zephyr/sys/util.h>
14
15 #include <zephyr/bluetooth/bluetooth.h>
16 #include <zephyr/bluetooth/mesh.h>
17
18 #include "mesh.h"
19 #include "net.h"
20 #include "transport.h"
21 #include "access.h"
22 #include "foundation.h"
23
24 #define LOG_LEVEL CONFIG_BT_MESH_MODEL_LOG_LEVEL
25 #include <zephyr/logging/log.h>
26 LOG_MODULE_REGISTER(bt_mesh_health_srv);
27
28 #define HEALTH_TEST_STANDARD 0x00
29
30 /* Health Server context of the primary element */
31 struct bt_mesh_health_srv *health_srv;
32
health_get_registered(const struct bt_mesh_model * mod,uint16_t company_id,struct net_buf_simple * msg)33 static void health_get_registered(const struct bt_mesh_model *mod,
34 uint16_t company_id,
35 struct net_buf_simple *msg)
36 {
37 struct bt_mesh_health_srv *srv = mod->rt->user_data;
38 uint8_t *test_id;
39
40 LOG_DBG("Company ID 0x%04x", company_id);
41
42 bt_mesh_model_msg_init(msg, OP_HEALTH_FAULT_STATUS);
43
44 test_id = net_buf_simple_add(msg, 1);
45 net_buf_simple_add_le16(msg, company_id);
46
47 if (srv->cb && srv->cb->fault_get_reg) {
48 uint8_t fault_count = net_buf_simple_tailroom(msg) - 4;
49 int err;
50
51 err = srv->cb->fault_get_reg(mod, company_id, test_id,
52 net_buf_simple_tail(msg),
53 &fault_count);
54 if (err) {
55 LOG_ERR("Failed to get faults (err %d)", err);
56 *test_id = HEALTH_TEST_STANDARD;
57 } else {
58 net_buf_simple_add(msg, fault_count);
59 }
60 } else {
61 LOG_WRN("No callback for getting faults");
62 *test_id = HEALTH_TEST_STANDARD;
63 }
64 }
65
health_get_current(const struct bt_mesh_model * mod,struct net_buf_simple * msg)66 static size_t health_get_current(const struct bt_mesh_model *mod,
67 struct net_buf_simple *msg)
68 {
69 struct bt_mesh_health_srv *srv = mod->rt->user_data;
70 const struct bt_mesh_comp *comp;
71 uint8_t *test_id, *company_ptr;
72 uint16_t company_id;
73 uint8_t fault_count;
74
75 bt_mesh_model_msg_init(msg, OP_HEALTH_CURRENT_STATUS);
76
77 test_id = net_buf_simple_add(msg, 1);
78 company_ptr = net_buf_simple_add(msg, sizeof(company_id));
79 comp = bt_mesh_comp_get();
80
81 if (srv->cb && srv->cb->fault_get_cur) {
82 fault_count = net_buf_simple_tailroom(msg);
83 int err;
84
85 err = srv->cb->fault_get_cur(mod, test_id, &company_id,
86 net_buf_simple_tail(msg),
87 &fault_count);
88 if (err) {
89 LOG_ERR("Failed to get faults (err %d)", err);
90 sys_put_le16(comp->cid, company_ptr);
91 *test_id = HEALTH_TEST_STANDARD;
92 fault_count = 0U;
93 } else {
94 sys_put_le16(company_id, company_ptr);
95 net_buf_simple_add(msg, fault_count);
96 }
97 } else {
98 LOG_WRN("No callback for getting faults");
99 sys_put_le16(comp->cid, company_ptr);
100 *test_id = HEALTH_TEST_STANDARD;
101 fault_count = 0U;
102 }
103
104 return fault_count;
105 }
106
health_fault_get(const struct bt_mesh_model * model,struct bt_mesh_msg_ctx * ctx,struct net_buf_simple * buf)107 static int health_fault_get(const struct bt_mesh_model *model,
108 struct bt_mesh_msg_ctx *ctx,
109 struct net_buf_simple *buf)
110 {
111 NET_BUF_SIMPLE_DEFINE(sdu, BT_MESH_TX_SDU_MAX);
112 uint16_t company_id;
113
114 company_id = net_buf_simple_pull_le16(buf);
115
116 LOG_DBG("company_id 0x%04x", company_id);
117
118 health_get_registered(model, company_id, &sdu);
119
120 if (bt_mesh_model_send(model, ctx, &sdu, NULL, NULL)) {
121 LOG_ERR("Unable to send Health Current Status response");
122 }
123
124 return 0;
125 }
126
health_fault_clear_unrel(const struct bt_mesh_model * model,struct bt_mesh_msg_ctx * ctx,struct net_buf_simple * buf)127 static int health_fault_clear_unrel(const struct bt_mesh_model *model,
128 struct bt_mesh_msg_ctx *ctx,
129 struct net_buf_simple *buf)
130 {
131 struct bt_mesh_health_srv *srv = model->rt->user_data;
132 uint16_t company_id;
133
134 company_id = net_buf_simple_pull_le16(buf);
135
136 LOG_DBG("company_id 0x%04x", company_id);
137
138 if (srv->cb && srv->cb->fault_clear) {
139 return srv->cb->fault_clear(model, company_id);
140 }
141
142 return 0;
143 }
144
health_fault_clear(const struct bt_mesh_model * model,struct bt_mesh_msg_ctx * ctx,struct net_buf_simple * buf)145 static int health_fault_clear(const struct bt_mesh_model *model,
146 struct bt_mesh_msg_ctx *ctx,
147 struct net_buf_simple *buf)
148 {
149 NET_BUF_SIMPLE_DEFINE(sdu, BT_MESH_TX_SDU_MAX);
150 struct bt_mesh_health_srv *srv = model->rt->user_data;
151 uint16_t company_id;
152
153 company_id = net_buf_simple_pull_le16(buf);
154
155 LOG_DBG("company_id 0x%04x", company_id);
156
157 if (srv->cb && srv->cb->fault_clear) {
158 int err;
159
160 err = srv->cb->fault_clear(model, company_id);
161 if (err) {
162 return err;
163 }
164 }
165
166 health_get_registered(model, company_id, &sdu);
167
168 if (bt_mesh_model_send(model, ctx, &sdu, NULL, NULL)) {
169 LOG_ERR("Unable to send Health Current Status response");
170 }
171
172 return 0;
173 }
174
health_fault_test_unrel(const struct bt_mesh_model * model,struct bt_mesh_msg_ctx * ctx,struct net_buf_simple * buf)175 static int health_fault_test_unrel(const struct bt_mesh_model *model,
176 struct bt_mesh_msg_ctx *ctx,
177 struct net_buf_simple *buf)
178 {
179 struct bt_mesh_health_srv *srv = model->rt->user_data;
180 uint16_t company_id;
181 uint8_t test_id;
182
183 test_id = net_buf_simple_pull_u8(buf);
184 company_id = net_buf_simple_pull_le16(buf);
185
186 LOG_DBG("test 0x%02x company 0x%04x", test_id, company_id);
187
188 if (srv->cb && srv->cb->fault_test) {
189 return srv->cb->fault_test(model, test_id, company_id);
190 }
191
192 return 0;
193 }
194
health_fault_test(const struct bt_mesh_model * model,struct bt_mesh_msg_ctx * ctx,struct net_buf_simple * buf)195 static int health_fault_test(const struct bt_mesh_model *model,
196 struct bt_mesh_msg_ctx *ctx,
197 struct net_buf_simple *buf)
198 {
199 NET_BUF_SIMPLE_DEFINE(sdu, BT_MESH_TX_SDU_MAX);
200 struct bt_mesh_health_srv *srv = model->rt->user_data;
201 uint16_t company_id;
202 uint8_t test_id;
203
204 LOG_DBG("");
205
206 test_id = net_buf_simple_pull_u8(buf);
207 company_id = net_buf_simple_pull_le16(buf);
208
209 LOG_DBG("test 0x%02x company 0x%04x", test_id, company_id);
210
211 if (srv->cb && srv->cb->fault_test) {
212 int err;
213
214 err = srv->cb->fault_test(model, test_id, company_id);
215 if (err) {
216 LOG_WRN("Running fault test failed with err %d", err);
217 return err;
218 }
219 }
220
221 health_get_registered(model, company_id, &sdu);
222
223 if (bt_mesh_model_send(model, ctx, &sdu, NULL, NULL)) {
224 LOG_ERR("Unable to send Health Current Status response");
225 }
226
227 return 0;
228 }
229
send_attention_status(const struct bt_mesh_model * model,struct bt_mesh_msg_ctx * ctx)230 static int send_attention_status(const struct bt_mesh_model *model,
231 struct bt_mesh_msg_ctx *ctx)
232 {
233 /* Needed size: opcode (2 bytes) + msg + MIC */
234 BT_MESH_MODEL_BUF_DEFINE(msg, OP_ATTENTION_STATUS, 1);
235 struct bt_mesh_health_srv *srv = model->rt->user_data;
236 uint8_t time;
237
238 time = k_ticks_to_ms_floor32(
239 k_work_delayable_remaining_get(&srv->attn_timer)) / 1000U;
240 LOG_DBG("%u second%s", time, (time == 1U) ? "" : "s");
241
242 bt_mesh_model_msg_init(&msg, OP_ATTENTION_STATUS);
243
244 net_buf_simple_add_u8(&msg, time);
245
246 if (bt_mesh_model_send(model, ctx, &msg, NULL, NULL)) {
247 LOG_ERR("Unable to send Attention Status");
248 }
249
250 return 0;
251 }
252
attention_get(const struct bt_mesh_model * model,struct bt_mesh_msg_ctx * ctx,struct net_buf_simple * buf)253 static int attention_get(const struct bt_mesh_model *model,
254 struct bt_mesh_msg_ctx *ctx,
255 struct net_buf_simple *buf)
256 {
257 LOG_DBG("");
258
259 return send_attention_status(model, ctx);
260 }
261
attention_set_unrel(const struct bt_mesh_model * model,struct bt_mesh_msg_ctx * ctx,struct net_buf_simple * buf)262 static int attention_set_unrel(const struct bt_mesh_model *model,
263 struct bt_mesh_msg_ctx *ctx,
264 struct net_buf_simple *buf)
265 {
266 uint8_t time;
267
268 time = net_buf_simple_pull_u8(buf);
269
270 LOG_DBG("%u second%s", time, (time == 1U) ? "" : "s");
271
272 bt_mesh_attention(model, time);
273
274 return 0;
275 }
276
attention_set(const struct bt_mesh_model * model,struct bt_mesh_msg_ctx * ctx,struct net_buf_simple * buf)277 static int attention_set(const struct bt_mesh_model *model,
278 struct bt_mesh_msg_ctx *ctx,
279 struct net_buf_simple *buf)
280 {
281 int err;
282
283 LOG_DBG("");
284
285 err = attention_set_unrel(model, ctx, buf);
286 if (err) {
287 return err;
288 }
289
290 return send_attention_status(model, ctx);
291 }
292
send_health_period_status(const struct bt_mesh_model * model,struct bt_mesh_msg_ctx * ctx)293 static int send_health_period_status(const struct bt_mesh_model *model,
294 struct bt_mesh_msg_ctx *ctx)
295 {
296 /* Needed size: opcode (2 bytes) + msg + MIC */
297 BT_MESH_MODEL_BUF_DEFINE(msg, OP_HEALTH_PERIOD_STATUS, 1);
298
299 bt_mesh_model_msg_init(&msg, OP_HEALTH_PERIOD_STATUS);
300
301 net_buf_simple_add_u8(&msg, model->pub->period_div);
302
303 if (bt_mesh_model_send(model, ctx, &msg, NULL, NULL)) {
304 LOG_ERR("Unable to send Health Period Status");
305 }
306
307 return 0;
308 }
309
health_period_get(const struct bt_mesh_model * model,struct bt_mesh_msg_ctx * ctx,struct net_buf_simple * buf)310 static int health_period_get(const struct bt_mesh_model *model,
311 struct bt_mesh_msg_ctx *ctx,
312 struct net_buf_simple *buf)
313 {
314 LOG_DBG("");
315
316 return send_health_period_status(model, ctx);
317 }
318
health_period_set_unrel(const struct bt_mesh_model * model,struct bt_mesh_msg_ctx * ctx,struct net_buf_simple * buf)319 static int health_period_set_unrel(const struct bt_mesh_model *model,
320 struct bt_mesh_msg_ctx *ctx,
321 struct net_buf_simple *buf)
322 {
323 uint8_t period;
324
325 period = net_buf_simple_pull_u8(buf);
326 if (period > 15) {
327 LOG_WRN("Prohibited period value %u", period);
328 return -EINVAL;
329 }
330
331 LOG_DBG("period %u", period);
332
333 model->pub->period_div = period;
334
335 return 0;
336 }
337
health_period_set(const struct bt_mesh_model * model,struct bt_mesh_msg_ctx * ctx,struct net_buf_simple * buf)338 static int health_period_set(const struct bt_mesh_model *model,
339 struct bt_mesh_msg_ctx *ctx,
340 struct net_buf_simple *buf)
341 {
342 int err;
343
344 LOG_DBG("");
345
346 err = health_period_set_unrel(model, ctx, buf);
347 if (err) {
348 return err;
349 }
350
351 return send_health_period_status(model, ctx);
352 }
353
354 const struct bt_mesh_model_op bt_mesh_health_srv_op[] = {
355 { OP_HEALTH_FAULT_GET, BT_MESH_LEN_EXACT(2), health_fault_get },
356 { OP_HEALTH_FAULT_CLEAR, BT_MESH_LEN_EXACT(2), health_fault_clear },
357 { OP_HEALTH_FAULT_CLEAR_UNREL, BT_MESH_LEN_EXACT(2), health_fault_clear_unrel },
358 { OP_HEALTH_FAULT_TEST, BT_MESH_LEN_EXACT(3), health_fault_test },
359 { OP_HEALTH_FAULT_TEST_UNREL, BT_MESH_LEN_EXACT(3), health_fault_test_unrel },
360 { OP_HEALTH_PERIOD_GET, BT_MESH_LEN_EXACT(0), health_period_get },
361 { OP_HEALTH_PERIOD_SET, BT_MESH_LEN_EXACT(1), health_period_set },
362 { OP_HEALTH_PERIOD_SET_UNREL, BT_MESH_LEN_EXACT(1), health_period_set_unrel },
363 { OP_ATTENTION_GET, BT_MESH_LEN_EXACT(0), attention_get },
364 { OP_ATTENTION_SET, BT_MESH_LEN_EXACT(1), attention_set },
365 { OP_ATTENTION_SET_UNREL, BT_MESH_LEN_EXACT(1), attention_set_unrel },
366 BT_MESH_MODEL_OP_END,
367 };
368
health_pub_update(const struct bt_mesh_model * mod)369 static int health_pub_update(const struct bt_mesh_model *mod)
370 {
371 struct bt_mesh_model_pub *pub = mod->pub;
372 size_t count;
373
374 LOG_DBG("");
375
376 count = health_get_current(mod, pub->msg);
377 if (count) {
378 pub->fast_period = 1U;
379 } else {
380 pub->fast_period = 0U;
381 }
382
383 return 0;
384 }
385
bt_mesh_health_srv_fault_update(const struct bt_mesh_elem * elem)386 int bt_mesh_health_srv_fault_update(const struct bt_mesh_elem *elem)
387 {
388 const struct bt_mesh_model *mod;
389
390 mod = bt_mesh_model_find(elem, BT_MESH_MODEL_ID_HEALTH_SRV);
391 if (!mod) {
392 return -EINVAL;
393 }
394
395 /* Let periodic publishing, if enabled, take care of sending the
396 * Health Current Status.
397 */
398 if (bt_mesh_model_pub_period_get(mod) > 0) {
399 return 0;
400 }
401
402 health_pub_update(mod);
403
404 return bt_mesh_model_publish(mod);
405 }
406
attention_off(struct k_work * work)407 static void attention_off(struct k_work *work)
408 {
409 struct k_work_delayable *dwork = k_work_delayable_from_work(work);
410 struct bt_mesh_health_srv *srv = CONTAINER_OF(dwork,
411 struct bt_mesh_health_srv,
412 attn_timer);
413 LOG_DBG("");
414
415 if (srv->cb && srv->cb->attn_off) {
416 srv->cb->attn_off(srv->model);
417 }
418 }
419
health_srv_init(const struct bt_mesh_model * model)420 static int health_srv_init(const struct bt_mesh_model *model)
421 {
422 struct bt_mesh_health_srv *srv = model->rt->user_data;
423
424 if (!srv) {
425 LOG_ERR("No Health Server context provided");
426 return -EINVAL;
427 }
428
429 if (!model->pub) {
430 LOG_ERR("Health Server has no publication support");
431 return -EINVAL;
432 }
433
434 model->pub->update = health_pub_update;
435
436 k_work_init_delayable(&srv->attn_timer, attention_off);
437
438 srv->model = model;
439
440 if (bt_mesh_model_in_primary(model)) {
441 health_srv = srv;
442 }
443
444 return 0;
445 }
446
447 const struct bt_mesh_model_cb bt_mesh_health_srv_cb = {
448 .init = health_srv_init,
449 };
450
bt_mesh_attention(const struct bt_mesh_model * model,uint8_t time)451 void bt_mesh_attention(const struct bt_mesh_model *model, uint8_t time)
452 {
453 struct bt_mesh_health_srv *srv;
454
455 if (!model) {
456 srv = health_srv;
457 if (!srv) {
458 LOG_WRN("No Health Server available");
459 return;
460 }
461
462 model = srv->model;
463 } else {
464 srv = model->rt->user_data;
465 }
466
467 if ((time > 0) && srv->cb && srv->cb->attn_on) {
468 srv->cb->attn_on(model);
469 }
470
471 k_work_reschedule(&srv->attn_timer, K_SECONDS(time));
472 }
473