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