1 /*
2 * SPDX-FileCopyrightText: 2017-2021 Espressif Systems (Shanghai) CO LTD
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <string.h>
8 #include <errno.h>
9
10 #include "mesh.h"
11 #include "mesh_main.h"
12 #include "transport.h"
13 #include "foundation.h"
14 #include "client_common.h"
15 #include "mesh_common.h"
16
17 #define HCI_TIME_FOR_START_ADV K_MSEC(5) /* Three adv related hci commands may take 4 ~ 5ms */
18
bt_mesh_client_pick_node(sys_slist_t * list,uint16_t tx_dst)19 static bt_mesh_client_node_t *bt_mesh_client_pick_node(sys_slist_t *list, uint16_t tx_dst)
20 {
21 bt_mesh_client_node_t *node = NULL;
22 sys_snode_t *cur = NULL;
23
24 bt_mesh_list_lock();
25 if (sys_slist_is_empty(list)) {
26 bt_mesh_list_unlock();
27 return NULL;
28 }
29
30 for (cur = sys_slist_peek_head(list);
31 cur != NULL; cur = sys_slist_peek_next(cur)) {
32 node = (bt_mesh_client_node_t *)cur;
33 if (node->ctx.addr == tx_dst) {
34 bt_mesh_list_unlock();
35 return node;
36 }
37 }
38
39 bt_mesh_list_unlock();
40 return NULL;
41 }
42
bt_mesh_is_client_recv_publish_msg(struct bt_mesh_model * model,struct bt_mesh_msg_ctx * ctx,struct net_buf_simple * buf,bool need_pub)43 bt_mesh_client_node_t *bt_mesh_is_client_recv_publish_msg(struct bt_mesh_model *model,
44 struct bt_mesh_msg_ctx *ctx,
45 struct net_buf_simple *buf, bool need_pub)
46 {
47 bt_mesh_client_internal_data_t *data = NULL;
48 bt_mesh_client_user_data_t *cli = NULL;
49 bt_mesh_client_node_t *node = NULL;
50
51 if (!model || !ctx || !buf) {
52 BT_ERR("%s, Invalid parameter", __func__);
53 return NULL;
54 }
55
56 cli = (bt_mesh_client_user_data_t *)model->user_data;
57 if (!cli) {
58 BT_ERR("Invalid client user data");
59 return NULL;
60 }
61
62 /** If the received message address is not a unicast address,
63 * the address may be a group/virtual address, and we push
64 * this message to the application layer.
65 */
66 if (!BLE_MESH_ADDR_IS_UNICAST(ctx->recv_dst)) {
67 BT_DBG("Unexpected status message 0x%08x", ctx->recv_op);
68 if (cli->publish_status && need_pub) {
69 cli->publish_status(ctx->recv_op, model, ctx, buf);
70 }
71 return NULL;
72 }
73
74 /** If the source address of the received status message is
75 * different with the destination address of the sending
76 * message, then the message is from another element and
77 * push it to application layer.
78 */
79 data = (bt_mesh_client_internal_data_t *)cli->internal_data;
80 if (!data) {
81 BT_ERR("Invalid client internal data");
82 return NULL;
83 }
84
85 if ((node = bt_mesh_client_pick_node(&data->queue, ctx->addr)) == NULL) {
86 BT_DBG("Unexpected status message 0x%08x", ctx->recv_op);
87 if (cli->publish_status && need_pub) {
88 cli->publish_status(ctx->recv_op, model, ctx, buf);
89 }
90 return NULL;
91 }
92
93 if (node->op_pending != ctx->recv_op) {
94 BT_DBG("Unexpected status message 0x%08x", ctx->recv_op);
95 if (cli->publish_status && need_pub) {
96 cli->publish_status(ctx->recv_op, model, ctx, buf);
97 }
98 return NULL;
99 }
100
101 if (k_delayed_work_remaining_get(&node->timer) == 0) {
102 BT_DBG("Unexpected status message 0x%08x", ctx->recv_op);
103 if (cli->publish_status && need_pub) {
104 cli->publish_status(ctx->recv_op, model, ctx, buf);
105 }
106 return NULL;
107 }
108
109 return node;
110 }
111
bt_mesh_client_check_node_in_list(sys_slist_t * list,uint16_t tx_dst)112 static bool bt_mesh_client_check_node_in_list(sys_slist_t *list, uint16_t tx_dst)
113 {
114 bt_mesh_client_node_t *node = NULL;
115 sys_snode_t *cur = NULL;
116
117 bt_mesh_list_lock();
118 if (sys_slist_is_empty(list)) {
119 bt_mesh_list_unlock();
120 return false;
121 }
122
123 for (cur = sys_slist_peek_head(list);
124 cur != NULL; cur = sys_slist_peek_next(cur)) {
125 node = (bt_mesh_client_node_t *)cur;
126 if (node->ctx.addr == tx_dst) {
127 bt_mesh_list_unlock();
128 return true;
129 }
130 }
131
132 bt_mesh_list_unlock();
133 return false;
134 }
135
bt_mesh_client_get_status_op(const bt_mesh_client_op_pair_t * op_pair,int size,uint32_t opcode)136 static uint32_t bt_mesh_client_get_status_op(const bt_mesh_client_op_pair_t *op_pair,
137 int size, uint32_t opcode)
138 {
139 if (!op_pair || size == 0) {
140 return 0;
141 }
142
143 const bt_mesh_client_op_pair_t *op = op_pair;
144 for (int i = 0; i < size; i++) {
145 if (op->cli_op == opcode) {
146 return op->status_op;
147 }
148 op++;
149 }
150
151 return 0;
152 }
153
bt_mesh_get_adv_duration(void)154 static int32_t bt_mesh_get_adv_duration(void)
155 {
156 uint16_t duration, adv_int;
157 uint8_t xmit;
158
159 xmit = bt_mesh_net_transmit_get(); /* Network transmit */
160 adv_int = BLE_MESH_TRANSMIT_INT(xmit);
161 duration = (BLE_MESH_TRANSMIT_COUNT(xmit) + 1) * (adv_int + 10);
162
163 return (int32_t)duration;
164 }
165
bt_mesh_client_calc_timeout(struct bt_mesh_msg_ctx * ctx,struct net_buf_simple * msg,uint32_t opcode,int32_t timeout)166 static int32_t bt_mesh_client_calc_timeout(struct bt_mesh_msg_ctx *ctx,
167 struct net_buf_simple *msg,
168 uint32_t opcode, int32_t timeout)
169 {
170 int32_t seg_retrans_to = 0, duration = 0, time = 0;
171 uint8_t seg_count = 0, seg_retrans_num = 0;
172 bool need_seg = false;
173 uint8_t mic_size = 0;
174
175 if (msg->len > BLE_MESH_SDU_UNSEG_MAX || ctx->send_rel) {
176 need_seg = true; /* Needs segmentation */
177 }
178
179 mic_size = (need_seg && net_buf_simple_tailroom(msg) >= BLE_MESH_MIC_LONG) ?
180 BLE_MESH_MIC_LONG : BLE_MESH_MIC_SHORT;
181
182 if (need_seg) {
183 /* Based on the message length, calculate how many segments are needed.
184 * All the messages sent from here are access messages.
185 */
186 seg_retrans_num = bt_mesh_get_seg_retrans_num();
187 seg_retrans_to = bt_mesh_get_seg_retrans_timeout(ctx->send_ttl);
188 seg_count = (msg->len + mic_size - 1) / 12U + 1U;
189
190 duration = bt_mesh_get_adv_duration();
191
192 /* Currenlty only consider the time consumption of the same segmented
193 * messages, but if there are other messages between any two retrans-
194 * missions of the same segmented messages, then the whole time will
195 * be longer.
196 *
197 * Since the transport behavior has been changed, i.e. start retransmit
198 * timer after the last segment is sent, so we can simplify the timeout
199 * calculation here. And the retransmit timer will be started event if
200 * the attempts reaches ZERO when the dst is a unicast address.
201 */
202 int32_t seg_duration = seg_count * (duration + HCI_TIME_FOR_START_ADV);
203 time = (seg_duration + seg_retrans_to) * seg_retrans_num;
204
205 BT_INFO("Original timeout %dms, calculated timeout %dms", timeout, time);
206
207 if (time < timeout) {
208 /* If the calculated time is smaller than the input timeout value,
209 * then use the original timeout value.
210 */
211 time = timeout;
212 }
213 } else {
214 /* For unsegmented access messages, directly use the timeout
215 * value from the application layer.
216 */
217 time = timeout;
218 }
219
220 BT_INFO("Client message 0x%08x with timeout %dms", opcode, time);
221
222 return time;
223 }
224
msg_send_start(uint16_t duration,int err,void * cb_data)225 static void msg_send_start(uint16_t duration, int err, void *cb_data)
226 {
227 bt_mesh_client_node_t *node = cb_data;
228
229 BT_DBG("%s, duration %ums", __func__, duration);
230
231 if (err) {
232 if (!k_delayed_work_free(&node->timer)) {
233 bt_mesh_client_free_node(node);
234 }
235 return;
236 }
237
238 k_delayed_work_submit(&node->timer, node->timeout);
239 }
240
241 static const struct bt_mesh_send_cb send_cb = {
242 .start = msg_send_start,
243 .end = NULL,
244 };
245
bt_mesh_client_send_msg(bt_mesh_client_common_param_t * param,struct net_buf_simple * msg,bool need_ack,k_work_handler_t timer_handler)246 int bt_mesh_client_send_msg(bt_mesh_client_common_param_t *param,
247 struct net_buf_simple *msg, bool need_ack,
248 k_work_handler_t timer_handler)
249 {
250 bt_mesh_client_internal_data_t *internal = NULL;
251 bt_mesh_client_user_data_t *client = NULL;
252 bt_mesh_client_node_t *node = NULL;
253 int err = 0;
254
255 if (!param || !param->model || !msg) {
256 BT_ERR("%s, Invalid parameter", __func__);
257 return -EINVAL;
258 }
259
260 client = (bt_mesh_client_user_data_t *)param->model->user_data;
261 if (!client) {
262 BT_ERR("Invalid client user data");
263 return -EINVAL;
264 }
265
266 internal = (bt_mesh_client_internal_data_t *)client->internal_data;
267 if (!internal) {
268 BT_ERR("Invalid client internal data");
269 return -EINVAL;
270 }
271
272 if (param->ctx.addr == BLE_MESH_ADDR_UNASSIGNED) {
273 BT_ERR("Invalid DST 0x%04x", param->ctx.addr);
274 return -EINVAL;
275 }
276
277 if (bt_mesh_set_client_model_role(param->model, param->msg_role)) {
278 BT_ERR("Failed to set client role");
279 return -EIO;
280 }
281
282 if (need_ack == false || !BLE_MESH_ADDR_IS_UNICAST(param->ctx.addr)) {
283 /* 1. If this is an unacknowledged message, send it directly.
284 * 2. If this is an acknowledged message, but the destination
285 * is not a unicast address, e.g. a group/virtual address,
286 * then all the corresponding responses will be treated as
287 * publish messages, and no timeout will be used.
288 */
289 err = bt_mesh_model_send(param->model, ¶m->ctx, msg, param->cb, param->cb_data);
290 if (err) {
291 BT_ERR("Failed to send client message 0x%08x", param->opcode);
292 }
293 return err;
294 }
295
296 if (!timer_handler) {
297 BT_ERR("Invalid timeout handler");
298 return -EINVAL;
299 }
300
301 if (bt_mesh_client_check_node_in_list(&internal->queue, param->ctx.addr)) {
302 BT_ERR("Busy sending message to DST 0x%04x", param->ctx.addr);
303 return -EBUSY;
304 }
305
306 /* Don't forget to free the node in the timeout (timer_handler) function. */
307 node = (bt_mesh_client_node_t *)bt_mesh_calloc(sizeof(bt_mesh_client_node_t));
308 if (!node) {
309 BT_ERR("%s, Out of memory", __func__);
310 return -ENOMEM;
311 }
312
313 memcpy(&node->ctx, ¶m->ctx, sizeof(struct bt_mesh_msg_ctx));
314 node->ctx.model = param->model;
315 node->opcode = param->opcode;
316 node->op_pending = bt_mesh_client_get_status_op(client->op_pair, client->op_pair_size, param->opcode);
317 if (node->op_pending == 0U) {
318 BT_ERR("Not found the status opcode in op_pair list");
319 bt_mesh_free(node);
320 return -EINVAL;
321 }
322 node->timeout = bt_mesh_client_calc_timeout(¶m->ctx, msg, param->opcode,
323 param->msg_timeout ? param->msg_timeout : CONFIG_BLE_MESH_CLIENT_MSG_TIMEOUT);
324
325 if (k_delayed_work_init(&node->timer, timer_handler)) {
326 BT_ERR("Failed to create a timer");
327 bt_mesh_free(node);
328 return -EIO;
329 }
330
331 bt_mesh_list_lock();
332 sys_slist_append(&internal->queue, &node->client_node);
333 bt_mesh_list_unlock();
334
335 /* "bt_mesh_model_send" will post the mesh packet to the mesh adv queue.
336 * Due to the higher priority of adv_thread (than btc task), we need to
337 * send the packet after the list item "node" is initialized properly.
338 */
339 err = bt_mesh_model_send(param->model, ¶m->ctx, msg, &send_cb, node);
340 if (err) {
341 BT_ERR("Failed to send client message 0x%08x", node->opcode);
342 k_delayed_work_free(&node->timer);
343 bt_mesh_client_free_node(node);
344 }
345
346 return err;
347 }
348
349 static bt_mesh_mutex_t client_model_lock;
350
bt_mesh_client_model_mutex_new(void)351 static inline void bt_mesh_client_model_mutex_new(void)
352 {
353 if (!client_model_lock.mutex) {
354 bt_mesh_mutex_create(&client_model_lock);
355 }
356 }
357
358 #if CONFIG_BLE_MESH_DEINIT
bt_mesh_client_model_mutex_free(void)359 static inline void bt_mesh_client_model_mutex_free(void)
360 {
361 bt_mesh_mutex_free(&client_model_lock);
362 }
363 #endif /* CONFIG_BLE_MESH_DEINIT */
364
bt_mesh_client_model_lock(void)365 void bt_mesh_client_model_lock(void)
366 {
367 bt_mesh_mutex_lock(&client_model_lock);
368 }
369
bt_mesh_client_model_unlock(void)370 void bt_mesh_client_model_unlock(void)
371 {
372 bt_mesh_mutex_unlock(&client_model_lock);
373 }
374
bt_mesh_client_init(struct bt_mesh_model * model)375 int bt_mesh_client_init(struct bt_mesh_model *model)
376 {
377 bt_mesh_client_internal_data_t *data = NULL;
378 bt_mesh_client_user_data_t *client = NULL;
379
380 if (!model || !model->op) {
381 BT_ERR("Invalid vendor client model");
382 return -EINVAL;
383 }
384
385 client = (bt_mesh_client_user_data_t *)model->user_data;
386 if (!client) {
387 BT_ERR("No vendor client context provided");
388 return -EINVAL;
389 }
390
391 if (!client->internal_data) {
392 data = bt_mesh_calloc(sizeof(bt_mesh_client_internal_data_t));
393 if (!data) {
394 BT_ERR("%s, Out of memory", __func__);
395 return -ENOMEM;
396 }
397
398 /* Init the client data queue */
399 sys_slist_init(&data->queue);
400
401 client->model = model;
402 client->internal_data = data;
403 } else {
404 bt_mesh_client_clear_list(client->internal_data);
405 }
406
407 bt_mesh_client_model_mutex_new();
408
409 return 0;
410 }
411
412 #if CONFIG_BLE_MESH_DEINIT
bt_mesh_client_deinit(struct bt_mesh_model * model)413 int bt_mesh_client_deinit(struct bt_mesh_model *model)
414 {
415 bt_mesh_client_user_data_t *client = NULL;
416
417 if (!model) {
418 BT_ERR("Invalid vendor client model");
419 return -EINVAL;
420 }
421
422 client = (bt_mesh_client_user_data_t *)model->user_data;
423 if (!client) {
424 BT_ERR("No vendor client context provided");
425 return -EINVAL;
426 }
427
428 if (client->internal_data) {
429 /* Remove items from the list */
430 bt_mesh_client_clear_list(client->internal_data);
431
432 /* Free the allocated internal data */
433 bt_mesh_free(client->internal_data);
434 client->internal_data = NULL;
435 }
436
437 bt_mesh_client_model_mutex_free();
438
439 return 0;
440 }
441 #endif /* CONFIG_BLE_MESH_DEINIT */
442
bt_mesh_client_free_node(bt_mesh_client_node_t * node)443 int bt_mesh_client_free_node(bt_mesh_client_node_t *node)
444 {
445 bt_mesh_client_internal_data_t *internal = NULL;
446 bt_mesh_client_user_data_t *client = NULL;
447
448 if (!node || !node->ctx.model) {
449 BT_ERR("Invalid client list item");
450 return -EINVAL;
451 }
452
453 client = (bt_mesh_client_user_data_t *)node->ctx.model->user_data;
454 if (!client) {
455 BT_ERR("Invalid client user data");
456 return -EINVAL;
457 }
458
459 internal = (bt_mesh_client_internal_data_t *)client->internal_data;
460 if (!internal) {
461 BT_ERR("Invalid client internal data");
462 return -EINVAL;
463 }
464
465 // Release the client node from the queue
466 bt_mesh_list_lock();
467 sys_slist_find_and_remove(&internal->queue, &node->client_node);
468 bt_mesh_list_unlock();
469 // Free the node
470 bt_mesh_free(node);
471
472 return 0;
473 }
474
bt_mesh_client_clear_list(void * data)475 int bt_mesh_client_clear_list(void *data)
476 {
477 bt_mesh_client_internal_data_t *internal = NULL;
478 bt_mesh_client_node_t *node = NULL;
479
480 if (!data) {
481 BT_ERR("%s, Invalid parameter", __func__);
482 return -EINVAL;
483 }
484
485 internal = (bt_mesh_client_internal_data_t *)data;
486
487 bt_mesh_list_lock();
488 while (!sys_slist_is_empty(&internal->queue)) {
489 node = (void *)sys_slist_get_not_empty(&internal->queue);
490 k_delayed_work_free(&node->timer);
491 bt_mesh_free(node);
492 }
493 bt_mesh_list_unlock();
494
495 return 0;
496 }
497
bt_mesh_set_client_model_role(struct bt_mesh_model * model,uint8_t role)498 int bt_mesh_set_client_model_role(struct bt_mesh_model *model, uint8_t role)
499 {
500 bt_mesh_client_user_data_t *client = NULL;
501
502 if (!model) {
503 BT_ERR("Invalid client model");
504 return -EINVAL;
505 }
506
507 client = (bt_mesh_client_user_data_t *)model->user_data;
508 if (!client) {
509 BT_ERR("Invalid client user data");
510 return -EINVAL;
511 }
512
513 if (role >= ROLE_NVAL) {
514 BT_ERR("Invalid client role 0x%02x", role);
515 return -EINVAL;
516 }
517
518 client->msg_role = role;
519 return 0;
520 }
521