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/util.h>
13 #include <zephyr/sys/byteorder.h>
14
15 #include <zephyr/bluetooth/bluetooth.h>
16 #include <zephyr/bluetooth/conn.h>
17 #include <zephyr/bluetooth/mesh.h>
18
19 #include "common/bt_str.h"
20
21 #include "net.h"
22 #include "foundation.h"
23 #include "msg.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_cli);
28
29 static int32_t msg_timeout;
30
31 struct health_fault_param {
32 uint16_t cid;
33 uint8_t *expect_test_id;
34 uint8_t *test_id;
35 uint8_t *faults;
36 size_t *fault_count;
37 };
38
health_fault_status(struct bt_mesh_model * model,struct bt_mesh_msg_ctx * ctx,struct net_buf_simple * buf)39 static int health_fault_status(struct bt_mesh_model *model,
40 struct bt_mesh_msg_ctx *ctx,
41 struct net_buf_simple *buf)
42 {
43 struct bt_mesh_health_cli *cli = model->user_data;
44 struct health_fault_param *param;
45 uint8_t test_id;
46 uint16_t cid;
47
48 LOG_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s", ctx->net_idx, ctx->app_idx,
49 ctx->addr, buf->len, bt_hex(buf->data, buf->len));
50
51 test_id = net_buf_simple_pull_u8(buf);
52 cid = net_buf_simple_pull_le16(buf);
53
54 if (bt_mesh_msg_ack_ctx_match(&cli->ack_ctx,
55 OP_HEALTH_FAULT_STATUS, ctx->addr,
56 (void **)¶m)) {
57 if (param->expect_test_id &&
58 (test_id != *param->expect_test_id)) {
59 goto done;
60 }
61
62 if (cid != param->cid) {
63 goto done;
64 }
65
66 if (param->test_id) {
67 *param->test_id = test_id;
68 }
69
70 if (param->faults && param->fault_count) {
71 if (buf->len > *param->fault_count) {
72 LOG_WRN("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
80 bt_mesh_msg_ack_ctx_rx(&cli->ack_ctx);
81 }
82
83 done:
84 if (cli->fault_status) {
85 cli->fault_status(cli, ctx->addr, test_id, cid,
86 buf->data, buf->len);
87 }
88
89 return 0;
90 }
91
health_current_status(struct bt_mesh_model * model,struct bt_mesh_msg_ctx * ctx,struct net_buf_simple * buf)92 static int health_current_status(struct bt_mesh_model *model,
93 struct bt_mesh_msg_ctx *ctx,
94 struct net_buf_simple *buf)
95 {
96 struct bt_mesh_health_cli *cli = model->user_data;
97 uint8_t test_id;
98 uint16_t cid;
99
100 LOG_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s", ctx->net_idx, ctx->app_idx,
101 ctx->addr, buf->len, bt_hex(buf->data, buf->len));
102
103 test_id = net_buf_simple_pull_u8(buf);
104 cid = net_buf_simple_pull_le16(buf);
105
106 LOG_DBG("Test ID 0x%02x Company ID 0x%04x Fault Count %u", test_id, cid, buf->len);
107
108 if (cli->current_status) {
109 cli->current_status(cli, ctx->addr, test_id, cid,
110 buf->data, buf->len);
111 }
112
113 return 0;
114 }
115
116 struct health_period_param {
117 uint8_t *divisor;
118 };
119
health_period_status(struct bt_mesh_model * model,struct bt_mesh_msg_ctx * ctx,struct net_buf_simple * buf)120 static int health_period_status(struct bt_mesh_model *model,
121 struct bt_mesh_msg_ctx *ctx,
122 struct net_buf_simple *buf)
123 {
124 struct bt_mesh_health_cli *cli = model->user_data;
125 struct health_period_param *param;
126 uint8_t divisor;
127
128 LOG_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s", ctx->net_idx, ctx->app_idx,
129 ctx->addr, buf->len, bt_hex(buf->data, buf->len));
130
131 divisor = net_buf_simple_pull_u8(buf);
132
133 if (bt_mesh_msg_ack_ctx_match(&cli->ack_ctx,
134 OP_HEALTH_PERIOD_STATUS, ctx->addr,
135 (void **)¶m)) {
136 if (param->divisor) {
137 *param->divisor = divisor;
138 }
139
140 bt_mesh_msg_ack_ctx_rx(&cli->ack_ctx);
141 }
142
143 if (cli->period_status) {
144 cli->period_status(cli, ctx->addr, divisor);
145 }
146
147 return 0;
148 }
149
150 struct health_attention_param {
151 uint8_t *attention;
152 };
153
health_attention_status(struct bt_mesh_model * model,struct bt_mesh_msg_ctx * ctx,struct net_buf_simple * buf)154 static int health_attention_status(struct bt_mesh_model *model,
155 struct bt_mesh_msg_ctx *ctx,
156 struct net_buf_simple *buf)
157 {
158 struct bt_mesh_health_cli *cli = model->user_data;
159 struct health_attention_param *param;
160 uint8_t attention;
161
162 LOG_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s", ctx->net_idx, ctx->app_idx,
163 ctx->addr, buf->len, bt_hex(buf->data, buf->len));
164
165 attention = net_buf_simple_pull_u8(buf);
166
167 if (bt_mesh_msg_ack_ctx_match(&cli->ack_ctx, OP_ATTENTION_STATUS,
168 ctx->addr, (void **)¶m)) {
169 if (param->attention) {
170 *param->attention = attention;
171 }
172
173 bt_mesh_msg_ack_ctx_rx(&cli->ack_ctx);
174 }
175
176 if (cli->attention_status) {
177 cli->attention_status(cli, ctx->addr, attention);
178 }
179 return 0;
180 }
181
182 const struct bt_mesh_model_op bt_mesh_health_cli_op[] = {
183 { OP_HEALTH_FAULT_STATUS, BT_MESH_LEN_MIN(3), health_fault_status },
184 { OP_HEALTH_CURRENT_STATUS, BT_MESH_LEN_MIN(3), health_current_status },
185 { OP_HEALTH_PERIOD_STATUS, BT_MESH_LEN_EXACT(1), health_period_status },
186 { OP_ATTENTION_STATUS, BT_MESH_LEN_EXACT(1), health_attention_status },
187 BT_MESH_MODEL_OP_END,
188 };
189
bt_mesh_health_cli_attention_get(struct bt_mesh_health_cli * cli,struct bt_mesh_msg_ctx * ctx,uint8_t * attention)190 int bt_mesh_health_cli_attention_get(struct bt_mesh_health_cli *cli, struct bt_mesh_msg_ctx *ctx,
191 uint8_t *attention)
192 {
193 BT_MESH_MODEL_BUF_DEFINE(msg, OP_ATTENTION_GET, 0);
194 struct health_attention_param param = {
195 .attention = attention,
196 };
197
198 bt_mesh_model_msg_init(&msg, OP_ATTENTION_GET);
199
200 const struct bt_mesh_msg_rsp_ctx rsp = {
201 .ack = &cli->ack_ctx,
202 .op = OP_ATTENTION_STATUS,
203 .user_data = ¶m,
204 .timeout = msg_timeout,
205 };
206
207 return bt_mesh_msg_ackd_send(cli->model, ctx, &msg, attention ? &rsp : NULL);
208 }
209
bt_mesh_health_cli_attention_set(struct bt_mesh_health_cli * cli,struct bt_mesh_msg_ctx * ctx,uint8_t attention,uint8_t * updated_attention)210 int bt_mesh_health_cli_attention_set(struct bt_mesh_health_cli *cli, struct bt_mesh_msg_ctx *ctx,
211 uint8_t attention, uint8_t *updated_attention)
212 {
213 BT_MESH_MODEL_BUF_DEFINE(msg, OP_ATTENTION_SET, 1);
214 struct health_attention_param param = {
215 .attention = updated_attention,
216 };
217
218 bt_mesh_model_msg_init(&msg, OP_ATTENTION_SET);
219 net_buf_simple_add_u8(&msg, attention);
220
221 const struct bt_mesh_msg_rsp_ctx rsp = {
222 .ack = &cli->ack_ctx,
223 .op = OP_ATTENTION_STATUS,
224 .user_data = ¶m,
225 .timeout = msg_timeout,
226 };
227
228 return bt_mesh_msg_ackd_send(cli->model, ctx, &msg, updated_attention ? &rsp : NULL);
229 }
230
bt_mesh_health_cli_attention_set_unack(struct bt_mesh_health_cli * cli,struct bt_mesh_msg_ctx * ctx,uint8_t attention)231 int bt_mesh_health_cli_attention_set_unack(struct bt_mesh_health_cli *cli,
232 struct bt_mesh_msg_ctx *ctx, uint8_t attention)
233 {
234 BT_MESH_MODEL_BUF_DEFINE(msg, OP_ATTENTION_SET_UNREL, 1);
235
236 bt_mesh_model_msg_init(&msg, OP_ATTENTION_SET_UNREL);
237 net_buf_simple_add_u8(&msg, attention);
238
239 return bt_mesh_msg_send(cli->model, ctx, &msg);
240 }
241
bt_mesh_health_cli_period_get(struct bt_mesh_health_cli * cli,struct bt_mesh_msg_ctx * ctx,uint8_t * divisor)242 int bt_mesh_health_cli_period_get(struct bt_mesh_health_cli *cli, struct bt_mesh_msg_ctx *ctx,
243 uint8_t *divisor)
244 {
245 BT_MESH_MODEL_BUF_DEFINE(msg, OP_HEALTH_PERIOD_GET, 0);
246 struct health_period_param param = {
247 .divisor = divisor,
248 };
249
250 bt_mesh_model_msg_init(&msg, OP_HEALTH_PERIOD_GET);
251
252 const struct bt_mesh_msg_rsp_ctx rsp = {
253 .ack = &cli->ack_ctx,
254 .op = OP_HEALTH_PERIOD_STATUS,
255 .user_data = ¶m,
256 .timeout = msg_timeout,
257 };
258
259 return bt_mesh_msg_ackd_send(cli->model, ctx, &msg, divisor ? &rsp : NULL);
260 }
261
bt_mesh_health_cli_period_set(struct bt_mesh_health_cli * cli,struct bt_mesh_msg_ctx * ctx,uint8_t divisor,uint8_t * updated_divisor)262 int bt_mesh_health_cli_period_set(struct bt_mesh_health_cli *cli, struct bt_mesh_msg_ctx *ctx,
263 uint8_t divisor, uint8_t *updated_divisor)
264 {
265 BT_MESH_MODEL_BUF_DEFINE(msg, OP_HEALTH_PERIOD_SET, 1);
266 struct health_period_param param = {
267 .divisor = updated_divisor,
268 };
269
270 bt_mesh_model_msg_init(&msg, OP_HEALTH_PERIOD_SET);
271 net_buf_simple_add_u8(&msg, divisor);
272
273 const struct bt_mesh_msg_rsp_ctx rsp = {
274 .ack = &cli->ack_ctx,
275 .op = OP_HEALTH_PERIOD_STATUS,
276 .user_data = ¶m,
277 .timeout = msg_timeout,
278 };
279
280 return bt_mesh_msg_ackd_send(cli->model, ctx, &msg, updated_divisor ? &rsp : NULL);
281 }
282
bt_mesh_health_cli_period_set_unack(struct bt_mesh_health_cli * cli,struct bt_mesh_msg_ctx * ctx,uint8_t divisor)283 int bt_mesh_health_cli_period_set_unack(struct bt_mesh_health_cli *cli,
284 struct bt_mesh_msg_ctx *ctx, uint8_t divisor)
285 {
286 BT_MESH_MODEL_BUF_DEFINE(msg, OP_HEALTH_PERIOD_SET_UNREL, 1);
287
288 bt_mesh_model_msg_init(&msg, OP_HEALTH_PERIOD_SET_UNREL);
289 net_buf_simple_add_u8(&msg, divisor);
290
291 return bt_mesh_msg_send(cli->model, ctx, &msg);
292 }
293
bt_mesh_health_cli_fault_test(struct bt_mesh_health_cli * cli,struct bt_mesh_msg_ctx * ctx,uint16_t cid,uint8_t test_id,uint8_t * faults,size_t * fault_count)294 int bt_mesh_health_cli_fault_test(struct bt_mesh_health_cli *cli, struct bt_mesh_msg_ctx *ctx,
295 uint16_t cid, uint8_t test_id, uint8_t *faults,
296 size_t *fault_count)
297 {
298 BT_MESH_MODEL_BUF_DEFINE(msg, OP_HEALTH_FAULT_TEST, 3);
299 struct health_fault_param param = {
300 .cid = cid,
301 .expect_test_id = &test_id,
302 .faults = faults,
303 .fault_count = fault_count,
304 };
305
306 bt_mesh_model_msg_init(&msg, OP_HEALTH_FAULT_TEST);
307 net_buf_simple_add_u8(&msg, test_id);
308 net_buf_simple_add_le16(&msg, cid);
309
310 const struct bt_mesh_msg_rsp_ctx rsp = {
311 .ack = &cli->ack_ctx,
312 .op = OP_HEALTH_FAULT_STATUS,
313 .user_data = ¶m,
314 .timeout = msg_timeout,
315 };
316
317 return bt_mesh_msg_ackd_send(cli->model, ctx, &msg, &rsp);
318 }
319
bt_mesh_health_cli_fault_test_unack(struct bt_mesh_health_cli * cli,struct bt_mesh_msg_ctx * ctx,uint16_t cid,uint8_t test_id)320 int bt_mesh_health_cli_fault_test_unack(struct bt_mesh_health_cli *cli,
321 struct bt_mesh_msg_ctx *ctx, uint16_t cid, uint8_t test_id)
322 {
323 BT_MESH_MODEL_BUF_DEFINE(msg, OP_HEALTH_FAULT_TEST_UNREL, 3);
324
325 bt_mesh_model_msg_init(&msg, OP_HEALTH_FAULT_TEST_UNREL);
326 net_buf_simple_add_u8(&msg, test_id);
327 net_buf_simple_add_le16(&msg, cid);
328
329 return bt_mesh_msg_send(cli->model, ctx, &msg);
330 }
331
bt_mesh_health_cli_fault_clear(struct bt_mesh_health_cli * cli,struct bt_mesh_msg_ctx * ctx,uint16_t cid,uint8_t * test_id,uint8_t * faults,size_t * fault_count)332 int bt_mesh_health_cli_fault_clear(struct bt_mesh_health_cli *cli, struct bt_mesh_msg_ctx *ctx,
333 uint16_t cid, uint8_t *test_id, uint8_t *faults,
334 size_t *fault_count)
335 {
336 BT_MESH_MODEL_BUF_DEFINE(msg, OP_HEALTH_FAULT_CLEAR, 2);
337 struct health_fault_param param = {
338 .cid = cid,
339 .test_id = test_id,
340 .faults = faults,
341 .fault_count = fault_count,
342 };
343
344 bt_mesh_model_msg_init(&msg, OP_HEALTH_FAULT_CLEAR);
345 net_buf_simple_add_le16(&msg, cid);
346
347 const struct bt_mesh_msg_rsp_ctx rsp = {
348 .ack = &cli->ack_ctx,
349 .op = OP_HEALTH_FAULT_STATUS,
350 .user_data = ¶m,
351 .timeout = msg_timeout,
352 };
353
354 return bt_mesh_msg_ackd_send(cli->model, ctx, &msg,
355 (!test_id && (!faults || !fault_count)) ? NULL : &rsp);
356 }
357
bt_mesh_health_cli_fault_clear_unack(struct bt_mesh_health_cli * cli,struct bt_mesh_msg_ctx * ctx,uint16_t cid)358 int bt_mesh_health_cli_fault_clear_unack(struct bt_mesh_health_cli *cli,
359 struct bt_mesh_msg_ctx *ctx, uint16_t cid)
360 {
361 BT_MESH_MODEL_BUF_DEFINE(msg, OP_HEALTH_FAULT_CLEAR_UNREL, 2);
362
363 bt_mesh_model_msg_init(&msg, OP_HEALTH_FAULT_CLEAR_UNREL);
364 net_buf_simple_add_le16(&msg, cid);
365
366 return bt_mesh_msg_send(cli->model, ctx, &msg);
367 }
368
bt_mesh_health_cli_fault_get(struct bt_mesh_health_cli * cli,struct bt_mesh_msg_ctx * ctx,uint16_t cid,uint8_t * test_id,uint8_t * faults,size_t * fault_count)369 int bt_mesh_health_cli_fault_get(struct bt_mesh_health_cli *cli, struct bt_mesh_msg_ctx *ctx,
370 uint16_t cid, uint8_t *test_id, uint8_t *faults,
371 size_t *fault_count)
372 {
373 BT_MESH_MODEL_BUF_DEFINE(msg, OP_HEALTH_FAULT_GET, 2);
374 struct health_fault_param param = {
375 .cid = cid,
376 .test_id = test_id,
377 .faults = faults,
378 .fault_count = fault_count,
379 };
380
381 bt_mesh_model_msg_init(&msg, OP_HEALTH_FAULT_GET);
382 net_buf_simple_add_le16(&msg, cid);
383
384 const struct bt_mesh_msg_rsp_ctx rsp = {
385 .ack = &cli->ack_ctx,
386 .op = OP_HEALTH_FAULT_STATUS,
387 .user_data = ¶m,
388 .timeout = msg_timeout,
389 };
390
391 return bt_mesh_msg_ackd_send(cli->model, ctx, &msg,
392 (!test_id && (!faults || !fault_count)) ? NULL : &rsp);
393 }
394
bt_mesh_health_cli_timeout_get(void)395 int32_t bt_mesh_health_cli_timeout_get(void)
396 {
397 return msg_timeout;
398 }
399
bt_mesh_health_cli_timeout_set(int32_t timeout)400 void bt_mesh_health_cli_timeout_set(int32_t timeout)
401 {
402 msg_timeout = timeout;
403 }
404
health_cli_init(struct bt_mesh_model * model)405 static int health_cli_init(struct bt_mesh_model *model)
406 {
407 struct bt_mesh_health_cli *cli = model->user_data;
408
409 LOG_DBG("primary %u", bt_mesh_model_in_primary(model));
410
411 if (!cli) {
412 LOG_ERR("No Health Client context provided");
413 return -EINVAL;
414 }
415
416 cli->model = model;
417 msg_timeout = CONFIG_BT_MESH_HEALTH_CLI_TIMEOUT;
418
419 cli->pub.msg = &cli->pub_buf;
420 net_buf_simple_init_with_data(&cli->pub_buf, cli->pub_data, sizeof(cli->pub_data));
421
422 bt_mesh_msg_ack_ctx_init(&cli->ack_ctx);
423 return 0;
424 }
425
health_cli_reset(struct bt_mesh_model * model)426 static void health_cli_reset(struct bt_mesh_model *model)
427 {
428 struct bt_mesh_health_cli *cli = model->user_data;
429
430 net_buf_simple_reset(cli->pub.msg);
431 }
432
433 const struct bt_mesh_model_cb bt_mesh_health_cli_cb = {
434 .init = health_cli_init,
435 .reset = health_cli_reset,
436 };
437