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