From c3faf5559a3e815f8d09489743dd902df12a97fb Mon Sep 17 00:00:00 2001 From: luoxu Date: Tue, 30 Dec 2025 11:40:21 +0800 Subject: [PATCH 1/6] fix(ble_mesh): Miscellaneous fixes for blob --- components/bt/esp_ble_mesh/v1.1/dfu/dfu_cli.c | 14 ++++++++------ components/bt/esp_ble_mesh/v1.1/mbt/blob_srv.c | 10 ++++++---- 2 files changed, 14 insertions(+), 10 deletions(-) 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..1a771ff0f8 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 @@ -887,15 +887,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 +905,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) { 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..dc61e14c09 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 @@ -777,9 +777,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 +802,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, From 3a8851422ff0068c7eafe865151444313e9638a3 Mon Sep 17 00:00:00 2001 From: luoxu Date: Tue, 30 Dec 2025 16:54:02 +0800 Subject: [PATCH 2/6] fix(ble_mesh): ensure blob client/server work correctly with varying chunk sizes --- components/bt/esp_ble_mesh/v1.1/dfu/dfu_cli.c | 15 ++++++++++++++- components/bt/esp_ble_mesh/v1.1/mbt/blob_cli.c | 7 +++++-- 2 files changed, 19 insertions(+), 3 deletions(-) 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 1a771ff0f8..bee0ecacdd 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 @@ -341,8 +341,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 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..985c46b92a 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 @@ -656,8 +656,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 From f959884b2f3d3644ff15230e7e498554e18e9e10 Mon Sep 17 00:00:00 2001 From: luoxu Date: Fri, 26 Sep 2025 21:07:15 +0800 Subject: [PATCH 3/6] fix(ble_mesh): fix blob transfer failure handling and chunk processing - Fix DFU server transfer error state ordering to ensure proper callback delivery - Add validation to discard chunks when blob server is not in busy state - Prevent chunk processing when server is not in active transfer state --- components/bt/esp_ble_mesh/v1.1/dfu/dfu_srv.c | 7 ++++++- components/bt/esp_ble_mesh/v1.1/mbt/blob_srv.c | 5 +++++ 2 files changed, 11 insertions(+), 1 deletion(-) 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/mbt/blob_srv.c b/components/bt/esp_ble_mesh/v1.1/mbt/blob_srv.c index dc61e14c09..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. From 8c8b43f564470c3a184a34d6bbcb41ff48113606 Mon Sep 17 00:00:00 2001 From: luoxu Date: Mon, 5 Jan 2026 21:16:36 +0800 Subject: [PATCH 4/6] fix(ble_mesh): added internal buffer to store dfu targets The protocol may still receive DFU-related packets from the network (such as forwarded or retransmitted ones) after DFU ends, while the user might release the targets immediately after DFU completion. However, since the list head is not empty, the protocol could access already-freed memory when iterating through the targets. --- .../api/models/esp_ble_mesh_dfu_model_api.c | 3 +- .../v1.1/btc/btc_ble_mesh_dfu_model.c | 7 +- components/bt/esp_ble_mesh/v1.1/dfu/dfu_cli.c | 88 ++++++++++++++++++- .../v1.1/include/mesh_v1.1/dfu/dfu_cli.h | 3 + 4 files changed, 98 insertions(+), 3 deletions(-) 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 bee0ecacdd..78c7219f04 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, @@ -839,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); } @@ -1202,6 +1282,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/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 From e039b33bb172370f5d7411f79643f1648484e059 Mon Sep 17 00:00:00 2001 From: luoxu Date: Mon, 5 Jan 2026 21:18:13 +0800 Subject: [PATCH 5/6] feat(ble_mesh): refuse to suspend dfu client when the last chunk sent --- .../bt/esp_ble_mesh/v1.1/mbt/blob_cli.c | 23 +++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) 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 985c46b92a..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; } @@ -711,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); @@ -1619,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; From bc2d56b72a1d9a4c5d75739b167e5ea40db3844c Mon Sep 17 00:00:00 2001 From: luoxu Date: Mon, 5 Jan 2026 21:19:50 +0800 Subject: [PATCH 6/6] feat(ble_mesh): ignored the previous server status to avoid drop target by mistake --- components/bt/esp_ble_mesh/v1.1/dfu/dfu_cli.c | 11 +++++++++++ 1 file changed, 11 insertions(+) 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 78c7219f04..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 @@ -1060,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);