1 /* Bluetooth Mesh */
2
3 /*
4 * SPDX-FileCopyrightText: 2017 Intel Corporation
5 * SPDX-FileContributor: 2018-2021 Espressif Systems (Shanghai) CO LTD
6 *
7 * SPDX-License-Identifier: Apache-2.0
8 */
9
10 #include <string.h>
11 #include <errno.h>
12
13 #include "btc_ble_mesh_health_model.h"
14
15 #include "mesh_config.h"
16 #include "foundation.h"
17 #include "mesh_common.h"
18
19 #if CONFIG_BLE_MESH_HEALTH_CLI
20 #include "health_cli.h"
21
22 static const bt_mesh_client_op_pair_t health_op_pair[] = {
23 { OP_HEALTH_FAULT_GET, OP_HEALTH_FAULT_STATUS },
24 { OP_HEALTH_FAULT_CLEAR, OP_HEALTH_FAULT_STATUS },
25 { OP_HEALTH_FAULT_TEST, OP_HEALTH_FAULT_STATUS },
26 { OP_HEALTH_PERIOD_GET, OP_HEALTH_PERIOD_STATUS },
27 { OP_HEALTH_PERIOD_SET, OP_HEALTH_PERIOD_STATUS },
28 { OP_ATTENTION_GET, OP_ATTENTION_STATUS },
29 { OP_ATTENTION_SET, OP_ATTENTION_STATUS },
30 };
31
32 static bt_mesh_mutex_t health_client_lock;
33
bt_mesh_health_client_mutex_new(void)34 static inline void bt_mesh_health_client_mutex_new(void)
35 {
36 if (!health_client_lock.mutex) {
37 bt_mesh_mutex_create(&health_client_lock);
38 }
39 }
40
41 #if CONFIG_BLE_MESH_DEINIT
bt_mesh_health_client_mutex_free(void)42 static inline void bt_mesh_health_client_mutex_free(void)
43 {
44 bt_mesh_mutex_free(&health_client_lock);
45 }
46 #endif /* CONFIG_BLE_MESH_DEINIT */
47
bt_mesh_health_client_lock(void)48 static inline void bt_mesh_health_client_lock(void)
49 {
50 bt_mesh_mutex_lock(&health_client_lock);
51 }
52
bt_mesh_health_client_unlock(void)53 static inline void bt_mesh_health_client_unlock(void)
54 {
55 bt_mesh_mutex_unlock(&health_client_lock);
56 }
57
timeout_handler(struct k_work * work)58 static void timeout_handler(struct k_work *work)
59 {
60 struct k_delayed_work *timer = NULL;
61 bt_mesh_client_node_t *node = NULL;
62 struct bt_mesh_msg_ctx ctx = {0};
63 uint32_t opcode = 0U;
64
65 BT_WARN("Receive health status message timeout");
66
67 bt_mesh_health_client_lock();
68
69 timer = CONTAINER_OF(work, struct k_delayed_work, work);
70
71 if (timer && !k_delayed_work_free(timer)) {
72 node = CONTAINER_OF(work, bt_mesh_client_node_t, timer.work);
73 if (node) {
74 memcpy(&ctx, &node->ctx, sizeof(ctx));
75 opcode = node->opcode;
76 bt_mesh_client_free_node(node);
77 bt_mesh_health_client_cb_evt_to_btc(
78 opcode, BTC_BLE_MESH_EVT_HEALTH_CLIENT_TIMEOUT, ctx.model, &ctx, NULL, 0);
79 }
80 }
81
82 bt_mesh_health_client_unlock();
83
84 return;
85 }
86
health_client_recv_status(struct bt_mesh_model * model,struct bt_mesh_msg_ctx * ctx,void * status,size_t len)87 static void health_client_recv_status(struct bt_mesh_model *model,
88 struct bt_mesh_msg_ctx *ctx,
89 void *status, size_t len)
90 {
91 bt_mesh_client_node_t *node = NULL;
92 struct net_buf_simple buf = {0};
93 uint8_t evt_type = 0xFF;
94
95 if (!model || !ctx || !status || !len) {
96 BT_ERR("%s, Invalid parameter", __func__);
97 return;
98 }
99
100 /* If it is a publish message, sent to the user directly. */
101 buf.data = (uint8_t *)status;
102 buf.len = (uint16_t)len;
103
104 bt_mesh_health_client_lock();
105
106 node = bt_mesh_is_client_recv_publish_msg(model, ctx, &buf, true);
107 if (!node) {
108 BT_DBG("Unexpected Health Status 0x%04x", ctx->recv_op);
109 } else {
110 switch (node->opcode) {
111 case OP_HEALTH_FAULT_GET:
112 case OP_HEALTH_PERIOD_GET:
113 case OP_ATTENTION_GET:
114 evt_type = BTC_BLE_MESH_EVT_HEALTH_CLIENT_GET_STATE;
115 break;
116 case OP_HEALTH_FAULT_CLEAR:
117 case OP_HEALTH_FAULT_TEST:
118 case OP_HEALTH_PERIOD_SET:
119 case OP_ATTENTION_SET:
120 evt_type = BTC_BLE_MESH_EVT_HEALTH_CLIENT_SET_STATE;
121 break;
122 default:
123 break;
124 }
125
126 if (!k_delayed_work_free(&node->timer)) {
127 uint32_t opcode = node->opcode;
128 bt_mesh_client_free_node(node);
129 bt_mesh_health_client_cb_evt_to_btc(
130 opcode, evt_type, model, ctx, (const uint8_t *)status, len);
131 }
132 }
133
134 bt_mesh_health_client_unlock();
135
136 switch (ctx->recv_op) {
137 case OP_HEALTH_FAULT_STATUS: {
138 struct bt_mesh_health_fault_status *val = status;
139 bt_mesh_free_buf(val->fault_array);
140 break;
141 }
142 case OP_HEALTH_CURRENT_STATUS: {
143 struct bt_mesh_health_current_status *val = status;
144 bt_mesh_free_buf(val->fault_array);
145 break;
146 }
147 default:
148 break;
149 }
150 }
151
health_fault_status(struct bt_mesh_model * model,struct bt_mesh_msg_ctx * ctx,struct net_buf_simple * buf)152 static void health_fault_status(struct bt_mesh_model *model,
153 struct bt_mesh_msg_ctx *ctx,
154 struct net_buf_simple *buf)
155 {
156 struct bt_mesh_health_fault_status status = {0};
157
158 BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s",
159 ctx->net_idx, ctx->app_idx, ctx->addr, buf->len,
160 bt_hex(buf->data, buf->len));
161
162 status.test_id = net_buf_simple_pull_u8(buf);
163 status.cid = net_buf_simple_pull_le16(buf);
164 status.fault_array = bt_mesh_alloc_buf(buf->len);
165 if (!status.fault_array) {
166 BT_ERR("%s, Out of memory", __func__);
167 return;
168 }
169
170 net_buf_simple_add_mem(status.fault_array, buf->data, buf->len);
171
172 health_client_recv_status(model, ctx, &status, sizeof(struct bt_mesh_health_fault_status));
173 }
174
health_current_status(struct bt_mesh_model * model,struct bt_mesh_msg_ctx * ctx,struct net_buf_simple * buf)175 static void health_current_status(struct bt_mesh_model *model,
176 struct bt_mesh_msg_ctx *ctx,
177 struct net_buf_simple *buf)
178 {
179 struct bt_mesh_health_current_status status = {0};
180
181 BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s",
182 ctx->net_idx, ctx->app_idx, ctx->addr, buf->len,
183 bt_hex(buf->data, buf->len));
184
185 status.test_id = net_buf_simple_pull_u8(buf);
186 status.cid = net_buf_simple_pull_le16(buf);
187 status.fault_array = bt_mesh_alloc_buf(buf->len);
188 if (!status.fault_array) {
189 BT_ERR("%s, Out of memory", __func__);
190 return;
191 }
192
193 net_buf_simple_add_mem(status.fault_array, buf->data, buf->len);
194
195 health_client_recv_status(model, ctx, &status, sizeof(struct bt_mesh_health_current_status));
196 }
197
health_period_status(struct bt_mesh_model * model,struct bt_mesh_msg_ctx * ctx,struct net_buf_simple * buf)198 static void health_period_status(struct bt_mesh_model *model,
199 struct bt_mesh_msg_ctx *ctx,
200 struct net_buf_simple *buf)
201 {
202 uint8_t status = 0U;
203
204 BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s",
205 ctx->net_idx, ctx->app_idx, ctx->addr, buf->len,
206 bt_hex(buf->data, buf->len));
207
208 status = net_buf_simple_pull_u8(buf);
209
210 health_client_recv_status(model, ctx, &status, sizeof(uint8_t));
211 }
212
health_attention_status(struct bt_mesh_model * model,struct bt_mesh_msg_ctx * ctx,struct net_buf_simple * buf)213 static void health_attention_status(struct bt_mesh_model *model,
214 struct bt_mesh_msg_ctx *ctx,
215 struct net_buf_simple *buf)
216 {
217 uint8_t status = 0U;
218
219 BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s",
220 ctx->net_idx, ctx->app_idx, ctx->addr, buf->len,
221 bt_hex(buf->data, buf->len));
222
223 status = net_buf_simple_pull_u8(buf);
224
225 health_client_recv_status(model, ctx, &status, sizeof(uint8_t));
226 }
227
228 const struct bt_mesh_model_op bt_mesh_health_cli_op[] = {
229 { OP_HEALTH_FAULT_STATUS, 3, health_fault_status },
230 { OP_HEALTH_CURRENT_STATUS, 3, health_current_status },
231 { OP_HEALTH_PERIOD_STATUS, 1, health_period_status },
232 { OP_ATTENTION_STATUS, 1, health_attention_status },
233 BLE_MESH_MODEL_OP_END,
234 };
235
bt_mesh_health_attention_get(bt_mesh_client_common_param_t * param)236 int bt_mesh_health_attention_get(bt_mesh_client_common_param_t *param)
237 {
238 BLE_MESH_MODEL_BUF_DEFINE(msg, OP_ATTENTION_GET, 0);
239
240 bt_mesh_model_msg_init(&msg, OP_ATTENTION_GET);
241
242 return bt_mesh_client_send_msg(param, &msg, true, timeout_handler);
243 }
244
bt_mesh_health_attention_set(bt_mesh_client_common_param_t * param,uint8_t attention,bool need_ack)245 int bt_mesh_health_attention_set(bt_mesh_client_common_param_t *param,
246 uint8_t attention, bool need_ack)
247 {
248 BLE_MESH_MODEL_BUF_DEFINE(msg, OP_ATTENTION_SET, 1);
249
250 bt_mesh_model_msg_init(&msg, need_ack ? OP_ATTENTION_SET : OP_ATTENTION_SET_UNREL);
251 net_buf_simple_add_u8(&msg, attention);
252
253 return bt_mesh_client_send_msg(param, &msg, need_ack, timeout_handler);
254 }
255
bt_mesh_health_period_get(bt_mesh_client_common_param_t * param)256 int bt_mesh_health_period_get(bt_mesh_client_common_param_t *param)
257 {
258 BLE_MESH_MODEL_BUF_DEFINE(msg, OP_HEALTH_PERIOD_GET, 0);
259
260 bt_mesh_model_msg_init(&msg, OP_HEALTH_PERIOD_GET);
261
262 return bt_mesh_client_send_msg(param, &msg, true, timeout_handler);
263 }
264
bt_mesh_health_period_set(bt_mesh_client_common_param_t * param,uint8_t divisor,bool need_ack)265 int bt_mesh_health_period_set(bt_mesh_client_common_param_t *param,
266 uint8_t divisor, bool need_ack)
267 {
268 BLE_MESH_MODEL_BUF_DEFINE(msg, OP_HEALTH_PERIOD_SET, 1);
269
270 bt_mesh_model_msg_init(&msg, need_ack ? OP_HEALTH_PERIOD_SET : OP_HEALTH_PERIOD_SET_UNREL);
271 net_buf_simple_add_u8(&msg, divisor);
272
273 return bt_mesh_client_send_msg(param, &msg, need_ack, timeout_handler);
274 }
275
bt_mesh_health_fault_test(bt_mesh_client_common_param_t * param,uint16_t cid,uint8_t test_id,bool need_ack)276 int bt_mesh_health_fault_test(bt_mesh_client_common_param_t *param,
277 uint16_t cid, uint8_t test_id, bool need_ack)
278 {
279 BLE_MESH_MODEL_BUF_DEFINE(msg, OP_HEALTH_FAULT_TEST, 3);
280
281 bt_mesh_model_msg_init(&msg, need_ack ? OP_HEALTH_FAULT_TEST : OP_HEALTH_FAULT_TEST_UNREL);
282 net_buf_simple_add_u8(&msg, test_id);
283 net_buf_simple_add_le16(&msg, cid);
284
285 return bt_mesh_client_send_msg(param, &msg, need_ack, timeout_handler);
286 }
287
bt_mesh_health_fault_clear(bt_mesh_client_common_param_t * param,uint16_t cid,bool need_ack)288 int bt_mesh_health_fault_clear(bt_mesh_client_common_param_t *param,
289 uint16_t cid, bool need_ack)
290 {
291 BLE_MESH_MODEL_BUF_DEFINE(msg, OP_HEALTH_FAULT_CLEAR, 2);
292
293 bt_mesh_model_msg_init(&msg, need_ack ? OP_HEALTH_FAULT_CLEAR : OP_HEALTH_FAULT_CLEAR_UNREL);
294 net_buf_simple_add_le16(&msg, cid);
295
296 return bt_mesh_client_send_msg(param, &msg, need_ack, timeout_handler);
297 }
298
bt_mesh_health_fault_get(bt_mesh_client_common_param_t * param,uint16_t cid)299 int bt_mesh_health_fault_get(bt_mesh_client_common_param_t *param, uint16_t cid)
300 {
301 BLE_MESH_MODEL_BUF_DEFINE(msg, OP_HEALTH_FAULT_GET, 2);
302
303 bt_mesh_model_msg_init(&msg, OP_HEALTH_FAULT_GET);
304 net_buf_simple_add_le16(&msg, cid);
305
306 return bt_mesh_client_send_msg(param, &msg, true, timeout_handler);
307 }
308
health_cli_init(struct bt_mesh_model * model)309 static int health_cli_init(struct bt_mesh_model *model)
310 {
311 health_internal_data_t *internal = NULL;
312 bt_mesh_health_client_t *client = NULL;
313
314 if (!model) {
315 BT_ERR("Invalid Health Client model");
316 return -EINVAL;
317 }
318
319 BT_DBG("primary %u", bt_mesh_model_in_primary(model));
320
321 client = (bt_mesh_health_client_t *)model->user_data;
322 if (!client) {
323 BT_ERR("No Health Client context provided");
324 return -EINVAL;
325 }
326
327 if (!client->internal_data) {
328 internal = bt_mesh_calloc(sizeof(health_internal_data_t));
329 if (!internal) {
330 BT_ERR("%s, Out of memory", __func__);
331 return -ENOMEM;
332 }
333
334 sys_slist_init(&internal->queue);
335
336 client->model = model;
337 client->op_pair_size = ARRAY_SIZE(health_op_pair);
338 client->op_pair = health_op_pair;
339 client->internal_data = internal;
340 } else {
341 bt_mesh_client_clear_list(client->internal_data);
342 }
343
344 bt_mesh_health_client_mutex_new();
345
346 return 0;
347 }
348
349 #if CONFIG_BLE_MESH_DEINIT
health_cli_deinit(struct bt_mesh_model * model)350 static int health_cli_deinit(struct bt_mesh_model *model)
351 {
352 bt_mesh_health_client_t *client = NULL;
353
354 if (!model) {
355 BT_ERR("Invalid Health Client model");
356 return -EINVAL;
357 }
358
359 BT_DBG("primary %u", bt_mesh_model_in_primary(model));
360
361 client = (bt_mesh_health_client_t *)model->user_data;
362 if (!client) {
363 BT_ERR("No Health Client context provided");
364 return -EINVAL;
365 }
366
367 if (client->internal_data) {
368 /* Remove items from the list */
369 bt_mesh_client_clear_list(client->internal_data);
370
371 /* Free the allocated internal data */
372 bt_mesh_free(client->internal_data);
373 client->internal_data = NULL;
374 }
375
376 bt_mesh_health_client_mutex_free();
377
378 return 0;
379 }
380 #endif /* CONFIG_BLE_MESH_DEINIT */
381
382 const struct bt_mesh_model_cb bt_mesh_health_cli_cb = {
383 .init = health_cli_init,
384 #if CONFIG_BLE_MESH_DEINIT
385 .deinit = health_cli_deinit,
386 #endif /* CONFIG_BLE_MESH_DEINIT */
387 };
388
389 #endif /* CONFIG_BLE_MESH_HEALTH_CLI */
390