diff --git a/components/bt/esp_ble_mesh/v1.1/api/models/esp_ble_mesh_dfu_model_api.c b/components/bt/esp_ble_mesh/v1.1/api/models/esp_ble_mesh_dfu_model_api.c index 84110f8ade..09cfd23da3 100644 --- a/components/bt/esp_ble_mesh/v1.1/api/models/esp_ble_mesh_dfu_model_api.c +++ b/components/bt/esp_ble_mesh/v1.1/api/models/esp_ble_mesh_dfu_model_api.c @@ -73,7 +73,8 @@ esp_err_t esp_ble_mesh_dfu_cli_img_send(esp_ble_mesh_dfu_cli_t *cli, arg.send_arg.xfer = xfer; return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_mesh_dfu_client_args_t), - NULL, NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); + btc_ble_mesh_dfu_client_arg_deep_copy, + btc_ble_mesh_dfu_client_arg_deep_free) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); } uint8_t esp_ble_mesh_dfu_cli_progress(esp_ble_mesh_dfu_cli_t *cli) diff --git a/components/bt/esp_ble_mesh/v1.1/btc/btc_ble_mesh_dfu_model.c b/components/bt/esp_ble_mesh/v1.1/btc/btc_ble_mesh_dfu_model.c index b6fe3ecdbd..d23497ef2f 100644 --- a/components/bt/esp_ble_mesh/v1.1/btc/btc_ble_mesh_dfu_model.c +++ b/components/bt/esp_ble_mesh/v1.1/btc/btc_ble_mesh_dfu_model.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2025-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -81,6 +81,11 @@ void btc_ble_mesh_dfu_client_arg_deep_copy(btc_msg_t *msg, void *p_dest, void *p } break; } + case BTC_BLE_MESH_ACT_DFU_CLIENT_IMG_SEND: + /* That will be freed when dfu completed or failed not on btc deep free */ + dst->send_arg.inputs =(struct esp_ble_mesh_blob_cli_inputs *) + dfu_targets_alloc((struct bt_mesh_blob_cli_inputs *)src->send_arg.inputs); + break; default: BT_DBG("%s, Unknown act %d", __func__, msg->act); break; diff --git a/components/bt/esp_ble_mesh/v1.1/dfu/dfu_cli.c b/components/bt/esp_ble_mesh/v1.1/dfu/dfu_cli.c index 21eea26d43..b4d1b90c9d 100644 --- a/components/bt/esp_ble_mesh/v1.1/dfu/dfu_cli.c +++ b/components/bt/esp_ble_mesh/v1.1/dfu/dfu_cli.c @@ -1,6 +1,6 @@ /* * SPDX-FileCopyrightText: 2020 Nordic Semiconductor ASA - * SPDX-FileContributor: 2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileContributor: 2025-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -64,6 +64,83 @@ static struct { sys_slist_t list; } dfu_req_list; +static struct bt_mesh_blob_cli_inputs cur_targets = {0}; + +/** + * inputs list must point to a list of bt_mesh_dfu_target nodes. + * That was required by dfu api + */ +void dfu_targets_free(void) +{ + sys_snode_t *n, *sn; + struct bt_mesh_dfu_target *target; + struct bt_mesh_blob_cli_inputs *inputs = &cur_targets; + + if (cur_targets.targets.head == NULL) { + return; + } + + SYS_SLIST_FOR_EACH_NODE_SAFE(&inputs->targets, n, sn) { + target = (struct bt_mesh_dfu_target *)n; + if (target->blob.pull) { + bt_mesh_free(target->blob.pull); + } + bt_mesh_free(target); + } + + inputs->app_idx = 0; + inputs->group = 0; + inputs->ttl = 0; + inputs->timeout_base = 0; + + sys_slist_init(&inputs->targets); +} + +struct bt_mesh_blob_cli_inputs *dfu_targets_alloc(struct bt_mesh_blob_cli_inputs *src) +{ + sys_snode_t *node; + struct bt_mesh_dfu_target *target_src = NULL; + struct bt_mesh_dfu_target *target_dst = NULL; + struct bt_mesh_blob_cli_inputs *dst = &cur_targets; + + if (cur_targets.targets.head != NULL || + cur_targets.targets.tail != NULL) { + BT_WARN("DFU targets busy"); + return NULL; + } + + dst->app_idx = src->app_idx; + dst->group = src->group; + dst->ttl = src->ttl; + dst->timeout_base = src->timeout_base; + + sys_slist_init(&dst->targets); + + SYS_SLIST_FOR_EACH_NODE(&src->targets, node) { + target_src = (struct bt_mesh_dfu_target *)node; + target_dst = bt_mesh_calloc(sizeof(struct bt_mesh_dfu_target)); + if (!target_dst) { + dfu_targets_free(); + return NULL; + } + memcpy(target_dst, target_src, sizeof(struct bt_mesh_dfu_target)); + if (target_dst->blob.pull) { + target_dst->blob.pull = bt_mesh_calloc(sizeof(struct bt_mesh_blob_target_pull)); + if (!target_dst->blob.pull) { + dfu_targets_free(); + return NULL; + } + memcpy(target_dst->blob.pull, target_src->blob.pull, sizeof(struct bt_mesh_blob_target_pull)); + } else { + target_dst->blob.pull = NULL; + } + target_dst->blob.n.next = NULL; + sys_slist_append(&dst->targets, &target_dst->blob.n); + } + + return dst; +} + static struct bt_mesh_dfu_target *target_get(struct bt_mesh_dfu_cli *cli, uint16_t addr) { @@ -277,6 +354,8 @@ static void dfu_failed(struct bt_mesh_dfu_cli *cli, if (cli->cb && cli->cb->ended) { cli->cb->ended(cli, reason); } + + dfu_targets_free(); } static int req_setup(struct bt_mesh_dfu_cli *cli, enum req type, @@ -341,8 +420,21 @@ static void blob_caps(struct bt_mesh_blob_cli *b, } cli->xfer.blob.block_size_log = caps->max_block_size_log; +#if CONFIG_BLE_MESH_LONG_PACKET + if (cli->xfer.blob.chunk_enh_params.long_pkt_cfg_used) { + cli->xfer.blob.chunk_size = MIN(caps->max_chunk_size, BLOB_TX_CHUNK_SIZE); + } else { +#if CONFIG_BLE_MESH_ALIGN_CHUNK_SIZE_TO_MAX_SEGMENT || \ + CONFIG_BLE_MESH_TX_BLOB_CHUNK_SIZE > BLOB_CHUNK_SIZE_MAX(BLE_MESH_EXT_TX_SDU_MAX) + cli->xfer.blob.chunk_size = MIN(caps->max_chunk_size, BLOB_CHUNK_SIZE_MAX(BLE_MESH_TX_SDU_MAX)); +#else + cli->xfer.blob.chunk_size = MIN(caps->max_chunk_size, + CONFIG_BLE_MESH_TX_BLOB_CHUNK_SIZE); +#endif + } +#else cli->xfer.blob.chunk_size = caps->max_chunk_size; - +#endif /* If mode is not already set and server reported it supports all modes * default to PUSH, otherwise set value reported by server. If mode * was set and server supports all modes, keep old value; set @@ -826,6 +918,7 @@ static void confirmed(struct bt_mesh_blob_cli *b) if (cli->cb && cli->cb->confirmed) { cli->cb->confirmed(cli); } + dfu_targets_free(); } else { dfu_failed(cli, BLE_MESH_DFU_ERR_INTERNAL); } @@ -887,15 +980,16 @@ static int handle_status(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx rsp->timeout_base = net_buf_simple_pull_le16(buf); rsp->blob_id = net_buf_simple_pull_le64(buf); rsp->img_idx = net_buf_simple_pull_u8(buf); - } else if (buf->len) { + } else if (buf->len == 0) { + rsp->ttl = 0U; + rsp->effect = BLE_MESH_DFU_EFFECT_NONE; + rsp->timeout_base = 0U; + rsp->blob_id = 0U; + rsp->img_idx = 0U; + } else { return -EINVAL; } - rsp->ttl = 0U; - rsp->effect = BLE_MESH_DFU_EFFECT_NONE; - rsp->timeout_base = 0U; - rsp->blob_id = 0U; - rsp->img_idx = 0U; } bt_mesh_dfu_client_cb_evt_to_btc(req->opcode, BTC_BLE_MESH_EVT_DFU_CLIENT_RECV_GET_RSP, req->dfu_cli->mod, &req->ctx, req->params, @@ -904,6 +998,7 @@ static int handle_status(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx bt_mesh_dfu_cli_rm_req_from_list(req); k_delayed_work_cancel(&req->timer); req_free(req); + return 0; } if (cli->op != BLE_MESH_DFU_OP_UPDATE_STATUS) { @@ -965,6 +1060,17 @@ static int handle_status(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx if (phase != BLE_MESH_DFU_PHASE_APPLYING && (target->effect == BLE_MESH_DFU_EFFECT_UNPROV || phase != BLE_MESH_DFU_PHASE_IDLE)) { + /** + * If user quickly enter the APPLY state from REFRESH, + * protocol may receive the last round of REFRESH info + * packets from the network. Therefore, in order to avoid + * misjudgment, the previous round of data packets are + * ignored here. + */ + if (phase == BLE_MESH_DFU_PHASE_VERIFY_OK) { + BT_DBG("MaybeReceivedOutdatedMsg"); + return 0; + } BT_WARN("Target 0x%04x in phase %u after apply", target->blob.addr, phase); target_failed(cli, target, BLE_MESH_DFU_ERR_WRONG_PHASE); @@ -1187,6 +1293,12 @@ static void dfu_cli_reset(struct bt_mesh_model *mod) { struct bt_mesh_dfu_cli *cli = mod->user_data; + if (cli->xfer.state == STATE_IDLE) { + return; + } + + dfu_targets_free(); + bt_mesh_dfu_req_list_free(); cli->req = NULL; cli->xfer.state = STATE_IDLE; diff --git a/components/bt/esp_ble_mesh/v1.1/dfu/dfu_srv.c b/components/bt/esp_ble_mesh/v1.1/dfu/dfu_srv.c index b36746a822..2181ced289 100644 --- a/components/bt/esp_ble_mesh/v1.1/dfu/dfu_srv.c +++ b/components/bt/esp_ble_mesh/v1.1/dfu/dfu_srv.c @@ -530,8 +530,13 @@ static void blob_end(struct bt_mesh_blob_srv *b, uint64_t id, bool success) BT_DBG("success: %u", success); if (!success) { - srv->update.phase = BLE_MESH_DFU_PHASE_TRANSFER_ERR; + /** + * Changed by Espressif, + * The xfer_failed must be executed before updating the state; + * otherwise, the end_cb inside xfer_failed will never be delivered. + */ xfer_failed(srv); + srv->update.phase = BLE_MESH_DFU_PHASE_TRANSFER_ERR; return; } diff --git a/components/bt/esp_ble_mesh/v1.1/include/mesh_v1.1/dfu/dfu_cli.h b/components/bt/esp_ble_mesh/v1.1/include/mesh_v1.1/dfu/dfu_cli.h index 306af5fbad..6688ff9142 100644 --- a/components/bt/esp_ble_mesh/v1.1/include/mesh_v1.1/dfu/dfu_cli.h +++ b/components/bt/esp_ble_mesh/v1.1/include/mesh_v1.1/dfu/dfu_cli.h @@ -261,6 +261,9 @@ struct bt_mesh_dfu_cli_xfer { const struct bt_mesh_dfu_cli_xfer_blob_params *blob_params; }; +void dfu_targets_free(void); +struct bt_mesh_blob_cli_inputs *dfu_targets_alloc(struct bt_mesh_blob_cli_inputs *src); + /** @brief Start distributing a DFU. * * Starts distribution of the firmware in the given slot to the list of DFU diff --git a/components/bt/esp_ble_mesh/v1.1/mbt/blob_cli.c b/components/bt/esp_ble_mesh/v1.1/mbt/blob_cli.c index 435ef3b5bc..4e47629f85 100644 --- a/components/bt/esp_ble_mesh/v1.1/mbt/blob_cli.c +++ b/components/bt/esp_ble_mesh/v1.1/mbt/blob_cli.c @@ -1,6 +1,6 @@ /* * SPDX-FileCopyrightText: 2020 Nordic Semiconductor ASA - * SPDX-FileContributor: 2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileContributor: 2025-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -53,6 +53,7 @@ _Static_assert((BLOB_BLOCK_STATUS_MSG_MAXLEN + BLE_MESH_MODEL_OP_LEN(BT_MESH_BLO NET_BUF_SIMPLE_DEFINE_STATIC(chunk_buf, BLOB_CHUNK_SDU_LEN(CHUNK_SIZE_MAX)); static bool chunk_sending; +static bool last_chunk_sent; struct block_status { enum bt_mesh_blob_status status; @@ -568,7 +569,8 @@ void blob_cli_broadcast_rsp(struct bt_mesh_blob_cli *cli, void blob_cli_broadcast_abort(struct bt_mesh_blob_cli *cli) { - if (!cli->tx.ctx.is_inited) { + if (!cli->tx.ctx.is_inited && + cli->state != BT_MESH_BLOB_CLI_STATE_SUSPENDED) { return; } @@ -656,8 +658,11 @@ static void xfer_start_tx(struct bt_mesh_blob_cli *cli, uint16_t dst) net_buf_simple_add_le32(&buf, cli->xfer->size); net_buf_simple_add_u8(&buf, cli->xfer->block_size_log); #if CONFIG_BLE_MESH_LONG_PACKET - /* todo: could let user select methold */ - net_buf_simple_add_le16(&buf, BLE_MESH_EXT_TX_SDU_MAX); + if (cli->xfer->chunk_enh_params.long_pkt_cfg_used) { + net_buf_simple_add_le16(&buf, BLE_MESH_EXT_TX_SDU_MAX); + } else { + net_buf_simple_add_le16(&buf, BLE_MESH_TX_SDU_MAX); + } #else net_buf_simple_add_le16(&buf, BLE_MESH_TX_SDU_MAX); #endif @@ -708,6 +713,13 @@ static void chunk_tx(struct bt_mesh_blob_cli *cli, uint16_t dst) chunk.offset = cli->xfer->chunk_size * cli->chunk_idx; chunk.data = net_buf_simple_add(&chunk_buf, chunk.size); + if ((cli->block.number == cli->block_count - 1) && + (cli->chunk_idx < cli->block.chunk_count)) { + last_chunk_sent = true; + } else { + last_chunk_sent = false; + } + err = cli->io->rd(cli->io, cli->xfer, &cli->block, &chunk); if (err || cli->state == BT_MESH_BLOB_CLI_STATE_NONE) { bt_mesh_blob_cli_cancel(cli); @@ -1616,6 +1628,16 @@ int bt_mesh_blob_cli_suspend(struct bt_mesh_blob_cli *cli) return -EINVAL; } + /* After the last chunk data is sent, if the server + * successfully receives the chunk, it will be in the + * complete state. At this time, if the client resumes + * from suspend and restarts the transmission, an error + * will occur, resulting in lost target */ + if (last_chunk_sent) { + BT_WARN("About to end, refuse to suspend"); + return -EINVAL; + } + cli->state = BT_MESH_BLOB_CLI_STATE_SUSPENDED; (void)k_work_cancel_delayable(&cli->tx.retry); cli->tx.ctx.is_inited = 0; diff --git a/components/bt/esp_ble_mesh/v1.1/mbt/blob_srv.c b/components/bt/esp_ble_mesh/v1.1/mbt/blob_srv.c index 88fe61ad0b..b0237f299f 100644 --- a/components/bt/esp_ble_mesh/v1.1/mbt/blob_srv.c +++ b/components/bt/esp_ble_mesh/v1.1/mbt/blob_srv.c @@ -751,6 +751,11 @@ static int handle_chunk(struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx, chunk.data = net_buf_simple_pull_mem(buf, chunk.size); chunk.offset = idx * srv->state.xfer.chunk_size; + if (!bt_mesh_blob_srv_is_busy(srv)) { + BT_ERR("Discord chunk(%d), because the blob server is not busy(%d)", idx, srv->phase); + return -EINVAL; + } + if (srv->phase == BT_MESH_BLOB_XFER_PHASE_WAITING_FOR_BLOCK || srv->phase == BT_MESH_BLOB_XFER_PHASE_COMPLETE) { // This Block is already complete received. @@ -777,9 +782,7 @@ static int handle_chunk(struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx, if (idx == srv->block.chunk_count - 1) { expected_size = srv->block.size % srv->state.xfer.chunk_size; - } - - if (expected_size == 0) { + } else { expected_size = srv->state.xfer.chunk_size; } @@ -804,7 +807,11 @@ static int handle_chunk(struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx, * complete. */ if (srv->phase == BT_MESH_BLOB_XFER_PHASE_SUSPENDED) { - phase_set(srv, BT_MESH_BLOB_XFER_PHASE_WAITING_FOR_CHUNK); + if(missing_chunks(&srv->block)) { + phase_set(srv, BT_MESH_BLOB_XFER_PHASE_WAITING_FOR_CHUNK); + } else { + phase_set(srv, BT_MESH_BLOB_XFER_PHASE_WAITING_FOR_BLOCK); + } } BT_INFO("%u/%u (%u bytes)", idx + 1, srv->block.chunk_count,