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