diff --git a/components/bt/esp_ble_mesh/btc/btc_ble_mesh_prov.c b/components/bt/esp_ble_mesh/btc/btc_ble_mesh_prov.c index 28e64b767..067a35ded 100644 --- a/components/bt/esp_ble_mesh/btc/btc_ble_mesh_prov.c +++ b/components/bt/esp_ble_mesh/btc/btc_ble_mesh_prov.c @@ -1944,8 +1944,8 @@ void btc_ble_mesh_model_call_handler(btc_msg_t *msg) break; } case BTC_BLE_MESH_ACT_SERVER_MODEL_SEND: { - /* arg->model_send.length contains opcode & message, 8 is used for TransMIC */ - struct net_buf_simple *buf = bt_mesh_alloc_buf(arg->model_send.length + 8); + /* arg->model_send.length contains opcode & message, 4 is used for TransMIC */ + struct net_buf_simple *buf = bt_mesh_alloc_buf(arg->model_send.length + 4); if (!buf) { BT_ERR("%s, Failed to allocate memory", __func__); break; @@ -1962,8 +1962,8 @@ void btc_ble_mesh_model_call_handler(btc_msg_t *msg) } case BTC_BLE_MESH_ACT_CLIENT_MODEL_SEND: { bt_mesh_role_param_t common = {0}; - /* arg->model_send.length contains opcode & message, 8 is used for TransMIC */ - struct net_buf_simple *buf = bt_mesh_alloc_buf(arg->model_send.length + 8); + /* arg->model_send.length contains opcode & message, 4 is used for TransMIC */ + struct net_buf_simple *buf = bt_mesh_alloc_buf(arg->model_send.length + 4); if (!buf) { BT_ERR("%s, Failed to allocate memory", __func__); break; diff --git a/components/bt/esp_ble_mesh/mesh_core/net.c b/components/bt/esp_ble_mesh/mesh_core/net.c index a06d62e5a..778092243 100644 --- a/components/bt/esp_ble_mesh/mesh_core/net.c +++ b/components/bt/esp_ble_mesh/mesh_core/net.c @@ -1394,6 +1394,24 @@ static bool ready_to_recv(void) return false; } +static bool ignore_net_msg(u16_t src, u16_t dst) +{ + if (IS_ENABLED(CONFIG_BLE_MESH_PROVISIONER) && + bt_mesh_is_provisioner_en() && + BLE_MESH_ADDR_IS_UNICAST(dst) && + bt_mesh_elem_find(dst)) { + /* If the destination address of the message is the element + * address of Provisioner, but Provisioner fails to find the + * node in its provisioning database, then this message will + * be ignored. + */ + if (!bt_mesh_provisioner_get_node_with_addr(src)) { + return true; + } + } + return false; +} + void bt_mesh_net_recv(struct net_buf_simple *data, s8_t rssi, enum bt_mesh_net_if net_if) { @@ -1411,6 +1429,10 @@ void bt_mesh_net_recv(struct net_buf_simple *data, s8_t rssi, return; } + if (ignore_net_msg(rx.ctx.addr, rx.ctx.recv_dst)) { + return; + } + /* Save the state so the buffer can later be relayed */ net_buf_simple_save(&buf, &state); diff --git a/components/bt/esp_ble_mesh/mesh_core/provisioner_main.c b/components/bt/esp_ble_mesh/mesh_core/provisioner_main.c index dcf3421f4..f60d9e22a 100644 --- a/components/bt/esp_ble_mesh/mesh_core/provisioner_main.c +++ b/components/bt/esp_ble_mesh/mesh_core/provisioner_main.c @@ -21,6 +21,7 @@ #include "access.h" #include "settings.h" #include "friend.h" +#include "transport.h" #include "mesh_common.h" #include "proxy_client.h" #include "provisioner_prov.h" @@ -380,7 +381,6 @@ int bt_mesh_provisioner_provision(const bt_mesh_addr_t *addr, const u8_t uuid[16 static int provisioner_remove_node(u16_t index, bool erase) { struct bt_mesh_node *node = NULL; - struct bt_mesh_rpl *rpl = NULL; bool is_prov = false; int i; @@ -398,17 +398,12 @@ static int provisioner_remove_node(u16_t index, bool erase) /* Reset corresponding network cache when reset the node */ bt_mesh_msg_cache_clear(node->unicast_addr, node->element_num); - /* Reset corresponding rpl when removing the node */ - for (i = 0; i < ARRAY_SIZE(bt_mesh.rpl); i++) { - rpl = &bt_mesh.rpl[i]; - if (rpl->src >= node->unicast_addr && - rpl->src < node->unicast_addr + node->element_num) { - memset(rpl, 0, sizeof(struct bt_mesh_rpl)); - - if (IS_ENABLED(CONFIG_BLE_MESH_SETTINGS)) { - bt_mesh_clear_rpl_single(node->unicast_addr); - } - } + /* Reset corresponding transport info when removing the node */ + for (i = 0; i < node->element_num; i++) { + bt_mesh_rx_reset_single(node->unicast_addr + i); + } + for (i = 0; i < node->element_num; i++) { + bt_mesh_tx_reset_single(node->unicast_addr + i); } if (IS_ENABLED(CONFIG_BLE_MESH_FRIEND)) { diff --git a/components/bt/esp_ble_mesh/mesh_core/transport.c b/components/bt/esp_ble_mesh/mesh_core/transport.c index 3d78e4365..bb8a1cc54 100644 --- a/components/bt/esp_ble_mesh/mesh_core/transport.c +++ b/components/bt/esp_ble_mesh/mesh_core/transport.c @@ -125,6 +125,19 @@ static void bt_mesh_tx_seg_unlock(void) bt_mesh_mutex_unlock(&tx_seg_lock); } +u8_t bt_mesh_get_seg_retrans_num(void) +{ + return SEG_RETRANSMIT_ATTEMPTS; +} + +s32_t bt_mesh_get_seg_retrans_timeout(u8_t ttl) +{ + struct seg_tx tx = { + .ttl = ttl, + }; + return SEG_RETRANSMIT_TIMEOUT(&tx); +} + void bt_mesh_set_hb_sub_dst(u16_t addr) { hb_sub_dst = addr; @@ -1130,13 +1143,33 @@ static void seg_rx_reset(struct seg_rx *rx, bool full_reset) } } +static u32_t incomplete_timeout(struct seg_rx *rx) +{ + u32_t timeout = 0U; + u8_t ttl = 0U; + + if (rx->ttl == BLE_MESH_TTL_DEFAULT) { + ttl = bt_mesh_default_ttl_get(); + } else { + ttl = rx->ttl; + } + + /* "The incomplete timer shall be set to a minimum of 10 seconds." */ + timeout = K_SECONDS(10); + + /* The less segments being received, the shorter timeout will be used. */ + timeout += K_MSEC(ttl * popcount(rx->block) * 100U); + + return MIN(timeout, K_SECONDS(60)); +} + static void seg_ack(struct k_work *work) { struct seg_rx *rx = CONTAINER_OF(work, struct seg_rx, ack); BT_DBG("rx %p", rx); - if (k_uptime_get_32() - rx->last > K_SECONDS(60)) { + if (k_uptime_get_32() - rx->last > incomplete_timeout(rx)) { BT_WARN("Incomplete timer expired"); seg_rx_reset(rx, false); return; @@ -1584,6 +1617,50 @@ void bt_mesh_tx_reset(void) } } +#if CONFIG_BLE_MESH_PROVISIONER +void bt_mesh_rx_reset_single(u16_t src) +{ + int i; + + if (!BLE_MESH_ADDR_IS_UNICAST(src)) { + return; + } + + for (i = 0; i < ARRAY_SIZE(seg_rx); i++) { + struct seg_rx *rx = &seg_rx[i]; + if (src == rx->src) { + seg_rx_reset(rx, true); + } + } + + for (i = 0; i < ARRAY_SIZE(bt_mesh.rpl); i++) { + struct bt_mesh_rpl *rpl = &bt_mesh.rpl[i]; + if (src == rpl->src) { + memset(rpl, 0, sizeof(struct bt_mesh_rpl)); + if (IS_ENABLED(CONFIG_BLE_MESH_SETTINGS)) { + bt_mesh_clear_rpl_single(src); + } + } + } +} + +void bt_mesh_tx_reset_single(u16_t dst) +{ + int i; + + if (!BLE_MESH_ADDR_IS_UNICAST(dst)) { + return; + } + + for (i = 0; i < ARRAY_SIZE(seg_tx); i++) { + struct seg_tx *tx = &seg_tx[i]; + if (dst == tx->dst) { + seg_tx_reset(tx); + } + } +} +#endif /* CONFIG_BLE_MESH_PROVISIONER */ + void bt_mesh_trans_init(void) { int i; diff --git a/components/bt/esp_ble_mesh/mesh_core/transport.h b/components/bt/esp_ble_mesh/mesh_core/transport.h index 028b62be8..31356c46b 100644 --- a/components/bt/esp_ble_mesh/mesh_core/transport.h +++ b/components/bt/esp_ble_mesh/mesh_core/transport.h @@ -80,6 +80,10 @@ struct bt_mesh_ctl_friend_sub_confirm { u8_t xact; } __packed; +u8_t bt_mesh_get_seg_retrans_num(void); + +s32_t bt_mesh_get_seg_retrans_timeout(u8_t ttl); + void bt_mesh_set_hb_sub_dst(u16_t addr); struct bt_mesh_app_key *bt_mesh_app_key_find(u16_t app_idx); @@ -88,6 +92,8 @@ bool bt_mesh_tx_in_progress(void); void bt_mesh_rx_reset(void); void bt_mesh_tx_reset(void); +void bt_mesh_rx_reset_single(u16_t src); +void bt_mesh_tx_reset_single(u16_t dst); int bt_mesh_ctl_send(struct bt_mesh_net_tx *tx, u8_t ctl_op, void *data, size_t data_len, u64_t *seq_auth, diff --git a/components/bt/esp_ble_mesh/mesh_models/client/client_common.c b/components/bt/esp_ble_mesh/mesh_models/client/client_common.c index 13b2896cb..a3f68834b 100644 --- a/components/bt/esp_ble_mesh/mesh_models/client/client_common.c +++ b/components/bt/esp_ble_mesh/mesh_models/client/client_common.c @@ -17,9 +17,15 @@ #include "mesh.h" #include "mesh_main.h" +#include "transport.h" +#include "foundation.h" #include "client_common.h" #include "mesh_common.h" +#define UNSEG_ACCESS_MSG_MAX_LEN 11 /* 11 octets (Opcode + Payload), 4 octets TransMIC */ +#define SEG_ACCESS_MSG_SEG_LEN 12 /* 12 * 32 = 384 octets (Opcode + Payload + TransMIC) */ +#define HCI_TIME_FOR_START_ADV K_MSEC(5) /* Three adv related hci commands may take 4 ~ 5ms */ + static bt_mesh_client_node_t *bt_mesh_client_pick_node(sys_slist_t *list, u16_t tx_dst) { bt_mesh_client_node_t *node = NULL; @@ -156,6 +162,100 @@ static u32_t bt_mesh_client_get_status_op(const bt_mesh_client_op_pair_t *op_pai return 0; } +static s32_t bt_mesh_get_adv_duration(void) +{ + u16_t duration, adv_int; + u8_t xmit; + + xmit = bt_mesh_net_transmit_get(); /* Network transmit */ + adv_int = BLE_MESH_TRANSMIT_INT(xmit); + duration = (BLE_MESH_TRANSMIT_COUNT(xmit) + 1) * (adv_int + 10); + + return (s32_t)duration; +} + +static s32_t bt_mesh_client_calc_timeout(struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *msg, + u32_t opcode, s32_t timeout) +{ + s32_t seg_retrans_to, duration, time; + u8_t seg_count, seg_retrans_num; + u8_t mic_size; + bool need_seg; + + if (msg->len > UNSEG_ACCESS_MSG_MAX_LEN || ctx->send_rel) { + need_seg = true; /* Needs segmentation */ + } + + mic_size = (need_seg && net_buf_simple_tailroom(msg) >= 8U) ? 8U : 4U; + + if (need_seg) { + /* Based on the message length, calculate how many segments are needed. + * All the messages sent from here are access messages. + */ + seg_retrans_num = bt_mesh_get_seg_retrans_num(); + seg_retrans_to = bt_mesh_get_seg_retrans_timeout(ctx->send_ttl); + seg_count = (msg->len + mic_size - 1) / 12U + 1U; + + duration = bt_mesh_get_adv_duration(); + + /* Currenlty only consider the time consumption of the same segmented + * messages, but if there are other messages between any two retrans- + * missions of the same segmented messages, then the whole time will + * be longer. + */ + if (duration + HCI_TIME_FOR_START_ADV < seg_retrans_to) { + s32_t seg_duration = seg_count * (duration + HCI_TIME_FOR_START_ADV); + time = (seg_duration + seg_retrans_to) * (seg_retrans_num - 1) + seg_duration; + } else { + /* If the duration is bigger than the segment retransmit timeout + * value. In this situation, the segment retransmit timeout value + * may need to be optimized based on the "Network Transmit" value. + */ + time = seg_count * (duration + HCI_TIME_FOR_START_ADV) * seg_retrans_num; + } + + BT_INFO("Original timeout %dms, calculated timeout %dms", timeout, time); + + if (time < timeout) { + /* If the calculated time is smaller than the input timeout value, + * then use the original timeout value. + */ + time = timeout; + } + } else { + /* For unsegmented access messages, directly use the timeout + * value from the application layer. + */ + time = timeout; + } + + BT_INFO("Client message 0x%08x with timeout %dms", opcode, time); + + return time; +} + +static void msg_send_start(u16_t duration, int err, void *cb_data) +{ + bt_mesh_client_node_t *node = cb_data; + + BT_DBG("%s, duration %ums", __func__, duration); + + if (err) { + if (!k_delayed_work_free(&node->timer)) { + bt_mesh_client_free_node(node); + } + return; + } + + k_delayed_work_submit(&node->timer, node->timeout); +} + +static const struct bt_mesh_send_cb send_cb = { + .start = msg_send_start, + .end = NULL, +}; + int bt_mesh_client_send_msg(struct bt_mesh_model *model, u32_t opcode, struct bt_mesh_msg_ctx *ctx, @@ -166,7 +266,7 @@ int bt_mesh_client_send_msg(struct bt_mesh_model *model, void *cb_data) { bt_mesh_client_internal_data_t *internal = NULL; - bt_mesh_client_user_data_t *cli = NULL; + bt_mesh_client_user_data_t *client = NULL; bt_mesh_client_node_t *node = NULL; int err = 0; @@ -175,43 +275,80 @@ int bt_mesh_client_send_msg(struct bt_mesh_model *model, return -EINVAL; } - cli = (bt_mesh_client_user_data_t *)model->user_data; - __ASSERT(cli, "Invalid client value when sent client msg."); - internal = (bt_mesh_client_internal_data_t *)cli->internal_data; - __ASSERT(internal, "Invalid internal value when sent client msg."); + client = (bt_mesh_client_user_data_t *)model->user_data; + if (!client) { + BT_ERR("%s, Invalid client user data", __func__); + return -EINVAL; + } + + internal = (bt_mesh_client_internal_data_t *)client->internal_data; + if (!internal) { + BT_ERR("%s, Invalid client internal data", __func__); + return -EINVAL; + } + + if (ctx->addr == BLE_MESH_ADDR_UNASSIGNED) { + BT_ERR("%s, Invalid DST 0x%04x", __func__, ctx->addr); + return -EINVAL; + } if (!need_ack) { /* If this is an unack message, send it directly. */ return bt_mesh_model_send(model, ctx, msg, cb, cb_data); } + if (!BLE_MESH_ADDR_IS_UNICAST(ctx->addr)) { + /* If an acknowledged message is not sent to a unicast address, + * for example to a group/virtual address, then all the + * corresponding responses will be treated as publish messages. + * And no timeout will be used for the message. + */ + return bt_mesh_model_send(model, ctx, msg, cb, cb_data); + } + + if (!timer_handler) { + BT_ERR("%s, Invalid timeout handler", __func__); + return -EINVAL; + } + if (bt_mesh_client_check_node_in_list(&internal->queue, ctx->addr)) { BT_ERR("%s, Busy sending message to DST 0x%04x", __func__, ctx->addr); - err = -EBUSY; - } else { - /* Don't forget to free the node in the timeout (timer_handler) function. */ - node = (bt_mesh_client_node_t *)bt_mesh_calloc(sizeof(bt_mesh_client_node_t)); - if (!node) { - BT_ERR("%s, Failed to allocate memory", __func__); - return -ENOMEM; - } - memcpy(&node->ctx, ctx, sizeof(struct bt_mesh_msg_ctx)); - node->ctx.model = model; - node->opcode = opcode; - if ((node->op_pending = bt_mesh_client_get_status_op(cli->op_pair, cli->op_pair_size, opcode)) == 0) { - BT_ERR("%s, Not found the status opcode in the op_pair list", __func__); - bt_mesh_free(node); - return -EINVAL; - } - if ((err = bt_mesh_model_send(model, ctx, msg, cb, cb_data)) != 0) { - bt_mesh_free(node); - } else { - bt_mesh_list_lock(); - sys_slist_append(&internal->queue, &node->client_node); - bt_mesh_list_unlock(); - k_delayed_work_init(&node->timer, timer_handler); - k_delayed_work_submit(&node->timer, timeout ? timeout : CONFIG_BLE_MESH_CLIENT_MSG_TIMEOUT); - } + return -EBUSY; + } + + /* Don't forget to free the node in the timeout (timer_handler) function. */ + node = (bt_mesh_client_node_t *)bt_mesh_calloc(sizeof(bt_mesh_client_node_t)); + if (!node) { + BT_ERR("%s, Failed to allocate memory", __func__); + return -ENOMEM; + } + + memcpy(&node->ctx, ctx, sizeof(struct bt_mesh_msg_ctx)); + node->ctx.model = model; + node->opcode = opcode; + node->op_pending = bt_mesh_client_get_status_op(client->op_pair, client->op_pair_size, opcode); + if (node->op_pending == 0U) { + BT_ERR("%s, Not found the status opcode in the op_pair list", __func__); + bt_mesh_free(node); + return -EINVAL; + } + node->timeout = bt_mesh_client_calc_timeout(ctx, msg, opcode, timeout ? timeout : CONFIG_BLE_MESH_CLIENT_MSG_TIMEOUT); + + k_delayed_work_init(&node->timer, timer_handler); + + bt_mesh_list_lock(); + sys_slist_append(&internal->queue, &node->client_node); + bt_mesh_list_unlock(); + + /* "bt_mesh_model_send" will post the mesh packet to the mesh adv queue. + * Due to the higher priority of adv_thread (than btc task), we need to + * send the packet after the list item "node" is initialized properly. + */ + err = bt_mesh_model_send(model, ctx, msg, &send_cb, node); + if (err) { + BT_ERR("Failed to send client message 0x%08x", node->opcode); + k_delayed_work_free(&node->timer); + bt_mesh_client_free_node(node); } return err; diff --git a/components/bt/esp_ble_mesh/mesh_models/client/include/client_common.h b/components/bt/esp_ble_mesh/mesh_models/client/include/client_common.h index 0e337d34b..ddf743b32 100644 --- a/components/bt/esp_ble_mesh/mesh_models/client/include/client_common.h +++ b/components/bt/esp_ble_mesh/mesh_models/client/include/client_common.h @@ -65,6 +65,7 @@ typedef struct { struct bt_mesh_msg_ctx ctx; /* Message context */ u32_t opcode; /* Message opcode */ u32_t op_pending; /* Expected status message opcode */ + s32_t timeout; /* Calculated message timeout value */ struct k_delayed_work timer; /* Time used to get response. Only for internal use. */ } bt_mesh_client_node_t;