xref: /nrf52832-nimble/packages/NimBLE-latest/nimble/host/mesh/src/health_srv.c (revision 042d53a763ad75cb1465103098bb88c245d95138)
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