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