1 /*
2 * Copyright (c) 2017 Intel Corporation
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <zephyr.h>
8 #include <string.h>
9 #include <errno.h>
10 #include <stdbool.h>
11 #include <zephyr/types.h>
12 #include <sys/util.h>
13 #include <sys/byteorder.h>
14
15 #include <bluetooth/bluetooth.h>
16 #include <bluetooth/conn.h>
17 #include <bluetooth/mesh.h>
18
19 #define BT_DBG_ENABLED IS_ENABLED(CONFIG_BT_MESH_DEBUG_MODEL)
20 #define LOG_MODULE_NAME bt_mesh_health_cli
21 #include "common/log.h"
22
23 #include "net.h"
24 #include "foundation.h"
25
26 static int32_t msg_timeout;
27
28 static struct bt_mesh_health_cli *health_cli;
29
30 struct health_fault_param {
31 uint16_t cid;
32 uint8_t *expect_test_id;
33 uint8_t *test_id;
34 uint8_t *faults;
35 size_t *fault_count;
36 };
37
health_fault_status(struct bt_mesh_model * model,struct bt_mesh_msg_ctx * ctx,struct net_buf_simple * buf)38 static int health_fault_status(struct bt_mesh_model *model,
39 struct bt_mesh_msg_ctx *ctx,
40 struct net_buf_simple *buf)
41 {
42 struct health_fault_param *param;
43 uint8_t test_id;
44 uint16_t cid;
45
46 BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s",
47 ctx->net_idx, ctx->app_idx, ctx->addr, buf->len,
48 bt_hex(buf->data, buf->len));
49
50 if (!bt_mesh_msg_ack_ctx_match(&health_cli->ack_ctx, OP_HEALTH_FAULT_STATUS, ctx->addr,
51 (void **)¶m)) {
52 return -ENOENT;
53 }
54
55 test_id = net_buf_simple_pull_u8(buf);
56 if (param->expect_test_id && test_id != *param->expect_test_id) {
57 BT_WARN("Health fault with unexpected Test ID");
58 return -ENOENT;
59 }
60
61 cid = net_buf_simple_pull_le16(buf);
62 if (cid != param->cid) {
63 BT_WARN("Health fault with unexpected Company ID");
64 return -ENOENT;
65 }
66
67 if (param->test_id) {
68 *param->test_id = test_id;
69 }
70
71 if (buf->len > *param->fault_count) {
72 BT_WARN("Got more faults than there's space for");
73 } else {
74 *param->fault_count = buf->len;
75 }
76
77 memcpy(param->faults, buf->data, *param->fault_count);
78
79 bt_mesh_msg_ack_ctx_rx(&health_cli->ack_ctx);
80
81 return 0;
82 }
83
health_current_status(struct bt_mesh_model * model,struct bt_mesh_msg_ctx * ctx,struct net_buf_simple * buf)84 static int health_current_status(struct bt_mesh_model *model,
85 struct bt_mesh_msg_ctx *ctx,
86 struct net_buf_simple *buf)
87 {
88 struct bt_mesh_health_cli *cli = model->user_data;
89 uint8_t test_id;
90 uint16_t cid;
91
92 BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s",
93 ctx->net_idx, ctx->app_idx, ctx->addr, buf->len,
94 bt_hex(buf->data, buf->len));
95
96 test_id = net_buf_simple_pull_u8(buf);
97 cid = net_buf_simple_pull_le16(buf);
98
99 BT_DBG("Test ID 0x%02x Company ID 0x%04x Fault Count %u",
100 test_id, cid, buf->len);
101
102 if (!cli->current_status) {
103 BT_WARN("No Current Status callback available");
104 return 0;
105 }
106
107 cli->current_status(cli, ctx->addr, test_id, cid, buf->data, buf->len);
108
109 return 0;
110 }
111
112 struct health_period_param {
113 uint8_t *divisor;
114 };
115
health_period_status(struct bt_mesh_model * model,struct bt_mesh_msg_ctx * ctx,struct net_buf_simple * buf)116 static int health_period_status(struct bt_mesh_model *model,
117 struct bt_mesh_msg_ctx *ctx,
118 struct net_buf_simple *buf)
119 {
120 struct health_period_param *param;
121
122 BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s",
123 ctx->net_idx, ctx->app_idx, ctx->addr, buf->len,
124 bt_hex(buf->data, buf->len));
125
126 if (!bt_mesh_msg_ack_ctx_match(&health_cli->ack_ctx, OP_HEALTH_PERIOD_STATUS, ctx->addr,
127 (void **)¶m)) {
128 return -ENOENT;
129 }
130
131 *param->divisor = net_buf_simple_pull_u8(buf);
132
133 bt_mesh_msg_ack_ctx_rx(&health_cli->ack_ctx);
134
135 return 0;
136 }
137
138 struct health_attention_param {
139 uint8_t *attention;
140 };
141
health_attention_status(struct bt_mesh_model * model,struct bt_mesh_msg_ctx * ctx,struct net_buf_simple * buf)142 static int health_attention_status(struct bt_mesh_model *model,
143 struct bt_mesh_msg_ctx *ctx,
144 struct net_buf_simple *buf)
145 {
146 struct health_attention_param *param;
147
148 BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s",
149 ctx->net_idx, ctx->app_idx, ctx->addr, buf->len,
150 bt_hex(buf->data, buf->len));
151
152 if (!bt_mesh_msg_ack_ctx_match(&health_cli->ack_ctx, OP_ATTENTION_STATUS, ctx->addr,
153 (void **)¶m)) {
154 return -ENOENT;
155 }
156
157 if (param->attention) {
158 *param->attention = net_buf_simple_pull_u8(buf);
159 }
160
161 bt_mesh_msg_ack_ctx_rx(&health_cli->ack_ctx);
162
163 return 0;
164 }
165
166 const struct bt_mesh_model_op bt_mesh_health_cli_op[] = {
167 { OP_HEALTH_FAULT_STATUS, BT_MESH_LEN_MIN(3), health_fault_status },
168 { OP_HEALTH_CURRENT_STATUS, BT_MESH_LEN_MIN(3), health_current_status },
169 { OP_HEALTH_PERIOD_STATUS, BT_MESH_LEN_EXACT(1), health_period_status },
170 { OP_ATTENTION_STATUS, BT_MESH_LEN_EXACT(1), health_attention_status },
171 BT_MESH_MODEL_OP_END,
172 };
173
cli_prepare(void * param,uint32_t op,uint16_t addr)174 static int cli_prepare(void *param, uint32_t op, uint16_t addr)
175 {
176 if (!health_cli) {
177 BT_ERR("No available Health Client context!");
178 return -EINVAL;
179 }
180
181 return bt_mesh_msg_ack_ctx_prepare(&health_cli->ack_ctx, op, addr, param);
182 }
183
bt_mesh_health_attention_get(uint16_t addr,uint16_t app_idx,uint8_t * attention)184 int bt_mesh_health_attention_get(uint16_t addr, uint16_t app_idx, uint8_t *attention)
185 {
186 BT_MESH_MODEL_BUF_DEFINE(msg, OP_ATTENTION_GET, 0);
187 struct bt_mesh_msg_ctx ctx = {
188 .app_idx = app_idx,
189 .addr = addr,
190 .send_ttl = BT_MESH_TTL_DEFAULT,
191 };
192 struct health_attention_param param = {
193 .attention = attention,
194 };
195 int err;
196
197 err = cli_prepare(¶m, OP_ATTENTION_STATUS, addr);
198 if (err) {
199 return err;
200 }
201
202 bt_mesh_model_msg_init(&msg, OP_ATTENTION_GET);
203
204 err = bt_mesh_model_send(health_cli->model, &ctx, &msg, NULL, NULL);
205 if (err) {
206 BT_ERR("model_send() failed (err %d)", err);
207 bt_mesh_msg_ack_ctx_clear(&health_cli->ack_ctx);
208 return err;
209 }
210
211 return bt_mesh_msg_ack_ctx_wait(&health_cli->ack_ctx, K_MSEC(msg_timeout));
212 }
213
bt_mesh_health_attention_set(uint16_t addr,uint16_t app_idx,uint8_t attention,uint8_t * updated_attention)214 int bt_mesh_health_attention_set(uint16_t addr, uint16_t app_idx, uint8_t attention,
215 uint8_t *updated_attention)
216 {
217 BT_MESH_MODEL_BUF_DEFINE(msg, OP_ATTENTION_SET, 1);
218 struct bt_mesh_msg_ctx ctx = {
219 .app_idx = app_idx,
220 .addr = addr,
221 .send_ttl = BT_MESH_TTL_DEFAULT,
222 };
223 struct health_attention_param param = {
224 .attention = updated_attention,
225 };
226 int err;
227
228 err = cli_prepare(¶m, OP_ATTENTION_STATUS, addr);
229 if (err) {
230 return err;
231 }
232
233 if (updated_attention) {
234 bt_mesh_model_msg_init(&msg, OP_ATTENTION_SET);
235 } else {
236 bt_mesh_model_msg_init(&msg, OP_ATTENTION_SET_UNREL);
237 }
238
239 net_buf_simple_add_u8(&msg, attention);
240
241 err = bt_mesh_model_send(health_cli->model, &ctx, &msg, NULL, NULL);
242 if (err) {
243 BT_ERR("model_send() failed (err %d)", err);
244 bt_mesh_msg_ack_ctx_clear(&health_cli->ack_ctx);
245 return err;
246 }
247
248 if (!updated_attention) {
249 bt_mesh_msg_ack_ctx_clear(&health_cli->ack_ctx);
250 return 0;
251 }
252
253 return bt_mesh_msg_ack_ctx_wait(&health_cli->ack_ctx, K_MSEC(msg_timeout));
254 }
255
bt_mesh_health_period_get(uint16_t addr,uint16_t app_idx,uint8_t * divisor)256 int bt_mesh_health_period_get(uint16_t addr, uint16_t app_idx, uint8_t *divisor)
257 {
258 BT_MESH_MODEL_BUF_DEFINE(msg, OP_HEALTH_PERIOD_GET, 0);
259 struct bt_mesh_msg_ctx ctx = {
260 .app_idx = app_idx,
261 .addr = addr,
262 .send_ttl = BT_MESH_TTL_DEFAULT,
263 };
264 struct health_period_param param = {
265 .divisor = divisor,
266 };
267 int err;
268
269 err = cli_prepare(¶m, OP_HEALTH_PERIOD_STATUS, addr);
270 if (err) {
271 return err;
272 }
273
274 bt_mesh_model_msg_init(&msg, OP_HEALTH_PERIOD_GET);
275
276 err = bt_mesh_model_send(health_cli->model, &ctx, &msg, NULL, NULL);
277 if (err) {
278 BT_ERR("model_send() failed (err %d)", err);
279 bt_mesh_msg_ack_ctx_clear(&health_cli->ack_ctx);
280 return err;
281 }
282
283 return bt_mesh_msg_ack_ctx_wait(&health_cli->ack_ctx, K_MSEC(msg_timeout));
284 }
285
bt_mesh_health_period_set(uint16_t addr,uint16_t app_idx,uint8_t divisor,uint8_t * updated_divisor)286 int bt_mesh_health_period_set(uint16_t addr, uint16_t app_idx, uint8_t divisor,
287 uint8_t *updated_divisor)
288 {
289 BT_MESH_MODEL_BUF_DEFINE(msg, OP_HEALTH_PERIOD_SET, 1);
290 struct bt_mesh_msg_ctx ctx = {
291 .app_idx = app_idx,
292 .addr = addr,
293 .send_ttl = BT_MESH_TTL_DEFAULT,
294 };
295 struct health_period_param param = {
296 .divisor = updated_divisor,
297 };
298 int err;
299
300 err = cli_prepare(¶m, OP_HEALTH_PERIOD_STATUS, addr);
301 if (err) {
302 return err;
303 }
304
305 if (updated_divisor) {
306 bt_mesh_model_msg_init(&msg, OP_HEALTH_PERIOD_SET);
307 } else {
308 bt_mesh_model_msg_init(&msg, OP_HEALTH_PERIOD_SET_UNREL);
309 }
310
311 net_buf_simple_add_u8(&msg, divisor);
312
313 err = bt_mesh_model_send(health_cli->model, &ctx, &msg, NULL, NULL);
314 if (err) {
315 BT_ERR("model_send() failed (err %d)", err);
316 bt_mesh_msg_ack_ctx_clear(&health_cli->ack_ctx);
317 return err;
318 }
319
320 if (!updated_divisor) {
321 bt_mesh_msg_ack_ctx_clear(&health_cli->ack_ctx);
322 return 0;
323 }
324
325 return bt_mesh_msg_ack_ctx_wait(&health_cli->ack_ctx, K_MSEC(msg_timeout));
326 }
327
bt_mesh_health_fault_test(uint16_t addr,uint16_t app_idx,uint16_t cid,uint8_t test_id,uint8_t * faults,size_t * fault_count)328 int bt_mesh_health_fault_test(uint16_t addr, uint16_t app_idx, uint16_t cid,
329 uint8_t test_id, uint8_t *faults,
330 size_t *fault_count)
331 {
332 BT_MESH_MODEL_BUF_DEFINE(msg, OP_HEALTH_FAULT_TEST, 3);
333 struct bt_mesh_msg_ctx ctx = {
334 .app_idx = app_idx,
335 .addr = addr,
336 .send_ttl = BT_MESH_TTL_DEFAULT,
337 };
338 struct health_fault_param param = {
339 .cid = cid,
340 .expect_test_id = &test_id,
341 .faults = faults,
342 .fault_count = fault_count,
343 };
344 int err;
345
346 err = cli_prepare(¶m, OP_HEALTH_FAULT_STATUS, addr);
347 if (err) {
348 return err;
349 }
350
351 if (faults) {
352 bt_mesh_model_msg_init(&msg, OP_HEALTH_FAULT_TEST);
353 } else {
354 bt_mesh_model_msg_init(&msg, OP_HEALTH_FAULT_TEST_UNREL);
355 }
356
357 net_buf_simple_add_u8(&msg, test_id);
358 net_buf_simple_add_le16(&msg, cid);
359
360 err = bt_mesh_model_send(health_cli->model, &ctx, &msg, NULL, NULL);
361 if (err) {
362 BT_ERR("model_send() failed (err %d)", err);
363 bt_mesh_msg_ack_ctx_clear(&health_cli->ack_ctx);
364 return err;
365 }
366
367 if (!faults) {
368 bt_mesh_msg_ack_ctx_clear(&health_cli->ack_ctx);
369 return 0;
370 }
371
372 return bt_mesh_msg_ack_ctx_wait(&health_cli->ack_ctx, K_MSEC(msg_timeout));
373 }
374
bt_mesh_health_fault_clear(uint16_t addr,uint16_t app_idx,uint16_t cid,uint8_t * test_id,uint8_t * faults,size_t * fault_count)375 int bt_mesh_health_fault_clear(uint16_t addr, uint16_t app_idx, uint16_t cid,
376 uint8_t *test_id, uint8_t *faults,
377 size_t *fault_count)
378 {
379 BT_MESH_MODEL_BUF_DEFINE(msg, OP_HEALTH_FAULT_CLEAR, 2);
380 struct bt_mesh_msg_ctx ctx = {
381 .app_idx = app_idx,
382 .addr = addr,
383 .send_ttl = BT_MESH_TTL_DEFAULT,
384 };
385 struct health_fault_param param = {
386 .cid = cid,
387 .test_id = test_id,
388 .faults = faults,
389 .fault_count = fault_count,
390 };
391 int err;
392
393 err = cli_prepare(¶m, OP_HEALTH_FAULT_STATUS, addr);
394 if (err) {
395 return err;
396 }
397
398 if (test_id) {
399 bt_mesh_model_msg_init(&msg, OP_HEALTH_FAULT_CLEAR);
400 } else {
401 bt_mesh_model_msg_init(&msg, OP_HEALTH_FAULT_CLEAR_UNREL);
402 }
403
404 net_buf_simple_add_le16(&msg, cid);
405
406 err = bt_mesh_model_send(health_cli->model, &ctx, &msg, NULL, NULL);
407 if (err) {
408 BT_ERR("model_send() failed (err %d)", err);
409 bt_mesh_msg_ack_ctx_clear(&health_cli->ack_ctx);
410 return err;
411 }
412
413 if (!test_id) {
414 bt_mesh_msg_ack_ctx_clear(&health_cli->ack_ctx);
415 return 0;
416 }
417
418 return bt_mesh_msg_ack_ctx_wait(&health_cli->ack_ctx, K_MSEC(msg_timeout));
419 }
420
bt_mesh_health_fault_get(uint16_t addr,uint16_t app_idx,uint16_t cid,uint8_t * test_id,uint8_t * faults,size_t * fault_count)421 int bt_mesh_health_fault_get(uint16_t addr, uint16_t app_idx, uint16_t cid,
422 uint8_t *test_id, uint8_t *faults,
423 size_t *fault_count)
424 {
425 BT_MESH_MODEL_BUF_DEFINE(msg, OP_HEALTH_FAULT_GET, 2);
426 struct bt_mesh_msg_ctx ctx = {
427 .app_idx = app_idx,
428 .addr = addr,
429 .send_ttl = BT_MESH_TTL_DEFAULT,
430 };
431 struct health_fault_param param = {
432 .cid = cid,
433 .test_id = test_id,
434 .faults = faults,
435 .fault_count = fault_count,
436 };
437 int err;
438
439 err = cli_prepare(¶m, OP_HEALTH_FAULT_STATUS, addr);
440 if (err) {
441 return err;
442 }
443
444 bt_mesh_model_msg_init(&msg, OP_HEALTH_FAULT_GET);
445 net_buf_simple_add_le16(&msg, cid);
446
447 err = bt_mesh_model_send(health_cli->model, &ctx, &msg, NULL, NULL);
448 if (err) {
449 BT_ERR("model_send() failed (err %d)", err);
450 bt_mesh_msg_ack_ctx_clear(&health_cli->ack_ctx);
451 return err;
452 }
453
454 return bt_mesh_msg_ack_ctx_wait(&health_cli->ack_ctx, K_MSEC(msg_timeout));
455 }
456
bt_mesh_health_cli_timeout_get(void)457 int32_t bt_mesh_health_cli_timeout_get(void)
458 {
459 return msg_timeout;
460 }
461
bt_mesh_health_cli_timeout_set(int32_t timeout)462 void bt_mesh_health_cli_timeout_set(int32_t timeout)
463 {
464 msg_timeout = timeout;
465 }
466
bt_mesh_health_cli_set(struct bt_mesh_model * model)467 int bt_mesh_health_cli_set(struct bt_mesh_model *model)
468 {
469 if (!model->user_data) {
470 BT_ERR("No Health Client context for given model");
471 return -EINVAL;
472 }
473
474 health_cli = model->user_data;
475 msg_timeout = 2 * MSEC_PER_SEC;
476
477 return 0;
478 }
479
health_cli_init(struct bt_mesh_model * model)480 static int health_cli_init(struct bt_mesh_model *model)
481 {
482 struct bt_mesh_health_cli *cli = model->user_data;
483
484 BT_DBG("primary %u", bt_mesh_model_in_primary(model));
485
486 if (!cli) {
487 BT_ERR("No Health Client context provided");
488 return -EINVAL;
489 }
490
491 cli = model->user_data;
492 cli->model = model;
493 msg_timeout = 2 * MSEC_PER_SEC;
494
495 /* Set the default health client pointer */
496 if (!health_cli) {
497 health_cli = cli;
498 }
499
500 bt_mesh_msg_ack_ctx_init(&health_cli->ack_ctx);
501 return 0;
502 }
503
504 const struct bt_mesh_model_cb bt_mesh_health_cli_cb = {
505 .init = health_cli_init,
506 };
507