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