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