From 8417c7006d2e021f5231cd16f5ab3f68659be1ac Mon Sep 17 00:00:00 2001 From: luoxu Date: Tue, 19 Nov 2024 17:51:49 +0800 Subject: [PATCH 1/4] feat(ble_mesh): Device Firmware Update (Zephyr v4.0.0) --- components/bt/esp_ble_mesh/v1.1/dfu/dfd.h | 29 + components/bt/esp_ble_mesh/v1.1/dfu/dfd_srv.c | 1306 +++++++++++++ .../esp_ble_mesh/v1.1/dfu/dfd_srv_internal.h | 47 + components/bt/esp_ble_mesh/v1.1/dfu/dfu.h | 44 + components/bt/esp_ble_mesh/v1.1/dfu/dfu_cli.c | 1265 +++++++++++++ .../bt/esp_ble_mesh/v1.1/dfu/dfu_metadata.c | 102 + .../bt/esp_ble_mesh/v1.1/dfu/dfu_slot.c | 402 ++++ .../bt/esp_ble_mesh/v1.1/dfu/dfu_slot.h | 135 ++ components/bt/esp_ble_mesh/v1.1/dfu/dfu_srv.c | 617 ++++++ .../v1.1/include/mesh_v1.1/dfu/dfd.h | 123 ++ .../v1.1/include/mesh_v1.1/dfu/dfd_srv.h | 313 ++++ .../v1.1/include/mesh_v1.1/dfu/dfu.h | 171 ++ .../v1.1/include/mesh_v1.1/dfu/dfu_cli.h | 409 ++++ .../v1.1/include/mesh_v1.1/dfu/dfu_metadata.h | 115 ++ .../v1.1/include/mesh_v1.1/dfu/dfu_srv.h | 266 +++ .../v1.1/include/mesh_v1.1/mbt/blob.h | 262 +++ .../v1.1/include/mesh_v1.1/mbt/blob_cli.h | 454 +++++ .../v1.1/include/mesh_v1.1/mbt/blob_srv.h | 220 +++ components/bt/esp_ble_mesh/v1.1/mbt/blob.h | 154 ++ .../bt/esp_ble_mesh/v1.1/mbt/blob_cli.c | 1658 +++++++++++++++++ .../bt/esp_ble_mesh/v1.1/mbt/blob_srv.c | 1013 ++++++++++ 21 files changed, 9105 insertions(+) create mode 100644 components/bt/esp_ble_mesh/v1.1/dfu/dfd.h create mode 100644 components/bt/esp_ble_mesh/v1.1/dfu/dfd_srv.c create mode 100644 components/bt/esp_ble_mesh/v1.1/dfu/dfd_srv_internal.h create mode 100644 components/bt/esp_ble_mesh/v1.1/dfu/dfu.h create mode 100644 components/bt/esp_ble_mesh/v1.1/dfu/dfu_cli.c create mode 100644 components/bt/esp_ble_mesh/v1.1/dfu/dfu_metadata.c create mode 100644 components/bt/esp_ble_mesh/v1.1/dfu/dfu_slot.c create mode 100644 components/bt/esp_ble_mesh/v1.1/dfu/dfu_slot.h create mode 100644 components/bt/esp_ble_mesh/v1.1/dfu/dfu_srv.c create mode 100644 components/bt/esp_ble_mesh/v1.1/include/mesh_v1.1/dfu/dfd.h create mode 100644 components/bt/esp_ble_mesh/v1.1/include/mesh_v1.1/dfu/dfd_srv.h create mode 100644 components/bt/esp_ble_mesh/v1.1/include/mesh_v1.1/dfu/dfu.h create mode 100644 components/bt/esp_ble_mesh/v1.1/include/mesh_v1.1/dfu/dfu_cli.h create mode 100644 components/bt/esp_ble_mesh/v1.1/include/mesh_v1.1/dfu/dfu_metadata.h create mode 100644 components/bt/esp_ble_mesh/v1.1/include/mesh_v1.1/dfu/dfu_srv.h create mode 100644 components/bt/esp_ble_mesh/v1.1/include/mesh_v1.1/mbt/blob.h create mode 100644 components/bt/esp_ble_mesh/v1.1/include/mesh_v1.1/mbt/blob_cli.h create mode 100644 components/bt/esp_ble_mesh/v1.1/include/mesh_v1.1/mbt/blob_srv.h create mode 100644 components/bt/esp_ble_mesh/v1.1/mbt/blob.h create mode 100644 components/bt/esp_ble_mesh/v1.1/mbt/blob_cli.c create mode 100644 components/bt/esp_ble_mesh/v1.1/mbt/blob_srv.c diff --git a/components/bt/esp_ble_mesh/v1.1/dfu/dfd.h b/components/bt/esp_ble_mesh/v1.1/dfu/dfd.h new file mode 100644 index 0000000000..3af9af7214 --- /dev/null +++ b/components/bt/esp_ble_mesh/v1.1/dfu/dfd.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2020 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define BT_MESH_DFD_OP_RECEIVERS_ADD BT_MESH_MODEL_OP_2(0x83, 0x11) +#define BT_MESH_DFD_OP_RECEIVERS_DELETE_ALL BT_MESH_MODEL_OP_2(0x83, 0x12) +#define BT_MESH_DFD_OP_RECEIVERS_STATUS BT_MESH_MODEL_OP_2(0x83, 0x13) +#define BT_MESH_DFD_OP_RECEIVERS_GET BT_MESH_MODEL_OP_2(0x83, 0x14) +#define BT_MESH_DFD_OP_RECEIVERS_LIST BT_MESH_MODEL_OP_2(0x83, 0x15) +#define BT_MESH_DFD_OP_CAPABILITIES_GET BT_MESH_MODEL_OP_2(0x83, 0x16) +#define BT_MESH_DFD_OP_CAPABILITIES_STATUS BT_MESH_MODEL_OP_2(0x83, 0x17) +#define BT_MESH_DFD_OP_GET BT_MESH_MODEL_OP_2(0x83, 0x18) +#define BT_MESH_DFD_OP_START BT_MESH_MODEL_OP_2(0x83, 0x19) +#define BT_MESH_DFD_OP_SUSPEND BT_MESH_MODEL_OP_2(0x83, 0x1a) +#define BT_MESH_DFD_OP_CANCEL BT_MESH_MODEL_OP_2(0x83, 0x1b) +#define BT_MESH_DFD_OP_APPLY BT_MESH_MODEL_OP_2(0x83, 0x1c) +#define BT_MESH_DFD_OP_STATUS BT_MESH_MODEL_OP_2(0x83, 0x1d) +#define BT_MESH_DFD_OP_UPLOAD_GET BT_MESH_MODEL_OP_2(0x83, 0x1e) +#define BT_MESH_DFD_OP_UPLOAD_START BT_MESH_MODEL_OP_2(0x83, 0x1f) +#define BT_MESH_DFD_OP_UPLOAD_START_OOB BT_MESH_MODEL_OP_2(0x83, 0x20) +#define BT_MESH_DFD_OP_UPLOAD_CANCEL BT_MESH_MODEL_OP_2(0x83, 0x21) +#define BT_MESH_DFD_OP_UPLOAD_STATUS BT_MESH_MODEL_OP_2(0x83, 0x22) +#define BT_MESH_DFD_OP_FW_GET BT_MESH_MODEL_OP_2(0x83, 0x23) +#define BT_MESH_DFD_OP_FW_GET_BY_INDEX BT_MESH_MODEL_OP_2(0x83, 0x24) +#define BT_MESH_DFD_OP_FW_DELETE BT_MESH_MODEL_OP_2(0x83, 0x25) +#define BT_MESH_DFD_OP_FW_DELETE_ALL BT_MESH_MODEL_OP_2(0x83, 0x26) +#define BT_MESH_DFD_OP_FW_STATUS BT_MESH_MODEL_OP_2(0x83, 0x27) diff --git a/components/bt/esp_ble_mesh/v1.1/dfu/dfd_srv.c b/components/bt/esp_ble_mesh/v1.1/dfu/dfd_srv.c new file mode 100644 index 0000000000..4a395a175b --- /dev/null +++ b/components/bt/esp_ble_mesh/v1.1/dfu/dfd_srv.c @@ -0,0 +1,1306 @@ +/* + * Copyright (c) 2020 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include "dfu_slot.h" +#include "dfd.h" +#include "dfu.h" +#include "dfd_srv_internal.h" +#include "net.h" +#include "transport.h" + +#define LOG_LEVEL CONFIG_BT_MESH_DFU_LOG_LEVEL +#include +LOG_MODULE_REGISTER(bt_mesh_dfd_srv); + +#define ERASE_BLOCK_SIZE DT_PROP(DT_CHOSEN(zephyr_flash), erase_block_size) + +#define DFD_UPLOAD_STATUS_MSG_MAXLEN (5 + CONFIG_BT_MESH_DFU_FWID_MAXLEN) + +BUILD_ASSERT((DFD_UPLOAD_STATUS_MSG_MAXLEN + BT_MESH_MODEL_OP_LEN(BT_MESH_DFD_OP_UPLOAD_STATUS) + + BT_MESH_MIC_SHORT) <= BT_MESH_TX_SDU_MAX, + "The Firmware Distribution Upload Status message does not fit into the maximum " + "outgoing SDU size."); + +#define DFD_UPLOAD_START_MSG_MAXLEN (16 + CONFIG_BT_MESH_DFU_FWID_MAXLEN + \ + CONFIG_BT_MESH_DFU_METADATA_MAXLEN) + +BUILD_ASSERT((DFD_UPLOAD_START_MSG_MAXLEN + BT_MESH_MODEL_OP_LEN(BT_MESH_DFD_OP_UPLOAD_START) + + BT_MESH_MIC_SHORT) <= BT_MESH_RX_SDU_MAX, + "The Firmware Distribution Upload Start message does not fit into the maximum " + "incoming SDU size."); + +#define DFD_RECEIVERS_LIST_MSG_MAXLEN (4 + CONFIG_BT_MESH_DFD_SRV_TARGETS_MAX * 5) + +BUILD_ASSERT((DFD_RECEIVERS_LIST_MSG_MAXLEN + BT_MESH_MODEL_OP_LEN(BT_MESH_DFD_OP_RECEIVERS_LIST) + + BT_MESH_MIC_SHORT) <= BT_MESH_TX_SDU_MAX, + "The Firmware Distribution Receivers List message does not fit into the maximum " + "outgoing SDU size."); + +#define DFD_RECEIVERS_ADD_MSG_MAXLEN (CONFIG_BT_MESH_DFD_SRV_TARGETS_MAX * 3) + +BUILD_ASSERT((DFD_RECEIVERS_ADD_MSG_MAXLEN + BT_MESH_MODEL_OP_LEN(BT_MESH_DFD_OP_RECEIVERS_ADD) + + BT_MESH_MIC_SHORT) <= BT_MESH_RX_SDU_MAX, + "The Firmware Distribution Receivers Add message does not fit into the maximum " + "incoming SDU size."); + +struct slot_search_ctx { + off_t offset; + size_t size; + bool failed; +}; + +static void dfd_phase_set(struct bt_mesh_dfd_srv *srv, + enum bt_mesh_dfd_phase new_phase) +{ + srv->phase = new_phase; + + if (srv->cb && srv->cb->phase) { + srv->cb->phase(srv, srv->phase); + } +} + +static struct bt_mesh_dfu_target *target_get(struct bt_mesh_dfd_srv *srv, + uint16_t addr) +{ + for (int i = 0; i < srv->target_cnt; ++i) { + if (addr == srv->targets[i].blob.addr) { + return &srv->targets[i]; + } + } + + return NULL; +} + +static bool is_busy(const struct bt_mesh_dfd_srv *srv) +{ + return srv->phase == BT_MESH_DFD_PHASE_TRANSFER_ACTIVE || + srv->phase == BT_MESH_DFD_PHASE_TRANSFER_SUCCESS || + srv->phase == BT_MESH_DFD_PHASE_APPLYING_UPDATE; +} + +static bool upload_is_busy(const struct bt_mesh_dfd_srv *srv) +{ + return bt_mesh_blob_srv_is_busy(&srv->upload.blob) || + srv->upload.phase == BT_MESH_DFD_UPLOAD_PHASE_TRANSFER_ACTIVE; +} + +static int slot_del(struct bt_mesh_dfd_srv *srv, const struct bt_mesh_dfu_slot *slot) +{ + if (srv->cb && srv->cb->del) { + srv->cb->del(srv, slot); + } + + return bt_mesh_dfu_slot_del(slot); +} + +static void receivers_status_rsp(struct bt_mesh_dfd_srv *srv, + struct bt_mesh_msg_ctx *ctx, + enum bt_mesh_dfd_status status) +{ + BT_MESH_MODEL_BUF_DEFINE(buf, BT_MESH_DFD_OP_RECEIVERS_STATUS, 3); + bt_mesh_model_msg_init(&buf, BT_MESH_DFD_OP_RECEIVERS_STATUS); + + net_buf_simple_add_u8(&buf, status); + net_buf_simple_add_le16(&buf, srv->target_cnt); + + bt_mesh_model_send(srv->mod, ctx, &buf, NULL, NULL); +} + +static int handle_receivers_add(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + enum bt_mesh_dfd_status status = BT_MESH_DFD_SUCCESS; + struct bt_mesh_dfd_srv *srv = mod->rt->user_data; + + if (buf->len % 3) { + return -EINVAL; + } + + if (bt_mesh_dfu_cli_is_busy(&srv->dfu)) { + receivers_status_rsp(srv, ctx, + BT_MESH_DFD_ERR_BUSY_WITH_DISTRIBUTION); + return 0; + } + + while (buf->len >= 3 && status == BT_MESH_DFD_SUCCESS) { + uint8_t img_idx; + uint16_t addr; + + addr = net_buf_simple_pull_le16(buf); + img_idx = net_buf_simple_pull_u8(buf); + + status = bt_mesh_dfd_srv_receiver_add(srv, addr, img_idx); + } + + receivers_status_rsp(srv, ctx, status); + + return 0; +} + +static int handle_receivers_delete_all(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + struct bt_mesh_dfd_srv *srv = mod->rt->user_data; + + receivers_status_rsp(srv, ctx, bt_mesh_dfd_srv_receivers_delete_all(srv)); + + return 0; +} + +static int handle_receivers_get(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + struct bt_mesh_dfd_srv *srv = mod->rt->user_data; + uint16_t first, cnt; + uint8_t progress; + int i; + + first = net_buf_simple_pull_le16(buf); + cnt = net_buf_simple_pull_le16(buf); + if (cnt == 0) { + return -EINVAL; + } + + /* Create a buffer that can fit the full target list, maxing out at TX_SDU_MAX: */ + NET_BUF_SIMPLE_DEFINE( + rsp, BT_MESH_MODEL_BUF_LEN(BT_MESH_DFD_OP_RECEIVERS_LIST, + DFD_RECEIVERS_LIST_MSG_MAXLEN)); + bt_mesh_model_msg_init(&rsp, BT_MESH_DFD_OP_RECEIVERS_LIST); + + net_buf_simple_add_le16(&rsp, srv->target_cnt); + net_buf_simple_add_le16(&rsp, first); + + cnt = MIN(cnt, srv->target_cnt - first); + progress = bt_mesh_dfu_cli_progress(&srv->dfu) / 2; + + for (i = 0; i < cnt && net_buf_simple_tailroom(&rsp) >= 5 + BT_MESH_MIC_SHORT; i++) { + const struct bt_mesh_dfu_target *t = &srv->targets[i + first]; + + net_buf_simple_add_le32( + &rsp, ((t->blob.addr & BIT_MASK(15)) | + ((t->phase & BIT_MASK(4)) << 15U) | + ((t->status & BIT_MASK(3)) << 19U) | + ((t->blob.status & BIT_MASK(4)) << 22U) | + ((progress & BIT_MASK(6)) << 26U))); + net_buf_simple_add_u8(&rsp, t->img_idx); + } + + bt_mesh_model_send(srv->mod, ctx, &rsp, NULL, NULL); + + return 0; +} + +static enum bt_mesh_dfu_iter slot_space_cb(const struct bt_mesh_dfu_slot *slot, + void *user_data) +{ + size_t *total = user_data; + + *total += slot->size; + + return BT_MESH_DFU_ITER_CONTINUE; +} + +static int handle_capabilities_get(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + size_t size = 0; + + BT_MESH_MODEL_BUF_DEFINE(rsp, BT_MESH_DFD_OP_CAPABILITIES_STATUS, 17); + bt_mesh_model_msg_init(&rsp, BT_MESH_DFD_OP_CAPABILITIES_STATUS); + + net_buf_simple_add_le16(&rsp, CONFIG_BT_MESH_DFD_SRV_TARGETS_MAX); + net_buf_simple_add_le16(&rsp, CONFIG_BT_MESH_DFU_SLOT_CNT); + net_buf_simple_add_le32(&rsp, CONFIG_BT_MESH_DFD_SRV_SLOT_MAX_SIZE); + net_buf_simple_add_le32(&rsp, CONFIG_BT_MESH_DFD_SRV_SLOT_SPACE); + + /* Remaining size */ + (void)bt_mesh_dfu_slot_foreach(slot_space_cb, &size); + size = MIN(size, CONFIG_BT_MESH_DFD_SRV_SLOT_SPACE); + + net_buf_simple_add_le32(&rsp, CONFIG_BT_MESH_DFD_SRV_SLOT_SPACE - size); + +#ifdef CONFIG_BT_MESH_DFD_SRV_OOB_UPLOAD + struct bt_mesh_dfd_srv *srv = mod->rt->user_data; + + if (srv->oob_schemes.count > 0) { + net_buf_simple_add_u8(&rsp, 1); + net_buf_simple_add_mem(&rsp, srv->oob_schemes.schemes, + srv->oob_schemes.count); + } else +#endif + { + net_buf_simple_add_u8(&rsp, 0); + } + + bt_mesh_model_send(mod, ctx, &rsp, NULL, NULL); + + return 0; +} + +static void status_rsp(struct bt_mesh_dfd_srv *srv, struct bt_mesh_msg_ctx *ctx, + enum bt_mesh_dfd_status status) +{ + BT_MESH_MODEL_BUF_DEFINE(rsp, BT_MESH_DFD_OP_STATUS, 12); + bt_mesh_model_msg_init(&rsp, BT_MESH_DFD_OP_STATUS); + + net_buf_simple_add_u8(&rsp, status); + net_buf_simple_add_u8(&rsp, srv->phase); + + if (srv->phase == BT_MESH_DFD_PHASE_IDLE || !srv->dfu.xfer.slot) { + bt_mesh_model_send(srv->mod, ctx, &rsp, NULL, NULL); + return; + } + + net_buf_simple_add_le16(&rsp, srv->inputs.group); + net_buf_simple_add_le16(&rsp, srv->inputs.app_idx); + net_buf_simple_add_u8(&rsp, srv->inputs.ttl); + net_buf_simple_add_le16(&rsp, srv->inputs.timeout_base); + net_buf_simple_add_u8(&rsp, ((srv->dfu.xfer.blob.mode & BIT_MASK(2)) | + ((srv->apply & BIT_MASK(1)) << 2))); + net_buf_simple_add_le16(&rsp, srv->slot_idx); + + bt_mesh_model_send(srv->mod, ctx, &rsp, NULL, NULL); +} + +static int handle_get(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + struct bt_mesh_dfd_srv *srv = mod->rt->user_data; + + status_rsp(srv, ctx, BT_MESH_DFD_SUCCESS); + + return 0; +} + +static int handle_start(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + struct bt_mesh_dfd_srv *srv = mod->rt->user_data; + struct bt_mesh_dfd_start_params params; + uint8_t byte; + + params.app_idx = net_buf_simple_pull_le16(buf); + params.ttl = net_buf_simple_pull_u8(buf); + params.timeout_base = net_buf_simple_pull_le16(buf); + byte = net_buf_simple_pull_u8(buf); + params.xfer_mode = byte & BIT_MASK(2); + params.apply = (byte >> 2U) & BIT_MASK(1); + params.slot_idx = net_buf_simple_pull_le16(buf); + + if (buf->len == 16) { + /* TODO: Virtual addresses not supported. */ + status_rsp(srv, ctx, BT_MESH_DFD_ERR_INTERNAL); + return 0; + } + + if (buf->len != 2) { + return -EINVAL; + } + + params.group = net_buf_simple_pull_le16(buf); + + status_rsp(srv, ctx, bt_mesh_dfd_srv_start(srv, ¶ms)); + + return 0; +} + +static int handle_suspend(const struct bt_mesh_model *mod, + struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + struct bt_mesh_dfd_srv *srv = mod->rt->user_data; + + status_rsp(srv, ctx, bt_mesh_dfd_srv_suspend(srv)); + + return 0; +} + +static int handle_cancel(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + struct bt_mesh_dfd_srv *srv = mod->rt->user_data; + + bt_mesh_dfd_srv_cancel(srv, ctx); + + return 0; +} + +static int handle_apply(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + struct bt_mesh_dfd_srv *srv = mod->rt->user_data; + + status_rsp(srv, ctx, bt_mesh_dfd_srv_apply(srv)); + + return 0; +} + +static void upload_status_rsp_with_progress(struct bt_mesh_dfd_srv *srv, + struct bt_mesh_msg_ctx *ctx, + enum bt_mesh_dfd_status status, + uint8_t progress) +{ + BT_MESH_MODEL_BUF_DEFINE(rsp, BT_MESH_DFD_OP_UPLOAD_STATUS, + DFD_UPLOAD_STATUS_MSG_MAXLEN); + bt_mesh_model_msg_init(&rsp, BT_MESH_DFD_OP_UPLOAD_STATUS); + + net_buf_simple_add_u8(&rsp, status); + net_buf_simple_add_u8(&rsp, srv->upload.phase); + + if (srv->upload.phase == BT_MESH_DFD_UPLOAD_PHASE_IDLE || + !srv->upload.slot) { + bt_mesh_model_send(srv->mod, ctx, &rsp, NULL, NULL); + return; + } + +#ifdef CONFIG_BT_MESH_DFD_SRV_OOB_UPLOAD + if (srv->upload.is_oob) { + net_buf_simple_add_u8(&rsp, progress | BIT(7)); + net_buf_simple_add_mem(&rsp, srv->upload.oob.current_fwid, + srv->upload.oob.current_fwid_len); + } else +#endif + { + net_buf_simple_add_u8(&rsp, progress); + net_buf_simple_add_mem(&rsp, srv->upload.slot->fwid, + srv->upload.slot->fwid_len); + } + + bt_mesh_model_send(srv->mod, ctx, &rsp, NULL, NULL); +} + +static void upload_status_rsp(struct bt_mesh_dfd_srv *srv, + struct bt_mesh_msg_ctx *ctx, + enum bt_mesh_dfd_status status) +{ + uint8_t progress; + +#ifdef CONFIG_BT_MESH_DFD_SRV_OOB_UPLOAD + if (srv->upload.is_oob) { + progress = srv->cb->oob_progress_get(srv, srv->upload.slot); + } else +#endif + { + progress = bt_mesh_blob_srv_progress(&srv->upload.blob); + } + + upload_status_rsp_with_progress(srv, ctx, status, progress); +} + +static int handle_upload_get(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + struct bt_mesh_dfd_srv *srv = mod->rt->user_data; + + upload_status_rsp(srv, ctx, BT_MESH_DFD_SUCCESS); + + return 0; +} + +static inline int set_upload_fwid(struct bt_mesh_dfd_srv *srv, struct bt_mesh_msg_ctx *ctx, + const uint8_t *fwid, size_t fwid_len) +{ + int err = bt_mesh_dfu_slot_fwid_set(srv->upload.slot, fwid, fwid_len); + + switch (err) { + case -EFBIG: /* Fwid too long */ + case -EALREADY: /* Other server is in progress with this fwid */ + bt_mesh_dfu_slot_release(srv->upload.slot); + srv->upload.slot = NULL; + upload_status_rsp(srv, ctx, BT_MESH_DFD_ERR_INTERNAL); + break; + case -EEXIST: /* Img with this fwid already is in list */ + srv->upload.phase = BT_MESH_DFD_UPLOAD_PHASE_TRANSFER_SUCCESS; + bt_mesh_dfu_slot_release(srv->upload.slot); + + err = bt_mesh_dfu_slot_get(fwid, fwid_len, &srv->upload.slot); + if (!err) { + upload_status_rsp_with_progress(srv, ctx, BT_MESH_DFD_SUCCESS, 100); + } else { + srv->upload.slot = NULL; + upload_status_rsp(srv, ctx, BT_MESH_DFD_ERR_INTERNAL); + } + break; + case 0: + srv->upload.phase = BT_MESH_DFD_UPLOAD_PHASE_TRANSFER_ACTIVE; + break; + case -EINVAL: /* Slot in wrong state. */ + default: + break; + } + + return err; +} + +static int handle_upload_start(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + struct bt_mesh_dfd_srv *srv = mod->rt->user_data; + size_t meta_len, fwid_len, size; + const uint8_t *meta, *fwid; + uint16_t timeout_base; + uint64_t blob_id; + int err; + uint8_t ttl; + + ttl = net_buf_simple_pull_u8(buf); + timeout_base = net_buf_simple_pull_le16(buf); + blob_id = net_buf_simple_pull_le64(buf); + size = net_buf_simple_pull_le32(buf); + meta_len = net_buf_simple_pull_u8(buf); + if (buf->len < meta_len) { + return -EINVAL; + } + + meta = net_buf_simple_pull_mem(buf, meta_len); + fwid_len = buf->len; + fwid = net_buf_simple_pull_mem(buf, fwid_len); + + LOG_DBG("Upload Start: size: %d, fwid: %s, metadata: %s", size, bt_hex(fwid, fwid_len), + bt_hex(meta, meta_len)); + + if (size > CONFIG_BT_MESH_DFD_SRV_SLOT_MAX_SIZE) { + upload_status_rsp(srv, ctx, + BT_MESH_DFD_ERR_INSUFFICIENT_RESOURCES); + return 0; + } + + if (upload_is_busy(srv)) { + if (!srv->upload.slot) { + LOG_WRN("Busy with no upload slot"); + upload_status_rsp(srv, ctx, BT_MESH_DFD_ERR_INTERNAL); + return 0; + } + + if (srv->upload.slot->fwid_len == fwid_len && + !memcmp(srv->upload.slot->fwid, fwid, fwid_len) && + srv->upload.slot->metadata_len == meta_len && + !memcmp(srv->upload.slot->metadata, meta, meta_len) && + srv->upload.blob.state.xfer.id == blob_id && + srv->upload.blob.state.ttl == ttl && + srv->upload.blob.state.timeout_base == timeout_base +#ifdef CONFIG_BT_MESH_DFD_SRV_OOB_UPLOAD + && !srv->upload.is_oob +#endif + ) { + LOG_DBG("Duplicate upload start"); + upload_status_rsp(srv, ctx, BT_MESH_DFD_SUCCESS); + return 0; + } + + LOG_WRN("Upload already in progress"); + upload_status_rsp(srv, ctx, BT_MESH_DFD_ERR_BUSY_WITH_UPLOAD); + return 0; + } + + /* This will be a no-op if the slot state isn't RESERVED, which is + * what we want. + */ + if (srv->upload.slot) { + bt_mesh_dfu_slot_release(srv->upload.slot); + } + +#ifdef CONFIG_BT_MESH_DFD_SRV_OOB_UPLOAD + srv->upload.is_oob = false; +#endif + srv->upload.slot = bt_mesh_dfu_slot_reserve(); + + if (!srv->upload.slot) { + LOG_WRN("No space for slot"); + upload_status_rsp(srv, ctx, + BT_MESH_DFD_ERR_INSUFFICIENT_RESOURCES); + return 0; + } + + err = set_upload_fwid(srv, ctx, fwid, fwid_len); + if (err) { + return err; + } + + err = bt_mesh_dfu_slot_info_set(srv->upload.slot, size, meta, meta_len); + switch (err) { + case -EFBIG: + upload_status_rsp(srv, ctx, BT_MESH_DFD_ERR_INTERNAL); + break; + case 0: + break; + default: + return err; + } + + srv->io = NULL; + err = srv->cb->recv(srv, srv->upload.slot, &srv->io); + if (err || !srv->io) { + LOG_ERR("App rejected upload. err: %d io: %p", err, srv->io); + bt_mesh_dfu_slot_release(srv->upload.slot); + upload_status_rsp(srv, ctx, BT_MESH_DFD_ERR_INTERNAL); + return 0; + } + + err = bt_mesh_blob_srv_recv(&srv->upload.blob, blob_id, srv->io, ttl, + timeout_base); + if (err) { + LOG_ERR("BLOB Server rejected upload (err: %d)", err); + bt_mesh_dfu_slot_release(srv->upload.slot); + upload_status_rsp(srv, ctx, BT_MESH_DFD_ERR_INTERNAL); + return 0; + } + + LOG_DBG("%s", bt_hex(fwid, fwid_len)); + + srv->upload.phase = BT_MESH_DFD_UPLOAD_PHASE_TRANSFER_ACTIVE; + upload_status_rsp(srv, ctx, BT_MESH_DFD_SUCCESS); + + return 0; +} + +static int handle_upload_start_oob(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + struct bt_mesh_dfd_srv *srv = mod->rt->user_data; + uint8_t uri_len; + uint8_t *uri; + uint16_t fwid_len; + uint8_t *fwid; + + uri_len = net_buf_simple_pull_u8(buf); + + if (uri_len > buf->len) { + return -EINVAL; + } + + uri = net_buf_simple_pull_mem(buf, uri_len); + fwid_len = buf->len; + fwid = net_buf_simple_pull_mem(buf, fwid_len); + + LOG_DBG("Upload OOB Start"); + LOG_HEXDUMP_DBG(uri, uri_len, "URI"); + LOG_HEXDUMP_DBG(fwid, fwid_len, "FWID"); + + if (upload_is_busy(srv)) { +#ifdef CONFIG_BT_MESH_DFD_SRV_OOB_UPLOAD + if (srv->upload.is_oob && + uri_len == srv->upload.oob.uri_len && + fwid_len == srv->upload.oob.current_fwid_len && + !memcmp(uri, srv->upload.oob.uri, uri_len) && + !memcmp(fwid, srv->upload.oob.current_fwid, fwid_len)) { + /* Same image, return SUCCESS for idempotency */ + upload_status_rsp(srv, ctx, BT_MESH_DFD_SUCCESS); + return 0; + } +#endif + upload_status_rsp(srv, ctx, BT_MESH_DFD_ERR_BUSY_WITH_UPLOAD); + return 0; +#ifdef CONFIG_BT_MESH_DFD_SRV_OOB_UPLOAD + } else if (srv->upload.is_oob && srv->upload.is_pending_oob_check) { + /* Ignore the request if we didn't confirm the previous one. */ + return 0; +#endif + } + +#ifdef CONFIG_BT_MESH_DFD_SRV_OOB_UPLOAD + if (uri_len > CONFIG_BT_MESH_DFU_URI_MAXLEN || + fwid_len > CONFIG_BT_MESH_DFU_FWID_MAXLEN) { + upload_status_rsp(srv, ctx, BT_MESH_DFD_ERR_INTERNAL); + return 0; + } + + struct bt_mesh_dfu_slot *slot = bt_mesh_dfu_slot_reserve(); + + if (slot == NULL) { + upload_status_rsp(srv, ctx, BT_MESH_DFD_ERR_INSUFFICIENT_RESOURCES); + return 0; + } + + /* This will be a no-op if the slot state isn't RESERVED, which is + * what we want. + */ + if (srv->upload.slot) { + bt_mesh_dfu_slot_release(srv->upload.slot); + } + + srv->upload.is_oob = true; + srv->upload.slot = slot; + memcpy(srv->upload.oob.uri, uri, uri_len); + srv->upload.oob.uri_len = uri_len; + memcpy(srv->upload.oob.current_fwid, fwid, fwid_len); + srv->upload.oob.current_fwid_len = fwid_len; + memcpy(&srv->upload.oob.ctx, ctx, sizeof(struct bt_mesh_msg_ctx)); + + int status = srv->cb->start_oob_upload(srv, srv->upload.slot, srv->upload.oob.uri, + srv->upload.oob.uri_len, + srv->upload.oob.current_fwid, + srv->upload.oob.current_fwid_len); + + if (status != BT_MESH_DFD_SUCCESS) { + upload_status_rsp(srv, ctx, status); + bt_mesh_dfu_slot_release(srv->upload.slot); + } else { + srv->upload.is_pending_oob_check = true; + } +#else + upload_status_rsp(srv, ctx, BT_MESH_DFD_ERR_URI_NOT_SUPPORTED); +#endif /* CONFIG_BT_MESH_DFD_SRV_OOB_UPLOAD */ + + return 0; +} + +static int handle_upload_cancel(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + struct bt_mesh_dfd_srv *srv = mod->rt->user_data; + + srv->upload.phase = BT_MESH_DFD_UPLOAD_PHASE_IDLE; +#ifdef CONFIG_BT_MESH_DFD_SRV_OOB_UPLOAD + if (srv->upload.is_oob) { + srv->cb->cancel_oob_upload(srv, srv->upload.slot); + } else +#endif + { + (void)bt_mesh_blob_srv_cancel(&srv->upload.blob); + } + upload_status_rsp(srv, ctx, BT_MESH_DFD_SUCCESS); + + return 0; +} + +static void fw_status_rsp(struct bt_mesh_dfd_srv *srv, + struct bt_mesh_msg_ctx *ctx, + enum bt_mesh_dfd_status status, uint16_t idx, + const uint8_t *fwid, size_t fwid_len) +{ + BT_MESH_MODEL_BUF_DEFINE(rsp, BT_MESH_DFD_OP_FW_STATUS, + 7 + CONFIG_BT_MESH_DFU_FWID_MAXLEN); + bt_mesh_model_msg_init(&rsp, BT_MESH_DFD_OP_FW_STATUS); + + net_buf_simple_add_u8(&rsp, status); + net_buf_simple_add_le16(&rsp, bt_mesh_dfu_slot_count()); + + net_buf_simple_add_le16(&rsp, idx); + if (fwid) { + net_buf_simple_add_mem(&rsp, fwid, fwid_len); + } + + bt_mesh_model_send(srv->mod, ctx, &rsp, NULL, NULL); +} + +static int handle_fw_get(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + struct bt_mesh_dfd_srv *srv = mod->rt->user_data; + struct bt_mesh_dfu_slot *slot; + const uint8_t *fwid; + size_t fwid_len; + int idx; + + fwid_len = buf->len; + fwid = net_buf_simple_pull_mem(buf, fwid_len); + + idx = bt_mesh_dfu_slot_get(fwid, fwid_len, &slot); + if (idx >= 0) { + fw_status_rsp(srv, ctx, BT_MESH_DFD_SUCCESS, idx, fwid, + fwid_len); + } else { + fw_status_rsp(srv, ctx, BT_MESH_DFD_ERR_FW_NOT_FOUND, 0xffff, + fwid, fwid_len); + } + + return 0; +} + +static int handle_fw_get_by_index(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + struct bt_mesh_dfd_srv *srv = mod->rt->user_data; + const struct bt_mesh_dfu_slot *slot; + uint16_t idx; + + idx = net_buf_simple_pull_le16(buf); + + slot = bt_mesh_dfu_slot_at(idx); + if (slot) { + fw_status_rsp(srv, ctx, BT_MESH_DFD_SUCCESS, idx, slot->fwid, + slot->fwid_len); + } else { + fw_status_rsp(srv, ctx, BT_MESH_DFD_ERR_FW_NOT_FOUND, idx, + NULL, 0); + } + + return 0; +} + +static int handle_fw_delete(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + struct bt_mesh_dfd_srv *srv = mod->rt->user_data; + const uint8_t *fwid; + size_t fwid_len; + + fwid_len = buf->len; + fwid = net_buf_simple_pull_mem(buf, fwid_len); + + enum bt_mesh_dfd_status status = bt_mesh_dfd_srv_fw_delete(srv, &fwid_len, &fwid); + + fw_status_rsp(srv, ctx, status, 0xffff, fwid, fwid_len); + + return 0; +} + +static enum bt_mesh_dfu_iter slot_del_cb(const struct bt_mesh_dfu_slot *slot, + void *user_data) +{ + struct bt_mesh_dfd_srv *srv = user_data; + + if (srv->cb && srv->cb->del) { + srv->cb->del(srv, slot); + } + + return BT_MESH_DFU_ITER_CONTINUE; +} + +static int handle_fw_delete_all(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + struct bt_mesh_dfd_srv *srv = mod->rt->user_data; + + fw_status_rsp(srv, ctx, bt_mesh_dfd_srv_fw_delete_all(srv), 0xffff, NULL, 0); + + return 0; +} + +const struct bt_mesh_model_op _bt_mesh_dfd_srv_op[] = { + { BT_MESH_DFD_OP_RECEIVERS_ADD, BT_MESH_LEN_MIN(3), handle_receivers_add }, + { BT_MESH_DFD_OP_RECEIVERS_DELETE_ALL, BT_MESH_LEN_EXACT(0), handle_receivers_delete_all }, + { BT_MESH_DFD_OP_RECEIVERS_GET, BT_MESH_LEN_EXACT(4), handle_receivers_get }, + { BT_MESH_DFD_OP_CAPABILITIES_GET, BT_MESH_LEN_EXACT(0), handle_capabilities_get }, + { BT_MESH_DFD_OP_GET, BT_MESH_LEN_EXACT(0), handle_get }, + { BT_MESH_DFD_OP_START, BT_MESH_LEN_MIN(10), handle_start }, + { BT_MESH_DFD_OP_SUSPEND, BT_MESH_LEN_EXACT(0), handle_suspend }, + { BT_MESH_DFD_OP_CANCEL, BT_MESH_LEN_EXACT(0), handle_cancel }, + { BT_MESH_DFD_OP_APPLY, BT_MESH_LEN_EXACT(0), handle_apply }, + { BT_MESH_DFD_OP_UPLOAD_GET, BT_MESH_LEN_EXACT(0), handle_upload_get }, + { BT_MESH_DFD_OP_UPLOAD_START, BT_MESH_LEN_MIN(16), handle_upload_start }, + { BT_MESH_DFD_OP_UPLOAD_START_OOB, BT_MESH_LEN_MIN(2), handle_upload_start_oob }, + { BT_MESH_DFD_OP_UPLOAD_CANCEL, BT_MESH_LEN_EXACT(0), handle_upload_cancel }, + { BT_MESH_DFD_OP_FW_GET, BT_MESH_LEN_MIN(0), handle_fw_get }, + { BT_MESH_DFD_OP_FW_GET_BY_INDEX, BT_MESH_LEN_EXACT(2), handle_fw_get_by_index }, + { BT_MESH_DFD_OP_FW_DELETE, BT_MESH_LEN_MIN(0), handle_fw_delete }, + { BT_MESH_DFD_OP_FW_DELETE_ALL, BT_MESH_LEN_EXACT(0), handle_fw_delete_all }, + + BT_MESH_MODEL_OP_END +}; + +static void dfu_suspended(struct bt_mesh_dfu_cli *cli) +{ + struct bt_mesh_dfd_srv *srv = + CONTAINER_OF(cli, struct bt_mesh_dfd_srv, dfu); + + dfd_phase_set(srv, BT_MESH_DFD_PHASE_TRANSFER_SUSPENDED); +} + +static void dfu_ended(struct bt_mesh_dfu_cli *cli, + enum bt_mesh_dfu_status reason) +{ + struct bt_mesh_dfd_srv *srv = + CONTAINER_OF(cli, struct bt_mesh_dfd_srv, dfu); + int err; + + LOG_DBG("reason: %u, phase: %u, apply: %u", reason, srv->phase, srv->apply); + + if (srv->phase == BT_MESH_DFD_PHASE_IDLE) { + return; + } + + if (srv->phase == BT_MESH_DFD_PHASE_CANCELING_UPDATE) { + dfd_phase_set(srv, BT_MESH_DFD_PHASE_IDLE); + return; + } + + if (reason != BT_MESH_DFU_SUCCESS) { + dfd_phase_set(srv, BT_MESH_DFD_PHASE_FAILED); + return; + } + + if (!srv->apply) { + dfd_phase_set(srv, BT_MESH_DFD_PHASE_TRANSFER_SUCCESS); + return; + } + + dfd_phase_set(srv, BT_MESH_DFD_PHASE_APPLYING_UPDATE); + + err = bt_mesh_dfu_cli_apply(cli); + if (err) { + LOG_ERR("Apply failed: %d", err); + dfd_phase_set(srv, BT_MESH_DFD_PHASE_FAILED); + } +} + +static void dfu_applied(struct bt_mesh_dfu_cli *cli) +{ + struct bt_mesh_dfd_srv *srv = + CONTAINER_OF(cli, struct bt_mesh_dfd_srv, dfu); + int err; + + if (srv->phase == BT_MESH_DFD_PHASE_CANCELING_UPDATE) { + dfd_phase_set(srv, BT_MESH_DFD_PHASE_FAILED); + return; + } + + if (srv->phase != BT_MESH_DFD_PHASE_APPLYING_UPDATE) { + return; + } + + err = bt_mesh_dfu_cli_confirm(cli); + if (err) { + LOG_ERR("Confirm failed: %d", err); + dfd_phase_set(srv, BT_MESH_DFD_PHASE_FAILED); + } +} + +static void dfu_confirmed(struct bt_mesh_dfu_cli *cli) +{ + struct bt_mesh_dfd_srv *srv = + CONTAINER_OF(cli, struct bt_mesh_dfd_srv, dfu); + + if (srv->phase != BT_MESH_DFD_PHASE_APPLYING_UPDATE && + srv->phase != BT_MESH_DFD_PHASE_CANCELING_UPDATE) { + return; + } + + dfd_phase_set(srv, BT_MESH_DFD_PHASE_COMPLETED); +} + +const struct bt_mesh_dfu_cli_cb _bt_mesh_dfd_srv_dfu_cb = { + .suspended = dfu_suspended, + .ended = dfu_ended, + .applied = dfu_applied, + .confirmed = dfu_confirmed, +}; + +static int upload_start(struct bt_mesh_blob_srv *b, struct bt_mesh_msg_ctx *ctx, + struct bt_mesh_blob_xfer *xfer) +{ + LOG_DBG(""); + return 0; +} + +static void upload_end(struct bt_mesh_blob_srv *b, uint64_t id, bool success) +{ + struct bt_mesh_dfd_srv *srv = + CONTAINER_OF(b, struct bt_mesh_dfd_srv, upload.blob); + + if (srv->upload.phase != BT_MESH_DFD_UPLOAD_PHASE_TRANSFER_ACTIVE) { + return; + } + + LOG_DBG("%u", success); + + if (success && (bt_mesh_dfu_slot_commit(srv->upload.slot) == 0)) { + srv->upload.phase = BT_MESH_DFD_UPLOAD_PHASE_TRANSFER_SUCCESS; + return; + } + + /* Will delete slot when we start a new upload */ + srv->upload.phase = BT_MESH_DFD_UPLOAD_PHASE_TRANSFER_ERROR; +} + +static void upload_timeout(struct bt_mesh_blob_srv *b) +{ + LOG_DBG(""); + + upload_end(b, b->state.xfer.id, false); +} + +const struct bt_mesh_blob_srv_cb _bt_mesh_dfd_srv_blob_cb = { + .start = upload_start, + .end = upload_end, + .suspended = upload_timeout, +}; + +static int dfd_srv_init(const struct bt_mesh_model *mod) +{ + struct bt_mesh_dfd_srv *srv = mod->rt->user_data; + + srv->mod = mod; + + if (IS_ENABLED(CONFIG_BT_MESH_MODEL_EXTENSIONS)) { + bt_mesh_model_extend(mod, srv->upload.blob.mod); + } + + return 0; +} + +static void dfd_srv_reset(const struct bt_mesh_model *mod) +{ + struct bt_mesh_dfd_srv *srv = mod->rt->user_data; + + dfd_phase_set(srv, BT_MESH_DFD_PHASE_IDLE); + srv->upload.phase = BT_MESH_DFD_UPLOAD_PHASE_IDLE; + + sys_slist_init(&srv->inputs.targets); + srv->target_cnt = 0; + + bt_mesh_dfu_slot_foreach(slot_del_cb, srv); + bt_mesh_dfu_slot_del_all(); +} + +const struct bt_mesh_model_cb _bt_mesh_dfd_srv_cb = { + .init = dfd_srv_init, + .reset = dfd_srv_reset, +}; + +enum bt_mesh_dfd_status bt_mesh_dfd_srv_receiver_add(struct bt_mesh_dfd_srv *srv, uint16_t addr, + uint8_t img_idx) +{ + struct bt_mesh_dfu_target *t; + struct bt_mesh_blob_target_pull *p; + + if (!BT_MESH_ADDR_IS_UNICAST(addr)) { + return BT_MESH_DFD_SUCCESS; + } + + t = target_get(srv, addr); + if (t) { + t->img_idx = img_idx; + return BT_MESH_DFD_SUCCESS; + } + + /* New target node, add it to the list */ + + if (srv->target_cnt == ARRAY_SIZE(srv->targets)) { + return BT_MESH_DFD_ERR_INSUFFICIENT_RESOURCES; + } + + t = &srv->targets[srv->target_cnt]; + p = &srv->pull_ctxs[srv->target_cnt]; + srv->target_cnt++; + + memset(t, 0, sizeof(*t)); + memset(p, 0, sizeof(*p)); + t->blob.addr = addr; + t->blob.pull = p; + t->img_idx = img_idx; + + LOG_DBG("Added receiver 0x%04x img: %u", t->blob.addr, + t->img_idx); + + return BT_MESH_DFD_SUCCESS; +} + +enum bt_mesh_dfd_status bt_mesh_dfd_srv_receivers_delete_all(struct bt_mesh_dfd_srv *srv) +{ + if (bt_mesh_dfu_cli_is_busy(&srv->dfu)) { + return BT_MESH_DFD_ERR_BUSY_WITH_DISTRIBUTION; + } + + sys_slist_init(&srv->inputs.targets); + srv->target_cnt = 0; + + return BT_MESH_DFD_SUCCESS; +} + +enum bt_mesh_dfd_status bt_mesh_dfd_srv_start(struct bt_mesh_dfd_srv *srv, + struct bt_mesh_dfd_start_params *params) +{ + int err, i; + struct bt_mesh_dfu_cli_xfer xfer = { 0 }; + + if (!srv->target_cnt) { + return BT_MESH_DFD_ERR_RECEIVERS_LIST_EMPTY; + } + + if (!bt_mesh_app_key_exists(params->app_idx)) { + return BT_MESH_DFD_ERR_INVALID_APPKEY_INDEX; + } + + xfer.mode = params->xfer_mode; + xfer.slot = bt_mesh_dfu_slot_at(params->slot_idx); + if (!xfer.slot) { + return BT_MESH_DFD_ERR_FW_NOT_FOUND; + } + + if (srv->inputs.app_idx == params->app_idx && + srv->inputs.timeout_base == params->timeout_base && + srv->inputs.group == params->group && srv->inputs.ttl == params->ttl && + srv->dfu.xfer.blob.mode == xfer.mode && srv->apply == params->apply && + srv->slot_idx == params->slot_idx) { + if (is_busy(srv) || + srv->phase == BT_MESH_DFD_PHASE_COMPLETED) { + LOG_WRN("Already completed or in progress"); + return BT_MESH_DFD_SUCCESS; + } else if (srv->phase == BT_MESH_DFD_PHASE_TRANSFER_SUSPENDED) { + bt_mesh_dfu_cli_resume(&srv->dfu); + dfd_phase_set(srv, BT_MESH_DFD_PHASE_TRANSFER_ACTIVE); + return BT_MESH_DFD_SUCCESS; + } + } else if (is_busy(srv) || + srv->phase == BT_MESH_DFD_PHASE_TRANSFER_SUSPENDED) { + LOG_WRN("Busy with distribution"); + return BT_MESH_DFD_ERR_BUSY_WITH_DISTRIBUTION; + } + + if (srv->phase == BT_MESH_DFD_PHASE_CANCELING_UPDATE) { + LOG_WRN("Canceling distribution"); + return BT_MESH_DFD_ERR_BUSY_WITH_DISTRIBUTION; + } + + srv->io = NULL; + err = srv->cb->send(srv, xfer.slot, &srv->io); + if (err || !srv->io) { + return BT_MESH_DFD_ERR_INTERNAL; + } + + sys_slist_init(&srv->inputs.targets); + for (i = 0; i < srv->target_cnt; i++) { + uint16_t addr = srv->targets[i].blob.addr; + + memset(&srv->targets[i].blob, 0, sizeof(struct bt_mesh_blob_target)); + memset(&srv->pull_ctxs[i], 0, sizeof(struct bt_mesh_blob_target_pull)); + srv->targets[i].blob.addr = addr; + srv->targets[i].blob.pull = &srv->pull_ctxs[i]; + + sys_slist_append(&srv->inputs.targets, &srv->targets[i].blob.n); + } + + srv->slot_idx = params->slot_idx; + srv->inputs.app_idx = params->app_idx; + srv->inputs.timeout_base = params->timeout_base; + srv->inputs.group = params->group; + srv->inputs.ttl = params->ttl; + srv->apply = params->apply; + + LOG_DBG("Distribution Start: slot: %d, appidx: %d, tb: %d, addr: %04X, ttl: %d, apply: %d", + params->slot_idx, params->app_idx, params->timeout_base, params->group, params->ttl, + params->apply); + + /* DFD Server will always retrieve targets' capabilities before distributing a firmware.*/ + xfer.blob_params = NULL; + + dfd_phase_set(srv, BT_MESH_DFD_PHASE_TRANSFER_ACTIVE); + err = bt_mesh_dfu_cli_send(&srv->dfu, &srv->inputs, srv->io, &xfer); + if (err) { + dfd_phase_set(srv, BT_MESH_DFD_PHASE_IDLE); + return BT_MESH_DFD_ERR_INTERNAL; + } + + return BT_MESH_DFD_SUCCESS; +} + +enum bt_mesh_dfd_status bt_mesh_dfd_srv_suspend(struct bt_mesh_dfd_srv *srv) +{ + int err; + + if (srv->phase == BT_MESH_DFD_PHASE_TRANSFER_SUSPENDED) { + return BT_MESH_DFD_SUCCESS; + } + + if (srv->phase != BT_MESH_DFD_PHASE_TRANSFER_ACTIVE) { + return BT_MESH_DFD_ERR_WRONG_PHASE; + } + + err = bt_mesh_dfu_cli_suspend(&srv->dfu); + if (err) { + return BT_MESH_DFD_ERR_SUSPEND_FAILED; + } + + srv->phase = BT_MESH_DFD_PHASE_TRANSFER_SUSPENDED; + return BT_MESH_DFD_SUCCESS; +} + +enum bt_mesh_dfd_status bt_mesh_dfd_srv_cancel(struct bt_mesh_dfd_srv *srv, + struct bt_mesh_msg_ctx *ctx) +{ + enum bt_mesh_dfd_phase prev_phase; + int err; + + if (srv->phase == BT_MESH_DFD_PHASE_CANCELING_UPDATE || + srv->phase == BT_MESH_DFD_PHASE_IDLE) { + if (ctx != NULL) { + status_rsp(srv, ctx, BT_MESH_DFD_SUCCESS); + } + return BT_MESH_DFD_SUCCESS; + } + + if (srv->phase == BT_MESH_DFD_PHASE_COMPLETED || + srv->phase == BT_MESH_DFD_PHASE_FAILED) { + dfd_phase_set(srv, BT_MESH_DFD_PHASE_IDLE); + if (ctx != NULL) { + status_rsp(srv, ctx, BT_MESH_DFD_SUCCESS); + } + return BT_MESH_DFD_SUCCESS; + } + + /* Phase TRANSFER_ACTIVE, TRANSFER_SUSPENDED, TRANSFER_SUCCESS, APPLYING_UPDATE: */ + + prev_phase = srv->phase; + dfd_phase_set(srv, BT_MESH_DFD_PHASE_CANCELING_UPDATE); + err = bt_mesh_dfu_cli_cancel(&srv->dfu, NULL); + if (err) { + if (ctx != NULL) { + status_rsp(srv, ctx, BT_MESH_DFD_ERR_INTERNAL); + } + return BT_MESH_DFD_ERR_INTERNAL; + } + + if (prev_phase == BT_MESH_DFD_PHASE_APPLYING_UPDATE && ctx) { + /* Disable randomization for the Firmware Distribution State message to avoid + * reordering when Firmware Distribution Server sends 2 messages in a row when + * cancelling the update (see section 6.2.3.10 of MshDFUv1.0). + */ + ctx->rnd_delay = false; + } + + if (ctx != NULL) { + status_rsp(srv, ctx, BT_MESH_DFD_SUCCESS); + } + + if (prev_phase == BT_MESH_DFD_PHASE_APPLYING_UPDATE) { + dfd_phase_set(srv, BT_MESH_DFD_PHASE_IDLE); + if (ctx != NULL) { + status_rsp(srv, ctx, BT_MESH_DFD_SUCCESS); + } + } + + return BT_MESH_DFD_SUCCESS; +} + +enum bt_mesh_dfd_status bt_mesh_dfd_srv_apply(struct bt_mesh_dfd_srv *srv) +{ + int err; + + if (srv->phase == BT_MESH_DFD_PHASE_IDLE || + srv->phase == BT_MESH_DFD_PHASE_CANCELING_UPDATE || + srv->phase == BT_MESH_DFD_PHASE_TRANSFER_ACTIVE || + srv->phase == BT_MESH_DFD_PHASE_TRANSFER_SUSPENDED || + srv->phase == BT_MESH_DFD_PHASE_FAILED) { + return BT_MESH_DFD_ERR_WRONG_PHASE; + } + + if (srv->phase == BT_MESH_DFD_PHASE_APPLYING_UPDATE || + srv->phase == BT_MESH_DFD_PHASE_COMPLETED) { + return BT_MESH_DFD_SUCCESS; + } + + err = bt_mesh_dfu_cli_apply(&srv->dfu); + if (err) { + return BT_MESH_DFD_ERR_INTERNAL; + } + + dfd_phase_set(srv, BT_MESH_DFD_PHASE_APPLYING_UPDATE); + return BT_MESH_DFD_SUCCESS; +} + +enum bt_mesh_dfd_status bt_mesh_dfd_srv_fw_delete(struct bt_mesh_dfd_srv *srv, size_t *fwid_len, + const uint8_t **fwid) +{ + struct bt_mesh_dfu_slot *slot; + int idx, err; + + if (srv->phase != BT_MESH_DFD_PHASE_IDLE) { + *fwid = NULL; + *fwid_len = 0; + return BT_MESH_DFD_ERR_BUSY_WITH_DISTRIBUTION; + } + + idx = bt_mesh_dfu_slot_get(*fwid, *fwid_len, &slot); + if (idx < 0) { + return BT_MESH_DFD_SUCCESS; + } + + err = slot_del(srv, slot); + if (err) { + *fwid = NULL; + *fwid_len = 0; + return BT_MESH_DFD_ERR_INTERNAL; + } else { + return BT_MESH_DFD_SUCCESS; + } +} + +enum bt_mesh_dfd_status bt_mesh_dfd_srv_fw_delete_all(struct bt_mesh_dfd_srv *srv) +{ + if (srv->phase != BT_MESH_DFD_PHASE_IDLE) { + return BT_MESH_DFD_ERR_BUSY_WITH_DISTRIBUTION; + } + + bt_mesh_dfu_slot_foreach(slot_del_cb, srv); + + bt_mesh_dfu_slot_del_all(); + + return BT_MESH_DFD_SUCCESS; +} + +#ifdef CONFIG_BT_MESH_DFD_SRV_OOB_UPLOAD +int bt_mesh_dfd_srv_oob_check_complete(struct bt_mesh_dfd_srv *srv, + const struct bt_mesh_dfu_slot *slot, int status, + uint8_t *fwid, size_t fwid_len) +{ + int err; + + if (slot != srv->upload.slot || !srv->upload.is_oob || + srv->upload.phase == BT_MESH_DFD_UPLOAD_PHASE_TRANSFER_ACTIVE || + !srv->upload.is_pending_oob_check) { + /* This should not happen, unless the application calls the function with a + * "wrong" pointer or at a wrong time. + */ + return -EINVAL; + } + + srv->upload.is_pending_oob_check = false; + + if (status != BT_MESH_DFD_SUCCESS) { + bt_mesh_dfu_slot_release(srv->upload.slot); + upload_status_rsp(srv, &srv->upload.oob.ctx, status); + return -ECANCELED; + } + + err = set_upload_fwid(srv, &srv->upload.oob.ctx, fwid, fwid_len); + + if (err) { + return err; + } + + upload_status_rsp(srv, &srv->upload.oob.ctx, BT_MESH_DFD_SUCCESS); + return 0; +} + +int bt_mesh_dfd_srv_oob_store_complete(struct bt_mesh_dfd_srv *srv, + const struct bt_mesh_dfu_slot *slot, bool success, + size_t size, const uint8_t *metadata, size_t metadata_len) +{ + int err = 0; + + if (srv->upload.phase != BT_MESH_DFD_UPLOAD_PHASE_TRANSFER_ACTIVE || + srv->upload.slot != slot || !srv->upload.is_oob) { + return -EINVAL; + } + + if (!success) { + goto error; + } + + err = bt_mesh_dfu_slot_info_set(srv->upload.slot, size, metadata, metadata_len); + if (err) { + goto error; + } + + err = bt_mesh_dfu_slot_commit(srv->upload.slot); + if (err) { + goto error; + } + + srv->upload.phase = BT_MESH_DFD_UPLOAD_PHASE_TRANSFER_SUCCESS; + return 0; + +error: + srv->upload.phase = BT_MESH_DFD_UPLOAD_PHASE_TRANSFER_ERROR; + bt_mesh_dfu_slot_release(srv->upload.slot); + return err; +} +#endif /* CONFIG_BT_MESH_DFD_SRV_OOB_UPLOAD */ diff --git a/components/bt/esp_ble_mesh/v1.1/dfu/dfd_srv_internal.h b/components/bt/esp_ble_mesh/v1.1/dfu/dfd_srv_internal.h new file mode 100644 index 0000000000..12c7d943f6 --- /dev/null +++ b/components/bt/esp_ble_mesh/v1.1/dfu/dfd_srv_internal.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2022 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_INCLUDE_BLUETOOTH_MESH_DFD_SRV_INTERNAL_H__ +#define ZEPHYR_INCLUDE_BLUETOOTH_MESH_DFD_SRV_INTERNAL_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct bt_mesh_dfd_start_params { + uint16_t app_idx, timeout_base, slot_idx, group; + enum bt_mesh_blob_xfer_mode xfer_mode; + uint8_t ttl; + bool apply; +}; + +enum bt_mesh_dfd_status bt_mesh_dfd_srv_receiver_add(struct bt_mesh_dfd_srv *srv, uint16_t addr, + uint8_t img_idx); + +enum bt_mesh_dfd_status bt_mesh_dfd_srv_receivers_delete_all(struct bt_mesh_dfd_srv *srv); + +enum bt_mesh_dfd_status bt_mesh_dfd_srv_start(struct bt_mesh_dfd_srv *srv, + struct bt_mesh_dfd_start_params *params); + +enum bt_mesh_dfd_status bt_mesh_dfd_srv_suspend(struct bt_mesh_dfd_srv *srv); + +enum bt_mesh_dfd_status bt_mesh_dfd_srv_cancel(struct bt_mesh_dfd_srv *srv, + struct bt_mesh_msg_ctx *ctx); + +enum bt_mesh_dfd_status bt_mesh_dfd_srv_apply(struct bt_mesh_dfd_srv *srv); + +enum bt_mesh_dfd_status bt_mesh_dfd_srv_fw_delete(struct bt_mesh_dfd_srv *srv, size_t *fwid_len, + const uint8_t **fwid); + +enum bt_mesh_dfd_status bt_mesh_dfd_srv_fw_delete_all(struct bt_mesh_dfd_srv *srv); + +#ifdef __cplusplus +} +#endif + +#endif /* ZEPHYR_INCLUDE_BLUETOOTH_MESH_DFD_SRV_INTERNAL_H__ */ diff --git a/components/bt/esp_ble_mesh/v1.1/dfu/dfu.h b/components/bt/esp_ble_mesh/v1.1/dfu/dfu.h new file mode 100644 index 0000000000..c1c5aca46c --- /dev/null +++ b/components/bt/esp_ble_mesh/v1.1/dfu/dfu.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2020 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define BT_MESH_DFU_OP_UPDATE_INFO_GET BT_MESH_MODEL_OP_2(0x83, 0x08) +#define BT_MESH_DFU_OP_UPDATE_INFO_STATUS BT_MESH_MODEL_OP_2(0x83, 0x09) + +#define BT_MESH_DFU_OP_UPDATE_METADATA_CHECK BT_MESH_MODEL_OP_2(0x83, 0x0a) +#define BT_MESH_DFU_OP_UPDATE_METADATA_STATUS BT_MESH_MODEL_OP_2(0x83, 0x0b) + +#define BT_MESH_DFU_OP_UPDATE_GET BT_MESH_MODEL_OP_2(0x83, 0x0c) +#define BT_MESH_DFU_OP_UPDATE_START BT_MESH_MODEL_OP_2(0x83, 0x0d) +#define BT_MESH_DFU_OP_UPDATE_CANCEL BT_MESH_MODEL_OP_2(0x83, 0x0e) +#define BT_MESH_DFU_OP_UPDATE_APPLY BT_MESH_MODEL_OP_2(0x83, 0x0f) +#define BT_MESH_DFU_OP_UPDATE_STATUS BT_MESH_MODEL_OP_2(0x83, 0x10) + +#define DFU_UPDATE_INFO_STATUS_MSG_MINLEN (4 + CONFIG_BT_MESH_DFU_FWID_MAXLEN + \ + CONFIG_BT_MESH_DFU_URI_MAXLEN) +#define DFU_UPDATE_START_MSG_MAXLEN (12 + CONFIG_BT_MESH_DFU_METADATA_MAXLEN) + +static inline uint16_t dfu_metadata_checksum(struct net_buf_simple *buf) +{ + /* Simple Fletcher-16 checksum to ensure duplicate start messages don't + * have different metadata. + */ + struct net_buf_simple_state state; + uint8_t sum[2] = {0, 0}; + + net_buf_simple_save(buf, &state); + + + while (buf->len) { + uint8_t byte = net_buf_simple_pull_u8(buf); + + sum[0] += byte; + sum[1] += sum[0]; + } + + net_buf_simple_restore(buf, &state); + + return (sum[0] << 8U) | sum[1]; +} 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 new file mode 100644 index 0000000000..f02b884d22 --- /dev/null +++ b/components/bt/esp_ble_mesh/v1.1/dfu/dfu_cli.c @@ -0,0 +1,1265 @@ +/* + * Copyright (c) 2020 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include "access.h" +#include "dfu.h" +#include "blob.h" +#include + +#define LOG_LEVEL CONFIG_BT_MESH_DFU_LOG_LEVEL +#include +LOG_MODULE_REGISTER(bt_mesh_dfu_cli); + +#define TARGETS_FOR_EACH(cli, target) \ + SYS_SLIST_FOR_EACH_CONTAINER( \ + (sys_slist_t *)&((cli)->blob.inputs)->targets, target, blob.n) + +#define MSG_CTX(cli, dst) \ + { \ + .app_idx = (cli)->blob.inputs->app_idx, .addr = dst, \ + .send_ttl = (cli)->blob.inputs->ttl, \ + } + +#define DFU_CLI(blob_cli) CONTAINER_OF(blob_cli, struct bt_mesh_dfu_cli, blob) + +BUILD_ASSERT((DFU_UPDATE_START_MSG_MAXLEN + BT_MESH_MODEL_OP_LEN(BT_MESH_DFU_OP_UPDATE_START) + + BT_MESH_MIC_SHORT) <= BT_MESH_TX_SDU_MAX, + "The Firmware Update Start message does not fit into the maximum outgoing SDU size."); + +BUILD_ASSERT((DFU_UPDATE_INFO_STATUS_MSG_MINLEN + + BT_MESH_MODEL_OP_LEN(BT_MESH_DFU_OP_UPDATE_INFO_STATUS) + BT_MESH_MIC_SHORT) + <= BT_MESH_RX_SDU_MAX, + "The Firmware Update Info Status message does not fit into the maximum incoming SDU " + "size."); + +enum req { + REQ_NONE, + REQ_METADATA, + REQ_IMG, + REQ_STATUS, +}; + +enum { + FLAG_FAILED = BIT(0), + FLAG_CANCELLED = BIT(1), + FLAG_SKIP_CAPS_GET = BIT(2), + FLAG_RESUME = BIT(3), + FLAG_COMPLETED = BIT(4), +}; + +enum { + STATE_IDLE, + STATE_TRANSFER, + STATE_REFRESH, + STATE_VERIFIED, + STATE_APPLY, + STATE_APPLIED, + STATE_CONFIRM, + STATE_CANCEL, + STATE_SUSPENDED, +}; + +static int32_t dfu_cli_timeout = (10 * MSEC_PER_SEC); + +static struct bt_mesh_dfu_target *target_get(struct bt_mesh_dfu_cli *cli, + uint16_t addr) +{ + struct bt_mesh_dfu_target *target; + + TARGETS_FOR_EACH(cli, target) { + if (addr == target->blob.addr) { + return target; + } + } + + return NULL; +} + +static void target_failed(struct bt_mesh_dfu_cli *cli, + struct bt_mesh_dfu_target *target, + enum bt_mesh_dfu_status status) +{ + target->status = status; + + LOG_ERR("Target 0x%04x failed: %u", target->blob.addr, status); + + /* Invalidate blob status to prevent the target from being included in + * future sending: + */ + if (target->blob.status == BT_MESH_BLOB_SUCCESS) { + target->blob.status = BT_MESH_BLOB_ERR_INTERNAL; + } + + if (cli->cb && cli->cb->lost_target) { + cli->cb->lost_target(cli, target); + } +} + +static void dfu_complete(struct bt_mesh_dfu_cli *cli) +{ + LOG_DBG(""); + + if (cli->cb && cli->cb->ended) { + cli->cb->ended(cli, BT_MESH_DFU_SUCCESS); + } +} + +static void dfu_applied(struct bt_mesh_dfu_cli *cli) +{ + LOG_DBG(""); + + cli->xfer.state = STATE_APPLIED; + + if (cli->cb && cli->cb->applied) { + cli->cb->applied(cli); + } +} + +static void dfu_failed(struct bt_mesh_dfu_cli *cli, + enum bt_mesh_dfu_status reason) +{ + LOG_DBG("%u", reason); + + cli->xfer.flags |= FLAG_FAILED; + + if (cli->cb && cli->cb->ended) { + cli->cb->ended(cli, reason); + } +} + +static int req_setup(struct bt_mesh_dfu_cli *cli, enum req type, uint16_t addr, + void *params) +{ + if (cli->req.type != REQ_NONE) { + return -EBUSY; + } + + cli->req.addr = addr; + cli->req.params = params; + cli->req.type = type; + + return 0; +} + +static int req_wait(struct bt_mesh_dfu_cli *cli, k_timeout_t timeout) +{ + int err; + + err = k_sem_take(&cli->req.sem, timeout); + cli->req.type = REQ_NONE; + + return err; +} + +static bool targets_active(struct bt_mesh_dfu_cli *cli) +{ + struct bt_mesh_dfu_target *target; + + TARGETS_FOR_EACH(cli, target) { + if (target->status == BT_MESH_DFU_SUCCESS) { + return true; + } + } + + return false; +} + +/******************************************************************************* + * Blob client + ******************************************************************************/ +static void refresh(struct bt_mesh_dfu_cli *cli); + +static void blob_caps(struct bt_mesh_blob_cli *b, + const struct bt_mesh_blob_cli_caps *caps) +{ + struct bt_mesh_dfu_cli *cli = DFU_CLI(b); + int err; + + if (!caps) { + dfu_failed(cli, BT_MESH_DFU_ERR_RESOURCES); + return; + } + + cli->xfer.blob.block_size_log = caps->max_block_size_log; + cli->xfer.blob.chunk_size = caps->max_chunk_size; + + /* 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 + * reported value otherwise. + */ + if (!(cli->xfer.blob.mode & BT_MESH_BLOB_XFER_MODE_ALL)) { + cli->xfer.blob.mode = + caps->modes == BT_MESH_BLOB_XFER_MODE_ALL ? + BT_MESH_BLOB_XFER_MODE_PUSH : caps->modes; + } else { + cli->xfer.blob.mode = + caps->modes == BT_MESH_BLOB_XFER_MODE_ALL ? + cli->xfer.blob.mode : caps->modes; + } + + err = bt_mesh_blob_cli_send(b, b->inputs, &cli->xfer.blob, cli->xfer.io); + if (err) { + LOG_ERR("Starting BLOB xfer failed: %d", err); + dfu_failed(cli, BT_MESH_DFU_ERR_BLOB_XFER_BUSY); + } +} + +static void blob_lost_target(struct bt_mesh_blob_cli *b, + struct bt_mesh_blob_target *blobt, + enum bt_mesh_blob_status reason) +{ + struct bt_mesh_dfu_target *target = + CONTAINER_OF(blobt, struct bt_mesh_dfu_target, blob); + struct bt_mesh_dfu_cli *cli = DFU_CLI(b); + + if ((cli->xfer.state == STATE_CONFIRM || cli->xfer.state == STATE_APPLY) && + target->effect == BT_MESH_DFU_EFFECT_UNPROV) { + /* Reset status for such targets to use them in consequent procedures. See sections + * 7.1.2.6 and 7.1.2.9 of the MeshDFU. + */ + target->blob.status = BT_MESH_BLOB_SUCCESS; + return; + } + + target_failed(cli, target, BT_MESH_DFU_ERR_INTERNAL); +} + +static void blob_suspended(struct bt_mesh_blob_cli *b) +{ + struct bt_mesh_dfu_cli *cli = DFU_CLI(b); + + LOG_DBG("BLOB transfer suspended"); + + cli->xfer.state = STATE_SUSPENDED; + + if (cli->cb && cli->cb->suspended) { + cli->cb->suspended(cli); + } +} + +static void blob_end(struct bt_mesh_blob_cli *b, + const struct bt_mesh_blob_xfer *xfer, bool success) +{ + struct bt_mesh_dfu_cli *cli = DFU_CLI(b); + + cli->req.img_cb = NULL; + + if (success) { + refresh(cli); + return; + } + + if (cli->xfer.state == STATE_CANCEL) { + /* The user cancelled the transfer, DFU will end when all + * targets have been notified. + */ + return; + } + + if (cli->xfer.state != STATE_TRANSFER) { + LOG_ERR("Blob failed in invalid state %u", cli->xfer.state); + return; + } + + dfu_failed(cli, BT_MESH_DFU_ERR_INTERNAL); +} + +const struct bt_mesh_blob_cli_cb _bt_mesh_dfu_cli_blob_handlers = { + .caps = blob_caps, + .lost_target = blob_lost_target, + .suspended = blob_suspended, + .end = blob_end, +}; + +/******************************************************************************* + * Message sending + ******************************************************************************/ + +static void tx_start(uint16_t dur, int err, void *cb_data); +static void tx_end(int err, void *cb_data); + +static const struct bt_mesh_send_cb send_cb = { + .start = tx_start, + .end = tx_end, +}; + +static void tx_start(uint16_t dur, int err, void *cb_data) +{ + if (err) { + tx_end(err, cb_data); + } +} + +static void tx_end(int err, void *cb_data) +{ + struct bt_mesh_dfu_cli *cli = cb_data; + + blob_cli_broadcast_tx_complete(&cli->blob); +} + +static int tx(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf, const struct bt_mesh_send_cb *cb, + struct bt_mesh_dfu_cli *cli) +{ + int err; + + err = bt_mesh_model_send(mod, ctx, buf, cb, cli); + if (err) { + LOG_ERR("Send err: %d", err); + if (cb) { + cb->end(err, cli); + } + return err; + } + + return 0; +} + +static int info_get(struct bt_mesh_dfu_cli *cli, struct bt_mesh_msg_ctx *ctx, + uint8_t idx, uint8_t max_count, + const struct bt_mesh_send_cb *cb) +{ + BT_MESH_MODEL_BUF_DEFINE(buf, BT_MESH_DFU_OP_UPDATE_INFO_GET, 2); + bt_mesh_model_msg_init(&buf, BT_MESH_DFU_OP_UPDATE_INFO_GET); + net_buf_simple_add_u8(&buf, idx); + net_buf_simple_add_u8(&buf, max_count); + + return tx(cli->mod, ctx, &buf, cb, cli); +} + +static void send_info_get(struct bt_mesh_blob_cli *b, uint16_t dst) +{ + struct bt_mesh_dfu_cli *cli = DFU_CLI(b); + struct bt_mesh_msg_ctx ctx = MSG_CTX(cli, dst); + + cli->req.img_cnt = 0xff; + + info_get(cli, &ctx, 0, cli->req.img_cnt, &send_cb); +} + +static void send_update_start(struct bt_mesh_blob_cli *b, uint16_t dst) +{ + struct bt_mesh_dfu_cli *cli = DFU_CLI(b); + struct bt_mesh_msg_ctx ctx = MSG_CTX(cli, dst); + struct bt_mesh_dfu_target *target; + + if (b->tx.ctx.force_unicast) { + target = target_get(cli, dst); + } else { + target = SYS_SLIST_PEEK_HEAD_CONTAINER( + (sys_slist_t *)&((cli)->blob.inputs)->targets, + target, blob.n); + } + + BT_MESH_MODEL_BUF_DEFINE(buf, BT_MESH_DFU_OP_UPDATE_START, + DFU_UPDATE_START_MSG_MAXLEN); + bt_mesh_model_msg_init(&buf, BT_MESH_DFU_OP_UPDATE_START); + + net_buf_simple_add_u8(&buf, cli->blob.inputs->ttl); + net_buf_simple_add_le16(&buf, cli->blob.inputs->timeout_base); + net_buf_simple_add_le64(&buf, cli->xfer.blob.id); + net_buf_simple_add_u8(&buf, target->img_idx); + net_buf_simple_add_mem(&buf, cli->xfer.slot->metadata, + cli->xfer.slot->metadata_len); + + (void)tx(cli->mod, &ctx, &buf, &send_cb, cli); +} + +static void send_update_get(struct bt_mesh_blob_cli *b, uint16_t dst) +{ + struct bt_mesh_dfu_cli *cli = DFU_CLI(b); + struct bt_mesh_msg_ctx ctx = MSG_CTX(cli, dst); + + BT_MESH_MODEL_BUF_DEFINE(buf, BT_MESH_DFU_OP_UPDATE_GET, 0); + bt_mesh_model_msg_init(&buf, BT_MESH_DFU_OP_UPDATE_GET); + + (void)tx(cli->mod, &ctx, &buf, &send_cb, cli); +} + +static void send_update_cancel(struct bt_mesh_blob_cli *b, uint16_t dst) +{ + struct bt_mesh_dfu_cli *cli = DFU_CLI(b); + struct bt_mesh_msg_ctx ctx = MSG_CTX(cli, dst); + + BT_MESH_MODEL_BUF_DEFINE(buf, BT_MESH_DFU_OP_UPDATE_CANCEL, 0); + bt_mesh_model_msg_init(&buf, BT_MESH_DFU_OP_UPDATE_CANCEL); + + (void)tx(cli->mod, &ctx, &buf, &send_cb, cli); +} + +static void send_update_apply(struct bt_mesh_blob_cli *b, uint16_t dst) +{ + struct bt_mesh_dfu_cli *cli = DFU_CLI(b); + struct bt_mesh_msg_ctx ctx = MSG_CTX(cli, dst); + + BT_MESH_MODEL_BUF_DEFINE(buf, BT_MESH_DFU_OP_UPDATE_APPLY, 0); + bt_mesh_model_msg_init(&buf, BT_MESH_DFU_OP_UPDATE_APPLY); + + (void)tx(cli->mod, &ctx, &buf, &send_cb, cli); +} + +/******************************************************************************* + * Distribution procedure + ******************************************************************************/ +static void transfer(struct bt_mesh_blob_cli *b); +static void apply(struct bt_mesh_dfu_cli *cli); +static void applied(struct bt_mesh_blob_cli *b); +static void confirmed(struct bt_mesh_blob_cli *b); +static void cancelled(struct bt_mesh_blob_cli *b); + +static void initiate(struct bt_mesh_dfu_cli *cli) +{ + struct blob_cli_broadcast_ctx tx = { + .send = send_update_start, + .next = transfer, + .acked = true, + }; + struct bt_mesh_dfu_target *target; + int img_idx = -1; + + /** If firmware img index is the same for all targets, we can send Firmware Update Start + * message using multicast address. Otherwise, it has to be send in a unicast way. + */ + TARGETS_FOR_EACH(cli, target) { + if (img_idx == -1) { + img_idx = target->img_idx; + } else if (target->img_idx != img_idx) { + tx.force_unicast = true; + break; + } + } + + LOG_DBG(""); + + cli->op = BT_MESH_DFU_OP_UPDATE_STATUS; + cli->xfer.state = STATE_TRANSFER; + + blob_cli_broadcast(&cli->blob, &tx); +} + +static void skip_targets_from_broadcast(struct bt_mesh_dfu_cli *cli, bool skip) +{ + struct bt_mesh_dfu_target *target; + + TARGETS_FOR_EACH(cli, target) { + /* If distributor is in the targets list, or target is in Verify phase, + * disable it until Retrieve Capabilities and BLOB Transfer procedures + * are completed. + */ + if (bt_mesh_has_addr(target->blob.addr) || + target->phase == BT_MESH_DFU_PHASE_VERIFY) { + target->blob.skip = skip; + break; + } + } +} + +static bool transfer_skip(struct bt_mesh_dfu_cli *cli) +{ + struct bt_mesh_dfu_target *target; + + TARGETS_FOR_EACH(cli, target) { + if (!bt_mesh_has_addr(target->blob.addr) || !target->blob.skip) { + return false; + } + } + + return true; +} + +static void transfer(struct bt_mesh_blob_cli *b) +{ + struct bt_mesh_dfu_cli *cli = DFU_CLI(b); + int err; + + LOG_DBG(""); + + if (!targets_active(cli)) { + dfu_failed(cli, BT_MESH_DFU_ERR_INTERNAL); + return; + } + + skip_targets_from_broadcast(cli, true); + + if (transfer_skip(cli)) { + /* If distributor only updates itself, or all targets are in Verify phase, + * proceed to the refresh step immediately. + */ + refresh(cli); + return; + } + + if (cli->xfer.flags & FLAG_RESUME) { + cli->xfer.flags ^= FLAG_RESUME; + err = bt_mesh_blob_cli_resume(b); + if (err) { + LOG_ERR("Resuming BLOB xfer failed: %d", err); + dfu_failed(cli, BT_MESH_DFU_ERR_BLOB_XFER_BUSY); + } + } else if (cli->xfer.flags & FLAG_SKIP_CAPS_GET) { + cli->xfer.flags ^= FLAG_SKIP_CAPS_GET; + err = bt_mesh_blob_cli_send(b, b->inputs, &cli->xfer.blob, cli->xfer.io); + if (err) { + LOG_ERR("Starting BLOB xfer failed: %d", err); + dfu_failed(cli, BT_MESH_DFU_ERR_BLOB_XFER_BUSY); + } + } else { + err = bt_mesh_blob_cli_caps_get(&cli->blob, cli->blob.inputs); + if (err) { + LOG_ERR("Failed starting blob xfer: %d", err); + dfu_failed(cli, BT_MESH_DFU_ERR_BLOB_XFER_BUSY); + } + } +} + +static void refreshed(struct bt_mesh_blob_cli *b) +{ + struct bt_mesh_dfu_cli *cli = DFU_CLI(b); + + if (!targets_active(cli)) { + dfu_failed(cli, BT_MESH_DFU_ERR_INTERNAL); + return; + } + + cli->xfer.state = STATE_VERIFIED; + dfu_complete(cli); +} + +static void refresh(struct bt_mesh_dfu_cli *cli) +{ + const struct blob_cli_broadcast_ctx tx = { + .send = send_update_get, + .next = refreshed, + .acked = true + }; + + LOG_DBG(""); + + cli->xfer.state = STATE_REFRESH; + cli->op = BT_MESH_DFU_OP_UPDATE_STATUS; + + /* If distributor is in the targets list, enable it again so it participates in Distribute + * Firmware procedure. + */ + skip_targets_from_broadcast(cli, false); + + blob_cli_broadcast(&cli->blob, &tx); +} + +static void apply(struct bt_mesh_dfu_cli *cli) +{ + const struct blob_cli_broadcast_ctx tx = { + .send = send_update_apply, + .next = applied, + .acked = true + }; + + LOG_DBG(""); + + cli->xfer.state = STATE_APPLY; + cli->op = BT_MESH_DFU_OP_UPDATE_STATUS; + + blob_cli_broadcast(&cli->blob, &tx); +} + +static void applied(struct bt_mesh_blob_cli *b) +{ + struct bt_mesh_dfu_cli *cli = DFU_CLI(b); + + if (!targets_active(cli)) { + dfu_failed(cli, BT_MESH_DFU_ERR_INTERNAL); + return; + } + + dfu_applied(cli); +} + +static enum bt_mesh_dfu_iter target_img_cb(struct bt_mesh_dfu_cli *cli, + struct bt_mesh_msg_ctx *ctx, + uint8_t idx, uint8_t cnt, + const struct bt_mesh_dfu_img *img, + void *cb_data) +{ + struct bt_mesh_dfu_target *target; + + if ((img->fwid_len != cli->xfer.slot->fwid_len) || + memcmp(cli->xfer.slot->fwid, img->fwid, img->fwid_len)) { + return BT_MESH_DFU_ITER_CONTINUE; + } + + target = target_get(cli, ctx->addr); + if (target) { + LOG_DBG("SUCCESS: 0x%04x applied dfu (as image %u)", ctx->addr, + idx); + target->phase = BT_MESH_DFU_PHASE_APPLY_SUCCESS; + blob_cli_broadcast_rsp(&cli->blob, &target->blob); + } else { + LOG_WRN("Target 0x%04x not found", ctx->addr); + } + + return BT_MESH_DFU_ITER_STOP; +} + +static void confirm(struct bt_mesh_dfu_cli *cli) +{ + const struct blob_cli_broadcast_ctx tx = { + .send = send_info_get, + .next = confirmed, + .acked = true, + .optional = true, + }; + + LOG_DBG(""); + + cli->op = BT_MESH_DFU_OP_UPDATE_INFO_STATUS; + cli->req.img_cb = target_img_cb; + cli->req.ttl = cli->blob.inputs->ttl; + + blob_cli_broadcast(&cli->blob, &tx); +} + +static void confirmed(struct bt_mesh_blob_cli *b) +{ + struct bt_mesh_dfu_cli *cli = DFU_CLI(b); + struct bt_mesh_dfu_target *target; + bool success = false; + + cli->req.img_cb = NULL; + + TARGETS_FOR_EACH(cli, target) { + if (target->status != BT_MESH_DFU_SUCCESS) { + /* Target either failed at earlier stage or during confirmation. In any + * case, the app is already notified. Don't consider the target here. + */ + continue; + } + + if (target->effect == BT_MESH_DFU_EFFECT_UNPROV) { + if (!target->blob.acked) { + success = true; + continue; + } + + LOG_DBG("Target 0x%04x still provisioned", target->blob.addr); + target->phase = BT_MESH_DFU_PHASE_APPLY_FAIL; + target_failed(cli, target, BT_MESH_DFU_ERR_INTERNAL); + } else if (!target->blob.acked) { + LOG_DBG("Target 0x%04x failed to respond", target->blob.addr); + target->phase = BT_MESH_DFU_PHASE_APPLY_FAIL; + target_failed(cli, target, BT_MESH_DFU_ERR_INTERNAL); + } else if (target->status == BT_MESH_DFU_SUCCESS) { + success = true; + } + } + + if (success) { + cli->xfer.state = STATE_IDLE; + cli->xfer.flags = FLAG_COMPLETED; + + if (cli->cb && cli->cb->confirmed) { + cli->cb->confirmed(cli); + } + } else { + dfu_failed(cli, BT_MESH_DFU_ERR_INTERNAL); + } +} + +static void cancel(struct bt_mesh_dfu_cli *cli) +{ + const struct blob_cli_broadcast_ctx tx = { + .send = send_update_cancel, + .next = cancelled, + .acked = true + }; + + LOG_DBG(""); + + cli->op = BT_MESH_DFU_OP_UPDATE_STATUS; + + blob_cli_broadcast(&cli->blob, &tx); +} + +static void cancelled(struct bt_mesh_blob_cli *b) +{ + struct bt_mesh_dfu_cli *cli = DFU_CLI(b); + + cli->xfer.flags |= FLAG_CANCELLED; + dfu_failed(cli, BT_MESH_DFU_ERR_INTERNAL); +} + +/******************************************************************************* + * Message handlers + ******************************************************************************/ + +static int handle_status(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + struct bt_mesh_dfu_cli *cli = mod->rt->user_data; + enum bt_mesh_dfu_status status; + enum bt_mesh_dfu_phase phase; + struct bt_mesh_dfu_target *target; + uint8_t byte; + + byte = net_buf_simple_pull_u8(buf); + status = byte & BIT_MASK(3); + phase = byte >> 5; + + if (cli->req.type == REQ_STATUS && cli->req.addr == ctx->addr) { + if (cli->req.params) { + struct bt_mesh_dfu_target_status *rsp = cli->req.params; + + rsp->status = status; + rsp->phase = phase; + if (buf->len == 13) { + rsp->ttl = net_buf_simple_pull_u8(buf); + rsp->effect = net_buf_simple_pull_u8(buf) & BIT_MASK(5); + 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) { + return -EINVAL; + } + + rsp->ttl = 0U; + rsp->effect = BT_MESH_DFU_EFFECT_NONE; + rsp->timeout_base = 0U; + rsp->blob_id = 0U; + rsp->img_idx = 0U; + } + k_sem_give(&cli->req.sem); + } + if (cli->op != BT_MESH_DFU_OP_UPDATE_STATUS) { + return 0; + } + + target = target_get(cli, ctx->addr); + if (!target) { + LOG_WRN("Unknown target 0x%04x", ctx->addr); + return -ENOENT; + } + + LOG_DBG("%u phase: %u, cur state: %u", status, phase, cli->xfer.state); + + target->phase = phase; + + if (cli->xfer.state == STATE_APPLY && phase == BT_MESH_DFU_PHASE_IDLE && + status == BT_MESH_DFU_ERR_WRONG_PHASE) { + LOG_DBG("Response received with Idle phase"); + blob_cli_broadcast_rsp(&cli->blob, &target->blob); + return 0; + } + + if (status != BT_MESH_DFU_SUCCESS) { + target_failed(cli, target, status); + blob_cli_broadcast_rsp(&cli->blob, &target->blob); + return 0; + } + + if (buf->len == 13) { + net_buf_simple_pull_u8(buf); /* ttl */ + target->effect = net_buf_simple_pull_u8(buf) & BIT_MASK(5); + net_buf_simple_pull_le16(buf); /* timeout */ + + if (net_buf_simple_pull_le64(buf) != cli->xfer.blob.id) { + LOG_WRN("Invalid BLOB ID"); + target_failed(cli, target, BT_MESH_DFU_ERR_BLOB_XFER_BUSY); + blob_cli_broadcast_rsp(&cli->blob, &target->blob); + return 0; + } + + target->img_idx = net_buf_simple_pull_u8(buf); + + LOG_DBG("Target 0x%04x receiving transfer", ctx->addr); + } else if (buf->len) { + return -EINVAL; + } + + if (cli->xfer.state == STATE_REFRESH) { + if (phase == BT_MESH_DFU_PHASE_VERIFY) { + LOG_DBG("Still pending..."); + return 0; + } else if (phase == BT_MESH_DFU_PHASE_VERIFY_FAIL) { + LOG_WRN("Verification failed on target 0x%04x", + target->blob.addr); + target_failed(cli, target, BT_MESH_DFU_ERR_WRONG_PHASE); + } + } else if (cli->xfer.state == STATE_APPLY) { + if (phase != BT_MESH_DFU_PHASE_APPLYING && + (target->effect == BT_MESH_DFU_EFFECT_UNPROV || + phase != BT_MESH_DFU_PHASE_IDLE)) { + LOG_WRN("Target 0x%04x in phase %u after apply", + target->blob.addr, phase); + target_failed(cli, target, BT_MESH_DFU_ERR_WRONG_PHASE); + blob_cli_broadcast_rsp(&cli->blob, &target->blob); + return 0; + } + return 0; + } else if (cli->xfer.state == STATE_CONFIRM) { + if (phase == BT_MESH_DFU_PHASE_APPLYING) { + LOG_DBG("Still pending..."); + return 0; + } + + if (phase != BT_MESH_DFU_PHASE_IDLE) { + LOG_WRN("Target 0x%04x in phase %u after apply", + target->blob.addr, phase); + target->phase = BT_MESH_DFU_PHASE_APPLY_FAIL; + target_failed(cli, target, BT_MESH_DFU_ERR_WRONG_PHASE); + blob_cli_broadcast_rsp(&cli->blob, &target->blob); + return 0; + } + } else if (cli->xfer.state == STATE_CANCEL) { + target->phase = BT_MESH_DFU_PHASE_TRANSFER_CANCELED; + } + + blob_cli_broadcast_rsp(&cli->blob, &target->blob); + + return 0; +} + +static int handle_info_status(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + struct bt_mesh_dfu_cli *cli = mod->rt->user_data; + struct bt_mesh_dfu_target *target; + enum bt_mesh_dfu_iter it = BT_MESH_DFU_ITER_CONTINUE; + uint8_t img_cnt, idx; + + if (!cli->req.img_cb || + (cli->req.type == REQ_IMG && cli->req.addr != ctx->addr)) { + LOG_WRN("Unexpected info status from 0x%04x", ctx->addr); + return 0; + } + + img_cnt = net_buf_simple_pull_u8(buf); + if (img_cnt < cli->req.img_cnt) { + cli->req.img_cnt = img_cnt; + } + + idx = net_buf_simple_pull_u8(buf); + if (idx >= img_cnt) { + LOG_WRN("Invalid idx %u", idx); + return -ENOENT; + } + + LOG_DBG("Image list from 0x%04x from index %u", ctx->addr, idx); + + while (buf->len && cli->req.img_cb && idx < cli->req.img_cnt) { + char uri_buf[CONFIG_BT_MESH_DFU_URI_MAXLEN + 1]; + struct bt_mesh_dfu_img img; + size_t uri_len; + + img.fwid_len = net_buf_simple_pull_u8(buf); + if (buf->len < img.fwid_len + 1) { + LOG_WRN("Invalid format: fwid"); + return -EINVAL; + } + + img.fwid = net_buf_simple_pull_mem(buf, img.fwid_len); + + uri_len = net_buf_simple_pull_u8(buf); + if (buf->len < uri_len) { + LOG_WRN("Invalid format: uri"); + return -EINVAL; + } + + LOG_DBG("\tImage %u\n\r\tfwid: %s", idx, bt_hex(img.fwid, img.fwid_len)); + + if (uri_len) { + size_t uri_buf_len = + MIN(CONFIG_BT_MESH_DFU_URI_MAXLEN, uri_len); + + memcpy(uri_buf, net_buf_simple_pull_mem(buf, uri_len), + uri_buf_len); + uri_buf[uri_buf_len] = '\0'; + img.uri = uri_buf; + } else { + img.uri = NULL; + } + + it = cli->req.img_cb(cli, ctx, idx, img_cnt, &img, + cli->req.params); + if (it != BT_MESH_DFU_ITER_CONTINUE) { + if (cli->req.type == REQ_IMG) { + k_sem_give(&cli->req.sem); + } + + return 0; + } + + idx++; + } + + if (idx < cli->req.img_cnt) { + LOG_DBG("Fetching more images (%u/%u)", idx, cli->req.img_cnt); + ctx->send_ttl = cli->req.ttl; + info_get(cli, ctx, idx, cli->req.img_cnt - idx, + (cli->req.type == REQ_IMG) ? NULL : &send_cb); + return 0; + } + + if (cli->req.type == REQ_IMG) { + k_sem_give(&cli->req.sem); + return 0; + } + + /* Confirm-procedure termination: */ + target = target_get(cli, ctx->addr); + if (target) { + LOG_WRN("Target 0x%04x failed to apply image: %s", ctx->addr, + bt_hex(cli->xfer.slot->fwid, cli->xfer.slot->fwid_len)); + target->phase = BT_MESH_DFU_PHASE_APPLY_FAIL; + target_failed(cli, target, BT_MESH_DFU_ERR_INTERNAL); + blob_cli_broadcast_rsp(&cli->blob, &target->blob); + } + + return 0; +} + +static int handle_metadata_status(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + struct bt_mesh_dfu_cli *cli = mod->rt->user_data; + struct bt_mesh_dfu_metadata_status *rsp = cli->req.params; + uint8_t hdr, idx; + + hdr = net_buf_simple_pull_u8(buf); + idx = net_buf_simple_pull_u8(buf); + + if (cli->req.type != REQ_METADATA || ctx->addr != cli->req.addr || + idx != rsp->idx) { + LOG_WRN("Unexpected metadata status from 0x%04x img %u", + ctx->addr, idx); + if (cli->req.type != REQ_METADATA) { + LOG_WRN("Expected %u", cli->req.type); + } else { + LOG_WRN("Expected 0x%04x img %u", cli->req.addr, idx); + } + + return 0; + } + + rsp->status = hdr & BIT_MASK(3); + rsp->effect = (hdr >> 3); + k_sem_give(&cli->req.sem); + + return 0; +} + +const struct bt_mesh_model_op _bt_mesh_dfu_cli_op[] = { + {BT_MESH_DFU_OP_UPDATE_STATUS, BT_MESH_LEN_MIN(1), handle_status}, + {BT_MESH_DFU_OP_UPDATE_INFO_STATUS, BT_MESH_LEN_MIN(2), handle_info_status}, + {BT_MESH_DFU_OP_UPDATE_METADATA_STATUS, BT_MESH_LEN_EXACT(2), handle_metadata_status}, + BT_MESH_MODEL_OP_END, +}; + +static int dfu_cli_init(const struct bt_mesh_model *mod) +{ + struct bt_mesh_dfu_cli *cli = mod->rt->user_data; + + if (mod->rt->elem_idx != 0) { + LOG_ERR("DFU update client must be instantiated on first elem"); + return -EINVAL; + } + + cli->mod = mod; + + if (IS_ENABLED(CONFIG_BT_MESH_MODEL_EXTENSIONS)) { + bt_mesh_model_extend(mod, cli->blob.mod); + } + + k_sem_init(&cli->req.sem, 0, 1); + + return 0; +} + +static void dfu_cli_reset(const struct bt_mesh_model *mod) +{ + struct bt_mesh_dfu_cli *cli = mod->rt->user_data; + + cli->req.type = REQ_NONE; + cli->req.addr = BT_MESH_ADDR_UNASSIGNED; + cli->req.img_cnt = 0; + cli->req.img_cb = NULL; + cli->xfer.state = STATE_IDLE; + cli->xfer.flags = 0; +} + +const struct bt_mesh_model_cb _bt_mesh_dfu_cli_cb = { + .init = dfu_cli_init, + .reset = dfu_cli_reset, +}; + +/******************************************************************************* + * Public API + ******************************************************************************/ + +int bt_mesh_dfu_cli_send(struct bt_mesh_dfu_cli *cli, + const struct bt_mesh_blob_cli_inputs *inputs, + const struct bt_mesh_blob_io *io, + const struct bt_mesh_dfu_cli_xfer *xfer) +{ + struct bt_mesh_dfu_target *target; + + if (bt_mesh_dfu_cli_is_busy(cli)) { + return -EBUSY; + } + + cli->xfer.blob.mode = xfer->mode; + cli->xfer.blob.size = xfer->slot->size; + + if (xfer->blob_id == 0) { + int err = bt_rand(&cli->xfer.blob.id, sizeof(cli->xfer.blob.id)); + + if (err) { + return err; + } + } else { + cli->xfer.blob.id = xfer->blob_id; + } + + cli->xfer.io = io; + cli->blob.inputs = inputs; + cli->xfer.slot = xfer->slot; + cli->xfer.flags = 0U; + + if (xfer->blob_params) { + cli->xfer.flags |= FLAG_SKIP_CAPS_GET; + cli->xfer.blob.block_size_log = xfer->blob_params->block_size_log; + cli->xfer.blob.chunk_size = xfer->blob_params->chunk_size; + } + + /* Phase will be set based on target status messages: */ + TARGETS_FOR_EACH(cli, target) { + target->status = BT_MESH_DFU_SUCCESS; + target->phase = BT_MESH_DFU_PHASE_UNKNOWN; + } + + initiate(cli); + return 0; +} + +int bt_mesh_dfu_cli_suspend(struct bt_mesh_dfu_cli *cli) +{ + int err; + + err = bt_mesh_blob_cli_suspend(&cli->blob); + if (!err) { + cli->xfer.state = STATE_SUSPENDED; + } + + return err; +} + +int bt_mesh_dfu_cli_resume(struct bt_mesh_dfu_cli *cli) +{ + struct bt_mesh_dfu_target *target; + + if (cli->xfer.state != STATE_SUSPENDED) { + return -EINVAL; + } + + cli->xfer.flags = FLAG_RESUME; + + /* Restore timed out targets. */ + TARGETS_FOR_EACH(cli, target) { + if (!!target->blob.timedout) { + target->status = BT_MESH_DFU_SUCCESS; + target->phase = BT_MESH_DFU_PHASE_UNKNOWN; + } + } + + initiate(cli); + return 0; +} + +int bt_mesh_dfu_cli_cancel(struct bt_mesh_dfu_cli *cli, + struct bt_mesh_msg_ctx *ctx) +{ + if (ctx) { + int err; + + err = req_setup(cli, REQ_STATUS, ctx->addr, NULL); + if (err) { + return err; + } + + BT_MESH_MODEL_BUF_DEFINE(buf, BT_MESH_DFU_OP_UPDATE_CANCEL, 0); + bt_mesh_model_msg_init(&buf, BT_MESH_DFU_OP_UPDATE_CANCEL); + + err = bt_mesh_model_send(cli->mod, ctx, &buf, NULL, NULL); + if (err) { + cli->req.type = REQ_NONE; + return err; + } + + return req_wait(cli, K_MSEC(dfu_cli_timeout)); + } + + if (cli->xfer.state == STATE_IDLE) { + return -EALREADY; + } + + cli->xfer.state = STATE_CANCEL; + blob_cli_broadcast_abort(&cli->blob); + cancel(cli); + return 0; +} + +int bt_mesh_dfu_cli_apply(struct bt_mesh_dfu_cli *cli) +{ + if (cli->xfer.state != STATE_VERIFIED) { + return -EBUSY; + } + + apply(cli); + + return 0; +} + +int bt_mesh_dfu_cli_confirm(struct bt_mesh_dfu_cli *cli) +{ + if (cli->xfer.state != STATE_APPLIED) { + return -EBUSY; + } + + cli->xfer.state = STATE_CONFIRM; + confirm(cli); + + return 0; +} + +uint8_t bt_mesh_dfu_cli_progress(struct bt_mesh_dfu_cli *cli) +{ + if (cli->xfer.state == STATE_TRANSFER) { + return bt_mesh_blob_cli_xfer_progress_active_get(&cli->blob); + } + + if (cli->xfer.state == STATE_IDLE) { + if (cli->xfer.flags & FLAG_COMPLETED) { + return 100U; + } + return 0U; + } + + return 100U; +} + +bool bt_mesh_dfu_cli_is_busy(struct bt_mesh_dfu_cli *cli) +{ + return (cli->xfer.state == STATE_TRANSFER || + cli->xfer.state == STATE_REFRESH || + cli->xfer.state == STATE_APPLY || + cli->xfer.state == STATE_CONFIRM) && + !(cli->xfer.flags & FLAG_FAILED); +} + +int bt_mesh_dfu_cli_imgs_get(struct bt_mesh_dfu_cli *cli, + struct bt_mesh_msg_ctx *ctx, + bt_mesh_dfu_img_cb_t cb, void *cb_data, + uint8_t max_count) +{ + int err; + + if (cli->req.img_cb) { + return -EBUSY; + } + + err = req_setup(cli, REQ_IMG, ctx->addr, NULL); + if (err) { + return err; + } + + cli->req.img_cb = cb; + cli->req.params = cb_data; + cli->req.ttl = ctx->send_ttl; + cli->req.img_cnt = max_count; + + err = info_get(cli, ctx, 0, cli->req.img_cnt, NULL); + if (err) { + cli->req.img_cb = NULL; + cli->req.type = REQ_NONE; + return err; + } + + err = req_wait(cli, K_MSEC(dfu_cli_timeout)); + + cli->req.img_cb = NULL; + + return err; +} + +int bt_mesh_dfu_cli_metadata_check(struct bt_mesh_dfu_cli *cli, + struct bt_mesh_msg_ctx *ctx, uint8_t img_idx, + const struct bt_mesh_dfu_slot *slot, + struct bt_mesh_dfu_metadata_status *rsp) +{ + int err; + + err = req_setup(cli, REQ_METADATA, ctx->addr, rsp); + if (err) { + return err; + } + + BT_MESH_MODEL_BUF_DEFINE(buf, BT_MESH_DFU_OP_UPDATE_METADATA_CHECK, + 1 + CONFIG_BT_MESH_DFU_METADATA_MAXLEN); + bt_mesh_model_msg_init(&buf, BT_MESH_DFU_OP_UPDATE_METADATA_CHECK); + + net_buf_simple_add_u8(&buf, img_idx); + + if (slot->metadata_len) { + net_buf_simple_add_mem(&buf, slot->metadata, + slot->metadata_len); + } + + rsp->idx = img_idx; + + err = bt_mesh_model_send(cli->mod, ctx, &buf, NULL, NULL); + if (err) { + cli->req.type = REQ_NONE; + return err; + } + + return req_wait(cli, K_MSEC(dfu_cli_timeout)); +} + +int bt_mesh_dfu_cli_status_get(struct bt_mesh_dfu_cli *cli, + struct bt_mesh_msg_ctx *ctx, + struct bt_mesh_dfu_target_status *rsp) +{ + int err; + + err = req_setup(cli, REQ_STATUS, ctx->addr, rsp); + if (err) { + return err; + } + + BT_MESH_MODEL_BUF_DEFINE(buf, BT_MESH_DFU_OP_UPDATE_GET, 0); + bt_mesh_model_msg_init(&buf, BT_MESH_DFU_OP_UPDATE_GET); + + err = bt_mesh_model_send(cli->mod, ctx, &buf, NULL, NULL); + if (err) { + cli->req.type = REQ_NONE; + return err; + } + + return req_wait(cli, K_MSEC(dfu_cli_timeout)); +} + +int32_t bt_mesh_dfu_cli_timeout_get(void) +{ + return dfu_cli_timeout; +} + +void bt_mesh_dfu_cli_timeout_set(int32_t t) +{ + dfu_cli_timeout = t; +} diff --git a/components/bt/esp_ble_mesh/v1.1/dfu/dfu_metadata.c b/components/bt/esp_ble_mesh/v1.1/dfu/dfu_metadata.c new file mode 100644 index 0000000000..51a2cd0306 --- /dev/null +++ b/components/bt/esp_ble_mesh/v1.1/dfu/dfu_metadata.c @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2021 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +#include +#include "crypto.h" +#include "access.h" + +int bt_mesh_dfu_metadata_decode(struct net_buf_simple *buf, + struct bt_mesh_dfu_metadata *metadata) +{ + if (buf->len < 12) { + return -EMSGSIZE; + } + + metadata->fw_ver.major = net_buf_simple_pull_u8(buf); + metadata->fw_ver.minor = net_buf_simple_pull_u8(buf); + metadata->fw_ver.revision = net_buf_simple_pull_le16(buf); + metadata->fw_ver.build_num = net_buf_simple_pull_le32(buf); + metadata->fw_size = net_buf_simple_pull_le24(buf); + metadata->fw_core_type = net_buf_simple_pull_u8(buf); + + if (metadata->fw_core_type & BT_MESH_DFU_FW_CORE_TYPE_APP) { + if (buf->len < 6) { + return -EMSGSIZE; + } + + metadata->comp_hash = net_buf_simple_pull_le32(buf); + metadata->elems = net_buf_simple_pull_le16(buf); + } + + metadata->user_data = buf->len > 0 ? buf->data : NULL; + metadata->user_data_len = buf->len; + + return 0; +} + + +int bt_mesh_dfu_metadata_encode(const struct bt_mesh_dfu_metadata *metadata, + struct net_buf_simple *buf) +{ + size_t md_len_min = 12 + metadata->user_data_len; + + if (metadata->fw_core_type & BT_MESH_DFU_FW_CORE_TYPE_APP) { + md_len_min += 6; + } + + if (net_buf_simple_tailroom(buf) < md_len_min) { + return -EMSGSIZE; + } + + net_buf_simple_add_u8(buf, metadata->fw_ver.major); + net_buf_simple_add_u8(buf, metadata->fw_ver.minor); + net_buf_simple_add_le16(buf, metadata->fw_ver.revision); + net_buf_simple_add_le32(buf, metadata->fw_ver.build_num); + net_buf_simple_add_le24(buf, metadata->fw_size); + net_buf_simple_add_u8(buf, metadata->fw_core_type); + net_buf_simple_add_le32(buf, metadata->comp_hash); + net_buf_simple_add_le16(buf, metadata->elems); + + if (metadata->user_data_len > 0) { + net_buf_simple_add_mem(buf, metadata->user_data, metadata->user_data_len); + } + + return 0; +} + +int bt_mesh_dfu_metadata_comp_hash_get(struct net_buf_simple *buf, uint8_t *key, uint32_t *hash) +{ + uint8_t mac[16]; + int err; + struct bt_mesh_sg sg = {.data = buf->data, .len = buf->len}; + + err = bt_mesh_aes_cmac_raw_key(key, &sg, 1, mac); + if (err) { + return err; + } + + *hash = sys_get_le32(mac); + + return 0; +} + +int bt_mesh_dfu_metadata_comp_hash_local_get(uint8_t *key, uint32_t *hash) +{ + NET_BUF_SIMPLE_DEFINE(buf, BT_MESH_TX_SDU_MAX); + int err; + + err = bt_mesh_comp_data_get_page_0(&buf, 0); + if (err) { + return err; + } + + err = bt_mesh_dfu_metadata_comp_hash_get(&buf, key, hash); + return err; +} diff --git a/components/bt/esp_ble_mesh/v1.1/dfu/dfu_slot.c b/components/bt/esp_ble_mesh/v1.1/dfu/dfu_slot.c new file mode 100644 index 0000000000..c11c16d44f --- /dev/null +++ b/components/bt/esp_ble_mesh/v1.1/dfu/dfu_slot.c @@ -0,0 +1,402 @@ +/* + * Copyright (c) 2020 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "dfu_slot.h" +#include +#include +#include +#include +#include + +#define LOG_LEVEL CONFIG_BT_MESH_DFU_LOG_LEVEL +#include +LOG_MODULE_REGISTER(bt_mesh_dfu_slot); + +#define SLOT_ENTRY_BUFLEN 25 + +#define DFU_SLOT_SETTINGS_PATH "bt/mesh-dfu/slot" + +#define HEADER_SIZE offsetof(struct slot, slot.fwid) + +#define PROP_HEADER "h" +#define PROP_FWID "id" +#define PROP_METADATA "m" + +static sys_slist_t list; + +static struct slot { + uint32_t idx; + struct bt_mesh_dfu_slot slot; + sys_snode_t n; +} slots[CONFIG_BT_MESH_DFU_SLOT_CNT]; + +static uint32_t slot_index; + +static char *slot_entry_encode(uint16_t idx, char buf[SLOT_ENTRY_BUFLEN], + const char *property) +{ + snprintf(buf, SLOT_ENTRY_BUFLEN, DFU_SLOT_SETTINGS_PATH "/%x/%s", idx, + property); + + return buf; +} + +static bool slot_eq(const struct bt_mesh_dfu_slot *slot, + const uint8_t *fwid, size_t fwid_len) +{ + return (slot->fwid_len == fwid_len) && + !memcmp(fwid, slot->fwid, fwid_len); +} + +static bool is_slot_committed(struct slot *slot_to_check) +{ + struct slot *s; + + SYS_SLIST_FOR_EACH_CONTAINER(&list, s, n) { + if (s == slot_to_check) { + return true; + } + } + + return false; +} + +static int slot_store(const struct slot *slot_to_store) +{ + uint16_t idx = ARRAY_INDEX(slots, slot_to_store); + char buf[SLOT_ENTRY_BUFLEN]; + int err; + + err = settings_save_one(slot_entry_encode(idx, buf, PROP_HEADER), + slot_to_store, HEADER_SIZE); + if (err) { + return err; + } + + err = settings_save_one(slot_entry_encode(idx, buf, PROP_FWID), + slot_to_store->slot.fwid, slot_to_store->slot.fwid_len); + if (err) { + return err; + } + + err = settings_save_one(slot_entry_encode(idx, buf, + PROP_METADATA), + slot_to_store->slot.metadata, slot_to_store->slot.metadata_len); + + return err; +} + +static void slot_erase(struct slot *slot_to_erase) +{ + uint16_t idx = ARRAY_INDEX(slots, slot_to_erase); + char buf[SLOT_ENTRY_BUFLEN]; + + (void)settings_delete(slot_entry_encode(idx, buf, PROP_HEADER)); + (void)settings_delete(slot_entry_encode(idx, buf, PROP_FWID)); + (void)settings_delete(slot_entry_encode(idx, buf, PROP_METADATA)); +} + +static void slot_index_defrag(void) +{ + slot_index = 0; + struct slot *s; + + SYS_SLIST_FOR_EACH_CONTAINER(&list, s, n) { + s->idx = ++slot_index; + slot_store(s); + } +} + +int bt_mesh_dfu_slot_count(void) +{ + int cnt = 0; + sys_snode_t *n; + + SYS_SLIST_FOR_EACH_NODE(&list, n) { + cnt++; + } + + return cnt; +} + +struct bt_mesh_dfu_slot *bt_mesh_dfu_slot_reserve(void) +{ + struct slot *slot = NULL; + + for (int i = 0; i < ARRAY_SIZE(slots); ++i) { + if (slots[i].idx == 0) { + slot = &slots[i]; + break; + } + } + + if (!slot) { + LOG_WRN("No space"); + return NULL; + } + + if (slot_index == UINT32_MAX) { + slot_index_defrag(); + } + + slot->slot.fwid_len = 0; + slot->slot.metadata_len = 0; + slot->slot.size = 0; + slot->idx = ++slot_index; + + LOG_DBG("Reserved slot #%u", slot - &slots[0]); + + return &slot->slot; +} + +int bt_mesh_dfu_slot_info_set(struct bt_mesh_dfu_slot *dfu_slot, size_t size, + const uint8_t *metadata, size_t metadata_len) +{ + struct slot *slot = CONTAINER_OF(dfu_slot, struct slot, slot); + + if (metadata_len > CONFIG_BT_MESH_DFU_METADATA_MAXLEN) { + return -EFBIG; + } + + if (slot->idx == 0 || is_slot_committed(slot)) { + return -EINVAL; + } + + slot->slot.size = size; + slot->slot.metadata_len = metadata_len; + memcpy(slot->slot.metadata, metadata, metadata_len); + return 0; +} + +int bt_mesh_dfu_slot_fwid_set(struct bt_mesh_dfu_slot *dfu_slot, + const uint8_t *fwid, size_t fwid_len) +{ + struct slot *slot = CONTAINER_OF(dfu_slot, struct slot, slot); + + if (fwid_len > CONFIG_BT_MESH_DFU_FWID_MAXLEN) { + return -EFBIG; + } + + if (slot->idx == 0 || is_slot_committed(slot)) { + return -EINVAL; + } + + for (int i = 0; i < ARRAY_SIZE(slots); i++) { + if (slots[i].idx != 0 && + slot_eq(&slots[i].slot, fwid, fwid_len)) { + return is_slot_committed(&slots[i]) ? + -EEXIST : -EALREADY; + } + } + + slot->slot.fwid_len = fwid_len; + memcpy(slot->slot.fwid, fwid, fwid_len); + return 0; +} + +int bt_mesh_dfu_slot_commit(struct bt_mesh_dfu_slot *dfu_slot) +{ + int err; + struct slot *slot = CONTAINER_OF(dfu_slot, struct slot, slot); + + if (slot->idx == 0 || + slot->slot.fwid_len == 0 || + slot->slot.size == 0 || + is_slot_committed(slot)) { + return -EINVAL; + } + + err = slot_store(slot); + if (err) { + LOG_WRN("Store failed (err: %d)", err); + return err; + } + + sys_slist_append(&list, &slot->n); + + LOG_DBG("Stored slot #%u: %s", ARRAY_INDEX(slots, slot), + bt_hex(slot->slot.fwid, slot->slot.fwid_len)); + return 0; +} + +void bt_mesh_dfu_slot_release(const struct bt_mesh_dfu_slot *dfu_slot) +{ + struct slot *slot = CONTAINER_OF(dfu_slot, struct slot, slot); + + if (is_slot_committed(slot)) { + return; + } + + slot->idx = 0; +} + +int bt_mesh_dfu_slot_del(const struct bt_mesh_dfu_slot *dfu_slot) +{ + struct slot *slot = CONTAINER_OF(dfu_slot, struct slot, slot); + + if (!sys_slist_find_and_remove(&list, &slot->n)) { + return -EINVAL; + } + + int idx = ARRAY_INDEX(slots, slot); + + LOG_DBG("%u", idx); + + slot_erase(slot); + slot->idx = 0; + + return 0; +} + +void bt_mesh_dfu_slot_del_all(void) +{ + struct slot *s; + SYS_SLIST_FOR_EACH_CONTAINER(&list, s, n) { + slot_erase(s); + s->idx = 0; + } + + sys_slist_init(&list); +} + +const struct bt_mesh_dfu_slot *bt_mesh_dfu_slot_at(uint16_t img_idx) +{ + struct slot *s; + + SYS_SLIST_FOR_EACH_CONTAINER(&list, s, n) { + if (!img_idx--) { + return &s->slot; + } + } + + return NULL; +} + +int bt_mesh_dfu_slot_get(const uint8_t *fwid, size_t fwid_len, struct bt_mesh_dfu_slot **slot) +{ + struct slot *s; + int idx = 0; + + SYS_SLIST_FOR_EACH_CONTAINER(&list, s, n) { + if (slot_eq(&s->slot, fwid, fwid_len)) { + if (slot) { + *slot = &s->slot; + } + return idx; + } + idx++; + } + + return -ENOENT; +} + +int bt_mesh_dfu_slot_img_idx_get(const struct bt_mesh_dfu_slot *dfu_slot) +{ + struct slot *s; + int idx = 0; + + SYS_SLIST_FOR_EACH_CONTAINER(&list, s, n) { + if (&s->slot == dfu_slot) { + return idx; + } + idx++; + } + + return -ENOENT; +} + +size_t bt_mesh_dfu_slot_foreach(bt_mesh_dfu_slot_cb_t cb, void *user_data) +{ + enum bt_mesh_dfu_iter iter; + size_t cnt = 0; + struct slot *s; + + SYS_SLIST_FOR_EACH_CONTAINER(&list, s, n) { + cnt++; + + if (!cb) { + continue; + } + + iter = cb(&s->slot, user_data); + if (iter != BT_MESH_DFU_ITER_CONTINUE) { + break; + } + } + + return cnt; +} + +static int slot_data_load(const char *key, size_t len_rd, + settings_read_cb read_cb, void *cb_arg) +{ + const char *prop; + size_t len; + uint16_t idx; + + idx = strtol(key, NULL, 16); + + if (idx >= ARRAY_SIZE(slots)) { + return 0; + } + + len = settings_name_next(key, &prop); + + if (!strncmp(prop, PROP_HEADER, len)) { + if (read_cb(cb_arg, &slots[idx], HEADER_SIZE) > 0) { + struct slot *s, *prev = NULL; + + SYS_SLIST_FOR_EACH_CONTAINER(&list, s, n) { + if (s->idx > slots[idx].idx) { + break; + } + + prev = s; + } + + if (prev == NULL) { + sys_slist_prepend(&list, &slots[idx].n); + } else { + sys_slist_insert(&list, &prev->n, &slots[idx].n); + } + + if (slots[idx].idx >= slot_index) { + slot_index = slots[idx].idx + 1; + } + } + return 0; + } + + if (!strncmp(prop, PROP_FWID, len)) { + if (read_cb(cb_arg, &slots[idx].slot.fwid, + sizeof(slots[idx].slot.fwid)) < 0) { + slots[idx].idx = 0; + sys_slist_find_and_remove(&list, &slots[idx].n); + return 0; + } + + slots[idx].slot.fwid_len = len_rd; + return 0; + } + + if (!strncmp(prop, PROP_METADATA, len)) { + if (read_cb(cb_arg, &slots[idx].slot.metadata, + sizeof(slots[idx].slot.metadata)) < 0) { + slots[idx].idx = 0; + sys_slist_find_and_remove(&list, &slots[idx].n); + return 0; + } + + slots[idx].slot.metadata_len = len_rd; + return 0; + } + + return 0; +} + +SETTINGS_STATIC_HANDLER_DEFINE(bt_mesh_dfu_slots, DFU_SLOT_SETTINGS_PATH, NULL, + slot_data_load, NULL, NULL); diff --git a/components/bt/esp_ble_mesh/v1.1/dfu/dfu_slot.h b/components/bt/esp_ble_mesh/v1.1/dfu/dfu_slot.h new file mode 100644 index 0000000000..969c6d3d25 --- /dev/null +++ b/components/bt/esp_ble_mesh/v1.1/dfu/dfu_slot.h @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2020 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +/** @brief Slot iteration callback. + * + * @param slot A valid DFU image slot. + * @param user_data User data passed to @ref bt_mesh_dfu_slot_foreach. + * + * @return Iteration action determining next step. + */ +typedef enum bt_mesh_dfu_iter (*bt_mesh_dfu_slot_cb_t)( + const struct bt_mesh_dfu_slot *slot, void *user_data); + +/** @brief Get the number of slots committed to the firmware list. + * + * @return Number of committed slots. + */ +int bt_mesh_dfu_slot_count(void); + +/** @brief Reserve a new DFU image slot for a distributable image. + * + * A DFU image slot represents a single distributable DFU image with all its + * metadata. The slot data must be set using @ref bt_mesh_dfu_slot_info_set and + * @ref bt_mesh_dfu_slot_fwid_set, and the slot committed using + * @ref bt_mesh_dfu_slot_commit for the slot to be considered part of the slot + * list. + * + * @return A pointer to the reserved slot, or NULL if allocation failed. + */ +struct bt_mesh_dfu_slot *bt_mesh_dfu_slot_reserve(void); + +/** @brief Set the size and metadata for a reserved slot. + * + * @param dfu_slot Pointer to the reserved slot for which to set the + * metadata. + * @param size The size of the image. + * @param metadata Metadata or NULL. + * @param metadata_len Length of the metadata, at most @c + * CONFIG_BT_MESH_DFU_METADATA_MAXLEN. + * + * @return 0 on success, (negative) error code otherwise. + */ +int bt_mesh_dfu_slot_info_set(struct bt_mesh_dfu_slot *dfu_slot, size_t size, + const uint8_t *metadata, size_t metadata_len); + +/** @brief Set the new fwid for the incoming image for a reserved slot. + * + * @param dfu_slot Pointer to the reserved slot for which to set the fwid. + * @param fwid Fwid to set. + * @param fwid_len Length of the fwid, at most @c + * CONFIG_BT_MESH_DFU_FWID_MAXLEN. + * + * @return 0 on success, (negative) error code otherwise. + */ +int bt_mesh_dfu_slot_fwid_set(struct bt_mesh_dfu_slot *dfu_slot, + const uint8_t *fwid, size_t fwid_len); + +/** @brief Commit the reserved slot to the list of slots, and store it + * persistently. + * + * If the commit fails for any reason, the slot will still be in the reserved + * state after this call. + * + * @param dfu_slot Pointer to the reserved slot. + * + * @return 0 on success, (negative) error code otherwise. + */ +int bt_mesh_dfu_slot_commit(struct bt_mesh_dfu_slot *dfu_slot); + +/** @brief Release a reserved slot so that it can be reserved again. + * + * @param dfu_slot Pointer to the reserved slot. + */ +void bt_mesh_dfu_slot_release(const struct bt_mesh_dfu_slot *dfu_slot); + +/** @brief Delete a committed DFU image slot. + * + * @param slot Slot to delete. Must be a valid pointer acquired from this + * module. + * + * @return 0 on success, or (negative) error code on failure. + */ +int bt_mesh_dfu_slot_del(const struct bt_mesh_dfu_slot *slot); + +/** @brief Delete all DFU image slots. + * + * @return 0 on success, or (negative) error code on failure. + */ +void bt_mesh_dfu_slot_del_all(void); + +/** @brief Get the DFU image slot at the given firmware image list index. + * + * @param idx DFU image slot index. + * + * @return The DFU image slot at the given index, or NULL if no slot exists with the + * given index. + */ +const struct bt_mesh_dfu_slot *bt_mesh_dfu_slot_at(uint16_t img_idx); + +/** @brief Get the committed DFU image slot for the image with the given + * firmware ID. + * + * @param fwid Firmware ID. + * @param fwid_len Firmware ID length. + * @param slot Slot pointer to fill. + * + * @return Slot index on success, or negative error code on failure. + */ +int bt_mesh_dfu_slot_get(const uint8_t *fwid, size_t fwid_len, struct bt_mesh_dfu_slot **slot); + +/** @brief Get the index in the firmware image list for the given slot. + * + * @param slot Slot to find. + * + * @return Slot index on success, or negative error code on failure. + */ +int bt_mesh_dfu_slot_img_idx_get(const struct bt_mesh_dfu_slot *slot); + +/** @brief Iterate through all DFU image slots. + * + * Calls the callback for every DFU image slot or until the callback returns + * something other than @ref BT_MESH_DFU_ITER_CONTINUE. + * + * @param cb Callback to call for each slot, or NULL to just count the + * number of slots. + * @param user_data User data to pass to the callback. + * + * @return The number of slots iterated over. + */ +size_t bt_mesh_dfu_slot_foreach(bt_mesh_dfu_slot_cb_t cb, void *user_data); 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 new file mode 100644 index 0000000000..0cb4b5e524 --- /dev/null +++ b/components/bt/esp_ble_mesh/v1.1/dfu/dfu_srv.c @@ -0,0 +1,617 @@ +/* + * Copyright (c) 2020 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "dfu.h" +#include "blob.h" +#include "access.h" + +#define LOG_LEVEL CONFIG_BT_MESH_DFU_LOG_LEVEL +#include +LOG_MODULE_REGISTER(bt_mesh_dfu_srv); + +#define UPDATE_IDX_NONE 0xff + +BUILD_ASSERT((DFU_UPDATE_START_MSG_MAXLEN + BT_MESH_MODEL_OP_LEN(BT_MESH_DFU_OP_UPDATE_START) + + BT_MESH_MIC_SHORT) <= BT_MESH_RX_SDU_MAX, + "The Firmware Update Start message does not fit into the maximum incoming SDU size."); + +BUILD_ASSERT((DFU_UPDATE_INFO_STATUS_MSG_MINLEN + + BT_MESH_MODEL_OP_LEN(BT_MESH_DFU_OP_UPDATE_INFO_STATUS) + BT_MESH_MIC_SHORT) + <= BT_MESH_TX_SDU_MAX, + "The Firmware Update Info Status message does not fit into the maximum outgoing SDU " + "size."); + +static void store_state(struct bt_mesh_dfu_srv *srv) +{ + bt_mesh_model_data_store(srv->mod, false, NULL, &srv->update, + sizeof(srv->update)); +} + +static void erase_state(struct bt_mesh_dfu_srv *srv) +{ + bt_mesh_model_data_store(srv->mod, false, NULL, NULL, 0); +} + +static void xfer_failed(struct bt_mesh_dfu_srv *srv) +{ + if (!bt_mesh_dfu_srv_is_busy(srv) || + srv->update.idx >= srv->img_count) { + return; + } + + erase_state(srv); + + if (srv->cb->end) { + srv->cb->end(srv, &srv->imgs[srv->update.idx], false); + } +} + +static enum bt_mesh_dfu_status metadata_check(struct bt_mesh_dfu_srv *srv, + uint8_t idx, + struct net_buf_simple *buf, + enum bt_mesh_dfu_effect *effect) +{ + *effect = BT_MESH_DFU_EFFECT_NONE; + + if (idx >= srv->img_count) { + return BT_MESH_DFU_ERR_FW_IDX; + } + + if (!srv->cb->check) { + return BT_MESH_DFU_SUCCESS; + } + + if (srv->cb->check(srv, &srv->imgs[idx], buf, effect)) { + *effect = BT_MESH_DFU_EFFECT_NONE; + return BT_MESH_DFU_ERR_METADATA; + } + + return BT_MESH_DFU_SUCCESS; +} + +static void apply_rsp_sent(int err, void *cb_params) +{ + struct bt_mesh_dfu_srv *srv = cb_params; + + if (err) { + /* return phase back to give client one more chance. */ + srv->update.phase = BT_MESH_DFU_PHASE_VERIFY_OK; + LOG_WRN("Apply response failed, wait for retry (err %d)", err); + return; + } + + LOG_DBG(""); + + if (!srv->cb->apply || srv->update.idx == UPDATE_IDX_NONE) { + srv->update.phase = BT_MESH_DFU_PHASE_IDLE; + store_state(srv); + LOG_DBG("Prerequisites for apply callback are wrong"); + return; + } + + store_state(srv); + + err = srv->cb->apply(srv, &srv->imgs[srv->update.idx]); + if (err) { + srv->update.phase = BT_MESH_DFU_PHASE_IDLE; + store_state(srv); + LOG_DBG("Application apply callback failed (err %d)", err); + } +} + +static void apply_rsp_sending(uint16_t duration, int err, void *cb_params) +{ + if (err) { + apply_rsp_sent(err, cb_params); + } +} + +static void verify(struct bt_mesh_dfu_srv *srv) +{ + srv->update.phase = BT_MESH_DFU_PHASE_VERIFY; + + if (srv->update.idx >= srv->img_count) { + bt_mesh_dfu_srv_rejected(srv); + return; + } + + if (!srv->cb->end) { + bt_mesh_dfu_srv_verified(srv); + return; + } + + srv->cb->end(srv, &srv->imgs[srv->update.idx], true); + if (srv->update.phase == BT_MESH_DFU_PHASE_VERIFY) { + store_state(srv); + } +} + +static int handle_info_get(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + struct bt_mesh_dfu_srv *srv = mod->rt->user_data; + uint8_t idx, limit; + + if (srv->update.phase == BT_MESH_DFU_PHASE_APPLYING) { + LOG_INF("Still applying, not responding"); + return -EBUSY; + } + + idx = net_buf_simple_pull_u8(buf); + limit = net_buf_simple_pull_u8(buf); + + LOG_DBG("from %u (limit: %u)", idx, limit); + + NET_BUF_SIMPLE_DEFINE(rsp, BT_MESH_TX_SDU_MAX); + bt_mesh_model_msg_init(&rsp, BT_MESH_DFU_OP_UPDATE_INFO_STATUS); + net_buf_simple_add_u8(&rsp, srv->img_count); + net_buf_simple_add_u8(&rsp, idx); + + for (; idx < srv->img_count && limit > 0; ++idx) { + uint32_t entry_len; + + if (!srv->imgs[idx].fwid) { + continue; + } + + entry_len = 2 + srv->imgs[idx].fwid_len; + if (srv->imgs[idx].uri) { + entry_len += strlen(srv->imgs[idx].uri); + } + + if (net_buf_simple_tailroom(&rsp) + BT_MESH_MIC_SHORT < entry_len) { + break; + } + + net_buf_simple_add_u8(&rsp, srv->imgs[idx].fwid_len); + net_buf_simple_add_mem(&rsp, srv->imgs[idx].fwid, + srv->imgs[idx].fwid_len); + + if (srv->imgs[idx].uri) { + size_t len = strlen(srv->imgs[idx].uri); + + net_buf_simple_add_u8(&rsp, len); + net_buf_simple_add_mem(&rsp, srv->imgs[idx].uri, len); + } else { + net_buf_simple_add_u8(&rsp, 0); + } + + limit--; + } + + if (srv->update.phase != BT_MESH_DFU_PHASE_IDLE) { + ctx->send_ttl = srv->update.ttl; + } + + bt_mesh_model_send(mod, ctx, &rsp, NULL, NULL); + + return 0; +} + +static int handle_metadata_check(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + struct bt_mesh_dfu_srv *srv = mod->rt->user_data; + enum bt_mesh_dfu_status status; + enum bt_mesh_dfu_effect effect; + uint8_t idx; + + BT_MESH_MODEL_BUF_DEFINE(rsp, BT_MESH_DFU_OP_UPDATE_METADATA_STATUS, 2); + bt_mesh_model_msg_init(&rsp, BT_MESH_DFU_OP_UPDATE_METADATA_STATUS); + + idx = net_buf_simple_pull_u8(buf); + status = metadata_check(srv, idx, buf, &effect); + + LOG_DBG("%u", idx); + + net_buf_simple_add_u8(&rsp, (status & BIT_MASK(3)) | (effect << 3)); + net_buf_simple_add_u8(&rsp, idx); + + if (srv->update.phase != BT_MESH_DFU_PHASE_IDLE) { + ctx->send_ttl = srv->update.ttl; + } + + bt_mesh_model_send(mod, ctx, &rsp, NULL, NULL); + + return 0; +} + +static void update_status_rsp(struct bt_mesh_dfu_srv *srv, + struct bt_mesh_msg_ctx *ctx, + enum bt_mesh_dfu_status status, + const struct bt_mesh_send_cb *send_cb) +{ + BT_MESH_MODEL_BUF_DEFINE(buf, BT_MESH_DFU_OP_UPDATE_STATUS, 14); + bt_mesh_model_msg_init(&buf, BT_MESH_DFU_OP_UPDATE_STATUS); + + net_buf_simple_add_u8(&buf, ((status & BIT_MASK(3)) | + (srv->update.phase << 5))); + + if (srv->update.phase != BT_MESH_DFU_PHASE_IDLE) { + net_buf_simple_add_u8(&buf, srv->update.ttl); + net_buf_simple_add_u8(&buf, srv->update.effect); + net_buf_simple_add_le16(&buf, srv->update.timeout_base); + net_buf_simple_add_le64(&buf, srv->blob.state.xfer.id); + net_buf_simple_add_u8(&buf, srv->update.idx); + + ctx->send_ttl = srv->update.ttl; + } + + bt_mesh_model_send(srv->mod, ctx, &buf, send_cb, srv); +} + +static int handle_get(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + struct bt_mesh_dfu_srv *srv = mod->rt->user_data; + + LOG_DBG(""); + + update_status_rsp(srv, ctx, BT_MESH_DFU_SUCCESS, NULL); + + return 0; +} + +static inline bool is_active_update(struct bt_mesh_dfu_srv *srv, uint8_t idx, + uint16_t timeout_base, + const uint64_t *blob_id, uint8_t ttl, + uint16_t meta_checksum) +{ + return (srv->update.idx != idx || srv->blob.state.xfer.id != *blob_id || + srv->update.ttl != ttl || + srv->update.timeout_base != timeout_base || + srv->update.meta != meta_checksum); +} + +static int handle_start(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + struct bt_mesh_dfu_srv *srv = mod->rt->user_data; + const struct bt_mesh_blob_io *io; + uint16_t timeout_base, meta_checksum; + enum bt_mesh_dfu_status status; + uint8_t ttl, idx; + uint64_t blob_id; + int err; + struct net_buf_simple_state buf_state; + + ttl = net_buf_simple_pull_u8(buf); + timeout_base = net_buf_simple_pull_le16(buf); + blob_id = net_buf_simple_pull_le64(buf); + idx = net_buf_simple_pull_u8(buf); + meta_checksum = dfu_metadata_checksum(buf); + + LOG_DBG("%u ttl: %u extra time: %u", idx, ttl, timeout_base); + + if ((!buf->len || meta_checksum == srv->update.meta) && + srv->update.phase == BT_MESH_DFU_PHASE_TRANSFER_ERR && + srv->update.ttl == ttl && + srv->update.timeout_base == timeout_base && + srv->update.idx == idx && + srv->blob.state.xfer.id == blob_id) { + srv->update.phase = BT_MESH_DFU_PHASE_TRANSFER_ACTIVE; + status = BT_MESH_DFU_SUCCESS; + store_state(srv); + /* blob srv will resume the transfer. */ + LOG_DBG("Resuming transfer"); + goto rsp; + } + + if (bt_mesh_dfu_srv_is_busy(srv)) { + if (is_active_update(srv, idx, timeout_base, &blob_id, ttl, + meta_checksum)) { + status = BT_MESH_DFU_ERR_WRONG_PHASE; + } else { + status = BT_MESH_DFU_SUCCESS; + srv->update.ttl = ttl; + srv->blob.state.xfer.id = blob_id; + } + + LOG_WRN("Busy. Phase: %u", srv->update.phase); + goto rsp; + } + + net_buf_simple_save(buf, &buf_state); + status = metadata_check(srv, idx, buf, + (enum bt_mesh_dfu_effect *)&srv->update.effect); + net_buf_simple_restore(buf, &buf_state); + if (status != BT_MESH_DFU_SUCCESS) { + goto rsp; + } + + srv->update.ttl = ttl; + srv->update.timeout_base = timeout_base; + srv->update.meta = meta_checksum; + + io = NULL; + err = srv->cb->start(srv, &srv->imgs[idx], buf, &io); + if (err == -EALREADY || (!err && bt_mesh_has_addr(ctx->addr))) { + /* This image has already been received or this is a + * self-update. Skip the transfer phase and proceed to + * verifying update. + */ + status = BT_MESH_DFU_SUCCESS; + srv->update.idx = idx; + srv->blob.state.xfer.id = blob_id; + srv->update.phase = BT_MESH_DFU_PHASE_VERIFY; + update_status_rsp(srv, ctx, status, NULL); + verify(srv); + return 0; + } + + if (err == -ENOMEM) { + status = BT_MESH_DFU_ERR_RESOURCES; + goto rsp; + } + + if (err == -EBUSY) { + status = BT_MESH_DFU_ERR_TEMPORARILY_UNAVAILABLE; + goto rsp; + } + + if (err || !io || !io->wr) { + status = BT_MESH_DFU_ERR_INTERNAL; + goto rsp; + } + + err = bt_mesh_blob_srv_recv(&srv->blob, blob_id, io, + ttl, timeout_base); + if (err) { + status = BT_MESH_DFU_ERR_BLOB_XFER_BUSY; + goto rsp; + } + + srv->update.idx = idx; + srv->update.phase = BT_MESH_DFU_PHASE_TRANSFER_ACTIVE; + status = BT_MESH_DFU_SUCCESS; + store_state(srv); + +rsp: + update_status_rsp(srv, ctx, status, NULL); + + return 0; +} + +static int handle_cancel(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + struct bt_mesh_dfu_srv *srv = mod->rt->user_data; + + if (srv->update.idx == UPDATE_IDX_NONE) { + goto rsp; + } + + LOG_DBG(""); + + bt_mesh_blob_srv_cancel(&srv->blob); + srv->update.phase = BT_MESH_DFU_PHASE_IDLE; + xfer_failed(srv); + +rsp: + update_status_rsp(srv, ctx, BT_MESH_DFU_SUCCESS, NULL); + + return 0; +} + +static int handle_apply(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + struct bt_mesh_dfu_srv *srv = mod->rt->user_data; + static const struct bt_mesh_send_cb send_cb = { + .start = apply_rsp_sending, + .end = apply_rsp_sent, + }; + + LOG_DBG(""); + + if (srv->update.phase == BT_MESH_DFU_PHASE_APPLYING) { + update_status_rsp(srv, ctx, BT_MESH_DFU_SUCCESS, NULL); + return 0; + } + + if (srv->update.phase != BT_MESH_DFU_PHASE_VERIFY_OK) { + LOG_WRN("Apply: Invalid phase %u", srv->update.phase); + update_status_rsp(srv, ctx, BT_MESH_DFU_ERR_WRONG_PHASE, NULL); + return 0; + } + + /* Postponing the apply callback until the response has been sent, in + * case it triggers a reboot: + */ + srv->update.phase = BT_MESH_DFU_PHASE_APPLYING; + update_status_rsp(srv, ctx, BT_MESH_DFU_SUCCESS, &send_cb); + + return 0; +} + +const struct bt_mesh_model_op _bt_mesh_dfu_srv_op[] = { + { BT_MESH_DFU_OP_UPDATE_INFO_GET, BT_MESH_LEN_EXACT(2), handle_info_get }, + { BT_MESH_DFU_OP_UPDATE_METADATA_CHECK, BT_MESH_LEN_MIN(1), handle_metadata_check }, + { BT_MESH_DFU_OP_UPDATE_GET, BT_MESH_LEN_EXACT(0), handle_get }, + { BT_MESH_DFU_OP_UPDATE_START, BT_MESH_LEN_MIN(12), handle_start }, + { BT_MESH_DFU_OP_UPDATE_CANCEL, BT_MESH_LEN_EXACT(0), handle_cancel }, + { BT_MESH_DFU_OP_UPDATE_APPLY, BT_MESH_LEN_EXACT(0), handle_apply }, + BT_MESH_MODEL_OP_END, +}; + +static int dfu_srv_init(const struct bt_mesh_model *mod) +{ + struct bt_mesh_dfu_srv *srv = mod->rt->user_data; + + srv->mod = mod; + srv->update.idx = UPDATE_IDX_NONE; + + if (!srv->cb || !srv->cb->start || !srv->imgs || srv->img_count == 0 || + srv->img_count == UPDATE_IDX_NONE) { + LOG_ERR("Invalid DFU Server initialization"); + return -EINVAL; + } + + if (IS_ENABLED(CONFIG_BT_MESH_MODEL_EXTENSIONS)) { + bt_mesh_model_extend(mod, srv->blob.mod); + } + + return 0; +} + +static int dfu_srv_settings_set(const struct bt_mesh_model *mod, const char *name, + size_t len_rd, settings_read_cb read_cb, + void *cb_arg) +{ + struct bt_mesh_dfu_srv *srv = mod->rt->user_data; + ssize_t len; + + if (len_rd < sizeof(srv->update)) { + return -EINVAL; + } + + len = read_cb(cb_arg, &srv->update, sizeof(srv->update)); + if (len < 0) { + return len; + } + + LOG_DBG("Recovered transfer (phase: %u, idx: %u)", srv->update.phase, + srv->update.idx); + if (srv->update.phase == BT_MESH_DFU_PHASE_TRANSFER_ACTIVE) { + LOG_DBG("Settings recovered mid-transfer, setting transfer error"); + srv->update.phase = BT_MESH_DFU_PHASE_TRANSFER_ERR; + } else if (srv->update.phase == BT_MESH_DFU_PHASE_VERIFY_OK) { + LOG_DBG("Settings recovered before application, setting verification fail"); + srv->update.phase = BT_MESH_DFU_PHASE_VERIFY_FAIL; + } + + return 0; +} + +static void dfu_srv_reset(const struct bt_mesh_model *mod) +{ + struct bt_mesh_dfu_srv *srv = mod->rt->user_data; + + srv->update.phase = BT_MESH_DFU_PHASE_IDLE; + erase_state(srv); +} + +const struct bt_mesh_model_cb _bt_mesh_dfu_srv_cb = { + .init = dfu_srv_init, + .settings_set = dfu_srv_settings_set, + .reset = dfu_srv_reset, +}; + +static void blob_suspended(struct bt_mesh_blob_srv *b) +{ + struct bt_mesh_dfu_srv *srv = CONTAINER_OF(b, struct bt_mesh_dfu_srv, blob); + + srv->update.phase = BT_MESH_DFU_PHASE_TRANSFER_ERR; + store_state(srv); +} + +static void blob_end(struct bt_mesh_blob_srv *b, uint64_t id, bool success) +{ + struct bt_mesh_dfu_srv *srv = + CONTAINER_OF(b, struct bt_mesh_dfu_srv, blob); + + LOG_DBG("success: %u", success); + + if (!success) { + srv->update.phase = BT_MESH_DFU_PHASE_TRANSFER_ERR; + xfer_failed(srv); + return; + } + + verify(srv); +} + +static int blob_recover(struct bt_mesh_blob_srv *b, + struct bt_mesh_blob_xfer *xfer, + const struct bt_mesh_blob_io **io) +{ + struct bt_mesh_dfu_srv *srv = + CONTAINER_OF(b, struct bt_mesh_dfu_srv, blob); + + if (!srv->cb->recover || + srv->update.phase != BT_MESH_DFU_PHASE_TRANSFER_ERR || + srv->update.idx >= srv->img_count) { + return -ENOTSUP; + } + + return srv->cb->recover(srv, &srv->imgs[srv->update.idx], io); +} + +const struct bt_mesh_blob_srv_cb _bt_mesh_dfu_srv_blob_cb = { + .suspended = blob_suspended, + .end = blob_end, + .recover = blob_recover, +}; + +void bt_mesh_dfu_srv_verified(struct bt_mesh_dfu_srv *srv) +{ + if (srv->update.phase != BT_MESH_DFU_PHASE_VERIFY) { + LOG_WRN("Wrong state"); + return; + } + + LOG_DBG(""); + + srv->update.phase = BT_MESH_DFU_PHASE_VERIFY_OK; + store_state(srv); +} + +void bt_mesh_dfu_srv_rejected(struct bt_mesh_dfu_srv *srv) +{ + if (srv->update.phase != BT_MESH_DFU_PHASE_VERIFY) { + LOG_WRN("Wrong state"); + return; + } + + LOG_DBG(""); + + srv->update.phase = BT_MESH_DFU_PHASE_VERIFY_FAIL; + store_state(srv); +} + +void bt_mesh_dfu_srv_cancel(struct bt_mesh_dfu_srv *srv) +{ + if (srv->update.phase == BT_MESH_DFU_PHASE_IDLE) { + LOG_WRN("Wrong state"); + return; + } + + (void)bt_mesh_blob_srv_cancel(&srv->blob); +} + +void bt_mesh_dfu_srv_applied(struct bt_mesh_dfu_srv *srv) +{ + if (srv->update.phase != BT_MESH_DFU_PHASE_APPLYING) { + LOG_WRN("Wrong state"); + return; + } + + LOG_DBG(""); + + srv->update.phase = BT_MESH_DFU_PHASE_IDLE; + store_state(srv); +} + +bool bt_mesh_dfu_srv_is_busy(const struct bt_mesh_dfu_srv *srv) +{ + return srv->update.phase != BT_MESH_DFU_PHASE_IDLE && + srv->update.phase != BT_MESH_DFU_PHASE_TRANSFER_ERR && + srv->update.phase != BT_MESH_DFU_PHASE_VERIFY_FAIL; +} + +uint8_t bt_mesh_dfu_srv_progress(const struct bt_mesh_dfu_srv *srv) +{ + if (!bt_mesh_dfu_srv_is_busy(srv)) { + return 0U; + } + + if (srv->update.phase == BT_MESH_DFU_PHASE_TRANSFER_ACTIVE) { + return bt_mesh_blob_srv_progress(&srv->blob); + } + + return 100U; +} diff --git a/components/bt/esp_ble_mesh/v1.1/include/mesh_v1.1/dfu/dfd.h b/components/bt/esp_ble_mesh/v1.1/include/mesh_v1.1/dfu/dfd.h new file mode 100644 index 0000000000..cddde76618 --- /dev/null +++ b/components/bt/esp_ble_mesh/v1.1/include/mesh_v1.1/dfu/dfd.h @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2020 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_INCLUDE_BLUETOOTH_MESH_DFD_H__ +#define ZEPHYR_INCLUDE_BLUETOOTH_MESH_DFD_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @defgroup bt_mesh_dfd Firmware Distribution models + * @ingroup bt_mesh + * @{ + */ + +/** Firmware distribution status. */ +enum bt_mesh_dfd_status { + /** The message was processed successfully. */ + BT_MESH_DFD_SUCCESS, + + /** Insufficient resources on the node. */ + BT_MESH_DFD_ERR_INSUFFICIENT_RESOURCES, + + /** The operation cannot be performed while the Server is in the current + * phase. + */ + BT_MESH_DFD_ERR_WRONG_PHASE, + + /** An internal error occurred on the node. */ + BT_MESH_DFD_ERR_INTERNAL, + + /** The requested firmware image is not stored on the Distributor. */ + BT_MESH_DFD_ERR_FW_NOT_FOUND, + + /** The AppKey identified by the AppKey Index is not known to the node. + */ + BT_MESH_DFD_ERR_INVALID_APPKEY_INDEX, + + /** There are no Target nodes in the Distribution Receivers List + * state. + */ + BT_MESH_DFD_ERR_RECEIVERS_LIST_EMPTY, + + /** Another firmware image distribution is in progress. */ + BT_MESH_DFD_ERR_BUSY_WITH_DISTRIBUTION, + + /** Another upload is in progress. */ + BT_MESH_DFD_ERR_BUSY_WITH_UPLOAD, + + /** The URI scheme name indicated by the Update URI is not supported. */ + BT_MESH_DFD_ERR_URI_NOT_SUPPORTED, + + /** The format of the Update URI is invalid. */ + BT_MESH_DFD_ERR_URI_MALFORMED, + + /** The URI is currently unreachable. */ + BT_MESH_DFD_ERR_URI_UNREACHABLE, + + /** The Check Firmware OOB procedure did not find any new firmware. */ + BT_MESH_DFD_ERR_NEW_FW_NOT_AVAILABLE, + + /** The suspension of the Distribute Firmware procedure failed. */ + BT_MESH_DFD_ERR_SUSPEND_FAILED, +}; + +/** Firmware distribution phases. */ +enum bt_mesh_dfd_phase { + /** No firmware distribution is in progress. */ + BT_MESH_DFD_PHASE_IDLE, + + /** Firmware distribution is in progress. */ + BT_MESH_DFD_PHASE_TRANSFER_ACTIVE, + + /** The Transfer BLOB procedure has completed successfully. */ + BT_MESH_DFD_PHASE_TRANSFER_SUCCESS, + + /** The Apply Firmware on Target Nodes procedure is being executed. */ + BT_MESH_DFD_PHASE_APPLYING_UPDATE, + + /** The Distribute Firmware procedure has completed successfully. */ + BT_MESH_DFD_PHASE_COMPLETED, + + /** The Distribute Firmware procedure has failed. */ + BT_MESH_DFD_PHASE_FAILED, + + /** The Cancel Firmware Update procedure is being executed. */ + BT_MESH_DFD_PHASE_CANCELING_UPDATE, + + /** The Transfer BLOB procedure is suspended. */ + BT_MESH_DFD_PHASE_TRANSFER_SUSPENDED, +}; + +/** Firmware upload phases. */ +enum bt_mesh_dfd_upload_phase { + /** No firmware upload is in progress. */ + BT_MESH_DFD_UPLOAD_PHASE_IDLE, + + /** The Store Firmware procedure is being executed. */ + BT_MESH_DFD_UPLOAD_PHASE_TRANSFER_ACTIVE, + + /** The Store Firmware procedure or Store Firmware OOB procedure failed. + */ + BT_MESH_DFD_UPLOAD_PHASE_TRANSFER_ERROR, + + /** The Store Firmware procedure or the Store Firmware OOB procedure + * completed successfully. + */ + BT_MESH_DFD_UPLOAD_PHASE_TRANSFER_SUCCESS, +}; + +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif /* ZEPHYR_INCLUDE_BLUETOOTH_MESH_DFD_H__ */ diff --git a/components/bt/esp_ble_mesh/v1.1/include/mesh_v1.1/dfu/dfd_srv.h b/components/bt/esp_ble_mesh/v1.1/include/mesh_v1.1/dfu/dfd_srv.h new file mode 100644 index 0000000000..2abfbf0f90 --- /dev/null +++ b/components/bt/esp_ble_mesh/v1.1/include/mesh_v1.1/dfu/dfd_srv.h @@ -0,0 +1,313 @@ +/* + * Copyright (c) 2020 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file + * @defgroup bt_mesh_dfd_srv Firmware Distribution Server model + * @ingroup bt_mesh_dfd + * @{ + * @brief API for the Firmware Distribution Server model + */ + +#ifndef ZEPHYR_INCLUDE_BLUETOOTH_MESH_DFD_SRV_H__ +#define ZEPHYR_INCLUDE_BLUETOOTH_MESH_DFD_SRV_H__ + +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef CONFIG_BT_MESH_DFD_SRV_TARGETS_MAX +#define CONFIG_BT_MESH_DFD_SRV_TARGETS_MAX 0 +#endif + +#ifndef CONFIG_BT_MESH_DFD_SRV_SLOT_MAX_SIZE +#define CONFIG_BT_MESH_DFD_SRV_SLOT_MAX_SIZE 0 +#endif + +#ifndef CONFIG_BT_MESH_DFD_SRV_SLOT_SPACE +#define CONFIG_BT_MESH_DFD_SRV_SLOT_SPACE 0 +#endif + +struct bt_mesh_dfd_srv; + +#ifdef CONFIG_BT_MESH_DFD_SRV_OOB_UPLOAD +/** + * + * @brief Initialization parameters for the @ref bt_mesh_dfd_srv with OOB + * upload support. + * + * @param[in] _cb Pointer to a @ref bt_mesh_dfd_srv_cb instance. + * @param[in] _oob_schemes Array of OOB schemes supported by the server, + * each scheme being a code point from the + * Bluetooth SIG Assigned Numbers document. + * @param[in] _oob_schemes_count Number of schemes in @c _oob_schemes. + */ +#define BT_MESH_DFD_SRV_OOB_INIT(_cb, _oob_schemes, _oob_schemes_count) \ + { \ + .cb = _cb, \ + .dfu = BT_MESH_DFU_CLI_INIT(&_bt_mesh_dfd_srv_dfu_cb), \ + .upload = { \ + .blob = { .cb = &_bt_mesh_dfd_srv_blob_cb }, \ + }, \ + .oob_schemes = { \ + .schemes = _oob_schemes, \ + .count = _oob_schemes_count, \ + }, \ + } +#endif /* CONFIG_BT_MESH_DFD_SRV_OOB_UPLOAD */ + +/** + * + * @brief Initialization parameters for the @ref bt_mesh_dfd_srv. + * + * @param[in] _cb Pointer to a @ref bt_mesh_dfd_srv_cb instance. + */ +#define BT_MESH_DFD_SRV_INIT(_cb) \ + { \ + .cb = _cb, \ + .dfu = BT_MESH_DFU_CLI_INIT(&_bt_mesh_dfd_srv_dfu_cb), \ + .upload = { \ + .blob = { .cb = &_bt_mesh_dfd_srv_blob_cb }, \ + }, \ + } + +/** + * + * @brief Firmware Distribution Server model Composition Data entry. + * + * @param _srv Pointer to a @ref bt_mesh_dfd_srv instance. + */ +#define BT_MESH_MODEL_DFD_SRV(_srv) \ + BT_MESH_MODEL_DFU_CLI(&(_srv)->dfu), \ + BT_MESH_MODEL_BLOB_SRV(&(_srv)->upload.blob), \ + BT_MESH_MODEL_CB(BT_MESH_MODEL_ID_DFD_SRV, _bt_mesh_dfd_srv_op, NULL, \ + _srv, &_bt_mesh_dfd_srv_cb) + +/** Firmware Distribution Server callbacks: */ +struct bt_mesh_dfd_srv_cb { + + /** @brief Slot receive callback. + * + * Called at the start of an upload procedure. The callback must fill + * @c io with a pointer to a writable BLOB stream for the Firmware Distribution + * Server to write the firmware image to. + * + * @param srv Firmware Distribution Server model instance. + * @param slot DFU image slot being received. + * @param io BLOB stream response pointer. + * + * @return 0 on success, or (negative) error code otherwise. + */ + int (*recv)(struct bt_mesh_dfd_srv *srv, + const struct bt_mesh_dfu_slot *slot, + const struct bt_mesh_blob_io **io); + +#ifdef CONFIG_BT_MESH_DFD_SRV_OOB_UPLOAD + /** @brief Firmware upload OOB start callback. + * + * Called at the start of an OOB firmware upload. The application must + * start a firmware check using an OOB mechanism, and then call + * @ref bt_mesh_dfd_srv_oob_check_complete. Depending on the return + * value of this function, the application must then start storing the + * firmware image using an OOB mechanism, and call + * @ref bt_mesh_dfd_srv_oob_store_complete. This callback is mandatory + * to support OOB uploads. + * + * @param srv Firmware Distribution Server model instance. + * @param slot Slot to be used for the upload. + * @param uri Pointer to buffer containing the URI used to + * check for new firmware. + * @param uri_len Length of the URI buffer. + * @param fwid Pointer to buffer containing the current + * firmware ID to be used when checking for + * availability of new firmware. + * @param fwid_len Length of the current firmware ID. Must be set + * to the length of the new firmware ID if it is + * available, or to 0 if new firmware is not + * available. + * + * @return BT_MESH_DFD_SUCCESS on success, or error code otherwise. + */ + int (*start_oob_upload)(struct bt_mesh_dfd_srv *srv, + const struct bt_mesh_dfu_slot *slot, + const char *uri, uint8_t uri_len, + const uint8_t *fwid, uint16_t fwid_len); + + /** @brief Cancel store OOB callback + * + * Called when an OOB store is cancelled. The application must stop + * any ongoing OOB image transfer. This callback is mandatory to + * support OOB uploads. + * + * @param srv Firmware Distribution Server model instance. + * @param slot DFU image slot to cancel + */ + void (*cancel_oob_upload)(struct bt_mesh_dfd_srv *srv, + const struct bt_mesh_dfu_slot *slot); + + /** @brief Get the progress of an ongoing OOB store + * + * Called by the Firmware Distribution Server model when it needs to + * get the current progress of an ongoing OOB store from the + * application. This callback is mandatory to support OOB uploads. + * + * @param srv Firmware Distribution Server model instance. + * @param slot DFU image slot to get progress for. + * + * @return The current progress of the ongoing OOB store, in percent. + */ + uint8_t (*oob_progress_get)(struct bt_mesh_dfd_srv *srv, + const struct bt_mesh_dfu_slot *slot); +#endif /* CONFIG_BT_MESH_DFD_SRV_OOB_UPLOAD */ + + /** @brief Slot delete callback. + * + * Called when the Firmware Distribution Server is about to delete a DFU image slot. + * All allocated data associated with the firmware slot should be + * deleted. + * + * @param srv Firmware Update Server instance. + * @param slot DFU image slot being deleted. + */ + void (*del)(struct bt_mesh_dfd_srv *srv, + const struct bt_mesh_dfu_slot *slot); + + /** @brief Slot send callback. + * + * Called at the start of a distribution procedure. The callback must + * fill @c io with a pointer to a readable BLOB stream for the Firmware + * Distribution Server to read the firmware image from. + * + * @param srv Firmware Distribution Server model instance. + * @param slot DFU image slot being sent. + * @param io BLOB stream response pointer. + * + * @return 0 on success, or (negative) error code otherwise. + */ + int (*send)(struct bt_mesh_dfd_srv *srv, + const struct bt_mesh_dfu_slot *slot, + const struct bt_mesh_blob_io **io); + + /** @brief Phase change callback (Optional). + * + * Called whenever the phase of the Firmware Distribution Server changes. + * + * @param srv Firmware Distribution Server model instance. + * @param phase New Firmware Distribution phase. + */ + void (*phase)(struct bt_mesh_dfd_srv *srv, enum bt_mesh_dfd_phase phase); +}; + +/** Firmware Distribution Server instance. */ +struct bt_mesh_dfd_srv { + const struct bt_mesh_dfd_srv_cb *cb; + const struct bt_mesh_model *mod; + struct bt_mesh_dfu_cli dfu; + struct bt_mesh_dfu_target targets[CONFIG_BT_MESH_DFD_SRV_TARGETS_MAX]; + struct bt_mesh_blob_target_pull pull_ctxs[CONFIG_BT_MESH_DFD_SRV_TARGETS_MAX]; + const struct bt_mesh_blob_io *io; + uint16_t target_cnt; + uint16_t slot_idx; + bool apply; + enum bt_mesh_dfd_phase phase; + struct bt_mesh_blob_cli_inputs inputs; + + struct { + enum bt_mesh_dfd_upload_phase phase; + struct bt_mesh_dfu_slot *slot; + const struct flash_area *area; + struct bt_mesh_blob_srv blob; +#ifdef CONFIG_BT_MESH_DFD_SRV_OOB_UPLOAD + bool is_oob; + bool is_pending_oob_check; + struct { + uint8_t uri_len; + uint8_t uri[CONFIG_BT_MESH_DFU_URI_MAXLEN]; + uint16_t current_fwid_len; + uint8_t current_fwid[CONFIG_BT_MESH_DFU_FWID_MAXLEN]; + struct bt_mesh_msg_ctx ctx; + } oob; +#endif /* CONFIG_BT_MESH_DFD_SRV_OOB_UPLOAD */ + } upload; + +#ifdef CONFIG_BT_MESH_DFD_SRV_OOB_UPLOAD + struct { + const uint8_t *schemes; + const uint8_t count; + } oob_schemes; +#endif /* CONFIG_BT_MESH_DFD_SRV_OOB_UPLOAD */ +}; + +#ifdef CONFIG_BT_MESH_DFD_SRV_OOB_UPLOAD +/** @brief Call when an OOB check has completed or failed + * + * This should be called by the application after an OOB check started by the @c start_oob_upload + * callback has completed or failed. The @p status param should be set to one of the following + * values: + * + * * @c BT_MESH_DFD_SUCCESS if the check was successful and a new firmware ID was found. + * * @c BT_MESH_DFD_ERR_URI_MALFORMED if the URI is not formatted correctly. + * * @c BT_MESH_DFD_ERR_URI_NOT_SUPPORTED if the URI scheme is not supported by the node. + * * @c BT_MESH_DFD_ERR_URI_UNREACHABLE if the URI can't be reached. + * * @c BT_MESH_DFD_ERR_NEW_FW_NOT_AVAILABLE if the check completes successfully but no new + * firmware is available. + * + * If this function returns 0, the application should then download the firmware to the + * slot. If an error code is returned, the application should abort the OOB upload. + * + * @param srv Firmware Distribution Server model instance. + * @param slot The slot used in the OOB upload. + * @param status The result of the firmware check. + * @param fwid If the check was successful and new firmware found, this should point to a + * buffer containing the new firmware ID to store. + * @param fwid_len The length of the firmware ID pointed to by @p fwid. + * + * @return 0 on success, (negative) error code otherwise. + */ +int bt_mesh_dfd_srv_oob_check_complete(struct bt_mesh_dfd_srv *srv, + const struct bt_mesh_dfu_slot *slot, int status, + uint8_t *fwid, size_t fwid_len); + +/** @brief Call when an OOB store has completed or failed + * + * This should be called by the application after an OOB store started after a successful call to + * @c bt_mesh_dfd_srv_oob_check_complete has completed successfully or failed. + * + * @param srv Firmware Distribution Server model instance. + * @param slot The slot used when storing the firmware image. + * @param success @c true if the OOB store completed successfully, @c false otherwise. + * @param size The size of the stored firmware image, in bytes. + * @param metadata Pointer to the metadata received OOB, or @c NULL if no metadata was + * received. + * @param metadata_len Size of the metadata pointed to by @p metadata. + * + * @return 0 on success, (negative) error code otherwise. + */ +int bt_mesh_dfd_srv_oob_store_complete(struct bt_mesh_dfd_srv *srv, + const struct bt_mesh_dfu_slot *slot, bool success, + size_t size, const uint8_t *metadata, size_t metadata_len); +#endif /* CONFIG_BT_MESH_DFD_SRV_OOB_UPLOAD */ + +/** @cond INTERNAL_HIDDEN */ +extern const struct bt_mesh_model_op _bt_mesh_dfd_srv_op[]; +extern const struct bt_mesh_model_cb _bt_mesh_dfd_srv_cb; +extern const struct bt_mesh_dfu_cli_cb _bt_mesh_dfd_srv_dfu_cb; +extern const struct bt_mesh_blob_srv_cb _bt_mesh_dfd_srv_blob_cb; +/** @endcond */ + +#ifdef __cplusplus +} +#endif + +#endif /* ZEPHYR_INCLUDE_BLUETOOTH_MESH_DFD_SRV_H__ */ + +/** @} */ diff --git a/components/bt/esp_ble_mesh/v1.1/include/mesh_v1.1/dfu/dfu.h b/components/bt/esp_ble_mesh/v1.1/include/mesh_v1.1/dfu/dfu.h new file mode 100644 index 0000000000..4745355f03 --- /dev/null +++ b/components/bt/esp_ble_mesh/v1.1/include/mesh_v1.1/dfu/dfu.h @@ -0,0 +1,171 @@ +/* + * Copyright (c) 2020 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_INCLUDE_BLUETOOTH_MESH_DFU_H__ +#define ZEPHYR_INCLUDE_BLUETOOTH_MESH_DFU_H__ + +#include + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @defgroup bt_mesh_dfu Bluetooth Mesh Device Firmware Update + * @ingroup bt_mesh + * @{ + */ + +#ifndef CONFIG_BT_MESH_DFU_FWID_MAXLEN +#define CONFIG_BT_MESH_DFU_FWID_MAXLEN 0 +#endif + +#ifndef CONFIG_BT_MESH_DFU_METADATA_MAXLEN +#define CONFIG_BT_MESH_DFU_METADATA_MAXLEN 0 +#endif + +#ifndef CONFIG_BT_MESH_DFU_URI_MAXLEN +#define CONFIG_BT_MESH_DFU_URI_MAXLEN 0 +#endif + +#ifndef CONFIG_BT_MESH_DFU_SLOT_CNT +#define CONFIG_BT_MESH_DFU_SLOT_CNT 0 +#endif + +/** DFU transfer phase. */ +enum bt_mesh_dfu_phase { + /** Ready to start a Receive Firmware procedure. */ + BT_MESH_DFU_PHASE_IDLE, + + /** The Transfer BLOB procedure failed. */ + BT_MESH_DFU_PHASE_TRANSFER_ERR, + + /** The Receive Firmware procedure is being executed. */ + BT_MESH_DFU_PHASE_TRANSFER_ACTIVE, + + /** The Verify Firmware procedure is being executed. */ + BT_MESH_DFU_PHASE_VERIFY, + + /** The Verify Firmware procedure completed successfully. */ + BT_MESH_DFU_PHASE_VERIFY_OK, + + /** The Verify Firmware procedure failed. */ + BT_MESH_DFU_PHASE_VERIFY_FAIL, + + /** The Apply New Firmware procedure is being executed. */ + BT_MESH_DFU_PHASE_APPLYING, + + /** Firmware transfer has been canceled. */ + BT_MESH_DFU_PHASE_TRANSFER_CANCELED, + + /** Firmware applying succeeded. */ + BT_MESH_DFU_PHASE_APPLY_SUCCESS, + + /** Firmware applying failed. */ + BT_MESH_DFU_PHASE_APPLY_FAIL, + + /** Phase of a node was not yet retrieved. */ + BT_MESH_DFU_PHASE_UNKNOWN, +}; + + +/** DFU status. */ +enum bt_mesh_dfu_status { + /** The message was processed successfully. */ + BT_MESH_DFU_SUCCESS, + + /** Insufficient resources on the node */ + BT_MESH_DFU_ERR_RESOURCES, + + /** The operation cannot be performed while the Server is in the current + * phase. + */ + BT_MESH_DFU_ERR_WRONG_PHASE, + + /** An internal error occurred on the node. */ + BT_MESH_DFU_ERR_INTERNAL, + + /** The message contains a firmware index value that is not expected. */ + BT_MESH_DFU_ERR_FW_IDX, + + /** The metadata check failed. */ + BT_MESH_DFU_ERR_METADATA, + + /** The Server cannot start a firmware update. */ + BT_MESH_DFU_ERR_TEMPORARILY_UNAVAILABLE, + + /** Another BLOB transfer is in progress. */ + BT_MESH_DFU_ERR_BLOB_XFER_BUSY, +}; + +/** Expected effect of a DFU transfer. */ +enum bt_mesh_dfu_effect { + /** No changes to node Composition Data. */ + BT_MESH_DFU_EFFECT_NONE, + + /** Node Composition Data changed and the node does not support remote + * provisioning. + */ + BT_MESH_DFU_EFFECT_COMP_CHANGE_NO_RPR, + + /** Node Composition Data changed, and remote provisioning is supported. + * The node supports remote provisioning and Composition Data Page + * 0x80. Page 0x80 contains different Composition Data than Page 0x0. + */ + BT_MESH_DFU_EFFECT_COMP_CHANGE, + + /** Node will be unprovisioned after the update. */ + BT_MESH_DFU_EFFECT_UNPROV, +}; + +/** Action for DFU iteration callbacks. */ +enum bt_mesh_dfu_iter { + /** Stop iterating. */ + BT_MESH_DFU_ITER_STOP, + + /** Continue iterating. */ + BT_MESH_DFU_ITER_CONTINUE, +}; + +/** DFU image instance. + * + * Each DFU image represents a single updatable firmware image. + */ +struct bt_mesh_dfu_img { + /** Firmware ID. */ + const void *fwid; + + /** Length of the firmware ID. */ + size_t fwid_len; + + /** Update URI, or NULL. */ + const char *uri; +}; + +/** DFU image slot for DFU distribution. */ +struct bt_mesh_dfu_slot { + /** Size of the firmware in bytes. */ + size_t size; + /** Length of the firmware ID. */ + size_t fwid_len; + /** Length of the metadata. */ + size_t metadata_len; + /** Firmware ID. */ + uint8_t fwid[CONFIG_BT_MESH_DFU_FWID_MAXLEN]; + /** Metadata. */ + uint8_t metadata[CONFIG_BT_MESH_DFU_METADATA_MAXLEN]; +}; + +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif /* ZEPHYR_INCLUDE_BLUETOOTH_MESH_DFU_H__ */ 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 new file mode 100644 index 0000000000..ad8881ebc2 --- /dev/null +++ b/components/bt/esp_ble_mesh/v1.1/include/mesh_v1.1/dfu/dfu_cli.h @@ -0,0 +1,409 @@ +/* + * Copyright (c) 2020 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file + * @defgroup bt_mesh_dfu_cli Firmware Uppdate Client model + * @ingroup bt_mesh_dfu + * @{ + * @brief API for the Bluetooth Mesh Firmware Update Client model + */ + +#ifndef ZEPHYR_INCLUDE_BLUETOOTH_MESH_DFU_CLI_H__ +#define ZEPHYR_INCLUDE_BLUETOOTH_MESH_DFU_CLI_H__ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct bt_mesh_dfu_cli; + +/** + * + * @brief Initialization parameters for the @ref bt_mesh_dfu_cli. + * + * @sa bt_mesh_dfu_cli_cb. + * + * @param _handlers Handler callback structure. + */ +#define BT_MESH_DFU_CLI_INIT(_handlers) \ + { \ + .cb = _handlers, \ + .blob = { .cb = &_bt_mesh_dfu_cli_blob_handlers }, \ + } + +/** + * + * @brief Firmware Update Client model Composition Data entry. + * + * @param _cli Pointer to a @ref bt_mesh_dfu_cli instance. + */ +#define BT_MESH_MODEL_DFU_CLI(_cli) \ + BT_MESH_MODEL_BLOB_CLI(&(_cli)->blob), \ + BT_MESH_MODEL_CB(BT_MESH_MODEL_ID_DFU_CLI, _bt_mesh_dfu_cli_op, NULL, \ + _cli, &_bt_mesh_dfu_cli_cb) + +/** DFU Target node. */ +struct bt_mesh_dfu_target { + /** BLOB Target node */ + struct bt_mesh_blob_target blob; + /** Image index on the Target node */ + uint8_t img_idx; + /** Expected DFU effect, see @ref bt_mesh_dfu_effect. */ + uint8_t effect; + /** Current DFU status, see @ref bt_mesh_dfu_status. */ + uint8_t status; + /** Current DFU phase, see @ref bt_mesh_dfu_phase. */ + uint8_t phase; +}; + +/** Metadata status response. */ +struct bt_mesh_dfu_metadata_status { + /** Image index. */ + uint8_t idx; + /** Status code. */ + enum bt_mesh_dfu_status status; + /** Effect of transfer. */ + enum bt_mesh_dfu_effect effect; +}; + +/** DFU Target node status parameters. */ +struct bt_mesh_dfu_target_status { + /** Status of the previous operation. */ + enum bt_mesh_dfu_status status; + /** Phase of the current DFU transfer. */ + enum bt_mesh_dfu_phase phase; + /** The effect the update will have on the Target device's state. */ + enum bt_mesh_dfu_effect effect; + /** BLOB ID used in the transfer. */ + uint64_t blob_id; + /** Image index to transfer. */ + uint8_t img_idx; + /** TTL used in the transfer. */ + uint8_t ttl; + + /** Additional response time for the Target nodes, in 10-second increments. + * + * The extra time can be used to give the Target nodes more time to respond + * to messages from the Client. The actual timeout will be calculated + * according to the following formula: + * + * @verbatim + * timeout = 20 seconds + (10 seconds * timeout_base) + (100 ms * TTL) + * @endverbatim + * + * If a Target node fails to respond to a message from the Client within the + * configured transfer timeout, the Target node is dropped. + */ + uint16_t timeout_base; +}; + +/** @brief DFU image callback. + * + * The image callback is called for every DFU image on the Target node when + * calling @ref bt_mesh_dfu_cli_imgs_get. + * + * @param cli Firmware Update Client model instance. + * @param ctx Message context of the received message. + * @param idx Image index. + * @param total Total number of images on the Target node. + * @param img Image information for the given image index. + * @param cb_data Callback data. + * + * @retval BT_MESH_DFU_ITER_STOP Stop iterating through the image list and + * return from @ref bt_mesh_dfu_cli_imgs_get. + * @retval BT_MESH_DFU_ITER_CONTINUE Continue iterating through the image list + * if any images remain. + */ +typedef enum bt_mesh_dfu_iter (*bt_mesh_dfu_img_cb_t)( + struct bt_mesh_dfu_cli *cli, struct bt_mesh_msg_ctx *ctx, uint8_t idx, + uint8_t total, const struct bt_mesh_dfu_img *img, void *cb_data); + +/** Firmware Update Client event callbacks. */ +struct bt_mesh_dfu_cli_cb { + /** @brief BLOB transfer is suspended. + * + * Called when the BLOB transfer is suspended due to response timeout from all Target nodes. + * + * @param cli Firmware Update Client model instance. + */ + void (*suspended)(struct bt_mesh_dfu_cli *cli); + + /** @brief DFU ended. + * + * Called when the DFU transfer ends, either because all Target nodes were + * lost or because the transfer was completed successfully. + * + * @param cli Firmware Update Client model instance. + * @param reason Reason for ending. + */ + void (*ended)(struct bt_mesh_dfu_cli *cli, + enum bt_mesh_dfu_status reason); + + /** @brief DFU transfer applied on all active Target nodes. + * + * Called at the end of the apply procedure started by @ref + * bt_mesh_dfu_cli_apply. + * + * @param cli Firmware Update Client model instance. + */ + void (*applied)(struct bt_mesh_dfu_cli *cli); + + /** @brief DFU transfer confirmed on all active Target nodes. + * + * Called at the end of the apply procedure started by @ref + * bt_mesh_dfu_cli_confirm. + * + * @param cli Firmware Update Client model instance. + */ + void (*confirmed)(struct bt_mesh_dfu_cli *cli); + + /** @brief DFU Target node was lost. + * + * A DFU Target node was dropped from the receivers list. The Target node's + * @c status is set to reflect the reason for the failure. + * + * @param cli Firmware Update Client model instance. + * @param target DFU Target node that was lost. + */ + void (*lost_target)(struct bt_mesh_dfu_cli *cli, + struct bt_mesh_dfu_target *target); +}; + +/** Firmware Update Client model instance. + * + * Should be initialized with @ref BT_MESH_DFU_CLI_INIT. + */ +struct bt_mesh_dfu_cli { + /** Callback structure. */ + const struct bt_mesh_dfu_cli_cb *cb; + /** Underlying BLOB Transfer Client. */ + struct bt_mesh_blob_cli blob; + + /* runtime state */ + + uint32_t op; + const struct bt_mesh_model *mod; + + struct { + const struct bt_mesh_dfu_slot *slot; + const struct bt_mesh_blob_io *io; + struct bt_mesh_blob_xfer blob; + uint8_t state; + uint8_t flags; + } xfer; + + struct { + uint8_t ttl; + uint8_t type; + uint8_t img_cnt; + uint16_t addr; + struct k_sem sem; + void *params; + bt_mesh_dfu_img_cb_t img_cb; + } req; +}; + +/** BLOB parameters for Firmware Update Client transfer: */ +struct bt_mesh_dfu_cli_xfer_blob_params { + /* Logarithmic representation of the block size. */ + uint8_t block_size_log; + /** Base chunk size. May be smaller for the last chunk. */ + uint16_t chunk_size; +}; + +/** Firmware Update Client transfer parameters: */ +struct bt_mesh_dfu_cli_xfer { + /** BLOB ID to use for this transfer, or 0 to set it randomly. */ + uint64_t blob_id; + /** DFU image slot to transfer. */ + const struct bt_mesh_dfu_slot *slot; + /** Transfer mode (Push (Push BLOB Transfer Mode) or Pull (Pull BLOB Transfer Mode)) */ + enum bt_mesh_blob_xfer_mode mode; + /** BLOB parameters to be used for the transfer, or NULL to retrieve Target nodes' + * capabilities before sending a firmware. + */ + const struct bt_mesh_dfu_cli_xfer_blob_params *blob_params; +}; + +/** @brief Start distributing a DFU. + * + * Starts distribution of the firmware in the given slot to the list of DFU + * Target nodes in @c ctx. The transfer runs in the background, and its end is + * signalled through the @ref bt_mesh_dfu_cli_cb::ended callback. + * + * @note The BLOB Transfer Client transfer inputs @c targets list must point to a list of @ref + * bt_mesh_dfu_target nodes. + * + * @param cli Firmware Update Client model instance. + * @param inputs BLOB Transfer Client transfer inputs. + * @param io BLOB stream to read BLOB from. + * @param xfer Firmware Update Client transfer parameters. + * + * @return 0 on success, or (negative) error code otherwise. + */ +int bt_mesh_dfu_cli_send(struct bt_mesh_dfu_cli *cli, + const struct bt_mesh_blob_cli_inputs *inputs, + const struct bt_mesh_blob_io *io, + const struct bt_mesh_dfu_cli_xfer *xfer); + +/** @brief Suspend a DFU transfer. + * + * @param cli Firmware Update Client instance. + * + * @return 0 on success, or (negative) error code otherwise. + */ +int bt_mesh_dfu_cli_suspend(struct bt_mesh_dfu_cli *cli); + +/** @brief Resume the suspended transfer. + * + * @param cli Firmware Update Client instance. + * + * @return 0 on success, or (negative) error code otherwise. + */ +int bt_mesh_dfu_cli_resume(struct bt_mesh_dfu_cli *cli); + +/** @brief Cancel a DFU transfer. + * + * Will cancel the ongoing DFU transfer, or the transfer on a specific Target + * node if @c ctx is valid. + * + * @param cli Firmware Update Client model instance. + * @param ctx Message context, or NULL to cancel the ongoing DFU transfer. + * + * @return 0 on success, or (negative) error code otherwise. + */ +int bt_mesh_dfu_cli_cancel(struct bt_mesh_dfu_cli *cli, + struct bt_mesh_msg_ctx *ctx); + +/** @brief Apply the completed DFU transfer. + * + * A transfer can only be applied after it has ended successfully. The Firmware + * Update Client's @c applied callback is called at the end of the apply procedure. + * + * @param cli Firmware Update Client model instance. + * + * @return 0 on success, or (negative) error code otherwise. + */ +int bt_mesh_dfu_cli_apply(struct bt_mesh_dfu_cli *cli); + +/** @brief Confirm that the active transfer has been applied on the Target nodes. + * + * A transfer can only be confirmed after it has been applied. The Firmware Update + * Client's @c confirmed callback is called at the end of the confirm + * procedure. + * + * Target nodes that have reported the effect as @ref BT_MESH_DFU_EFFECT_UNPROV + * are expected to not respond to the query, and will fail if they do. + * + * @param cli Firmware Update Client model instance. + * + * @return 0 on success, or (negative) error code otherwise. + */ +int bt_mesh_dfu_cli_confirm(struct bt_mesh_dfu_cli *cli); + +/** @brief Get progress as a percentage of completion. + * + * @param cli Firmware Update Client model instance. + * + * @return The progress of the current transfer in percent, or 0 if no + * transfer is active. + */ +uint8_t bt_mesh_dfu_cli_progress(struct bt_mesh_dfu_cli *cli); + +/** @brief Check whether a DFU transfer is in progress. + * + * @param cli Firmware Update Client model instance. + * + * @return true if the BLOB Transfer Client is currently participating in a transfer, + * false otherwise. + */ +bool bt_mesh_dfu_cli_is_busy(struct bt_mesh_dfu_cli *cli); + +/** @brief Perform a DFU image list request. + * + * Requests the full list of DFU images on a Target node, and iterates through + * them, calling the @c cb for every image. + * + * The DFU image list request can be used to determine which image index the + * Target node holds its different firmwares in. + * + * Waits for a response until the procedure timeout expires. + * + * @param cli Firmware Update Client model instance. + * @param ctx Message context. + * @param cb Callback to call for each image index. + * @param cb_data Callback data to pass to @c cb. + * @param max_count Max number of images to return. + * + * @return 0 on success, or (negative) error code otherwise. + */ +int bt_mesh_dfu_cli_imgs_get(struct bt_mesh_dfu_cli *cli, + struct bt_mesh_msg_ctx *ctx, + bt_mesh_dfu_img_cb_t cb, void *cb_data, + uint8_t max_count); + +/** @brief Perform a metadata check for the given DFU image slot. + * + * The metadata check procedure allows the Firmware Update Client to check if a Target + * node will accept a transfer of this DFU image slot, and what the effect would be. + * + * Waits for a response until the procedure timeout expires. + * + * @param cli Firmware Update Client model instance. + * @param ctx Message context. + * @param img_idx Target node's image index to check. + * @param slot DFU image slot to check for. + * @param rsp Metadata status response buffer. + * + * @return 0 on success, or (negative) error code otherwise. + */ +int bt_mesh_dfu_cli_metadata_check(struct bt_mesh_dfu_cli *cli, + struct bt_mesh_msg_ctx *ctx, uint8_t img_idx, + const struct bt_mesh_dfu_slot *slot, + struct bt_mesh_dfu_metadata_status *rsp); + +/** @brief Get the status of a Target node. + * + * @param cli Firmware Update Client model instance. + * @param ctx Message context. + * @param rsp Response data buffer. + * + * @return 0 on success, or (negative) error code otherwise. + */ +int bt_mesh_dfu_cli_status_get(struct bt_mesh_dfu_cli *cli, + struct bt_mesh_msg_ctx *ctx, + struct bt_mesh_dfu_target_status *rsp); + +/** @brief Get the current procedure timeout value. + * + * @return The configured procedure timeout. + */ +int32_t bt_mesh_dfu_cli_timeout_get(void); + +/** @brief Set the procedure timeout value. + * + * @param timeout The new procedure timeout. + */ +void bt_mesh_dfu_cli_timeout_set(int32_t timeout); + +/** @cond INTERNAL_HIDDEN */ +extern const struct bt_mesh_blob_cli_cb _bt_mesh_dfu_cli_blob_handlers; +extern const struct bt_mesh_model_cb _bt_mesh_dfu_cli_cb; +extern const struct bt_mesh_model_op _bt_mesh_dfu_cli_op[]; +/** @endcond */ + +#ifdef __cplusplus +} +#endif + +#endif /* ZEPHYR_INCLUDE_BLUETOOTH_MESH_DFU_CLI_H__ */ + +/** @} */ diff --git a/components/bt/esp_ble_mesh/v1.1/include/mesh_v1.1/dfu/dfu_metadata.h b/components/bt/esp_ble_mesh/v1.1/include/mesh_v1.1/dfu/dfu_metadata.h new file mode 100644 index 0000000000..8f6430c4b0 --- /dev/null +++ b/components/bt/esp_ble_mesh/v1.1/include/mesh_v1.1/dfu/dfu_metadata.h @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2021 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file + * @defgroup bt_mesh_dfu_metadata Bluetooth Mesh Device Firmware Update (DFU) metadata + * @ingroup bt_mesh_dfu + * @{ + * @brief Common types and functions for the Bluetooth Mesh DFU metadata. + */ + +#ifndef ZEPHYR_INCLUDE_BLUETOOTH_MESH_DFU_METADATA_H__ +#define ZEPHYR_INCLUDE_BLUETOOTH_MESH_DFU_METADATA_H__ + +#include + +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** Firmware version. */ +struct bt_mesh_dfu_metadata_fw_ver { + /** Firmware major version. */ + uint8_t major; + /** Firmware minor version. */ + uint8_t minor; + /** Firmware revision. */ + uint16_t revision; + /** Firmware build number. */ + uint32_t build_num; +}; + +/** Firmware core type. */ +enum bt_mesh_dfu_metadata_fw_core_type { + /** Application core. */ + BT_MESH_DFU_FW_CORE_TYPE_APP = BIT(0), + /** Network core. */ + BT_MESH_DFU_FW_CORE_TYPE_NETWORK = BIT(1), + /** Application-specific BLOB. */ + BT_MESH_DFU_FW_CORE_TYPE_APP_SPECIFIC_BLOB = BIT(2), +}; + +/** Firmware metadata. */ +struct bt_mesh_dfu_metadata { + /** New firmware version. */ + struct bt_mesh_dfu_metadata_fw_ver fw_ver; + /** New firmware size. */ + uint32_t fw_size; + /** New firmware core type. */ + enum bt_mesh_dfu_metadata_fw_core_type fw_core_type; + /** Hash of incoming Composition Data. */ + uint32_t comp_hash; + /** New number of node elements. */ + uint16_t elems; + /** Application-specific data for new firmware. This field is optional. */ + uint8_t *user_data; + /** Length of the application-specific field. */ + uint32_t user_data_len; +}; + +/** @brief Decode a firmware metadata from a network buffer. + * + * @param buf Buffer containing a raw metadata to be decoded. + * @param metadata Pointer to a metadata structure to be filled. + * + * @return 0 on success, or (negative) error code otherwise. + */ +int bt_mesh_dfu_metadata_decode(struct net_buf_simple *buf, + struct bt_mesh_dfu_metadata *metadata); + +/** @brief Encode a firmware metadata into a network buffer. + * + * @param metadata Firmware metadata to be encoded. + * @param buf Buffer to store the encoded metadata. + * + * @return 0 on success, or (negative) error code otherwise. + */ +int bt_mesh_dfu_metadata_encode(const struct bt_mesh_dfu_metadata *metadata, + struct net_buf_simple *buf); + +/** @brief Compute hash of the Composition Data state. + * + * The format of the Composition Data is defined in MshPRTv1.1: 4.2.2.1. + * + * @param buf Pointer to buffer holding Composition Data. + * @param key 128-bit key to be used in the hash computation. + * @param hash Pointer to a memory location to which the hash will be stored. + * + * @return 0 on success, or (negative) error code otherwise. + */ +int bt_mesh_dfu_metadata_comp_hash_get(struct net_buf_simple *buf, uint8_t *key, uint32_t *hash); + +/** @brief Compute hash of the Composition Data Page 0 of this device. + * + * @param key 128-bit key to be used in the hash computation. + * @param hash Pointer to a memory location to which the hash will be stored. + * + * @return 0 on success, or (negative) error code otherwise. + */ +int bt_mesh_dfu_metadata_comp_hash_local_get(uint8_t *key, uint32_t *hash); + +#ifdef __cplusplus +} +#endif + +#endif /* ZEPHYR_INCLUDE_BLUETOOTH_MESH_DFU_METADATA_H__ */ + +/** @} */ diff --git a/components/bt/esp_ble_mesh/v1.1/include/mesh_v1.1/dfu/dfu_srv.h b/components/bt/esp_ble_mesh/v1.1/include/mesh_v1.1/dfu/dfu_srv.h new file mode 100644 index 0000000000..2beaf2d977 --- /dev/null +++ b/components/bt/esp_ble_mesh/v1.1/include/mesh_v1.1/dfu/dfu_srv.h @@ -0,0 +1,266 @@ +/* + * Copyright (c) 2020 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file + * @defgroup bt_mesh_dfu_srv Firmware Update Server model + * @ingroup bt_mesh_dfu + * @{ + * @brief API for the Bluetooth Mesh Firmware Update Server model + */ + +#ifndef ZEPHYR_INCLUDE_BLUETOOTH_MESH_DFU_SRV_H__ +#define ZEPHYR_INCLUDE_BLUETOOTH_MESH_DFU_SRV_H__ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct bt_mesh_dfu_srv; + +/** + * + * @brief Initialization parameters for @ref bt_mesh_dfu_srv. + * + * @param _handlers DFU handler function structure. + * @param _imgs List of @ref bt_mesh_dfu_img managed by this Server. + * @param _img_count Number of DFU images managed by this Server. + */ +#define BT_MESH_DFU_SRV_INIT(_handlers, _imgs, _img_count) \ + { \ + .blob = { .cb = &_bt_mesh_dfu_srv_blob_cb }, .cb = _handlers, \ + .imgs = _imgs, .img_count = _img_count, \ + } + +/** + * + * @brief Firmware Update Server model entry. + * + * @param _srv Pointer to a @ref bt_mesh_dfu_srv instance. + */ +#define BT_MESH_MODEL_DFU_SRV(_srv) \ + BT_MESH_MODEL_BLOB_SRV(&(_srv)->blob), \ + BT_MESH_MODEL_CB(BT_MESH_MODEL_ID_DFU_SRV, _bt_mesh_dfu_srv_op, NULL, \ + _srv, &_bt_mesh_dfu_srv_cb) + +/** @brief Firmware Update Server event callbacks. */ +struct bt_mesh_dfu_srv_cb { + /** @brief Transfer check callback. + * + * The transfer check can be used to validate the incoming transfer + * before it starts. The contents of the metadata is implementation + * specific, and should contain all the information the application + * needs to determine whether this image should be accepted, and what + * the effect of the transfer would be. + * + * If applying the image will have an effect on the provisioning state + * of the mesh stack, this can be communicated through the @c effect + * return parameter. + * + * The metadata check can be performed both as part of starting a new + * transfer and as a separate procedure. + * + * This handler is optional. + * + * @param srv Firmware Update Server instance. + * @param img DFU image the metadata check is performed on. + * @param metadata Image metadata. + * @param effect Return parameter for the image effect on the + * provisioning state of the mesh stack. + * + * @return 0 on success, or (negative) error code otherwise. + */ + int (*check)(struct bt_mesh_dfu_srv *srv, + const struct bt_mesh_dfu_img *img, + struct net_buf_simple *metadata, + enum bt_mesh_dfu_effect *effect); + + /** @brief Transfer start callback. + * + * Called when the Firmware Update Server is ready to start a new DFU transfer. + * The application must provide an initialized BLOB stream to be used + * during the DFU transfer. + * + * The following error codes are treated specially, and should be used + * to communicate these issues: + * - @c -ENOMEM: The device cannot fit this image. + * - @c -EBUSY: The application is temporarily unable to accept the + * transfer. + * - @c -EALREADY: The device has already received and verified this + * image, and there's no need to transfer it again. The Firmware Update model + * will skip the transfer phase, and mark the image as verified. + * + * This handler is mandatory. + * + * @param srv Firmware Update Server instance. + * @param img DFU image being updated. + * @param metadata Image metadata. + * @param io BLOB stream return parameter. Must be set to a + * valid BLOB stream by the callback. + * + * @return 0 on success, or (negative) error code otherwise. Return + * codes @c -ENOMEM, @c -EBUSY @c -EALREADY will be passed to + * the updater, other error codes are reported as internal + * errors. + */ + int (*start)(struct bt_mesh_dfu_srv *srv, + const struct bt_mesh_dfu_img *img, + struct net_buf_simple *metadata, + const struct bt_mesh_blob_io **io); + + /** @brief Transfer end callback. + * + * This handler is optional. + * + * If the transfer is successful, the application should verify the + * firmware image, and call either @ref bt_mesh_dfu_srv_verified or + * @ref bt_mesh_dfu_srv_rejected depending on the outcome. + * + * If the transfer fails, the Firmware Update Server will be available for new + * transfers immediately after this function returns. + * + * @param srv Firmware Update Server instance. + * @param img DFU image that failed the update. + * @param success Whether the DFU transfer was successful. + */ + void (*end)(struct bt_mesh_dfu_srv *srv, + const struct bt_mesh_dfu_img *img, bool success); + + /** @brief Transfer recovery callback. + * + * If the device reboots in the middle of a transfer, the Firmware Update Server + * calls this function when the Bluetooth Mesh subsystem is started. + * + * This callback is optional, but transfers will not be recovered after + * a reboot without it. + * + * @param srv Firmware Update Server instance. + * @param img DFU image being updated. + * @param io BLOB stream return parameter. Must be set to a valid BLOB + * stream by the callback. + * + * @return 0 on success, or (negative) error code to abandon the + * transfer. + */ + int (*recover)(struct bt_mesh_dfu_srv *srv, + const struct bt_mesh_dfu_img *img, + const struct bt_mesh_blob_io **io); + + /** @brief Transfer apply callback. + * + * Called after a transfer has been validated, and the updater sends an + * apply message to the Target nodes. + * + * This handler is optional. + * + * @param srv Firmware Update Server instance. + * @param img DFU image that should be applied. + * + * @return 0 on success, or (negative) error code otherwise. + */ + int (*apply)(struct bt_mesh_dfu_srv *srv, + const struct bt_mesh_dfu_img *img); +}; + +/** @brief Firmware Update Server instance. + * + * Should be initialized with @ref BT_MESH_DFU_SRV_INIT. + */ +struct bt_mesh_dfu_srv { + /** Underlying BLOB Transfer Server. */ + struct bt_mesh_blob_srv blob; + /** Callback structure. */ + const struct bt_mesh_dfu_srv_cb *cb; + /** List of updatable images. */ + const struct bt_mesh_dfu_img *imgs; + /** Number of updatable images. */ + size_t img_count; + + /* Runtime state */ + const struct bt_mesh_model *mod; + struct { + /* Effect of transfer, @see bt_mesh_dfu_effect. */ + uint8_t effect; + /* Current phase, @see bt_mesh_dfu_phase. */ + uint8_t phase; + uint8_t ttl; + uint8_t idx; + uint16_t timeout_base; + uint16_t meta; + } update; +}; + +/** @brief Accept the received DFU transfer. + * + * Should be called at the end of a successful DFU transfer. + * + * If the DFU transfer completes successfully, the application should verify + * the image validity (including any image authentication or integrity checks), + * and call this function if the image is ready to be applied. + * + * @param srv Firmware Update Server instance. + */ +void bt_mesh_dfu_srv_verified(struct bt_mesh_dfu_srv *srv); + +/** @brief Reject the received DFU transfer. + * + * Should be called at the end of a successful DFU transfer. + * + * If the DFU transfer completes successfully, the application should verify + * the image validity (including any image authentication or integrity checks), + * and call this function if one of the checks fail. + * + * @param srv Firmware Update Server instance. + */ +void bt_mesh_dfu_srv_rejected(struct bt_mesh_dfu_srv *srv); + +/** @brief Cancel the ongoing DFU transfer. + * + * @param srv Firmware Update Server instance. + */ +void bt_mesh_dfu_srv_cancel(struct bt_mesh_dfu_srv *srv); + +/** @brief Confirm that the received DFU transfer was applied. + * + * Should be called as a result of the @ref bt_mesh_dfu_srv_cb.apply callback. + * + * @param srv Firmware Update Server instance. + */ +void bt_mesh_dfu_srv_applied(struct bt_mesh_dfu_srv *srv); + +/** @brief Check if the Firmware Update Server is busy processing a transfer. + * + * @param srv Firmware Update Server instance. + * + * @return true if a DFU procedure is in progress, false otherwise. + */ +bool bt_mesh_dfu_srv_is_busy(const struct bt_mesh_dfu_srv *srv); + +/** @brief Get the progress of the current DFU procedure, in percent. + * + * @param srv Firmware Update Server instance. + * + * @return The current transfer progress in percent. + */ +uint8_t bt_mesh_dfu_srv_progress(const struct bt_mesh_dfu_srv *srv); + +/** @cond INTERNAL_HIDDEN */ +extern const struct bt_mesh_model_op _bt_mesh_dfu_srv_op[]; +extern const struct bt_mesh_model_cb _bt_mesh_dfu_srv_cb; +extern const struct bt_mesh_blob_srv_cb _bt_mesh_dfu_srv_blob_cb; +/** @endcond */ + +#ifdef __cplusplus +} +#endif + +#endif /* ZEPHYR_INCLUDE_BLUETOOTH_MESH_DFU_SRV_H__ */ + +/** @} */ diff --git a/components/bt/esp_ble_mesh/v1.1/include/mesh_v1.1/mbt/blob.h b/components/bt/esp_ble_mesh/v1.1/include/mesh_v1.1/mbt/blob.h new file mode 100644 index 0000000000..18304f39f6 --- /dev/null +++ b/components/bt/esp_ble_mesh/v1.1/include/mesh_v1.1/mbt/blob.h @@ -0,0 +1,262 @@ +/* + * Copyright (c) 2020 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_INCLUDE_BLUETOOTH_MESH_BLOB_H__ +#define ZEPHYR_INCLUDE_BLUETOOTH_MESH_BLOB_H__ + +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @defgroup bt_mesh_blob Bluetooth Mesh BLOB model API + * @ingroup bt_mesh + * @{ + */ + +#ifndef CONFIG_BT_MESH_BLOB_CHUNK_COUNT_MAX +#define CONFIG_BT_MESH_BLOB_CHUNK_COUNT_MAX 0 +#endif + +/** BLOB transfer mode. */ +enum bt_mesh_blob_xfer_mode { + /** No valid transfer mode. */ + BT_MESH_BLOB_XFER_MODE_NONE, + /** Push mode (Push BLOB Transfer Mode). */ + BT_MESH_BLOB_XFER_MODE_PUSH, + /** Pull mode (Pull BLOB Transfer Mode). */ + BT_MESH_BLOB_XFER_MODE_PULL, + /** Both modes are valid. */ + BT_MESH_BLOB_XFER_MODE_ALL, +}; + +/** Transfer phase. */ +enum bt_mesh_blob_xfer_phase { + /** The BLOB Transfer Server is awaiting configuration. */ + BT_MESH_BLOB_XFER_PHASE_INACTIVE, + /** The BLOB Transfer Server is ready to receive a BLOB transfer. */ + BT_MESH_BLOB_XFER_PHASE_WAITING_FOR_START, + /** The BLOB Transfer Server is waiting for the next block of data. */ + BT_MESH_BLOB_XFER_PHASE_WAITING_FOR_BLOCK, + /** The BLOB Transfer Server is waiting for the next chunk of data. */ + BT_MESH_BLOB_XFER_PHASE_WAITING_FOR_CHUNK, + /** The BLOB was transferred successfully. */ + BT_MESH_BLOB_XFER_PHASE_COMPLETE, + /** The BLOB transfer is paused. */ + BT_MESH_BLOB_XFER_PHASE_SUSPENDED, +}; + +/** BLOB model status codes. */ +enum bt_mesh_blob_status { + /** The message was processed successfully. */ + BT_MESH_BLOB_SUCCESS, + /** The Block Number field value is not within the range of blocks being + * transferred. + */ + BT_MESH_BLOB_ERR_INVALID_BLOCK_NUM, + /** The block size is smaller than the size indicated by the Min Block + * Size Log state or is larger than the size indicated by the Max Block + * Size Log state. + */ + BT_MESH_BLOB_ERR_INVALID_BLOCK_SIZE, + /** The chunk size exceeds the size indicated by the Max Chunk Size + * state, or the number of chunks exceeds the number specified by the + * Max Total Chunks state. + */ + BT_MESH_BLOB_ERR_INVALID_CHUNK_SIZE, + /** The operation cannot be performed while the server is in the current + * phase. + */ + BT_MESH_BLOB_ERR_WRONG_PHASE, + /** A parameter value in the message cannot be accepted. */ + BT_MESH_BLOB_ERR_INVALID_PARAM, + /** The message contains a BLOB ID value that is not expected. */ + BT_MESH_BLOB_ERR_WRONG_BLOB_ID, + /** There is not enough space available in memory to receive the BLOB. + */ + BT_MESH_BLOB_ERR_BLOB_TOO_LARGE, + /** The transfer mode is not supported by the BLOB Transfer Server + * model. + */ + BT_MESH_BLOB_ERR_UNSUPPORTED_MODE, + /** An internal error occurred on the node. */ + BT_MESH_BLOB_ERR_INTERNAL, + /** The requested information cannot be provided while the server is in + * the current phase. + */ + BT_MESH_BLOB_ERR_INFO_UNAVAILABLE, +}; + +/** BLOB transfer data block. */ +struct bt_mesh_blob_block { + /** Block size in bytes */ + size_t size; + /** Offset in bytes from the start of the BLOB. */ + off_t offset; + /** Block number */ + uint16_t number; + /** Number of chunks in block. */ + uint16_t chunk_count; + /** Bitmap of missing chunks. */ + uint8_t missing[DIV_ROUND_UP(CONFIG_BT_MESH_BLOB_CHUNK_COUNT_MAX, + 8)]; +}; + +/** BLOB data chunk. */ +struct bt_mesh_blob_chunk { + /** Offset of the chunk data from the start of the block. */ + off_t offset; + /** Chunk data size. */ + size_t size; + /** Chunk data. */ + uint8_t *data; +}; + +/** BLOB transfer. */ +struct bt_mesh_blob_xfer { + /** BLOB ID. */ + uint64_t id; + /** Total BLOB size in bytes. */ + size_t size; + /** BLOB transfer mode. */ + enum bt_mesh_blob_xfer_mode mode; + /* Logarithmic representation of the block size. */ + uint8_t block_size_log; + /** Base chunk size. May be smaller for the last chunk. */ + uint16_t chunk_size; +}; + +/** BLOB stream interaction mode. */ +enum bt_mesh_blob_io_mode { + /** Read data from the stream. */ + BT_MESH_BLOB_READ, + /** Write data to the stream. */ + BT_MESH_BLOB_WRITE, +}; + +/** BLOB stream. */ +struct bt_mesh_blob_io { + /** @brief Open callback. + * + * Called when the reader is opened for reading. + * + * @param io BLOB stream. + * @param xfer BLOB transfer. + * @param mode Direction of the stream (read/write). + * + * @return 0 on success, or (negative) error code otherwise. + */ + int (*open)(const struct bt_mesh_blob_io *io, + const struct bt_mesh_blob_xfer *xfer, + enum bt_mesh_blob_io_mode mode); + + /** @brief Close callback. + * + * Called when the reader is closed. + * + * @param io BLOB stream. + * @param xfer BLOB transfer. + */ + void (*close)(const struct bt_mesh_blob_io *io, + const struct bt_mesh_blob_xfer *xfer); + + /** @brief Block start callback. + * + * Called when a new block is opened for sending. Each block is only + * sent once, and are always sent in increasing order. The data chunks + * inside a single block may be requested out of order and multiple + * times. + * + * @param io BLOB stream. + * @param xfer BLOB transfer. + * @param block Block that was started. + */ + int (*block_start)(const struct bt_mesh_blob_io *io, + const struct bt_mesh_blob_xfer *xfer, + const struct bt_mesh_blob_block *block); + + /** @brief Block end callback. + * + * Called when the current block has been transmitted in full. + * No data from this block will be requested again, and the application + * data associated with this block may be discarded. + * + * @param io BLOB stream. + * @param xfer BLOB transfer. + * @param block Block that finished sending. + */ + void (*block_end)(const struct bt_mesh_blob_io *io, + const struct bt_mesh_blob_xfer *xfer, + const struct bt_mesh_blob_block *block); + + /** @brief Chunk data write callback. + * + * Used by the BLOB Transfer Server on incoming data. + * + * Each block is divided into chunks of data. This callback is called + * when a new chunk of data is received. Chunks may be received in + * any order within their block. + * + * If the callback returns successfully, this chunk will be marked as + * received, and will not be received again unless the block is + * restarted due to a transfer suspension. If the callback returns a + * non-zero value, the chunk remains unreceived, and the BLOB Transfer + * Client will attempt to resend it later. + * + * Note that the Client will only perform a limited number of attempts + * at delivering a chunk before dropping a Target node from the transfer. + * The number of retries performed by the Client is implementation + * specific. + * + * @param io BLOB stream. + * @param xfer BLOB transfer. + * @param block Block the chunk is part of. + * @param chunk Received chunk. + * + * @return 0 on success, or (negative) error code otherwise. + */ + int (*wr)(const struct bt_mesh_blob_io *io, + const struct bt_mesh_blob_xfer *xfer, + const struct bt_mesh_blob_block *block, + const struct bt_mesh_blob_chunk *chunk); + + /** @brief Chunk data read callback. + * + * Used by the BLOB Transfer Client to fetch outgoing data. + * + * The Client calls the chunk data request callback to populate a chunk + * message going out to the Target nodes. The data request callback + * may be called out of order and multiple times for each offset, and + * cannot be used as an indication of progress. + * + * Returning a non-zero status code on the chunk data request callback + * results in termination of the transfer. + * + * @param io BLOB stream. + * @param xfer BLOB transfer. + * @param block Block the chunk is part of. + * @param chunk Chunk to get the data of. The buffer pointer to by the + * @c data member should be filled by the callback. + * + * @return 0 on success, or (negative) error code otherwise. + */ + int (*rd)(const struct bt_mesh_blob_io *io, + const struct bt_mesh_blob_xfer *xfer, + const struct bt_mesh_blob_block *block, + const struct bt_mesh_blob_chunk *chunk); +}; + +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif /* ZEPHYR_INCLUDE_BLUETOOTH_MESH_BLOB_H__ */ diff --git a/components/bt/esp_ble_mesh/v1.1/include/mesh_v1.1/mbt/blob_cli.h b/components/bt/esp_ble_mesh/v1.1/include/mesh_v1.1/mbt/blob_cli.h new file mode 100644 index 0000000000..574ec82715 --- /dev/null +++ b/components/bt/esp_ble_mesh/v1.1/include/mesh_v1.1/mbt/blob_cli.h @@ -0,0 +1,454 @@ +/* + * Copyright (c) 2020 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_INCLUDE_BLUETOOTH_MESH_BLOB_CLI_H_ +#define ZEPHYR_INCLUDE_BLUETOOTH_MESH_BLOB_CLI_H_ + +#include + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @defgroup bt_mesh_blob_cli Bluetooth Mesh BLOB Transfer Client model API + * @ingroup bt_mesh + * @{ + */ + +struct bt_mesh_blob_cli; + +/** + * + * @brief BLOB Transfer Client model Composition Data entry. + * + * @param _cli Pointer to a @ref bt_mesh_blob_cli instance. + */ +#define BT_MESH_MODEL_BLOB_CLI(_cli) \ + BT_MESH_MODEL_CB(BT_MESH_MODEL_ID_BLOB_CLI, _bt_mesh_blob_cli_op, \ + NULL, _cli, &_bt_mesh_blob_cli_cb) + +/** Target node's Pull mode (Pull BLOB Transfer Mode) context used + * while sending chunks to the Target node. + */ +struct bt_mesh_blob_target_pull { + /** Timestamp when the Block Report Timeout Timer expires for this Target node. */ + int64_t block_report_timestamp; + + /** Missing chunks reported by this Target node. */ + uint8_t missing[DIV_ROUND_UP(CONFIG_BT_MESH_BLOB_CHUNK_COUNT_MAX, 8)]; +}; + +/** BLOB Transfer Client Target node. */ +struct bt_mesh_blob_target { + /** Linked list node */ + sys_snode_t n; + + /** Target node address. */ + uint16_t addr; + + /** Target node's Pull mode context. + * Needs to be initialized when sending a BLOB in Pull mode. + */ + struct bt_mesh_blob_target_pull *pull; + + /** BLOB transfer status, see @ref bt_mesh_blob_status. */ + uint8_t status; + + uint8_t procedure_complete:1, /* Procedure has been completed. */ + acked:1, /* Message has been acknowledged. Not used when sending. */ + timedout:1, /* Target node didn't respond after specified timeout. */ + skip:1; /* Skip Target node from broadcast. */ +}; + +/** BLOB transfer information. + * + * If @c phase is @ref BT_MESH_BLOB_XFER_PHASE_INACTIVE, + * the fields below @c phase are not initialized. + * If @c phase is @ref BT_MESH_BLOB_XFER_PHASE_WAITING_FOR_START, + * the fields below @c id are not initialized. + */ +struct bt_mesh_blob_xfer_info { + /** BLOB transfer status. */ + enum bt_mesh_blob_status status; + + /** BLOB transfer mode. */ + enum bt_mesh_blob_xfer_mode mode; + + /** BLOB transfer phase. */ + enum bt_mesh_blob_xfer_phase phase; + + /** BLOB ID. */ + uint64_t id; + + /** BLOB size in octets. */ + uint32_t size; + + /** Logarithmic representation of the block size. */ + uint8_t block_size_log; + + /** MTU size in octets. */ + uint16_t mtu_size; + + /** Bit field indicating blocks that were not received. */ + const uint8_t *missing_blocks; +}; + +/** BLOB Transfer Client transfer inputs. */ +struct bt_mesh_blob_cli_inputs { + /** Linked list of Target nodes. Each node should point to @ref + * bt_mesh_blob_target::n. + */ + sys_slist_t targets; + + /** AppKey index to send with. */ + uint16_t app_idx; + + /** Group address destination for the BLOB transfer, or @ref + * BT_MESH_ADDR_UNASSIGNED to send every message to each Target + * node individually. + */ + uint16_t group; + + /** Time to live value of BLOB transfer messages. */ + uint8_t ttl; + + /** Additional response time for the Target nodes, in 10-second increments. + * + * The extra time can be used to give the Target nodes more time to respond + * to messages from the Client. The actual timeout will be calculated + * according to the following formula: + * + * @verbatim + * timeout = 20 seconds + (10 seconds * timeout_base) + (100 ms * TTL) + * @endverbatim + * + * If a Target node fails to respond to a message from the Client within the + * configured transfer timeout, the Target node is dropped. + */ + uint16_t timeout_base; +}; + +/** Transfer capabilities of a Target node. */ +struct bt_mesh_blob_cli_caps { + /** Max BLOB size. */ + size_t max_size; + + /** Logarithmic representation of the minimum block size. */ + uint8_t min_block_size_log; + + /** Logarithmic representation of the maximum block size. */ + uint8_t max_block_size_log; + + /** Max number of chunks per block. */ + uint16_t max_chunks; + + /** Max chunk size. */ + uint16_t max_chunk_size; + + /** Max MTU size. */ + uint16_t mtu_size; + + /** Supported transfer modes. */ + enum bt_mesh_blob_xfer_mode modes; +}; + +/** BLOB Transfer Client state. */ +enum bt_mesh_blob_cli_state { + /** No transfer is active. */ + BT_MESH_BLOB_CLI_STATE_NONE, + /** Retrieving transfer capabilities. */ + BT_MESH_BLOB_CLI_STATE_CAPS_GET, + /** Sending transfer start. */ + BT_MESH_BLOB_CLI_STATE_START, + /** Sending block start. */ + BT_MESH_BLOB_CLI_STATE_BLOCK_START, + /** Sending block chunks. */ + BT_MESH_BLOB_CLI_STATE_BLOCK_SEND, + /** Checking block status. */ + BT_MESH_BLOB_CLI_STATE_BLOCK_CHECK, + /** Checking transfer status. */ + BT_MESH_BLOB_CLI_STATE_XFER_CHECK, + /** Cancelling transfer. */ + BT_MESH_BLOB_CLI_STATE_CANCEL, + /** Transfer is suspended. */ + BT_MESH_BLOB_CLI_STATE_SUSPENDED, + /** Checking transfer progress. */ + BT_MESH_BLOB_CLI_STATE_XFER_PROGRESS_GET, +}; + +/** Event handler callbacks for the BLOB Transfer Client model. + * + * All handlers are optional. + */ +struct bt_mesh_blob_cli_cb { + /** @brief Capabilities retrieval completion callback. + * + * Called when the capabilities retrieval procedure completes, indicating that + * a common set of acceptable transfer parameters have been established + * for the given list of Target nodes. All compatible Target nodes have + * status code @ref BT_MESH_BLOB_SUCCESS. + * + * @param cli BLOB Transfer Client instance. + * @param caps Safe transfer capabilities if the transfer capabilities + * of at least one Target node has satisfied the Client, or NULL otherwise. + */ + void (*caps)(struct bt_mesh_blob_cli *cli, + const struct bt_mesh_blob_cli_caps *caps); + + /** @brief Target node loss callback. + * + * Called whenever a Target node has been lost due to some error in the + * transfer. Losing a Target node is not considered a fatal error for + * the Client until all Target nodes have been lost. + * + * @param cli BLOB Transfer Client instance. + * @param target Target node that was lost. + * @param reason Reason for the Target node loss. + */ + void (*lost_target)(struct bt_mesh_blob_cli *cli, + struct bt_mesh_blob_target *target, + enum bt_mesh_blob_status reason); + + /** @brief Transfer is suspended. + * + * Called when the transfer is suspended due to response timeout from all Target nodes. + * + * @param cli BLOB Transfer Client instance. + */ + void (*suspended)(struct bt_mesh_blob_cli *cli); + + /** @brief Transfer end callback. + * + * Called when the transfer ends. + * + * @param cli BLOB Transfer Client instance. + * @param xfer Completed transfer. + * @param success Status of the transfer. + * Is @c true if at least one Target + * node received the whole transfer. + */ + void (*end)(struct bt_mesh_blob_cli *cli, + const struct bt_mesh_blob_xfer *xfer, bool success); + + /** @brief Transfer progress callback + * + * The content of @c info is invalidated upon exit from the callback. + * Therefore it needs to be copied if it is planned to be used later. + * + * @param cli BLOB Transfer Client instance. + * @param target Target node that responded to the request. + * @param info BLOB transfer information. + */ + void (*xfer_progress)(struct bt_mesh_blob_cli *cli, + struct bt_mesh_blob_target *target, + const struct bt_mesh_blob_xfer_info *info); + + /** @brief End of Get Transfer Progress procedure. + * + * Called when all Target nodes have responded or the procedure timed-out. + * + * @param cli BLOB Transfer Client instance. + */ + void (*xfer_progress_complete)(struct bt_mesh_blob_cli *cli); +}; + +/** @cond INTERNAL_HIDDEN */ +struct blob_cli_broadcast_ctx { + /** Called for every Target node in unicast mode, or once in case of multicast mode. */ + void (*send)(struct bt_mesh_blob_cli *cli, uint16_t dst); + /** Called after every @ref blob_cli_broadcast_ctx::send callback. */ + void (*send_complete)(struct bt_mesh_blob_cli *cli, uint16_t dst); + /** If @ref blob_cli_broadcast_ctx::acked is true, called after all Target nodes + * have confirmed reception by @ref blob_cli_broadcast_rsp. Otherwise, called + * after transmission has been completed. + */ + void (*next)(struct bt_mesh_blob_cli *cli); + /** If true, every transmission needs to be confirmed by @ref blob_cli_broadcast_rsp before + * @ref blob_cli_broadcast_ctx::next is called. + */ + bool acked; + /** If true, the message is always sent in a unicast way. */ + bool force_unicast; + /** If true, non-responsive Target nodes won't be dropped after transfer has timed out. */ + bool optional; + /** Set to true by the BLOB Transfer Client between blob_cli_broadcast + * and broadcast_complete calls. + */ + bool is_inited; + /* Defines a time in ms by which the broadcast API postpones sending the message to a next + * target or completing the broadcast. + */ + uint32_t post_send_delay_ms; +}; +/** INTERNAL_HIDDEN @endcond */ + +/** BLOB Transfer Client model instance. */ +struct bt_mesh_blob_cli { + /** Event handler callbacks */ + const struct bt_mesh_blob_cli_cb *cb; + + /* Runtime state */ + const struct bt_mesh_model *mod; + + struct { + struct bt_mesh_blob_target *target; + struct blob_cli_broadcast_ctx ctx; + struct k_work_delayable retry; + /* Represents Client Timeout timer in a timestamp. Used in Pull mode only. */ + int64_t cli_timestamp; + struct k_work_delayable complete; + uint16_t pending; + uint8_t retries; + uint8_t sending : 1, + cancelled : 1; + } tx; + + const struct bt_mesh_blob_io *io; + const struct bt_mesh_blob_cli_inputs *inputs; + const struct bt_mesh_blob_xfer *xfer; + uint32_t chunk_interval_ms; + uint16_t block_count; + uint16_t chunk_idx; + uint16_t mtu_size; + enum bt_mesh_blob_cli_state state; + struct bt_mesh_blob_block block; + struct bt_mesh_blob_cli_caps caps; +}; + +/** @brief Retrieve transfer capabilities for a list of Target nodes. + * + * Queries the availability and capabilities of all Target nodes, producing a + * cumulative set of transfer capabilities for the Target nodes, and returning + * it through the @ref bt_mesh_blob_cli_cb::caps callback. + * + * Retrieving the capabilities may take several seconds, depending on the + * number of Target nodes and mesh network performance. The end of the procedure + * is indicated through the @ref bt_mesh_blob_cli_cb::caps callback. + * + * This procedure is not required, but strongly recommended as a + * preparation for a transfer to maximize performance and the chances of + * success. + * + * @param cli BLOB Transfer Client instance. + * @param inputs Statically allocated BLOB Transfer Client transfer inputs. + * + * @return 0 on success, or (negative) error code otherwise. + */ +int bt_mesh_blob_cli_caps_get(struct bt_mesh_blob_cli *cli, + const struct bt_mesh_blob_cli_inputs *inputs); + +/** @brief Perform a BLOB transfer. + * + * Starts sending the transfer to the Target nodes. Only Target nodes with a + * @c status of @ref BT_MESH_BLOB_SUCCESS will be considered. + * + * The transfer will keep going either until all Target nodes have been dropped, or + * the full BLOB has been sent. + * + * The BLOB transfer may take several minutes, depending on the number of + * Target nodes, size of the BLOB and mesh network performance. The end of the + * transfer is indicated through the @ref bt_mesh_blob_cli_cb::end callback. + * + * A Client only supports one transfer at the time. + * + * @param cli BLOB Transfer Client instance. + * @param inputs Statically allocated BLOB Transfer Client transfer inputs. + * @param xfer Statically allocated transfer parameters. + * @param io BLOB stream to read the transfer from. + * + * @return 0 on success, or (negative) error code otherwise. + */ +int bt_mesh_blob_cli_send(struct bt_mesh_blob_cli *cli, + const struct bt_mesh_blob_cli_inputs *inputs, + const struct bt_mesh_blob_xfer *xfer, + const struct bt_mesh_blob_io *io); + +/** @brief Suspend the active transfer. + * + * @param cli BLOB Transfer Client instance. + * + * @return 0 on success, or (negative) error code otherwise. + */ +int bt_mesh_blob_cli_suspend(struct bt_mesh_blob_cli *cli); + +/** @brief Resume the suspended transfer. + * + * @param cli BLOB Transfer Client instance. + * + * @return 0 on success, or (negative) error code otherwise. + */ +int bt_mesh_blob_cli_resume(struct bt_mesh_blob_cli *cli); + +/** @brief Cancel an ongoing transfer. + * + * @param cli BLOB Transfer Client instance. + */ +void bt_mesh_blob_cli_cancel(struct bt_mesh_blob_cli *cli); + +/** @brief Get the progress of BLOB transfer. + * + * This function can only be used if the BLOB Transfer Client is currently + * not performing a BLOB transfer. + * To get progress of the active BLOB transfer, use the + * @ref bt_mesh_blob_cli_xfer_progress_active_get function. + * + * @param cli BLOB Transfer Client instance. + * @param inputs Statically allocated BLOB Transfer Client transfer inputs. + * + * @return 0 on success, or (negative) error code otherwise. + */ +int bt_mesh_blob_cli_xfer_progress_get(struct bt_mesh_blob_cli *cli, + const struct bt_mesh_blob_cli_inputs *inputs); + +/** @brief Get the current progress of the active transfer in percent. + * + * @param cli BLOB Transfer Client instance. + * + * @return The current transfer progress, or 0 if no transfer is active. + */ +uint8_t bt_mesh_blob_cli_xfer_progress_active_get(struct bt_mesh_blob_cli *cli); + +/** @brief Get the current state of the BLOB Transfer Client. + * + * @param cli BLOB Transfer Client instance. + * + * @return true if the BLOB Transfer Client is currently participating in a transfer or + * retrieving the capabilities and false otherwise. + */ +bool bt_mesh_blob_cli_is_busy(struct bt_mesh_blob_cli *cli); + +/** @brief Set chunk sending interval in ms + * + * This function is optional, and can be used to define how fast chunks are sent in the BLOB Client + * Model. + * Without an added delay, for example a Bluetooth Mesh DFU can cause network blockage by + * constantly sending the next chunks, especially if the chunks are sent to group addresses or + * multiple unicast addresses. + * + * @note: Big intervals may cause timeouts. Increasing the @c timeout_base accordingly can + * circumvent this. + * + * @param cli BLOB Transfer Client instance. + * @param interval_ms the delay before each chunk is sent out in ms. + */ +void bt_mesh_blob_cli_set_chunk_interval_ms(struct bt_mesh_blob_cli *cli, uint32_t interval_ms); + +/** @cond INTERNAL_HIDDEN */ +extern const struct bt_mesh_model_op _bt_mesh_blob_cli_op[]; +extern const struct bt_mesh_model_cb _bt_mesh_blob_cli_cb; +/** @endcond */ + +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif /* ZEPHYR_INCLUDE_BLUETOOTH_MESH_BLOB_CLI_H_ */ diff --git a/components/bt/esp_ble_mesh/v1.1/include/mesh_v1.1/mbt/blob_srv.h b/components/bt/esp_ble_mesh/v1.1/include/mesh_v1.1/mbt/blob_srv.h new file mode 100644 index 0000000000..8a0c6b67a1 --- /dev/null +++ b/components/bt/esp_ble_mesh/v1.1/include/mesh_v1.1/mbt/blob_srv.h @@ -0,0 +1,220 @@ +/* + * Copyright (c) 2020 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_INCLUDE_BLUETOOTH_MESH_BLOB_SRV_H_ +#define ZEPHYR_INCLUDE_BLUETOOTH_MESH_BLOB_SRV_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @defgroup bt_mesh_blob_srv Bluetooth Mesh BLOB Transfer Server model API + * @ingroup bt_mesh + * @{ + */ + +struct bt_mesh_blob_srv; + +/** + * + * @brief Max number of blocks in a single transfer. + */ +#if defined(CONFIG_BT_MESH_BLOB_SRV) +#define BT_MESH_BLOB_BLOCKS_MAX \ + (DIV_ROUND_UP(CONFIG_BT_MESH_BLOB_SIZE_MAX, \ + CONFIG_BT_MESH_BLOB_BLOCK_SIZE_MIN)) +#else +#define BT_MESH_BLOB_BLOCKS_MAX 1 +#endif + +/** + * + * @brief BLOB Transfer Server model composition data entry. + * + * @param _srv Pointer to a @ref bt_mesh_blob_srv instance. + */ +#define BT_MESH_MODEL_BLOB_SRV(_srv) \ + BT_MESH_MODEL_CB(BT_MESH_MODEL_ID_BLOB_SRV, _bt_mesh_blob_srv_op, \ + NULL, _srv, &_bt_mesh_blob_srv_cb) + +/** @brief BLOB Transfer Server model event handlers. + * + * All callbacks are optional. + */ +struct bt_mesh_blob_srv_cb { + /** @brief Transfer start callback. + * + * Called when the transfer has started with the prepared BLOB ID. + * + * @param srv BLOB Transfer Server instance. + * @param ctx Message context for the incoming start message. The + * entire transfer will be sent from the same source + * address. + * @param xfer Transfer parameters. + * + * @return 0 on success, or (negative) error code to reject the + * transfer. + */ + int (*start)(struct bt_mesh_blob_srv *srv, struct bt_mesh_msg_ctx *ctx, + struct bt_mesh_blob_xfer *xfer); + + /** @brief Transfer end callback. + * + * Called when the transfer ends, either because it was cancelled, or + * because it finished successfully. A new transfer may be prepared. + * + * @note The transfer may end before it's started if the start + * parameters are invalid. + * + * @param srv BLOB Transfer Server instance. + * @param id BLOB ID of the cancelled transfer. + * @param success Whether the transfer was successful. + */ + void (*end)(struct bt_mesh_blob_srv *srv, uint64_t id, bool success); + + /** @brief Transfer suspended callback. + * + * Called if the Server timed out while waiting for a transfer packet. + * A suspended transfer may resume later from the start of the current + * block. Any received chunks in the current block should be discarded, + * they will be received again if the transfer resumes. + * + * The transfer will call @c resumed again when resuming. + * + * @note The BLOB Transfer Server does not run a timer in the suspended state, + * and it's up to the application to determine whether the + * transfer should be permanently cancelled. Without interaction, + * the transfer will be suspended indefinitely, and the BLOB Transfer + * Server will not accept any new transfers. + * + * @param srv BLOB Transfer Server instance. + */ + void (*suspended)(struct bt_mesh_blob_srv *srv); + + /** @brief Transfer resume callback. + * + * Called if the transfer is resumed after being suspended. + * + * @param srv BLOB Transfer Server instance. + */ + void (*resume)(struct bt_mesh_blob_srv *srv); + + /** @brief Transfer recovery callback. + * + * Called when the Bluetooth Mesh subsystem is started if the device is rebooted + * in the middle of a transfer. + * + * Transfers will not be resumed after a reboot if this callback is not + * defined. + * + * @param srv BLOB Transfer Server instance. + * @param xfer Transfer to resume. + * @param io BLOB stream return parameter. Must be set to a valid + * BLOB stream by the callback. + * + * @return 0 on success, or (negative) error code to abandon the + * transfer. + */ + int (*recover)(struct bt_mesh_blob_srv *srv, + struct bt_mesh_blob_xfer *xfer, + const struct bt_mesh_blob_io **io); +}; + +/** @brief BLOB Transfer Server model instance. */ +struct bt_mesh_blob_srv { + /** Event handler callbacks. */ + const struct bt_mesh_blob_srv_cb *cb; + + /* Runtime state: */ + const struct bt_mesh_blob_io *io; + struct k_work_delayable rx_timeout; + struct bt_mesh_blob_block block; + const struct bt_mesh_model *mod; + enum bt_mesh_blob_xfer_phase phase; + + struct bt_mesh_blob_srv_state { + struct bt_mesh_blob_xfer xfer; + uint16_t cli; + uint16_t app_idx; + uint16_t timeout_base; + uint16_t mtu_size; + uint8_t ttl; + + /* Bitfield of pending blocks. */ + ATOMIC_DEFINE(blocks, BT_MESH_BLOB_BLOCKS_MAX); + } state; + + /* Pull mode (Pull BLOB Transfer Mode) behavior. */ + struct { + uint16_t chunk_idx; + struct k_work_delayable report; + } pull; +}; + +/** @brief Prepare BLOB Transfer Server for an incoming transfer. + * + * Before a BLOB Transfer Server can receive a transfer, the transfer must be prepared + * through some application level mechanism. The BLOB Transfer Server will only accept + * incoming transfers with a matching BLOB ID. + * + * @param srv BLOB Transfer Server instance. + * @param id BLOB ID to accept. + * @param io BLOB stream to write the incoming BLOB to. + * @param ttl Time to live value to use in responses to the BLOB Transfer Client. + * @param timeout_base Extra time for the Client to respond in addition to the + * base 10 seconds, in 10-second increments. + * + * @return 0 on success, or (negative) error code on failure. + */ +int bt_mesh_blob_srv_recv(struct bt_mesh_blob_srv *srv, uint64_t id, + const struct bt_mesh_blob_io *io, uint8_t ttl, + uint16_t timeout_base); + +/** @brief Cancel the current BLOB transfer. + * + * Tells the BLOB Transfer Client to drop this device from the list of Targets for the + * current transfer. Note that the client may continue sending the transfer to + * other Targets. + * + * @param srv BLOB Transfer Server instance. + * + * @return 0 on success, or (negative) error code on failure. + */ +int bt_mesh_blob_srv_cancel(struct bt_mesh_blob_srv *srv); + +/** @brief Get the current state of the BLOB Transfer Server. + * + * @param srv BLOB Transfer Server instance. + * + * @return true if the BLOB Transfer Server is currently participating in a transfer, + * false otherwise. + */ +bool bt_mesh_blob_srv_is_busy(const struct bt_mesh_blob_srv *srv); + +/** @brief Get the current progress of the active transfer in percent. + * + * @param srv BLOB Transfer Server instance. + * + * @return The current transfer progress, or 0 if no transfer is active. + */ +uint8_t bt_mesh_blob_srv_progress(const struct bt_mesh_blob_srv *srv); + +/** @cond INTERNAL_HIDDEN */ +extern const struct bt_mesh_model_op _bt_mesh_blob_srv_op[]; +extern const struct bt_mesh_model_cb _bt_mesh_blob_srv_cb; +/** @endcond */ + +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif /* ZEPHYR_INCLUDE_BLUETOOTH_MESH_BLOB_SRV_H_ */ diff --git a/components/bt/esp_ble_mesh/v1.1/mbt/blob.h b/components/bt/esp_ble_mesh/v1.1/mbt/blob.h new file mode 100644 index 0000000000..162725480e --- /dev/null +++ b/components/bt/esp_ble_mesh/v1.1/mbt/blob.h @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2020 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#define BT_MESH_BLOB_OP_XFER_GET BT_MESH_MODEL_OP_2(0x83, 0x00) +#define BT_MESH_BLOB_OP_XFER_START BT_MESH_MODEL_OP_2(0x83, 0x01) +#define BT_MESH_BLOB_OP_XFER_CANCEL BT_MESH_MODEL_OP_2(0x83, 0x02) +#define BT_MESH_BLOB_OP_XFER_STATUS BT_MESH_MODEL_OP_2(0x83, 0x03) +#define BT_MESH_BLOB_OP_BLOCK_GET BT_MESH_MODEL_OP_2(0x83, 0x05) +#define BT_MESH_BLOB_OP_BLOCK_START BT_MESH_MODEL_OP_2(0x83, 0x04) +#define BT_MESH_BLOB_OP_CHUNK BT_MESH_MODEL_OP_1(0x66) +#define BT_MESH_BLOB_OP_BLOCK_STATUS BT_MESH_MODEL_OP_1(0x67) +#define BT_MESH_BLOB_OP_BLOCK_REPORT BT_MESH_MODEL_OP_1(0x68) +#define BT_MESH_BLOB_OP_INFO_GET BT_MESH_MODEL_OP_2(0x83, 0x06) +#define BT_MESH_BLOB_OP_INFO_STATUS BT_MESH_MODEL_OP_2(0x83, 0x07) + +#define BLOB_BLOCK_NOT_SET 0xffff + +#define BLOB_CHUNK_SDU_OVERHEAD \ + (BT_MESH_MODEL_OP_LEN(BT_MESH_BLOB_OP_CHUNK) + 2 + BT_MESH_MIC_SHORT) + +#define BLOB_CHUNK_SIZE_MAX(sdu_max) ((sdu_max) - BLOB_CHUNK_SDU_OVERHEAD) +#define BLOB_CHUNK_SDU_LEN(chunk_size) (BLOB_CHUNK_SDU_OVERHEAD + (chunk_size)) + +#if CONFIG_BT_MESH_ALIGN_CHUNK_SIZE_TO_MAX_SEGMENT || \ + CONFIG_BT_MESH_RX_BLOB_CHUNK_SIZE > BLOB_CHUNK_SIZE_MAX(BT_MESH_RX_SDU_MAX) +#define BLOB_RX_CHUNK_SIZE BLOB_CHUNK_SIZE_MAX(BT_MESH_RX_SDU_MAX) +#else +#define BLOB_RX_CHUNK_SIZE CONFIG_BT_MESH_RX_BLOB_CHUNK_SIZE +#endif + +#if CONFIG_BT_MESH_ALIGN_CHUNK_SIZE_TO_MAX_SEGMENT || \ + CONFIG_BT_MESH_TX_BLOB_CHUNK_SIZE > BLOB_CHUNK_SIZE_MAX(BT_MESH_TX_SDU_MAX) +#define BLOB_TX_CHUNK_SIZE BLOB_CHUNK_SIZE_MAX(BT_MESH_TX_SDU_MAX) +#else +#define BLOB_TX_CHUNK_SIZE CONFIG_BT_MESH_TX_BLOB_CHUNK_SIZE +#endif + +/* Utility macros for calculating log2 of a number at compile time. + * Used to determine the log2 representation of the block size, which + * is configured as a raw number, but encoded as log2. + * + * The macros expand to a series of ternary expressions, effectively + * searching through power of twos until a match is found. + * According to MshMBTv1.0, the block size cannot be larger than 2^20, + * so we'll stop the search at 20. + */ +#define _BLOB_LOG_2_CEIL(l, x) ((x) <= (1U << l)) ? l : +#define _BLOB_LOG_2_FLOOR(l, x) ((x) < (1U << (l + 1))) ? l : + +#define BLOB_BLOCK_SIZE_LOG_CEIL(x) (LISTIFY(20, _BLOB_LOG_2_CEIL, (), x) 20) +#define BLOB_BLOCK_SIZE_LOG_FLOOR(x) (LISTIFY(20, _BLOB_LOG_2_FLOOR, (), x) 20) + +/* Log2 representation of the minimum block size */ +#define BLOB_BLOCK_SIZE_LOG_MIN BLOB_BLOCK_SIZE_LOG_CEIL(CONFIG_BT_MESH_BLOB_BLOCK_SIZE_MIN) +/* Log2 representation of the maximum block size */ +#define BLOB_BLOCK_SIZE_LOG_MAX BLOB_BLOCK_SIZE_LOG_FLOOR(CONFIG_BT_MESH_BLOB_BLOCK_SIZE_MAX) + +#if defined(CONFIG_BT_MESH_BLOB_SRV) +#define BLOB_BLOCK_REPORT_STATUS_MSG_MAXLEN ( \ + MAX(sizeof(((struct bt_mesh_blob_block *)0)->missing), \ + CONFIG_BT_MESH_BLOB_SRV_PULL_REQ_COUNT * 3)) +#define BLOB_BLOCK_STATUS_MSG_MAXLEN (5 + \ + MAX(sizeof(((struct bt_mesh_blob_block *)0)->missing), \ + CONFIG_BT_MESH_BLOB_SRV_PULL_REQ_COUNT * 3)) +#else +#define BLOB_BLOCK_REPORT_STATUS_MSG_MAXLEN sizeof(((struct bt_mesh_blob_srv *)0)->block.missing) +#define BLOB_BLOCK_STATUS_MSG_MAXLEN (5 + sizeof(((struct bt_mesh_blob_srv *)0)->block.missing)) +#endif + +#define BLOB_XFER_STATUS_MSG_MAXLEN (17 + sizeof(((struct bt_mesh_blob_srv *)0)->state.blocks)) + +enum bt_mesh_blob_chunks_missing { + BT_MESH_BLOB_CHUNKS_MISSING_ALL, + BT_MESH_BLOB_CHUNKS_MISSING_NONE, + BT_MESH_BLOB_CHUNKS_MISSING_SOME, + BT_MESH_BLOB_CHUNKS_MISSING_ENCODED, +}; + +static inline size_t blob_block_size(size_t xfer_size, uint8_t block_size_log, + uint32_t idx) +{ + if (((idx + 1U) << block_size_log) <= xfer_size) { + return (1U << block_size_log); + } + + return xfer_size & BIT_MASK(block_size_log); +} + +static inline void blob_chunk_missing_set(uint8_t *missing_chunks, + int idx, bool missing) +{ + WRITE_BIT(missing_chunks[idx / 8], idx % 8, missing); +} + +static inline bool +blob_chunk_missing_get(const uint8_t *missing_chunks, int idx) +{ + return !!(missing_chunks[idx / 8] & BIT(idx % 8)); +} + +static inline void blob_chunk_missing_set_all(struct bt_mesh_blob_block *block) +{ + size_t bytes = block->chunk_count / 8; + + memset(block->missing, 0xff, bytes); + if (block->chunk_count % 8) { + block->missing[bytes] = BIT_MASK(block->chunk_count % 8); + } +} + +static inline void blob_chunk_missing_set_none(struct bt_mesh_blob_block *block) +{ + memset(block->missing, 0, sizeof(block->missing)); +} + +/** @brief Perform a message broadcast to all BLOB Transfer Client Target nodes. + * + * Will send to a group or each Target node individually, repeating until + * all Target nodes have responded or the retry time has run out. + * + * @param cli BLOB Transfer Client instance + * @param ctx Broadcast context + */ +void blob_cli_broadcast(struct bt_mesh_blob_cli *cli, + const struct blob_cli_broadcast_ctx *ctx); + +/** @brief Register that a Target node responded to a broadcast. + * + * @param cli BLOB Transfer Client instance + * @param target Target node that responded. + */ +void blob_cli_broadcast_rsp(struct bt_mesh_blob_cli *cli, + struct bt_mesh_blob_target *target); + +/** @brief Notify the BLOB Transfer Client that the requested transmission is complete. + * + * Should be called once for each call to the @ref blob_cli_broadcast_ctx.send + * callback. + * + * @param cli BLOB Transfer Client instance. + */ +void blob_cli_broadcast_tx_complete(struct bt_mesh_blob_cli *cli); + +/** @brief Aborts any ongoing BLOB Transfer Client operations. + * + * @param cli BLOB Transfer Client instance. + */ +void blob_cli_broadcast_abort(struct bt_mesh_blob_cli *cli); 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 new file mode 100644 index 0000000000..40d3d37a6c --- /dev/null +++ b/components/bt/esp_ble_mesh/v1.1/mbt/blob_cli.c @@ -0,0 +1,1658 @@ +/* + * Copyright (c) 2020 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include +#include +#include "mesh.h" +#include "blob.h" +#include "net.h" +#include "transport.h" + +#define LOG_LEVEL CONFIG_BT_MESH_MODEL_LOG_LEVEL +#include +LOG_MODULE_REGISTER(bt_mesh_blob_cli); + +#define TARGETS_FOR_EACH(cli, target) \ + SYS_SLIST_FOR_EACH_CONTAINER((sys_slist_t *)&(cli)->inputs->targets, \ + target, n) + +/* The Maximum BLOB Poll Interval - T_MBPI */ +#define BLOB_POLL_TIME_MAX_SECS 30 + +#define CLIENT_TIMEOUT_MSEC(cli) (10 * MSEC_PER_SEC * (cli->inputs->timeout_base + 2) + \ + 100 * cli->inputs->ttl) +#define BLOCK_REPORT_TIME_MSEC ((BLOB_POLL_TIME_MAX_SECS * 2 + 7) * 1000) + +/* BLOB Client is running Send Data State Machine from section 6.2.4.2. */ +#define SENDING_CHUNKS_IN_PULL_MODE(cli) ((cli)->state == BT_MESH_BLOB_CLI_STATE_BLOCK_SEND && \ + (cli)->xfer->mode == BT_MESH_BLOB_XFER_MODE_PULL) +#define UNICAST_MODE(cli) ((cli)->inputs->group == BT_MESH_ADDR_UNASSIGNED || \ + (cli)->tx.ctx.force_unicast) + +BUILD_ASSERT((BLOB_XFER_STATUS_MSG_MAXLEN + BT_MESH_MODEL_OP_LEN(BT_MESH_BLOB_OP_XFER_STATUS) + + BT_MESH_MIC_SHORT) <= BT_MESH_RX_SDU_MAX, + "The BLOB Transfer Status message does not fit into the maximum incoming SDU size."); + +BUILD_ASSERT((BLOB_BLOCK_REPORT_STATUS_MSG_MAXLEN + + BT_MESH_MODEL_OP_LEN(BT_MESH_BLOB_OP_BLOCK_REPORT) + BT_MESH_MIC_SHORT) + <= BT_MESH_RX_SDU_MAX, + "The BLOB Partial Block Report message does not fit into the maximum incoming SDU " + "size."); + +BUILD_ASSERT((BLOB_BLOCK_STATUS_MSG_MAXLEN + BT_MESH_MODEL_OP_LEN(BT_MESH_BLOB_OP_BLOCK_STATUS) + + BT_MESH_MIC_SHORT) <= BT_MESH_RX_SDU_MAX, + "The BLOB Block Status message does not fit into the maximum incoming SDU size."); + + +struct block_status { + enum bt_mesh_blob_status status; + enum bt_mesh_blob_chunks_missing missing; + struct bt_mesh_blob_block block; +}; + +static struct bt_mesh_blob_target *next_target(struct bt_mesh_blob_cli *cli, + struct bt_mesh_blob_target **current); +static void transfer_cancel(struct bt_mesh_blob_cli *cli); + +static void start_retry_timer(struct bt_mesh_blob_cli *cli) +{ + k_timeout_t next_timeout; + + if (SENDING_CHUNKS_IN_PULL_MODE(cli)) { + int64_t next_timeout_ms = cli->tx.cli_timestamp; + struct bt_mesh_blob_target *target = NULL; + + TARGETS_FOR_EACH(cli, target) { + if (!target->procedure_complete && + target->status == BT_MESH_BLOB_SUCCESS && + target->pull->block_report_timestamp < next_timeout_ms) { + next_timeout_ms = target->pull->block_report_timestamp; + } + } + + /* cli_timestamp and block_report_timestamp represent absolute time, while + * k_work_* functions use relative time. + */ + next_timeout_ms -= k_uptime_get(); + next_timeout = next_timeout_ms <= 0 ? K_NO_WAIT : K_MSEC(next_timeout_ms); + } else { + next_timeout = K_MSEC(CLIENT_TIMEOUT_MSEC(cli) / + CONFIG_BT_MESH_BLOB_CLI_BLOCK_RETRIES); + } + + (void)k_work_reschedule(&cli->tx.retry, next_timeout); +} + +static void cli_state_reset(struct bt_mesh_blob_cli *cli) +{ + k_work_cancel_delayable(&cli->tx.retry); + cli->xfer = NULL; + cli->state = BT_MESH_BLOB_CLI_STATE_NONE; + cli->tx.ctx.is_inited = 0; + cli->tx.cli_timestamp = 0ll; + cli->tx.sending = 0; +} + +static struct bt_mesh_blob_target *target_get(struct bt_mesh_blob_cli *cli, + uint16_t addr) +{ + struct bt_mesh_blob_target *target; + + TARGETS_FOR_EACH(cli, target) { + if (target->addr == addr) { + return target; + } + } + + LOG_ERR("Unknown target 0x%04x", addr); + return NULL; +} + +static void target_drop(struct bt_mesh_blob_cli *cli, + struct bt_mesh_blob_target *target, + enum bt_mesh_blob_status reason) +{ + LOG_WRN("Dropping 0x%04x: %u", target->addr, reason); + + target->status = reason; + if (cli->cb && cli->cb->lost_target) { + cli->cb->lost_target(cli, target, reason); + } +} + +static uint32_t targets_reset(struct bt_mesh_blob_cli *cli) +{ + struct bt_mesh_blob_target *target; + uint32_t count = 0; + + TARGETS_FOR_EACH(cli, target) { + if (target->status == BT_MESH_BLOB_SUCCESS) { + target->acked = 0U; + count++; + } + } + + return count; +} + +static bool targets_active(struct bt_mesh_blob_cli *cli) +{ + struct bt_mesh_blob_target *target; + + TARGETS_FOR_EACH(cli, target) { + if (target->status == BT_MESH_BLOB_SUCCESS) { + return true; + } + } + + return false; +} + +static bool targets_timedout(struct bt_mesh_blob_cli *cli) +{ + struct bt_mesh_blob_target *target; + + TARGETS_FOR_EACH(cli, target) { + if (!!target->timedout) { + return true; + } + } + + return false; +} + +static int io_open(struct bt_mesh_blob_cli *cli) +{ + if (!cli->io->open) { + return 0; + } + + return cli->io->open(cli->io, cli->xfer, BT_MESH_BLOB_READ); +} + +static void io_close(struct bt_mesh_blob_cli *cli) +{ + if (!cli->io->close) { + return; + } + + cli->io->close(cli->io, cli->xfer); +} + +static uint16_t next_missing_chunk(struct bt_mesh_blob_cli *cli, + const uint8_t *missing_chunks, + uint16_t idx) +{ + do { + if (blob_chunk_missing_get(missing_chunks, idx)) { + break; + } + } while (++idx < cli->block.chunk_count); + + return idx; +} + +/* Used in Pull mode to collect all missing chunks from each target in cli->block.missing. */ +static void update_missing_chunks(struct bt_mesh_blob_cli *cli) +{ + struct bt_mesh_blob_target *target; + + memset(cli->block.missing, 0, sizeof(cli->block.missing)); + + TARGETS_FOR_EACH(cli, target) { + if (target->procedure_complete || target->timedout) { + continue; + } + + for (size_t idx = 0; idx < cli->block.chunk_count; idx++) { + bool missing = blob_chunk_missing_get(cli->block.missing, idx) || + blob_chunk_missing_get(target->pull->missing, idx); + blob_chunk_missing_set(cli->block.missing, idx, missing); + } + } +} + +static inline size_t chunk_size(const struct bt_mesh_blob_xfer *xfer, + const struct bt_mesh_blob_block *block, + uint16_t chunk_idx) +{ + if ((chunk_idx == block->chunk_count - 1) && + (block->size % xfer->chunk_size)) { + return block->size % xfer->chunk_size; + } + + return xfer->chunk_size; +} + +static int chunk_idx_decode(struct net_buf_simple *buf) +{ + uint16_t data; + uint8_t byte; + + if (buf->len == 0) { + return -EINVAL; + } + + byte = net_buf_simple_pull_u8(buf); + + /* utf-8 decoding */ + if ((byte & 0xf0) == 0xe0) { /* 0x800 - 0xffff */ + if (buf->len < 2) { + return -EINVAL; + } + + data = (byte & 0x0f) << 12; + data |= (net_buf_simple_pull_u8(buf) & 0x3f) << 6; + data |= (net_buf_simple_pull_u8(buf) & 0x3f); + } else if ((byte & 0xe0) == 0xc0) { /* 0x80 - 0x7ff */ + if (buf->len < 1) { + return -EINVAL; + } + + data = (byte & 0x1f) << 6; + data |= (net_buf_simple_pull_u8(buf) & 0x3f); + } else { /* 0x00 - 0x7f */ + data = byte & 0x7f; + } + + return data; +} + +static void block_set(struct bt_mesh_blob_cli *cli, uint16_t block_idx) +{ + cli->block.number = block_idx; + cli->block.offset = block_idx * (1UL << cli->xfer->block_size_log); + cli->block.size = blob_block_size(cli->xfer->size, cli->xfer->block_size_log, + block_idx); + cli->block.chunk_count = + DIV_ROUND_UP(cli->block.size, cli->xfer->chunk_size); + + if (cli->xfer->mode == BT_MESH_BLOB_XFER_MODE_PUSH) { + blob_chunk_missing_set_all(&cli->block); + } else { + struct bt_mesh_blob_target *target; + + /* In pull mode, the server will tell us which blocks are missing. */ + memset(cli->block.missing, 0, sizeof(cli->block.missing)); + + TARGETS_FOR_EACH(cli, target) { + memset(target->pull->missing, 0, sizeof(target->pull->missing)); + } + } + + LOG_DBG("%u size: %u chunks: %u", block_idx, cli->block.size, + cli->block.chunk_count); +} + +static void suspend(struct bt_mesh_blob_cli *cli) +{ + cli->state = BT_MESH_BLOB_CLI_STATE_SUSPENDED; + + if (cli->cb && cli->cb->suspended) { + cli->cb->suspended(cli); + } +} + +static void end(struct bt_mesh_blob_cli *cli, bool success) +{ + const struct bt_mesh_blob_xfer *xfer = cli->xfer; + + LOG_DBG("%u", success); + + io_close(cli); + cli_state_reset(cli); + if (cli->cb && cli->cb->end) { + cli->cb->end(cli, xfer, success); + } +} + +static enum bt_mesh_blob_status caps_adjust(struct bt_mesh_blob_cli *cli, + const struct bt_mesh_blob_cli_caps *in) +{ + if (!(in->modes & cli->caps.modes)) { + return BT_MESH_BLOB_ERR_UNSUPPORTED_MODE; + } + + if ((in->min_block_size_log > cli->caps.max_block_size_log) || + (in->max_block_size_log < cli->caps.min_block_size_log)) { + return BT_MESH_BLOB_ERR_INVALID_BLOCK_SIZE; + } + + cli->caps.min_block_size_log = + MAX(cli->caps.min_block_size_log, in->min_block_size_log); + cli->caps.max_block_size_log = + MIN(cli->caps.max_block_size_log, in->max_block_size_log); + cli->caps.max_chunks = MIN(cli->caps.max_chunks, in->max_chunks); + cli->caps.mtu_size = MIN(cli->caps.mtu_size, in->mtu_size); + cli->caps.max_chunk_size = MIN(cli->caps.max_chunk_size, in->max_chunk_size); + cli->caps.modes &= in->modes; + cli->caps.max_size = MIN(cli->caps.max_size, in->max_size); + + return BT_MESH_BLOB_SUCCESS; +} + +/******************************************************************************* + * TX State machine + * + * All messages in the transfer are going out to all the targets, either through + * group messaging or directly to each. The TX state machine implements this + * pattern for the transfer state machine to use. It will send the messages to + * all devices (through the group or directly), repeating until it receives a + * response from each device, or the attempts run out. Messages may also be + * marked as unacked if they require no response. + ******************************************************************************/ + +static struct bt_mesh_blob_target *next_target(struct bt_mesh_blob_cli *cli, + struct bt_mesh_blob_target **current) +{ + if (*current) { + *current = SYS_SLIST_PEEK_NEXT_CONTAINER(*current, n); + } else { + *current = SYS_SLIST_PEEK_HEAD_CONTAINER( + (sys_slist_t *)&cli->inputs->targets, *current, n); + } + + while (*current) { + if ((*current)->acked || (*current)->procedure_complete || + (*current)->status != BT_MESH_BLOB_SUCCESS || (*current)->timedout || + (*current)->skip) { + goto next; + } + + if (SENDING_CHUNKS_IN_PULL_MODE(cli) && + (k_uptime_get() < (*current)->pull->block_report_timestamp || + !blob_chunk_missing_get((*current)->pull->missing, cli->chunk_idx))) { + /* Skip targets that didn't time out or timed out, but confirmed + * the currently transmitted chunk (cli->chunk_idx). + */ + goto next; + } + + break; + +next: + *current = SYS_SLIST_PEEK_NEXT_CONTAINER(*current, n); + } + + return *current; +} + +static void send(struct bt_mesh_blob_cli *cli) +{ + cli->tx.sending = 1U; + if (UNICAST_MODE(cli)) { + cli->tx.ctx.send(cli, cli->tx.target->addr); + } else { + cli->tx.ctx.send(cli, cli->inputs->group); + } +} + +static void broadcast_complete(struct bt_mesh_blob_cli *cli) +{ + LOG_DBG("%s", cli->tx.cancelled ? "cancelling" : "continuing"); + + cli->tx.ctx.is_inited = 0; + k_work_cancel_delayable(&cli->tx.retry); + + if (cli->tx.cancelled) { + transfer_cancel(cli); + } else { + __ASSERT(cli->tx.ctx.next, "No next callback"); + cli->tx.ctx.next(cli); + } +} + +static void tx_complete(struct k_work *work) +{ + struct k_work_delayable *dwork = k_work_delayable_from_work(work); + struct bt_mesh_blob_cli *cli = CONTAINER_OF(dwork, struct bt_mesh_blob_cli, tx.complete); + + if (!cli->tx.ctx.is_inited || !cli->tx.sending) { + return; + } + + cli->tx.sending = 0U; + + if (cli->tx.cancelled) { + broadcast_complete(cli); + return; + } + + if (cli->tx.ctx.send_complete) { + cli->tx.ctx.send_complete(cli, cli->tx.target->addr); + } + + if (UNICAST_MODE(cli) && next_target(cli, &cli->tx.target)) { + send(cli); + return; + } + + if (cli->tx.ctx.acked && cli->tx.pending) { + start_retry_timer(cli); + return; + } + + broadcast_complete(cli); +} + +static void drop_remaining_targets(struct bt_mesh_blob_cli *cli) +{ + struct bt_mesh_blob_target *target; + + LOG_DBG(""); + + cli->tx.pending = 0; + + TARGETS_FOR_EACH(cli, target) { + if (!target->acked && !target->timedout && !target->procedure_complete && + !target->skip) { + target->timedout = 1U; + target_drop(cli, target, BT_MESH_BLOB_ERR_INTERNAL); + } + } + + /* Update missing chunks to exclude chunks from dropped targets. */ + if (SENDING_CHUNKS_IN_PULL_MODE(cli)) { + update_missing_chunks(cli); + } +} + +static void retry_timeout(struct k_work *work) +{ + struct bt_mesh_blob_cli *cli = + CONTAINER_OF(work, struct bt_mesh_blob_cli, tx.retry.work); + + /* When sending chunks in Pull mode, timeout is handled differently. Client will drop all + * non-responsive servers by cli_timestamp. By calling broadcast_complete(), client will + * either retransmit the missing chunks (if any), or proceed to the next block, or suspend + * the transfer if all targets timed out. All this is handled in block_check_end(). + * Retry logic for all other procedures in Pull mode is handled as in Push mode. + */ + if (SENDING_CHUNKS_IN_PULL_MODE(cli)) { + if (k_uptime_get() >= cli->tx.cli_timestamp) { + LOG_DBG("Transfer timed out."); + + if (!cli->tx.ctx.optional) { + drop_remaining_targets(cli); + } + } + + broadcast_complete(cli); + return; + } + + LOG_DBG("%u", cli->tx.retries); + + cli->tx.retries--; + cli->tx.target = NULL; + + __ASSERT(!cli->tx.sending, "still sending"); + __ASSERT(cli->tx.ctx.is_inited, "ctx is not initialized"); + + if (!cli->tx.retries) { + LOG_DBG("Transfer timed out."); + + if (!cli->tx.ctx.optional) { + drop_remaining_targets(cli); + } + + broadcast_complete(cli); + return; + } + + if (!cli->tx.ctx.acked || !next_target(cli, &cli->tx.target) || cli->tx.cancelled) { + broadcast_complete(cli); + return; + } + + send(cli); +} + +void blob_cli_broadcast(struct bt_mesh_blob_cli *cli, + const struct blob_cli_broadcast_ctx *ctx) +{ + if (cli->tx.ctx.is_inited || cli->tx.sending) { + LOG_ERR("BLOB cli busy"); + return; + } + + cli->tx.cancelled = 0U; + cli->tx.retries = CONFIG_BT_MESH_BLOB_CLI_BLOCK_RETRIES; + cli->tx.ctx = *ctx; + cli->tx.ctx.is_inited = 1U; + + cli->tx.pending = targets_reset(cli); + + LOG_DBG("%u targets", cli->tx.pending); + + cli->tx.target = NULL; + if (!next_target(cli, &cli->tx.target)) { + LOG_DBG("No active targets"); + broadcast_complete(cli); + return; + } + + send(cli); +} + +void blob_cli_broadcast_tx_complete(struct bt_mesh_blob_cli *cli) +{ + k_work_schedule(&cli->tx.complete, K_MSEC(cli->tx.ctx.post_send_delay_ms)); +} + +void blob_cli_broadcast_rsp(struct bt_mesh_blob_cli *cli, + struct bt_mesh_blob_target *target) +{ + if (target->acked) { + return; + } + + LOG_DBG("0x%04x, pending: %d", target->addr, cli->tx.pending); + + target->acked = 1U; + + if (!--cli->tx.pending && !cli->tx.sending) { + broadcast_complete(cli); + } +} + +void blob_cli_broadcast_abort(struct bt_mesh_blob_cli *cli) +{ + if (!cli->tx.ctx.is_inited) { + return; + } + + if ((cli)->state >= BT_MESH_BLOB_CLI_STATE_START) { + io_close(cli); + } + + cli_state_reset(cli); +} + +static void send_start(uint16_t duration, int err, void *cb_data); +static void send_end(int err, void *user_data); + +static int tx(struct bt_mesh_blob_cli *cli, uint16_t addr, + struct net_buf_simple *buf) +{ + static const struct bt_mesh_send_cb end_cb = { + .start = send_start, + .end = send_end, + }; + struct bt_mesh_msg_ctx ctx = { + .app_idx = cli->inputs->app_idx, + .addr = addr, + .send_ttl = cli->inputs->ttl, + }; + int err; + + err = bt_mesh_model_send(cli->mod, &ctx, buf, &end_cb, cli); + if (err) { + LOG_ERR("Send err: %d", err); + send_end(err, cli); + return err; + } + + return 0; +} + +static void send_start(uint16_t duration, int err, void *cb_data) +{ + if (err) { + LOG_ERR("TX Start failed: %d", err); + send_end(err, cb_data); + } +} + +static void send_end(int err, void *user_data) +{ + struct bt_mesh_blob_cli *cli = user_data; + + if (!cli->tx.ctx.is_inited) { + return; + } + + blob_cli_broadcast_tx_complete(cli); +} + +/******************************************************************************* + * TX + ******************************************************************************/ + +static void info_get_tx(struct bt_mesh_blob_cli *cli, uint16_t dst) +{ + BT_MESH_MODEL_BUF_DEFINE(buf, BT_MESH_BLOB_OP_INFO_GET, 0); + bt_mesh_model_msg_init(&buf, BT_MESH_BLOB_OP_INFO_GET); + + tx(cli, dst, &buf); +} + +static void xfer_start_tx(struct bt_mesh_blob_cli *cli, uint16_t dst) +{ + BT_MESH_MODEL_BUF_DEFINE(buf, BT_MESH_BLOB_OP_XFER_START, 16); + bt_mesh_model_msg_init(&buf, BT_MESH_BLOB_OP_XFER_START); + net_buf_simple_add_u8(&buf, cli->xfer->mode << 6); + net_buf_simple_add_le64(&buf, cli->xfer->id); + net_buf_simple_add_le32(&buf, cli->xfer->size); + net_buf_simple_add_u8(&buf, cli->xfer->block_size_log); + net_buf_simple_add_le16(&buf, BT_MESH_TX_SDU_MAX); + + tx(cli, dst, &buf); +} + +static void xfer_get_tx(struct bt_mesh_blob_cli *cli, uint16_t dst) +{ + BT_MESH_MODEL_BUF_DEFINE(buf, BT_MESH_BLOB_OP_XFER_GET, 0); + bt_mesh_model_msg_init(&buf, BT_MESH_BLOB_OP_XFER_GET); + + tx(cli, dst, &buf); +} + +static void xfer_cancel_tx(struct bt_mesh_blob_cli *cli, uint16_t dst) +{ + BT_MESH_MODEL_BUF_DEFINE(buf, BT_MESH_BLOB_OP_XFER_CANCEL, 8); + bt_mesh_model_msg_init(&buf, BT_MESH_BLOB_OP_XFER_CANCEL); + net_buf_simple_add_le64(&buf, cli->xfer->id); + + tx(cli, dst, &buf); +} + +static void block_start_tx(struct bt_mesh_blob_cli *cli, uint16_t dst) +{ + BT_MESH_MODEL_BUF_DEFINE(buf, BT_MESH_BLOB_OP_BLOCK_START, 4); + bt_mesh_model_msg_init(&buf, BT_MESH_BLOB_OP_BLOCK_START); + net_buf_simple_add_le16(&buf, cli->block.number); + net_buf_simple_add_le16(&buf, cli->xfer->chunk_size); + + tx(cli, dst, &buf); +} + +static void chunk_tx(struct bt_mesh_blob_cli *cli, uint16_t dst) +{ + NET_BUF_SIMPLE_DEFINE(buf, BT_MESH_TX_SDU_MAX); + struct bt_mesh_blob_chunk chunk; + int err; + + bt_mesh_model_msg_init(&buf, BT_MESH_BLOB_OP_CHUNK); + net_buf_simple_add_le16(&buf, cli->chunk_idx); + + chunk.size = chunk_size(cli->xfer, &cli->block, cli->chunk_idx); + chunk.offset = cli->xfer->chunk_size * cli->chunk_idx; + chunk.data = net_buf_simple_add(&buf, chunk.size); + + 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); + return; + } + + tx(cli, dst, &buf); +} + +static void block_get_tx(struct bt_mesh_blob_cli *cli, uint16_t dst) +{ + BT_MESH_MODEL_BUF_DEFINE(buf, BT_MESH_BLOB_OP_BLOCK_GET, 0); + bt_mesh_model_msg_init(&buf, BT_MESH_BLOB_OP_BLOCK_GET); + + tx(cli, dst, &buf); +} + +/************************************************************************************************** + * State machine + * + * The BLOB Client state machine walks through the steps in the BLOB transfer in the following + * fashion: + * + * .---------------------------------------. + * V | + * xfer_start -> block_set -> block_start -> chunk_send -> chunk_send_end | + * A | | + * | V | + * | [more missing chunks?]-----[Yes]-----+ + * | | | + * | [No] | + * | | | + * | V | + * | [mode?] | + * | .---[Push]---' '---[Pull]---. | + * | | | | + * | V V | + * | block_check block_report_wait | + * | | | | + * | '-----------. .-------------' | + * | | | | + * | V V | + * | block_check_end | + * | | | + * | V | + * | [block completed?]------[No]------' + * | | + * | [Yes] + * | | + * | V + * '-------------------[No]------------[last block sent?] + * | + * [Yes] + * | + * V + * confirm_transfer + * | + * V + * transfer_complete + * + * In each state, the Client transmits a message to all target nodes. In each state, except when + * sending chunks (chunk_send), the Client expects a response from all target nodes, before + * proceeding to the next state. + * + * When a target node responds, the Client calls @ref blob_cli_broadcast_rsp for the corresponding + * target. Once all target nodes has responded, the Client proceeds to the next state. + * + * When sending chunks in Push mode, the Client will proceed to the next state (block_check) after + * transmitting all missing chunks. In the block_check state, the Client will request a block status + * from all target nodes. If any targets have missing chunks, the Client will resend them. + * + * When sending chunks in Pull mode, the Client addresses each target node individually using + * @ref bt_mesh_blob_target_pull structure. The Client uses @ref bt_mesh_blob_cli::block::missing + * to keep all missing chunks for the current block. Missing chunks for an individual target + * is kept in @ref bt_mesh_blob_target_pull::missing. The Client uses @ref + * bt_mesh_blob_target_pull::block_report_timeout to decide if it can send a chunk to this target. + * + * After sending all reported missing chunks to each target, the Client updates + * @ref bt_mesh_blob_target_pull::block_report_timestamp value for every target individually in + * chunk_tx_complete. The Client then proceeds to block_report_wait state and uses the earliest of + * all block_report_timestamp and cli_timestamp to schedule the retry timer. When the retry + * timer expires, the Client proceeds to the block_check_end state. + * + * In Pull mode, target nodes send a Partial Block Report message to the Client to inform about + * missing chunks. The Client doesn't control when these messages are sent by target nodes, and + * therefore it can't use @ref blob_cli_broadcast_rsp when it receives them. When the Client + * receives the Partial Block Report message, it updates missing chunks, resets + * block_report_timestamp, and explicitly calls @ref broadcast_complete to proceed to + * block_check_end state. + * + **************************************************************************************************/ +static void caps_collected(struct bt_mesh_blob_cli *cli); +static void block_start(struct bt_mesh_blob_cli *cli); +static void chunk_send(struct bt_mesh_blob_cli *cli); +static void block_check(struct bt_mesh_blob_cli *cli); +static void block_check_end(struct bt_mesh_blob_cli *cli); +static void block_report_wait(struct bt_mesh_blob_cli *cli); +static void chunk_send_end(struct bt_mesh_blob_cli *cli); +static void confirm_transfer(struct bt_mesh_blob_cli *cli); +static void transfer_complete(struct bt_mesh_blob_cli *cli); + +static void caps_get(struct bt_mesh_blob_cli *cli) +{ + const struct blob_cli_broadcast_ctx ctx = { + .send = info_get_tx, + .next = caps_collected, + .acked = true, + }; + + cli->state = BT_MESH_BLOB_CLI_STATE_CAPS_GET; + blob_cli_broadcast(cli, &ctx); +} + +static void caps_collected(struct bt_mesh_blob_cli *cli) +{ + struct bt_mesh_blob_target *target; + bool success = false; + + cli->state = BT_MESH_BLOB_CLI_STATE_NONE; + + cli_state_reset(cli); + + TARGETS_FOR_EACH(cli, target) { + if (target->status == BT_MESH_BLOB_SUCCESS) { + success = true; + break; + } + } + + while (success && + (1UL << cli->caps.max_block_size_log) > + (cli->caps.max_chunk_size * cli->caps.max_chunks)) { + cli->caps.max_block_size_log--; + } + + if (cli->cb && cli->cb->caps) { + cli->cb->caps(cli, success ? &cli->caps : NULL); + } +} + +static int xfer_start(struct bt_mesh_blob_cli *cli) +{ + const struct blob_cli_broadcast_ctx ctx = { + .send = xfer_start_tx, + .next = block_start, + .acked = true, + }; + int err; + + err = io_open(cli); + if (err) { + return -EIO; + } + + cli->state = BT_MESH_BLOB_CLI_STATE_START; + + blob_cli_broadcast(cli, &ctx); + return 0; +} + +static void block_start(struct bt_mesh_blob_cli *cli) +{ + const struct blob_cli_broadcast_ctx ctx = { + .send = block_start_tx, + .next = chunk_send, + .acked = true, + }; + struct bt_mesh_blob_target *target; + + + if (!targets_active(cli)) { + if (targets_timedout(cli)) { + suspend(cli); + return; + } + + end(cli, false); + return; + } + + LOG_DBG("%u (%u chunks, %u/%u)", cli->block.number, + cli->block.chunk_count, cli->block.number + 1, cli->block_count); + + cli->chunk_idx = 0; + cli->state = BT_MESH_BLOB_CLI_STATE_BLOCK_START; + /* Client Timeout Timer in Send Data State Machine is initialized initially after + * transmitting the first bunch of chunks (see block_report_wait()). Next time it will be + * updated after every Partial Block Report message. + */ + cli->tx.cli_timestamp = 0ll; + + TARGETS_FOR_EACH(cli, target) { + target->procedure_complete = 0U; + + if (cli->xfer->mode == BT_MESH_BLOB_XFER_MODE_PULL) { + target->pull->block_report_timestamp = 0ll; + } + } + + if (cli->io->block_start) { + cli->io->block_start(cli->io, cli->xfer, &cli->block); + if (cli->state == BT_MESH_BLOB_CLI_STATE_NONE) { + return; + } + } + + blob_cli_broadcast(cli, &ctx); +} + +static void chunk_tx_complete(struct bt_mesh_blob_cli *cli, uint16_t dst) +{ + if (cli->xfer->mode != BT_MESH_BLOB_XFER_MODE_PULL) { + return; + } + + /* Update Block Report Timer individually for each target after sending out the last chunk + * in current iteration. + */ + uint16_t chunk_idx = next_missing_chunk(cli, cli->tx.target->pull->missing, + cli->chunk_idx + 1); + if (chunk_idx < cli->block.chunk_count) { + /* Will send more chunks to this target in this iteration. */ + return; + } + + /* This was the last chunk sent for this target. Now start the Block Report Timeout Timer. + */ + struct bt_mesh_blob_target *target; + int64_t timestamp = k_uptime_get() + BLOCK_REPORT_TIME_MSEC; + + if (!UNICAST_MODE(cli)) { + /* If using group addressing, reset timestamp for all targets after all chunks are + * sent to the group address + */ + TARGETS_FOR_EACH(cli, target) { + target->pull->block_report_timestamp = timestamp; + } + return; + } + + cli->tx.target->pull->block_report_timestamp = timestamp; +} + +static void chunk_send(struct bt_mesh_blob_cli *cli) +{ + struct blob_cli_broadcast_ctx ctx = { + .send = chunk_tx, + .next = chunk_send_end, + .acked = false, + .post_send_delay_ms = cli->chunk_interval_ms, + }; + + if (cli->xfer->mode == BT_MESH_BLOB_XFER_MODE_PULL) { + ctx.send_complete = chunk_tx_complete; + } + + if (!targets_active(cli)) { + if (targets_timedout(cli)) { + suspend(cli); + return; + } + + end(cli, false); + return; + } + + LOG_DBG("%u / %u size: %u", cli->chunk_idx + 1, cli->block.chunk_count, + chunk_size(cli->xfer, &cli->block, cli->chunk_idx)); + + cli->state = BT_MESH_BLOB_CLI_STATE_BLOCK_SEND; + blob_cli_broadcast(cli, &ctx); +} + +static void chunk_send_end(struct bt_mesh_blob_cli *cli) +{ + /* In pull mode, the partial block reports are used to confirm which + * chunks have been received, while in push mode, we just assume that a + * sent chunk has been received. + */ + if (cli->xfer->mode == BT_MESH_BLOB_XFER_MODE_PUSH) { + blob_chunk_missing_set(cli->block.missing, cli->chunk_idx, false); + } + + cli->chunk_idx = next_missing_chunk(cli, cli->block.missing, cli->chunk_idx + 1); + if (cli->chunk_idx < cli->block.chunk_count) { + chunk_send(cli); + return; + } + + if (cli->xfer->mode == BT_MESH_BLOB_XFER_MODE_PUSH) { + block_check(cli); + } else { + block_report_wait(cli); + } +} + +/* The block checking pair(block_check - block_check_end) + * is relevant only for Push mode. + */ +static void block_check(struct bt_mesh_blob_cli *cli) +{ + const struct blob_cli_broadcast_ctx ctx = { + .send = block_get_tx, + .next = block_check_end, + .acked = true, + }; + + cli->state = BT_MESH_BLOB_CLI_STATE_BLOCK_CHECK; + + LOG_DBG(""); + + blob_cli_broadcast(cli, &ctx); +} + +static void block_report_wait(struct bt_mesh_blob_cli *cli) +{ + const struct blob_cli_broadcast_ctx ctx = { + .next = block_check_end, + .acked = false, + }; + + /* Check if all servers already confirmed all chunks during the transmission. */ + if (next_missing_chunk(cli, cli->block.missing, 0) >= cli->block.chunk_count) { + block_check_end(cli); + return; + } + + LOG_DBG("Waiting for partial block report..."); + cli->tx.ctx = ctx; + + /* Start Client Timeout Timer in Send Data sub-procedure for the first time. */ + if (!cli->tx.cli_timestamp) { + cli->tx.cli_timestamp = k_uptime_get() + CLIENT_TIMEOUT_MSEC(cli); + } + + start_retry_timer(cli); +} + +static void block_check_end(struct bt_mesh_blob_cli *cli) +{ + LOG_DBG(""); + + if (!targets_active(cli)) { + if (targets_timedout(cli)) { + suspend(cli); + return; + } + + end(cli, false); + return; + } + + cli->chunk_idx = next_missing_chunk(cli, cli->block.missing, 0); + if (cli->chunk_idx < cli->block.chunk_count) { + chunk_send(cli); + return; + } + + LOG_DBG("No more missing chunks for block %u", cli->block.number); + + if (cli->io->block_end) { + cli->io->block_end(cli->io, cli->xfer, &cli->block); + if (cli->state == BT_MESH_BLOB_CLI_STATE_NONE) { + return; + } + } + + if (cli->block.number == cli->block_count - 1) { + struct bt_mesh_blob_target *target; + + TARGETS_FOR_EACH(cli, target) { + target->procedure_complete = 0U; + } + + confirm_transfer(cli); + return; + } + + block_set(cli, cli->block.number + 1); + block_start(cli); +} + +static void confirm_transfer(struct bt_mesh_blob_cli *cli) +{ + const struct blob_cli_broadcast_ctx ctx = { + .send = xfer_get_tx, + .next = transfer_complete, + .acked = true, + }; + + LOG_DBG(""); + + cli->state = BT_MESH_BLOB_CLI_STATE_XFER_CHECK; + + blob_cli_broadcast(cli, &ctx); +} + +static void progress_checked(struct bt_mesh_blob_cli *cli) +{ + LOG_DBG(""); + + cli->state = BT_MESH_BLOB_CLI_STATE_NONE; + + if (cli->cb && cli->cb->end) { + cli->cb->xfer_progress_complete(cli); + } +} + +static void check_transfer(struct bt_mesh_blob_cli *cli) +{ + const struct blob_cli_broadcast_ctx ctx = { + .send = xfer_get_tx, + .next = progress_checked, + .acked = true, + }; + + LOG_DBG(""); + + cli->state = BT_MESH_BLOB_CLI_STATE_XFER_PROGRESS_GET; + + blob_cli_broadcast(cli, &ctx); +} + +static void transfer_cancel(struct bt_mesh_blob_cli *cli) +{ + const struct blob_cli_broadcast_ctx ctx = { + .send = xfer_cancel_tx, + .next = transfer_complete, + .acked = true, + }; + + LOG_DBG(""); + + cli->state = BT_MESH_BLOB_CLI_STATE_CANCEL; + + blob_cli_broadcast(cli, &ctx); +} + +static void transfer_complete(struct bt_mesh_blob_cli *cli) +{ + bool success = targets_active(cli) && + cli->state == BT_MESH_BLOB_CLI_STATE_XFER_CHECK; + + end(cli, success); +} + +/******************************************************************************* + * RX + ******************************************************************************/ + +static void rx_block_status(struct bt_mesh_blob_cli *cli, + struct bt_mesh_blob_target *target, + struct block_status *block) +{ + if (cli->state != BT_MESH_BLOB_CLI_STATE_BLOCK_START && + cli->state != BT_MESH_BLOB_CLI_STATE_BLOCK_SEND && + cli->state != BT_MESH_BLOB_CLI_STATE_BLOCK_CHECK) { + LOG_WRN("Invalid state %u", cli->state); + return; + } + + LOG_DBG("0x%04x: block: %u status: %u", target->addr, block->block.number, block->status); + + if (block->status != BT_MESH_BLOB_SUCCESS) { + target_drop(cli, target, block->status); + blob_cli_broadcast_rsp(cli, target); + return; + } + + if (block->block.number != cli->block.number) { + LOG_DBG("Invalid block num (expected %u)", cli->block.number); + return; + } + + if (block->missing == BT_MESH_BLOB_CHUNKS_MISSING_NONE) { + target->procedure_complete = 1U; + + if (cli->xfer->mode == BT_MESH_BLOB_XFER_MODE_PULL) { + memset(target->pull->missing, 0, sizeof(target->pull->missing)); + update_missing_chunks(cli); + } + + LOG_DBG("Target 0x%04x received all chunks", target->addr); + } else if (block->missing == BT_MESH_BLOB_CHUNKS_MISSING_ALL) { + blob_chunk_missing_set_all(&cli->block); + } else if (cli->xfer->mode == BT_MESH_BLOB_XFER_MODE_PULL) { + memcpy(target->pull->missing, block->block.missing, sizeof(block->block.missing)); + + LOG_DBG("Missing: %s", bt_hex(target->pull->missing, cli->block.chunk_count)); + + update_missing_chunks(cli); + + /* Target has responded. Reset the timestamp so that client can start transmitting + * missing chunks to it. + */ + target->pull->block_report_timestamp = 0ll; + } else { + for (int i = 0; i < ARRAY_SIZE(block->block.missing); ++i) { + cli->block.missing[i] |= block->block.missing[i]; + } + } + + if (SENDING_CHUNKS_IN_PULL_MODE(cli)) { + if (!cli->tx.sending) { + /* If not sending, then the retry timer is running. Call + * broadcast_complete() to proceed to block_check_end() and start + * transmitting missing chunks. + */ + broadcast_complete(cli); + } + + /* When sending chunks in Pull mode, we don't confirm transaction when receiving + * Partial Block Report message. + */ + return; + } + + blob_cli_broadcast_rsp(cli, target); +} + +static int handle_xfer_status(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + struct bt_mesh_blob_cli *cli = mod->rt->user_data; + enum bt_mesh_blob_xfer_phase expected_phase; + struct bt_mesh_blob_target *target; + struct bt_mesh_blob_xfer_info info = { 0 }; + uint8_t status_and_mode; + + status_and_mode = net_buf_simple_pull_u8(buf); + info.status = status_and_mode & BIT_MASK(4); + info.mode = status_and_mode >> 6; + info.phase = net_buf_simple_pull_u8(buf); + + if (buf->len) { + info.id = net_buf_simple_pull_le64(buf); + } + + if (buf->len >= 7) { + info.size = net_buf_simple_pull_le32(buf); + info.block_size_log = net_buf_simple_pull_u8(buf); + info.mtu_size = net_buf_simple_pull_le16(buf); + info.missing_blocks = net_buf_simple_pull(buf, buf->len); + } + + LOG_DBG("status: %u %s phase: %u %s", info.status, + info.mode == BT_MESH_BLOB_XFER_MODE_PUSH ? "push" : "pull", + info.phase, bt_hex(&info.id, 8)); + + + if (cli->state != BT_MESH_BLOB_CLI_STATE_START && + cli->state != BT_MESH_BLOB_CLI_STATE_XFER_CHECK && + cli->state != BT_MESH_BLOB_CLI_STATE_CANCEL && + cli->state != BT_MESH_BLOB_CLI_STATE_XFER_PROGRESS_GET) { + LOG_WRN("Wrong state: %d", cli->state); + return -EBUSY; + } + + target = target_get(cli, ctx->addr); + if (!target) { + return -ENOENT; + } + + if (cli->state == BT_MESH_BLOB_CLI_STATE_START) { + expected_phase = BT_MESH_BLOB_XFER_PHASE_WAITING_FOR_BLOCK; + } else if (cli->state == BT_MESH_BLOB_CLI_STATE_XFER_CHECK) { + expected_phase = BT_MESH_BLOB_XFER_PHASE_COMPLETE; + } else if (cli->state != BT_MESH_BLOB_CLI_STATE_XFER_PROGRESS_GET) { + expected_phase = BT_MESH_BLOB_XFER_PHASE_INACTIVE; + } else { /* cli->state == BT_MESH_BLOB_CLI_STATE_XFER_PROGRESS_GET */ + blob_cli_broadcast_rsp(cli, target); + if (cli->cb && cli->cb->xfer_progress) { + cli->cb->xfer_progress(cli, target, &info); + } + return 0; + } + + if (info.status != BT_MESH_BLOB_SUCCESS) { + target_drop(cli, target, info.status); + } else if (info.phase != expected_phase) { + LOG_WRN("Wrong phase: %u != %u", expected_phase, info.phase); + return -EINVAL; + } else if (info.phase != BT_MESH_BLOB_XFER_PHASE_INACTIVE && + info.id != cli->xfer->id) { + target_drop(cli, target, BT_MESH_BLOB_ERR_WRONG_BLOB_ID); + } + + blob_cli_broadcast_rsp(cli, target); + + return 0; +} + +static int handle_block_report(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + struct bt_mesh_blob_cli *cli = mod->rt->user_data; + struct block_status status = { + .status = BT_MESH_BLOB_SUCCESS, + .block.number = cli->block.number, + .missing = (buf->len ? BT_MESH_BLOB_CHUNKS_MISSING_ENCODED : + BT_MESH_BLOB_CHUNKS_MISSING_NONE), + }; + struct bt_mesh_blob_target *target; + + if (!cli->xfer) { + return -EINVAL; + } + + if (cli->xfer->mode == BT_MESH_BLOB_XFER_MODE_PUSH) { + LOG_WRN("Unexpected encoded block report in push mode"); + return -EINVAL; + } + + LOG_DBG(""); + + target = target_get(cli, ctx->addr); + if (!target) { + return -ENOENT; + } + + while (buf->len) { + int idx; + + idx = chunk_idx_decode(buf); + if (idx < 0) { + return idx; + } + + blob_chunk_missing_set(status.block.missing, idx, true); + } + + /* If all chunks were already confirmed by this target, Send Data State Machine is in Final + * state for this target. Therefore, the message should be ignored. + */ + if (next_missing_chunk(cli, target->pull->missing, 0) >= cli->block.chunk_count) { + LOG_DBG("All chunks already confirmed"); + return 0; + } + + cli->tx.cli_timestamp = k_uptime_get() + CLIENT_TIMEOUT_MSEC(cli); + + rx_block_status(cli, target, &status); + + return 0; +} + +static int handle_block_status(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + struct bt_mesh_blob_cli *cli = mod->rt->user_data; + struct bt_mesh_blob_target *target; + struct block_status status = { 0 }; + uint8_t status_and_format; + uint16_t chunk_size; + size_t len; + int idx; + + target = target_get(cli, ctx->addr); + if (!target) { + return -ENOENT; + } + + status_and_format = net_buf_simple_pull_u8(buf); + status.status = status_and_format & BIT_MASK(4); + status.missing = status_and_format >> 6; + status.block.number = net_buf_simple_pull_le16(buf); + chunk_size = net_buf_simple_pull_le16(buf); + status.block.chunk_count = + DIV_ROUND_UP(cli->block.size, chunk_size); + + LOG_DBG("status: %u block: %u encoding: %u", status.status, + status.block.number, status.missing); + + switch (status.missing) { + case BT_MESH_BLOB_CHUNKS_MISSING_ALL: + blob_chunk_missing_set_all(&status.block); + break; + case BT_MESH_BLOB_CHUNKS_MISSING_NONE: + break; + case BT_MESH_BLOB_CHUNKS_MISSING_SOME: + if (buf->len > sizeof(status.block.missing)) { + return -EINVAL; + } + + len = buf->len; + memcpy(status.block.missing, net_buf_simple_pull_mem(buf, len), + len); + + LOG_DBG("Missing: %s", bt_hex(status.block.missing, len)); + break; + case BT_MESH_BLOB_CHUNKS_MISSING_ENCODED: + /** MshMBTv1.0: 5.3.8: An empty Missing Chunks field entails that there are no + * missing chunks for this block. + */ + if (!buf->len) { + status.missing = BT_MESH_BLOB_CHUNKS_MISSING_NONE; + } + + while (buf->len) { + idx = chunk_idx_decode(buf); + if (idx < 0 || idx >= status.block.chunk_count) { + LOG_ERR("Invalid encoding"); + return -EINVAL; + } + + LOG_DBG("Missing %d", idx); + + blob_chunk_missing_set(status.block.missing, idx, true); + } + break; + } + + rx_block_status(cli, target, &status); + + return 0; +} + +static int handle_info_status(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + struct bt_mesh_blob_cli *cli = mod->rt->user_data; + struct bt_mesh_blob_cli_caps caps; + enum bt_mesh_blob_status status; + struct bt_mesh_blob_target *target; + + if (cli->state != BT_MESH_BLOB_CLI_STATE_CAPS_GET) { + return -EBUSY; + } + + caps.min_block_size_log = net_buf_simple_pull_u8(buf); + caps.max_block_size_log = net_buf_simple_pull_u8(buf); + caps.max_chunks = net_buf_simple_pull_le16(buf); + caps.max_chunk_size = net_buf_simple_pull_le16(buf); + caps.max_size = net_buf_simple_pull_le32(buf); + caps.mtu_size = net_buf_simple_pull_le16(buf); + caps.modes = net_buf_simple_pull_u8(buf); + + if (caps.min_block_size_log < 0x06 || + caps.max_block_size_log > 0x20 || + caps.max_block_size_log < caps.min_block_size_log || + caps.max_chunks == 0 || caps.max_chunk_size < 8 || + caps.max_size == 0 || caps.mtu_size < 0x14) { + return -EINVAL; + } + + LOG_DBG("0x%04x\n\tblock size: %u - %u\n\tchunks: %u\n\tchunk size: %u\n" + "\tblob size: %u\n\tmtu size: %u\n\tmodes: %x", + ctx->addr, caps.min_block_size_log, caps.max_block_size_log, + caps.max_chunks, caps.max_chunk_size, caps.max_size, + caps.mtu_size, caps.modes); + + target = target_get(cli, ctx->addr); + if (!target) { + return -ENOENT; + } + + status = caps_adjust(cli, &caps); + if (status != BT_MESH_BLOB_SUCCESS) { + target_drop(cli, target, status); + } + + blob_cli_broadcast_rsp(cli, target); + + return 0; +} + +const struct bt_mesh_model_op _bt_mesh_blob_cli_op[] = { + { BT_MESH_BLOB_OP_XFER_STATUS, BT_MESH_LEN_MIN(2), handle_xfer_status }, + { BT_MESH_BLOB_OP_BLOCK_REPORT, BT_MESH_LEN_MIN(0), handle_block_report }, + { BT_MESH_BLOB_OP_BLOCK_STATUS, BT_MESH_LEN_MIN(5), handle_block_status }, + { BT_MESH_BLOB_OP_INFO_STATUS, BT_MESH_LEN_EXACT(13), handle_info_status }, + BT_MESH_MODEL_OP_END, +}; + +static int blob_cli_init(const struct bt_mesh_model *mod) +{ + struct bt_mesh_blob_cli *cli = mod->rt->user_data; + + cli->mod = mod; + + bt_mesh_blob_cli_set_chunk_interval_ms(cli, CONFIG_BT_MESH_TX_BLOB_CHUNK_SEND_INTERVAL); + cli->tx.cli_timestamp = 0ll; + k_work_init_delayable(&cli->tx.retry, retry_timeout); + k_work_init_delayable(&cli->tx.complete, tx_complete); + + return 0; +} + +static void blob_cli_reset(const struct bt_mesh_model *mod) +{ + struct bt_mesh_blob_cli *cli = mod->rt->user_data; + + cli_state_reset(cli); +} + +const struct bt_mesh_model_cb _bt_mesh_blob_cli_cb = { + .init = blob_cli_init, + .reset = blob_cli_reset, +}; + + +int bt_mesh_blob_cli_caps_get(struct bt_mesh_blob_cli *cli, + const struct bt_mesh_blob_cli_inputs *inputs) +{ + if (bt_mesh_blob_cli_is_busy(cli)) { + return -EBUSY; + } + + cli->inputs = inputs; + + cli->caps.min_block_size_log = 0x06; + cli->caps.max_block_size_log = 0x20; + cli->caps.max_chunks = CONFIG_BT_MESH_BLOB_CHUNK_COUNT_MAX; + cli->caps.max_chunk_size = BLOB_TX_CHUNK_SIZE; + cli->caps.max_size = 0xffffffff; + cli->caps.mtu_size = 0xffff; + cli->caps.modes = BT_MESH_BLOB_XFER_MODE_ALL; + + if (!targets_reset(cli)) { + LOG_ERR("No valid targets"); + return -ENODEV; + } + + caps_get(cli); + + return 0; +} + +int bt_mesh_blob_cli_send(struct bt_mesh_blob_cli *cli, + const struct bt_mesh_blob_cli_inputs *inputs, + const struct bt_mesh_blob_xfer *xfer, + const struct bt_mesh_blob_io *io) +{ + if (bt_mesh_blob_cli_is_busy(cli)) { + LOG_ERR("BLOB Client is busy"); + return -EBUSY; + } + + if (!(xfer->mode & BT_MESH_BLOB_XFER_MODE_ALL) || xfer->block_size_log < 0x06 || + xfer->block_size_log > 0x20 || xfer->chunk_size < 8 || + xfer->chunk_size > BLOB_TX_CHUNK_SIZE) { + LOG_ERR("Incompatible transfer parameters"); + return -EINVAL; + } + + cli->xfer = xfer; + cli->inputs = inputs; + cli->io = io; + + if (cli->xfer->block_size_log == 0x20) { + cli->block_count = 1; + } else { + cli->block_count = DIV_ROUND_UP(cli->xfer->size, (1U << cli->xfer->block_size_log)); + } + + block_set(cli, 0); + + if (cli->block.chunk_count > CONFIG_BT_MESH_BLOB_CHUNK_COUNT_MAX) { + LOG_ERR("Too many chunks"); + return -EINVAL; + } + + if (!targets_reset(cli)) { + LOG_ERR("No valid targets"); + return -ENODEV; + } + + LOG_DBG("\n\tblock size log: %u\n\tchunk size: %u\n" + "\tblob size: %u\n\tmode: %x", + cli->xfer->block_size_log, cli->xfer->chunk_size, + cli->xfer->size, cli->xfer->mode); + + return xfer_start(cli); +} + +int bt_mesh_blob_cli_suspend(struct bt_mesh_blob_cli *cli) +{ + if (cli->state == BT_MESH_BLOB_CLI_STATE_SUSPENDED) { + return 0; + } + + if (cli->state != BT_MESH_BLOB_CLI_STATE_BLOCK_START && + cli->state != BT_MESH_BLOB_CLI_STATE_BLOCK_SEND && + cli->state != BT_MESH_BLOB_CLI_STATE_BLOCK_CHECK) { + LOG_WRN("BLOB xfer not started: %d", cli->state); + return -EINVAL; + } + + cli->state = BT_MESH_BLOB_CLI_STATE_SUSPENDED; + (void)k_work_cancel_delayable(&cli->tx.retry); + cli->tx.ctx.is_inited = 0; + cli->tx.sending = 0; + cli->tx.cli_timestamp = 0ll; + return 0; +} + +int bt_mesh_blob_cli_resume(struct bt_mesh_blob_cli *cli) +{ + struct bt_mesh_blob_target *target; + + if (cli->state != BT_MESH_BLOB_CLI_STATE_SUSPENDED) { + LOG_WRN("Not suspended"); + return -EINVAL; + } + + /* Restore timed out targets. */ + TARGETS_FOR_EACH(cli, target) { + if (!!target->timedout) { + target->status = BT_MESH_BLOB_SUCCESS; + target->timedout = 0U; + } + } + + if (!targets_reset(cli)) { + LOG_ERR("No valid targets"); + return -ENODEV; + } + + block_set(cli, 0); + return xfer_start(cli); +} + +void bt_mesh_blob_cli_cancel(struct bt_mesh_blob_cli *cli) +{ + if (!bt_mesh_blob_cli_is_busy(cli)) { + LOG_WRN("BLOB xfer already cancelled"); + return; + } + + LOG_DBG(""); + + if (cli->state == BT_MESH_BLOB_CLI_STATE_CAPS_GET || + cli->state == BT_MESH_BLOB_CLI_STATE_SUSPENDED) { + cli_state_reset(cli); + return; + } + + cli->tx.cancelled = 1U; + cli->state = BT_MESH_BLOB_CLI_STATE_CANCEL; +} + +int bt_mesh_blob_cli_xfer_progress_get(struct bt_mesh_blob_cli *cli, + const struct bt_mesh_blob_cli_inputs *inputs) +{ + if (bt_mesh_blob_cli_is_busy(cli)) { + return -EBUSY; + } + + cli->inputs = inputs; + + check_transfer(cli); + + return 0; +} + +uint8_t bt_mesh_blob_cli_xfer_progress_active_get(struct bt_mesh_blob_cli *cli) +{ + if (cli->state < BT_MESH_BLOB_CLI_STATE_START) { + return 0; + } + + return (100U * cli->block.number) / cli->block_count; +} + +bool bt_mesh_blob_cli_is_busy(struct bt_mesh_blob_cli *cli) +{ + return cli->state != BT_MESH_BLOB_CLI_STATE_NONE; +} + +void bt_mesh_blob_cli_set_chunk_interval_ms(struct bt_mesh_blob_cli *cli, uint32_t interval_ms) +{ + cli->chunk_interval_ms = interval_ms; +} 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 new file mode 100644 index 0000000000..2d4ec561f8 --- /dev/null +++ b/components/bt/esp_ble_mesh/v1.1/mbt/blob_srv.c @@ -0,0 +1,1013 @@ +/* + * Copyright (c) 2020 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include +#include +#include "net.h" +#include "access.h" +#include "transport.h" +#include "lpn.h" +#include "blob.h" + +#define LOG_LEVEL CONFIG_BT_MESH_MODEL_LOG_LEVEL +#include +LOG_MODULE_REGISTER(bt_mesh_blob_srv); + +#define MTU_SIZE_MAX (BT_MESH_RX_SDU_MAX - BT_MESH_MIC_SHORT) + +/* The Receive BLOB Timeout Timer */ +#define SERVER_TIMEOUT_SECS(srv) (10 * (1 + (srv)->state.timeout_base)) +/* The initial timer value used by an instance of the Pull BLOB State machine - T_BPI */ +#define REPORT_TIMER_TIMEOUT K_SECONDS(CONFIG_BT_MESH_BLOB_REPORT_TIMEOUT) + +BUILD_ASSERT(BLOB_BLOCK_SIZE_LOG_MIN <= BLOB_BLOCK_SIZE_LOG_MAX, + "The must be at least one number between the min and " + "max block size that is the power of two."); + +BUILD_ASSERT((BLOB_XFER_STATUS_MSG_MAXLEN + BT_MESH_MODEL_OP_LEN(BT_MESH_BLOB_OP_XFER_STATUS) + + BT_MESH_MIC_SHORT) <= BT_MESH_TX_SDU_MAX, + "The BLOB Transfer Status message does not fit into the maximum outgoing SDU size."); + +BUILD_ASSERT((BLOB_BLOCK_REPORT_STATUS_MSG_MAXLEN + + BT_MESH_MODEL_OP_LEN(BT_MESH_BLOB_OP_BLOCK_REPORT) + BT_MESH_MIC_SHORT) + <= BT_MESH_TX_SDU_MAX, + "The BLOB Partial Block Report message does not fit into the maximum outgoing SDU " + "size."); + +BUILD_ASSERT((BLOB_BLOCK_STATUS_MSG_MAXLEN + BT_MESH_MODEL_OP_LEN(BT_MESH_BLOB_OP_BLOCK_STATUS) + + BT_MESH_MIC_SHORT) <= BT_MESH_TX_SDU_MAX, + "The BLOB Block Status message does not fit into the maximum outgoing SDU size."); + +static void cancel(struct bt_mesh_blob_srv *srv); +static void suspend(struct bt_mesh_blob_srv *srv); + +static inline uint32_t block_count_get(const struct bt_mesh_blob_srv *srv) +{ + return DIV_ROUND_UP(srv->state.xfer.size, + (1U << srv->state.xfer.block_size_log)); +} + +static inline uint32_t max_chunk_size(const struct bt_mesh_blob_srv *srv) +{ + return MIN((srv->state.mtu_size - 2 - BT_MESH_MODEL_OP_LEN(BT_MESH_BLOB_OP_CHUNK)), + BLOB_RX_CHUNK_SIZE); +} + +static inline uint32_t max_chunk_count(const struct bt_mesh_blob_srv *srv) +{ + return MIN(8 * (srv->state.mtu_size - 6), + CONFIG_BT_MESH_BLOB_CHUNK_COUNT_MAX); +} + +static inline uint32_t missing_chunks(const struct bt_mesh_blob_block *block) +{ + int i; + uint32_t count = 0; + + for (i = 0; i < ARRAY_SIZE(block->missing); ++i) { + count += POPCOUNT(block->missing[i]); + } + + return count; +} + +static void store_state(const struct bt_mesh_blob_srv *srv) +{ + if (!IS_ENABLED(CONFIG_BT_SETTINGS)) { + return; + } + + /* Convert bit count to byte count: */ + uint32_t block_len = DIV_ROUND_UP(block_count_get(srv), 8); + + bt_mesh_model_data_store( + srv->mod, false, NULL, &srv->state, + offsetof(struct bt_mesh_blob_srv_state, blocks) + block_len); +} + +static void erase_state(struct bt_mesh_blob_srv *srv) +{ + if (!IS_ENABLED(CONFIG_BT_SETTINGS)) { + return; + } + + bt_mesh_model_data_store(srv->mod, false, NULL, NULL, 0); +} + +static int io_open(struct bt_mesh_blob_srv *srv) +{ + if (!srv->io->open) { + return 0; + } + + return srv->io->open(srv->io, &srv->state.xfer, BT_MESH_BLOB_WRITE); +} + +static void io_close(struct bt_mesh_blob_srv *srv) +{ + if (!srv->io->close) { + return; + } + + srv->io->close(srv->io, &srv->state.xfer); +} + +static void reset_timer(struct bt_mesh_blob_srv *srv) +{ + uint32_t timeout_secs = + srv->state.xfer.mode == BT_MESH_BLOB_XFER_MODE_PULL ? + MAX(SERVER_TIMEOUT_SECS(srv), + CONFIG_BT_MESH_BLOB_REPORT_TIMEOUT + 1) : + SERVER_TIMEOUT_SECS(srv); + k_work_reschedule(&srv->rx_timeout, K_SECONDS(timeout_secs)); +} + +static void buf_chunk_index_add(struct net_buf_simple *buf, uint16_t chunk) +{ + /* utf-8 encoded: */ + if (chunk < 0x80) { + net_buf_simple_add_u8(buf, chunk); + } else if (chunk < 0x8000) { + net_buf_simple_add_u8(buf, 0xc0 | chunk >> 6); + net_buf_simple_add_u8(buf, 0x80 | (chunk & BIT_MASK(6))); + } else { + net_buf_simple_add_u8(buf, 0xe0 | chunk >> 12); + net_buf_simple_add_u8(buf, 0x80 | ((chunk >> 6) & BIT_MASK(6))); + net_buf_simple_add_u8(buf, 0x80 | (chunk & BIT_MASK(6))); + } +} + +static int pull_req_max(const struct bt_mesh_blob_srv *srv) +{ + int count = CONFIG_BT_MESH_BLOB_SRV_PULL_REQ_COUNT; + +#if defined(CONFIG_BT_MESH_LOW_POWER) + /* No point in requesting more than the friend node can hold: */ + if (bt_mesh_lpn_established()) { + uint32_t segments_per_chunk = DIV_ROUND_UP( + BLOB_CHUNK_SDU_LEN(srv->state.xfer.chunk_size), + BT_MESH_APP_SEG_SDU_MAX); + + count = MIN(CONFIG_BT_MESH_BLOB_SRV_PULL_REQ_COUNT, + bt_mesh.lpn.queue_size / segments_per_chunk); + } +#endif + + return MIN(count, missing_chunks(&srv->block)); +} + +static void report_sent(int err, void *cb_data) +{ + struct bt_mesh_blob_srv *srv = cb_data; + + LOG_DBG(""); + + if (IS_ENABLED(CONFIG_BT_MESH_LOW_POWER) && bt_mesh_lpn_established()) { + bt_mesh_lpn_poll(); + } + + if (k_work_delayable_is_pending(&srv->rx_timeout)) { + k_work_reschedule(&srv->pull.report, REPORT_TIMER_TIMEOUT); + } +} + +static void block_report(struct bt_mesh_blob_srv *srv) +{ + static const struct bt_mesh_send_cb report_cb = { .end = report_sent }; + struct bt_mesh_msg_ctx ctx = { + .app_idx = srv->state.app_idx, + .send_ttl = srv->state.ttl, + .addr = srv->state.cli, + }; + int count; + int i; + + LOG_DBG("rx BLOB Timeout Timer: %i", k_work_delayable_is_pending(&srv->rx_timeout)); + + BT_MESH_MODEL_BUF_DEFINE(buf, BT_MESH_BLOB_OP_BLOCK_REPORT, + BLOB_BLOCK_REPORT_STATUS_MSG_MAXLEN); + bt_mesh_model_msg_init(&buf, BT_MESH_BLOB_OP_BLOCK_REPORT); + + count = pull_req_max(srv); + + for (i = 0; i < srv->block.chunk_count && count; ++i) { + if (blob_chunk_missing_get(srv->block.missing, i)) { + buf_chunk_index_add(&buf, i); + count--; + } + } + + (void)bt_mesh_model_send(srv->mod, &ctx, &buf, &report_cb, srv); +} + +static void phase_set(struct bt_mesh_blob_srv *srv, + enum bt_mesh_blob_xfer_phase phase) +{ + srv->phase = phase; + LOG_DBG("Phase: %u", phase); +} + +static void cancel(struct bt_mesh_blob_srv *srv) +{ + /* TODO: Could this state be preserved instead somehow? Wiping the + * entire transfer state is a bit overkill + */ + phase_set(srv, BT_MESH_BLOB_XFER_PHASE_INACTIVE); + srv->state.xfer.mode = BT_MESH_BLOB_XFER_MODE_NONE; + srv->state.ttl = BT_MESH_TTL_DEFAULT; + srv->block.number = 0xffff; + memset(srv->block.missing, 0, sizeof(srv->block.missing)); + srv->state.xfer.chunk_size = 0xffff; + k_work_cancel_delayable(&srv->rx_timeout); + k_work_cancel_delayable(&srv->pull.report); + io_close(srv); + erase_state(srv); + + if (srv->cb && srv->cb->end) { + srv->cb->end(srv, srv->state.xfer.id, false); + } +} + +static void suspend(struct bt_mesh_blob_srv *srv) +{ + LOG_DBG(""); + k_work_cancel_delayable(&srv->rx_timeout); + k_work_cancel_delayable(&srv->pull.report); + phase_set(srv, BT_MESH_BLOB_XFER_PHASE_SUSPENDED); + if (srv->cb && srv->cb->suspended) { + srv->cb->suspended(srv); + } +} + +static void resume(struct bt_mesh_blob_srv *srv) +{ + LOG_DBG("Resuming"); + + phase_set(srv, BT_MESH_BLOB_XFER_PHASE_WAITING_FOR_BLOCK); + reset_timer(srv); +} + +static void end(struct bt_mesh_blob_srv *srv) +{ + phase_set(srv, BT_MESH_BLOB_XFER_PHASE_COMPLETE); + k_work_cancel_delayable(&srv->rx_timeout); + k_work_cancel_delayable(&srv->pull.report); + io_close(srv); + erase_state(srv); + + if (srv->cb && srv->cb->end) { + srv->cb->end(srv, srv->state.xfer.id, true); + } +} + +static bool all_blocks_received(struct bt_mesh_blob_srv *srv) +{ + for (int i = 0; i < ARRAY_SIZE(srv->state.blocks); ++i) { + if (srv->state.blocks[i]) { + return false; + } + } + + return true; +} + +static bool pull_mode_xfer_complete(struct bt_mesh_blob_srv *srv) +{ + return srv->state.xfer.mode == BT_MESH_BLOB_XFER_MODE_PULL && + srv->phase == BT_MESH_BLOB_XFER_PHASE_WAITING_FOR_CHUNK && + all_blocks_received(srv); +} + +static void timeout(struct k_work *work) +{ + struct bt_mesh_blob_srv *srv = + CONTAINER_OF(work, struct bt_mesh_blob_srv, rx_timeout.work); + + LOG_DBG(""); + + if (srv->phase == BT_MESH_BLOB_XFER_PHASE_WAITING_FOR_START) { + cancel(srv); + } else if (pull_mode_xfer_complete(srv)) { + end(srv); + } else { + suspend(srv); + } +} + +static void report_timeout(struct k_work *work) +{ + struct bt_mesh_blob_srv *srv = + CONTAINER_OF(work, struct bt_mesh_blob_srv, pull.report.work); + + LOG_DBG(""); + + if (srv->phase != BT_MESH_BLOB_XFER_PHASE_WAITING_FOR_BLOCK && + srv->phase != BT_MESH_BLOB_XFER_PHASE_WAITING_FOR_CHUNK) { + return; + } + + block_report(srv); +} + +/******************************************************************************* + * Message handling + ******************************************************************************/ + +static void xfer_status_rsp(struct bt_mesh_blob_srv *srv, + struct bt_mesh_msg_ctx *ctx, + enum bt_mesh_blob_status status) +{ + BT_MESH_MODEL_BUF_DEFINE(buf, BT_MESH_BLOB_OP_XFER_STATUS, + BLOB_XFER_STATUS_MSG_MAXLEN); + bt_mesh_model_msg_init(&buf, BT_MESH_BLOB_OP_XFER_STATUS); + + net_buf_simple_add_u8(&buf, ((status & BIT_MASK(4)) | + (srv->state.xfer.mode << 6))); + net_buf_simple_add_u8(&buf, srv->phase); + + if (srv->phase == BT_MESH_BLOB_XFER_PHASE_INACTIVE) { + goto send; + } + + net_buf_simple_add_le64(&buf, srv->state.xfer.id); + + if (srv->phase == BT_MESH_BLOB_XFER_PHASE_WAITING_FOR_START) { + goto send; + } + + net_buf_simple_add_le32(&buf, srv->state.xfer.size); + net_buf_simple_add_u8(&buf, srv->state.xfer.block_size_log); + net_buf_simple_add_le16(&buf, srv->state.mtu_size); + net_buf_simple_add_mem(&buf, srv->state.blocks, + DIV_ROUND_UP(block_count_get(srv), 8)); + +send: + ctx->send_ttl = srv->state.ttl; + (void)bt_mesh_model_send(srv->mod, ctx, &buf, NULL, NULL); +} + +static void block_status_rsp(struct bt_mesh_blob_srv *srv, + struct bt_mesh_msg_ctx *ctx, + enum bt_mesh_blob_status status) +{ + enum bt_mesh_blob_chunks_missing format; + uint32_t missing; + int i; + + BT_MESH_MODEL_BUF_DEFINE(buf, BT_MESH_BLOB_OP_BLOCK_STATUS, + BLOB_BLOCK_STATUS_MSG_MAXLEN); + bt_mesh_model_msg_init(&buf, BT_MESH_BLOB_OP_BLOCK_STATUS); + + if (srv->phase == BT_MESH_BLOB_XFER_PHASE_INACTIVE || + srv->phase == BT_MESH_BLOB_XFER_PHASE_WAITING_FOR_START) { + missing = srv->block.chunk_count; + } else if (srv->phase == BT_MESH_BLOB_XFER_PHASE_COMPLETE) { + missing = 0U; + } else { + missing = missing_chunks(&srv->block); + } + + if (srv->state.xfer.mode == BT_MESH_BLOB_XFER_MODE_PULL) { + format = BT_MESH_BLOB_CHUNKS_MISSING_ENCODED; + } else if (missing == srv->block.chunk_count) { + format = BT_MESH_BLOB_CHUNKS_MISSING_ALL; + } else if (missing == 0) { + format = BT_MESH_BLOB_CHUNKS_MISSING_NONE; + } else { + format = BT_MESH_BLOB_CHUNKS_MISSING_SOME; + } + + LOG_DBG("Status: %u, missing: %u/%u", status, missing, srv->block.chunk_count); + + net_buf_simple_add_u8(&buf, (status & BIT_MASK(4)) | (format << 6)); + net_buf_simple_add_le16(&buf, srv->block.number); + net_buf_simple_add_le16(&buf, srv->state.xfer.chunk_size); + + if (format == BT_MESH_BLOB_CHUNKS_MISSING_SOME) { + net_buf_simple_add_mem(&buf, srv->block.missing, + DIV_ROUND_UP(srv->block.chunk_count, + 8)); + + LOG_DBG("Bits: %s", + bt_hex(srv->block.missing, + DIV_ROUND_UP(srv->block.chunk_count, 8))); + + } else if (format == BT_MESH_BLOB_CHUNKS_MISSING_ENCODED) { + int count = pull_req_max(srv); + + for (i = 0; (i < srv->block.chunk_count) && count; ++i) { + if (blob_chunk_missing_get(srv->block.missing, i)) { + LOG_DBG("Missing %u", i); + buf_chunk_index_add(&buf, i); + count--; + } + } + } + + if (srv->phase != BT_MESH_BLOB_XFER_PHASE_INACTIVE) { + ctx->send_ttl = srv->state.ttl; + } + + (void)bt_mesh_model_send(srv->mod, ctx, &buf, NULL, NULL); +} + +static int handle_xfer_get(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + struct bt_mesh_blob_srv *srv = mod->rt->user_data; + + LOG_DBG(""); + + if (pull_mode_xfer_complete(srv)) { + /* The client requested transfer. If we are in Pull mode and all blocks were + * received, we should change the Transfer state here to Complete so that the client + * receives the correct state. + */ + end(srv); + } + + xfer_status_rsp(srv, ctx, BT_MESH_BLOB_SUCCESS); + + return 0; +} + +static int handle_xfer_start(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + struct bt_mesh_blob_srv *srv = mod->rt->user_data; + enum bt_mesh_blob_status status; + enum bt_mesh_blob_xfer_mode mode; + uint64_t id; + size_t size; + uint8_t block_size_log; + uint32_t block_count; + uint16_t mtu_size; + int err; + + mode = (net_buf_simple_pull_u8(buf) >> 6); + id = net_buf_simple_pull_le64(buf); + size = net_buf_simple_pull_le32(buf); + block_size_log = net_buf_simple_pull_u8(buf); + mtu_size = net_buf_simple_pull_le16(buf); + + LOG_DBG("\n\tsize: %u block size: %u\n\tmtu_size: %u\n\tmode: %s", + size, (1U << block_size_log), mtu_size, + mode == BT_MESH_BLOB_XFER_MODE_PUSH ? "push" : "pull"); + + if (mode != BT_MESH_BLOB_XFER_MODE_PULL && + mode != BT_MESH_BLOB_XFER_MODE_PUSH) { + LOG_WRN("Invalid mode 0x%x", mode); + return -EINVAL; + } + + if (srv->phase == BT_MESH_BLOB_XFER_PHASE_INACTIVE) { + status = BT_MESH_BLOB_ERR_WRONG_PHASE; + LOG_WRN("Uninitialized"); + goto rsp; + } + + if (srv->state.xfer.id != id) { + status = BT_MESH_BLOB_ERR_WRONG_BLOB_ID; + /* bt_hex uses static array for the resulting hex string. + * Not possible to use bt_hex in the same logging function twice. + */ + LOG_WRN("Invalid ID: %s", bt_hex(&id, sizeof(uint64_t))); + LOG_WRN("Expected ID: %s", bt_hex(&srv->state.xfer.id, sizeof(uint64_t))); + goto rsp; + } + + if (srv->phase != BT_MESH_BLOB_XFER_PHASE_WAITING_FOR_START) { + if (srv->state.xfer.mode != mode || + srv->state.xfer.size != size || + srv->state.xfer.block_size_log != block_size_log || + srv->state.mtu_size > mtu_size) { + status = BT_MESH_BLOB_ERR_WRONG_PHASE; + LOG_WRN("Busy"); + goto rsp; + } + + if (srv->phase == BT_MESH_BLOB_XFER_PHASE_SUSPENDED) { + resume(srv); + store_state(srv); + } else { + LOG_DBG("Duplicate"); + } + + status = BT_MESH_BLOB_SUCCESS; + goto rsp; + } + + if (size > CONFIG_BT_MESH_BLOB_SIZE_MAX) { + LOG_WRN("Too large"); + status = BT_MESH_BLOB_ERR_BLOB_TOO_LARGE; + goto rsp; + } + + if (((1U << block_size_log) < CONFIG_BT_MESH_BLOB_BLOCK_SIZE_MIN) || + ((1U << block_size_log) > CONFIG_BT_MESH_BLOB_BLOCK_SIZE_MAX)) { + LOG_WRN("Invalid block size: %u", block_size_log); + status = BT_MESH_BLOB_ERR_INVALID_BLOCK_SIZE; + goto rsp; + } + + srv->state.cli = ctx->addr; + srv->state.app_idx = ctx->app_idx; + srv->state.mtu_size = MIN(mtu_size, MTU_SIZE_MAX); + srv->state.xfer.id = id; + srv->state.xfer.size = size; + srv->state.xfer.mode = mode; + srv->state.xfer.block_size_log = block_size_log; + srv->state.xfer.chunk_size = 0xffff; + srv->block.number = 0xffff; + + block_count = block_count_get(srv); + if (block_count > BT_MESH_BLOB_BLOCKS_MAX) { + LOG_WRN("Invalid block count (%u)", block_count); + status = BT_MESH_BLOB_ERR_INVALID_PARAM; + cancel(srv); + goto rsp; + } + + memset(srv->state.blocks, 0, sizeof(srv->state.blocks)); + for (int i = 0; i < block_count; i++) { + atomic_set_bit(srv->state.blocks, i); + } + + err = io_open(srv); + if (err) { + LOG_ERR("Couldn't open stream (err: %d)", err); + status = BT_MESH_BLOB_ERR_INTERNAL; + cancel(srv); + goto rsp; + } + + if (srv->cb && srv->cb->start) { + err = srv->cb->start(srv, ctx, &srv->state.xfer); + if (err) { + LOG_ERR("Couldn't start transfer (err: %d)", err); + status = BT_MESH_BLOB_ERR_INTERNAL; + cancel(srv); + goto rsp; + } + } + + reset_timer(srv); + phase_set(srv, BT_MESH_BLOB_XFER_PHASE_WAITING_FOR_BLOCK); + store_state(srv); + status = BT_MESH_BLOB_SUCCESS; + +rsp: + xfer_status_rsp(srv, ctx, status); + + return 0; +} + +static int handle_xfer_cancel(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + enum bt_mesh_blob_status status = BT_MESH_BLOB_SUCCESS; + struct bt_mesh_blob_srv *srv = mod->rt->user_data; + uint64_t id; + + id = net_buf_simple_pull_le64(buf); + + LOG_DBG("%u", (uint32_t)id); + + if (srv->phase == BT_MESH_BLOB_XFER_PHASE_INACTIVE) { + goto rsp; + } + + if (srv->state.xfer.id != id) { + status = BT_MESH_BLOB_ERR_WRONG_BLOB_ID; + goto rsp; + } + + cancel(srv); + +rsp: + xfer_status_rsp(srv, ctx, status); + + return 0; +} + +static int handle_block_get(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + enum bt_mesh_blob_status status; + struct bt_mesh_blob_srv *srv = mod->rt->user_data; + + switch (srv->phase) { + case BT_MESH_BLOB_XFER_PHASE_WAITING_FOR_BLOCK: + case BT_MESH_BLOB_XFER_PHASE_WAITING_FOR_CHUNK: + case BT_MESH_BLOB_XFER_PHASE_COMPLETE: + status = BT_MESH_BLOB_SUCCESS; + break; + case BT_MESH_BLOB_XFER_PHASE_SUSPENDED: + status = BT_MESH_BLOB_ERR_INFO_UNAVAILABLE; + break; + case BT_MESH_BLOB_XFER_PHASE_WAITING_FOR_START: + case BT_MESH_BLOB_XFER_PHASE_INACTIVE: + status = BT_MESH_BLOB_ERR_WRONG_PHASE; + break; + default: + status = BT_MESH_BLOB_ERR_INTERNAL; + break; + } + + LOG_DBG(""); + + block_status_rsp(srv, ctx, status); + + return 0; +} + +static int handle_block_start(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + struct bt_mesh_blob_srv *srv = mod->rt->user_data; + enum bt_mesh_blob_status status; + uint16_t block_number, chunk_size; + int err; + + block_number = net_buf_simple_pull_le16(buf); + chunk_size = net_buf_simple_pull_le16(buf); + + if (srv->phase == BT_MESH_BLOB_XFER_PHASE_WAITING_FOR_START || + srv->phase == BT_MESH_BLOB_XFER_PHASE_INACTIVE) { + status = BT_MESH_BLOB_ERR_WRONG_PHASE; + goto rsp; + } + + reset_timer(srv); + + if (srv->phase == BT_MESH_BLOB_XFER_PHASE_WAITING_FOR_CHUNK) { + if (block_number != srv->block.number || + chunk_size != srv->state.xfer.chunk_size) { + status = BT_MESH_BLOB_ERR_WRONG_PHASE; + } else { + status = BT_MESH_BLOB_SUCCESS; + } + + goto rsp; + } + + if (block_number >= block_count_get(srv)) { + status = BT_MESH_BLOB_ERR_INVALID_BLOCK_NUM; + goto rsp; + } + + if (!chunk_size || chunk_size > max_chunk_size(srv) || + (DIV_ROUND_UP((1 << srv->state.xfer.block_size_log), chunk_size) > + max_chunk_count(srv))) { + LOG_WRN("Invalid chunk size: (chunk size: %u, max: %u, block log: %u, count: %u)", + chunk_size, max_chunk_size(srv), + srv->state.xfer.block_size_log, + max_chunk_count(srv)); + status = BT_MESH_BLOB_ERR_INVALID_CHUNK_SIZE; + goto rsp; + } + + srv->block.size = blob_block_size( + srv->state.xfer.size, srv->state.xfer.block_size_log, block_number); + srv->block.number = block_number; + srv->block.chunk_count = DIV_ROUND_UP(srv->block.size, chunk_size); + srv->state.xfer.chunk_size = chunk_size; + srv->block.offset = block_number * (1UL << srv->state.xfer.block_size_log); + + if (srv->phase == BT_MESH_BLOB_XFER_PHASE_COMPLETE || + !atomic_test_bit(srv->state.blocks, block_number)) { + memset(srv->block.missing, 0, sizeof(srv->block.missing)); + status = BT_MESH_BLOB_SUCCESS; + goto rsp; + } + + if (srv->phase == BT_MESH_BLOB_XFER_PHASE_SUSPENDED && srv->cb && + srv->cb->resume) { + srv->cb->resume(srv); + } + + phase_set(srv, BT_MESH_BLOB_XFER_PHASE_WAITING_FOR_CHUNK); + blob_chunk_missing_set_all(&srv->block); + + LOG_DBG("%u: (%u/%u)\n\tsize: %u\n\tchunk size: %u\n\tchunk count: %u", + srv->block.number, srv->block.number + 1, block_count_get(srv), + srv->block.size, chunk_size, srv->block.chunk_count); + + if (srv->io->block_start) { + err = srv->io->block_start(srv->io, &srv->state.xfer, + &srv->block); + if (err) { + cancel(srv); + status = BT_MESH_BLOB_ERR_INTERNAL; + goto rsp; + } + } + + if (srv->state.xfer.mode == BT_MESH_BLOB_XFER_MODE_PULL) { + /* Wait for the client to send the first chunk */ + k_work_reschedule(&srv->pull.report, REPORT_TIMER_TIMEOUT); + } + + status = BT_MESH_BLOB_SUCCESS; + +rsp: + block_status_rsp(srv, ctx, status); + + return 0; +} + +static int handle_chunk(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + struct bt_mesh_blob_srv *srv = mod->rt->user_data; + struct bt_mesh_blob_chunk chunk; + size_t expected_size = 0; + uint16_t idx; + int err; + + idx = net_buf_simple_pull_le16(buf); + chunk.size = buf->len; + chunk.data = net_buf_simple_pull_mem(buf, chunk.size); + chunk.offset = idx * srv->state.xfer.chunk_size; + + if (srv->phase != BT_MESH_BLOB_XFER_PHASE_WAITING_FOR_CHUNK || + idx >= srv->block.chunk_count) { + LOG_ERR("Invalid phase or index (%u %u)", srv->phase, + idx); + return -EINVAL; + } + + if (idx == srv->block.chunk_count - 1) { + expected_size = srv->block.size % srv->state.xfer.chunk_size; + } + + if (expected_size == 0) { + expected_size = srv->state.xfer.chunk_size; + } + + if (chunk.size != expected_size) { + LOG_ERR("Unexpected size: %u != %u", expected_size, chunk.size); + return -EINVAL; + } + + LOG_DBG("%u/%u (%u bytes)", idx + 1, srv->block.chunk_count, + chunk.size); + + reset_timer(srv); + if (srv->state.xfer.mode == BT_MESH_BLOB_XFER_MODE_PULL) { + k_work_reschedule(&srv->pull.report, REPORT_TIMER_TIMEOUT); + } + + if (!blob_chunk_missing_get(srv->block.missing, idx)) { + LOG_DBG("Duplicate chunk %u", idx); + return -EALREADY; + } + + err = srv->io->wr(srv->io, &srv->state.xfer, &srv->block, &chunk); + if (err) { + return err; + } + + blob_chunk_missing_set(srv->block.missing, idx, false); + if (missing_chunks(&srv->block)) { + return 0; + } + + if (srv->state.xfer.mode == BT_MESH_BLOB_XFER_MODE_PULL) { + block_report(srv); + } + + if (srv->io->block_end) { + srv->io->block_end(srv->io, &srv->state.xfer, &srv->block); + } + + atomic_clear_bit(srv->state.blocks, srv->block.number); + + if (!all_blocks_received(srv)) { + phase_set(srv, BT_MESH_BLOB_XFER_PHASE_WAITING_FOR_BLOCK); + store_state(srv); + return 0; + } + + if (srv->state.xfer.mode == BT_MESH_BLOB_XFER_MODE_PULL) { + /* By spec (section 5.2.4), the BLOB Server stops sending BLOB Partial Block Report + * messages "If the current block is the last block, then the server determines that + * the client knows the transfer is complete. For example, a higher-layer model may + * indicate that the client considers the transfer complete." + * + * We don't have any way for higher-layer model to indicate that the transfer is + * complete. Therefore we need to keep sending Partial Block Report messages until + * the client sends BLOB Transfer Get message or the Block Timer expires. + */ + return 0; + } + + end(srv); + return 0; +} + +static int handle_info_get(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + struct bt_mesh_blob_srv *srv = mod->rt->user_data; + + LOG_DBG(""); + + BT_MESH_MODEL_BUF_DEFINE(rsp, BT_MESH_BLOB_OP_INFO_STATUS, 15); + bt_mesh_model_msg_init(&rsp, BT_MESH_BLOB_OP_INFO_STATUS); + net_buf_simple_add_u8(&rsp, BLOB_BLOCK_SIZE_LOG_MIN); + net_buf_simple_add_u8(&rsp, BLOB_BLOCK_SIZE_LOG_MAX); + net_buf_simple_add_le16(&rsp, CONFIG_BT_MESH_BLOB_CHUNK_COUNT_MAX); + net_buf_simple_add_le16(&rsp, BLOB_RX_CHUNK_SIZE); + net_buf_simple_add_le32(&rsp, CONFIG_BT_MESH_BLOB_SIZE_MAX); + net_buf_simple_add_le16(&rsp, MTU_SIZE_MAX); + net_buf_simple_add_u8(&rsp, BT_MESH_BLOB_XFER_MODE_ALL); + + if (srv->phase != BT_MESH_BLOB_XFER_PHASE_INACTIVE) { + ctx->send_ttl = srv->state.ttl; + } + + (void)bt_mesh_model_send(srv->mod, ctx, &rsp, NULL, NULL); + + return 0; +} + +const struct bt_mesh_model_op _bt_mesh_blob_srv_op[] = { + { BT_MESH_BLOB_OP_XFER_GET, BT_MESH_LEN_EXACT(0), handle_xfer_get }, + { BT_MESH_BLOB_OP_XFER_START, BT_MESH_LEN_EXACT(16), handle_xfer_start }, + { BT_MESH_BLOB_OP_XFER_CANCEL, BT_MESH_LEN_EXACT(8), handle_xfer_cancel }, + { BT_MESH_BLOB_OP_BLOCK_GET, BT_MESH_LEN_EXACT(0), handle_block_get }, + { BT_MESH_BLOB_OP_BLOCK_START, BT_MESH_LEN_EXACT(4), handle_block_start }, + { BT_MESH_BLOB_OP_CHUNK, BT_MESH_LEN_MIN(2), handle_chunk }, + { BT_MESH_BLOB_OP_INFO_GET, BT_MESH_LEN_EXACT(0), handle_info_get }, + BT_MESH_MODEL_OP_END, +}; + +static int blob_srv_init(const struct bt_mesh_model *mod) +{ + struct bt_mesh_blob_srv *srv = mod->rt->user_data; + + srv->mod = mod; + srv->state.ttl = BT_MESH_TTL_DEFAULT; + srv->block.number = 0xffff; + srv->state.xfer.chunk_size = 0xffff; + k_work_init_delayable(&srv->rx_timeout, timeout); + k_work_init_delayable(&srv->pull.report, report_timeout); + + return 0; +} + +static int blob_srv_settings_set(const struct bt_mesh_model *mod, const char *name, + size_t len_rd, settings_read_cb read_cb, + void *cb_arg) +{ + struct bt_mesh_blob_srv *srv = mod->rt->user_data; + ssize_t len; + + if (len_rd < offsetof(struct bt_mesh_blob_srv_state, blocks)) { + return -EINVAL; + } + + len = read_cb(cb_arg, &srv->state, sizeof(srv->state)); + if (len < 0) { + return len; + } + + srv->block.number = 0xffff; + srv->state.xfer.chunk_size = 0xffff; + + if (block_count_get(srv) > BT_MESH_BLOB_BLOCKS_MAX) { + LOG_WRN("Loaded block count too high (%u, max: %u)", + block_count_get(srv), BT_MESH_BLOB_BLOCKS_MAX); + return 0; + } + + /* If device restarted before it handled `XFER_START` server we restore state into + * BT_MESH_BLOB_XFER_PHASE_WAITING_FOR_START phase, so `XFER_START` can be accepted + * as it would before reboot + */ + if (srv->state.cli == BT_MESH_ADDR_UNASSIGNED) { + LOG_DBG("Transfer (id=%llu) waiting for start", srv->state.xfer.id); + phase_set(srv, BT_MESH_BLOB_XFER_PHASE_WAITING_FOR_START); + } else { + phase_set(srv, BT_MESH_BLOB_XFER_PHASE_SUSPENDED); + + LOG_DBG("Recovered transfer from 0x%04x (%llu)", srv->state.cli, + srv->state.xfer.id); + } + + return 0; +} + +static int blob_srv_start(const struct bt_mesh_model *mod) +{ + struct bt_mesh_blob_srv *srv = mod->rt->user_data; + int err = -ENOTSUP; + + if (srv->phase == BT_MESH_BLOB_XFER_PHASE_INACTIVE) { + return 0; + } + + if (srv->cb && srv->cb->recover) { + srv->io = NULL; + err = srv->cb->recover(srv, &srv->state.xfer, &srv->io); + if (!err && srv->io) { + err = io_open(srv); + } + } + + if (err || !srv->io) { + LOG_WRN("Abandoning transfer."); + phase_set(srv, BT_MESH_BLOB_XFER_PHASE_INACTIVE); + srv->state.xfer.mode = BT_MESH_BLOB_XFER_MODE_NONE; + srv->state.ttl = BT_MESH_TTL_DEFAULT; + erase_state(srv); + } + + return 0; +} + +static void blob_srv_reset(const struct bt_mesh_model *mod) +{ + struct bt_mesh_blob_srv *srv = mod->rt->user_data; + + phase_set(srv, BT_MESH_BLOB_XFER_PHASE_INACTIVE); + srv->state.xfer.mode = BT_MESH_BLOB_XFER_MODE_NONE; + k_work_cancel_delayable(&srv->rx_timeout); + k_work_cancel_delayable(&srv->pull.report); + erase_state(srv); +} + +const struct bt_mesh_model_cb _bt_mesh_blob_srv_cb = { + .init = blob_srv_init, + .settings_set = blob_srv_settings_set, + .start = blob_srv_start, + .reset = blob_srv_reset, +}; + +int bt_mesh_blob_srv_recv(struct bt_mesh_blob_srv *srv, uint64_t id, + const struct bt_mesh_blob_io *io, uint8_t ttl, + uint16_t timeout_base) +{ + if (bt_mesh_blob_srv_is_busy(srv)) { + return -EBUSY; + } + + if (!io || !io->wr) { + return -EINVAL; + } + + srv->state.xfer.id = id; + srv->state.ttl = ttl; + srv->state.timeout_base = timeout_base; + srv->io = io; + srv->block.number = 0xffff; + srv->state.xfer.chunk_size = 0xffff; + phase_set(srv, BT_MESH_BLOB_XFER_PHASE_WAITING_FOR_START); + store_state(srv); + + return 0; +} + +int bt_mesh_blob_srv_cancel(struct bt_mesh_blob_srv *srv) +{ + if (!bt_mesh_blob_srv_is_busy(srv)) { + return -EALREADY; + } + + cancel(srv); + + return 0; +} + +bool bt_mesh_blob_srv_is_busy(const struct bt_mesh_blob_srv *srv) +{ + return srv->phase != BT_MESH_BLOB_XFER_PHASE_INACTIVE && + srv->phase != BT_MESH_BLOB_XFER_PHASE_SUSPENDED && + srv->phase != BT_MESH_BLOB_XFER_PHASE_COMPLETE; +} + +uint8_t bt_mesh_blob_srv_progress(const struct bt_mesh_blob_srv *srv) +{ + uint32_t total; + uint32_t received; + + if (srv->phase == BT_MESH_BLOB_XFER_PHASE_INACTIVE || + srv->phase == BT_MESH_BLOB_XFER_PHASE_WAITING_FOR_START) { + return 0; + } + + total = block_count_get(srv); + + received = 0; + for (int i = 0; i < total; ++i) { + if (!atomic_test_bit(srv->state.blocks, i)) { + received++; + } + } + + return (100U * received) / total; +} From f19c2a36f2ebe41d05b40f9dffde68c46a5ba266 Mon Sep 17 00:00:00 2001 From: luoxu Date: Tue, 19 Nov 2024 17:57:17 +0800 Subject: [PATCH 2/4] feat(ble_mesh): Adapt DFU/MBT to esp-idf --- components/bt/CMakeLists.txt | 13 + components/bt/common/btc/core/btc_task.c | 4 + .../bt/common/btc/include/btc/btc_task.h | 4 + components/bt/esp_ble_mesh/Kconfig.in | 207 +- .../bt/esp_ble_mesh/api/esp_ble_mesh_defs.h | 8 +- .../bt/esp_ble_mesh/btc/btc_ble_mesh_prov.c | 62 +- .../esp_ble_mesh/common/include/mesh/kernel.h | 2 +- .../esp_ble_mesh/common/include/mesh/timer.h | 134 +- .../esp_ble_mesh/common/include/mesh/utils.h | 106 + components/bt/esp_ble_mesh/common/timer.c | 24 +- components/bt/esp_ble_mesh/core/access.c | 33 +- components/bt/esp_ble_mesh/core/access.h | 2 + .../esp_ble_mesh/core/include/mesh/access.h | 18 +- .../bt/esp_ble_mesh/core/storage/settings.c | 41 +- .../bt/esp_ble_mesh/core/storage/settings.h | 4 + components/bt/esp_ble_mesh/core/transport.h | 1 + .../api/models/esp_ble_mesh_dfu_model_api.c | 148 ++ .../api/models/esp_ble_mesh_dfu_slot_api.c | 102 + .../api/models/esp_ble_mesh_mbt_model_api.c | 56 +- .../include/esp_ble_mesh_blob_model_api.h | 696 ++++++ .../include/esp_ble_mesh_dfu_model_api.h | 999 ++++++++ .../include/esp_ble_mesh_dfu_slot_api.h | 148 ++ .../include/esp_ble_mesh_mbt_model_api.h | 533 +++-- .../v1.1/btc/btc_ble_mesh_dfu_model.c | 414 ++++ .../v1.1/btc/btc_ble_mesh_dfu_slot.c | 79 + .../v1.1/btc/include/btc_ble_mesh_dfu_model.h | 74 + .../v1.1/btc/include/btc_ble_mesh_dfu_slot.h | 36 + .../v1.1/btc/include/btc_ble_mesh_mbt_model.h | 11 +- components/bt/esp_ble_mesh/v1.1/dfu/dfd.h | 54 +- components/bt/esp_ble_mesh/v1.1/dfu/dfd_srv.c | 1757 +++++++------- .../esp_ble_mesh/v1.1/dfu/dfd_srv_internal.h | 30 +- components/bt/esp_ble_mesh/v1.1/dfu/dfu.h | 57 +- components/bt/esp_ble_mesh/v1.1/dfu/dfu_cli.c | 1892 ++++++++------- .../bt/esp_ble_mesh/v1.1/dfu/dfu_metadata.c | 127 +- .../bt/esp_ble_mesh/v1.1/dfu/dfu_slot.c | 530 +++-- .../bt/esp_ble_mesh/v1.1/dfu/dfu_slot.h | 22 +- components/bt/esp_ble_mesh/v1.1/dfu/dfu_srv.c | 806 +++---- .../v1.1/include/mesh_v1.1/dfu/dfd.h | 131 +- .../v1.1/include/mesh_v1.1/dfu/dfd_srv.h | 373 +-- .../v1.1/include/mesh_v1.1/dfu/dfu.h | 178 +- .../v1.1/include/mesh_v1.1/dfu/dfu_cli.h | 353 +-- .../v1.1/include/mesh_v1.1/dfu/dfu_metadata.h | 72 +- .../v1.1/include/mesh_v1.1/dfu/dfu_srv.h | 300 ++- .../v1.1/include/mesh_v1.1/mbt/blob.h | 408 ++-- .../v1.1/include/mesh_v1.1/mbt/blob_cli.h | 455 ++-- .../v1.1/include/mesh_v1.1/mbt/blob_srv.h | 227 +- components/bt/esp_ble_mesh/v1.1/mbt/blob.h | 114 +- .../bt/esp_ble_mesh/v1.1/mbt/blob_cli.c | 2063 +++++++++-------- .../bt/esp_ble_mesh/v1.1/mbt/blob_srv.c | 1434 ++++++------ docs/doxygen/Doxyfile | 2 + .../esp-ble-mesh/ble-mesh-feature-list.rst | 9 + .../api-reference/bluetooth/esp-ble-mesh.rst | 9 + 52 files changed, 9633 insertions(+), 5729 deletions(-) create mode 100644 components/bt/esp_ble_mesh/v1.1/api/models/esp_ble_mesh_dfu_model_api.c create mode 100644 components/bt/esp_ble_mesh/v1.1/api/models/esp_ble_mesh_dfu_slot_api.c create mode 100644 components/bt/esp_ble_mesh/v1.1/api/models/include/esp_ble_mesh_blob_model_api.h create mode 100644 components/bt/esp_ble_mesh/v1.1/api/models/include/esp_ble_mesh_dfu_model_api.h create mode 100644 components/bt/esp_ble_mesh/v1.1/api/models/include/esp_ble_mesh_dfu_slot_api.h create mode 100644 components/bt/esp_ble_mesh/v1.1/btc/btc_ble_mesh_dfu_model.c create mode 100644 components/bt/esp_ble_mesh/v1.1/btc/btc_ble_mesh_dfu_slot.c create mode 100644 components/bt/esp_ble_mesh/v1.1/btc/include/btc_ble_mesh_dfu_model.h create mode 100644 components/bt/esp_ble_mesh/v1.1/btc/include/btc_ble_mesh_dfu_slot.h diff --git a/components/bt/CMakeLists.txt b/components/bt/CMakeLists.txt index 1f942a3a21..7d4cc40fd3 100644 --- a/components/bt/CMakeLists.txt +++ b/components/bt/CMakeLists.txt @@ -47,6 +47,9 @@ set(ble_mesh_v11_include_dirs "esp_ble_mesh/v1.1/api/core/include" "esp_ble_mesh/v1.1/api/models/include" "esp_ble_mesh/v1.1/btc/include" + "esp_ble_mesh/v1.1/include" + "esp_ble_mesh/v1.1/dfu" + "esp_ble_mesh/v1.1/mbt" ) if(CONFIG_IDF_DOC_BUILD) @@ -623,9 +626,13 @@ if(CONFIG_BT_ENABLED) "esp_ble_mesh/v1.1/api/core/esp_ble_mesh_sar_model_api.c" "esp_ble_mesh/v1.1/api/core/esp_ble_mesh_srpl_model_api.c" "esp_ble_mesh/v1.1/api/models/esp_ble_mesh_mbt_model_api.c" + "esp_ble_mesh/v1.1/api/models/esp_ble_mesh_dfu_model_api.c" + "esp_ble_mesh/v1.1/api/models/esp_ble_mesh_dfu_slot_api.c" "esp_ble_mesh/v1.1/btc/btc_ble_mesh_agg_model.c" "esp_ble_mesh/v1.1/btc/btc_ble_mesh_brc_model.c" "esp_ble_mesh/v1.1/btc/btc_ble_mesh_df_model.c" + "esp_ble_mesh/v1.1/btc/btc_ble_mesh_dfu_model.c" + "esp_ble_mesh/v1.1/btc/btc_ble_mesh_dfu_slot.c" "esp_ble_mesh/v1.1/btc/btc_ble_mesh_lcd_model.c" "esp_ble_mesh/v1.1/btc/btc_ble_mesh_mbt_model.c" "esp_ble_mesh/v1.1/btc/btc_ble_mesh_odp_model.c" @@ -633,6 +640,12 @@ if(CONFIG_BT_ENABLED) "esp_ble_mesh/v1.1/btc/btc_ble_mesh_rpr_model.c" "esp_ble_mesh/v1.1/btc/btc_ble_mesh_sar_model.c" "esp_ble_mesh/v1.1/btc/btc_ble_mesh_srpl_model.c" + "esp_ble_mesh/v1.1/mbt/blob_srv.c" + "esp_ble_mesh/v1.1/mbt/blob_cli.c" + "esp_ble_mesh/v1.1/dfu/dfu_cli.c" + "esp_ble_mesh/v1.1/dfu/dfu_srv.c" + "esp_ble_mesh/v1.1/dfu/dfu_slot.c" + "esp_ble_mesh/v1.1/dfu/dfu_metadata.c" "esp_ble_mesh/lib/ext.c") if(CONFIG_BLE_MESH_SAR_ENHANCEMENT) list(APPEND srcs "esp_ble_mesh/core/transport.enh.c") diff --git a/components/bt/common/btc/core/btc_task.c b/components/bt/common/btc/core/btc_task.c index 41c0b4368e..3bc91c9659 100644 --- a/components/bt/common/btc/core/btc_task.c +++ b/components/bt/common/btc/core/btc_task.c @@ -89,6 +89,7 @@ #include "btc_ble_mesh_rpr_model.h" #include "btc_ble_mesh_sar_model.h" #include "btc_ble_mesh_srpl_model.h" +#include "btc_ble_mesh_dfu_model.h" #endif /* CONFIG_BLE_MESH_V11_SUPPORT */ #endif /* #if CONFIG_BLE_MESH */ @@ -262,6 +263,9 @@ static const btc_func_t profile_tab[BTC_PID_NUM] = { #if CONFIG_BLE_MESH_MBT_SRV [BTC_PID_MBT_SERVER] = {btc_ble_mesh_mbt_server_call_handler, btc_ble_mesh_mbt_server_cb_handler }, #endif /* CONFIG_BLE_MESH_MBT_SRV */ +#if CONFIG_BLE_MESH_DFU_CLI + [BTC_PID_DFU_CLIENT] = {btc_ble_mesh_dfu_client_call_handler, btc_ble_mesh_dfu_client_cb_handler}, +#endif /* CONFIG_BLE_MESH_DFU_CLI */ #if CONFIG_BLE_MESH_BLE_COEX_SUPPORT || CONFIG_BLE_MESH_USE_BLE_50 [BTC_PID_BLE_MESH_BLE_COEX] = {btc_ble_mesh_ble_call_handler, btc_ble_mesh_ble_cb_handler }, #endif /* CONFIG_BLE_MESH_BLE_COEX_SUPPORT || CONFIG_BLE_MESH_USE_BLE_50 */ diff --git a/components/bt/common/btc/include/btc/btc_task.h b/components/bt/common/btc/include/btc/btc_task.h index 0436558242..e87ce59311 100644 --- a/components/bt/common/btc/include/btc/btc_task.h +++ b/components/bt/common/btc/include/btc/btc_task.h @@ -106,6 +106,10 @@ typedef enum { BTC_PID_TIME_SCENE_SERVER, BTC_PID_MBT_CLIENT, BTC_PID_MBT_SERVER, + BTC_PID_BLOB_CLIENT, + BTC_PID_BLOB_SERVER, + BTC_PID_DFU_CLIENT, + BTC_PID_DFU_SERVER, BTC_PID_BLE_MESH_BLE_COEX, #endif /* CONFIG_BLE_MESH */ #if (BLE_FEAT_ISO_EN == TRUE) diff --git a/components/bt/esp_ble_mesh/Kconfig.in b/components/bt/esp_ble_mesh/Kconfig.in index 3f8f0a37d7..c1beb54857 100644 --- a/components/bt/esp_ble_mesh/Kconfig.in +++ b/components/bt/esp_ble_mesh/Kconfig.in @@ -1636,11 +1636,13 @@ if BLE_MESH Enable support for Lighting server models. config BLE_MESH_MBT_CLI - bool "BLOB Transfer Client model" + bool "BLOB Transfer Client model(Deprecated)" depends on BLE_MESH_V11_SUPPORT default n help Enable support for BLOB Transfer Client model. + Warn: This version of the Mesh Binary Large Object Transfer Model will be deprecated, + and a new version will be released in the future. if BLE_MESH_MBT_CLI @@ -1655,11 +1657,212 @@ if BLE_MESH endif # BLE_MESH_MBT_CLI config BLE_MESH_MBT_SRV - bool "BLOB Transfer Server model" + bool "BLOB Transfer Server model(Deprecated)" depends on BLE_MESH_V11_SUPPORT default n help Enable support for BLOB Transfer Server model. + Warn: This version of the Mesh Binary Large Object Transfer Model will be deprecated, + and a new version will be released in the future. + + menu "Binary Larger Object Transfer model" + + config BLE_MESH_BLOB_SRV + bool "Support for BLOB Transfer Server model" + depends on BLE_MESH_V11_SUPPORT + help + Enable the Binary Large Object (BLOB) Transfer Server model. + + if BLE_MESH_BLOB_SRV + + config BLE_MESH_BLOB_SRV_PULL_REQ_COUNT + int "Number of chunks to request for each pull" + default 4 + range 1 16 + help + In Pull mode (Pull BLOB Transfer Mode), the BLOB Transfer Server + requests a fixed number of chunks from the Client. Use this option to + control the chunk count in the request. If the BLOB Transfer Server + is instantiated on a Low Power node, the pull request count will be + trimmed to not overflow the Friend queue. + + config BLE_MESH_BLOB_SIZE_MAX + int "Largest BLOB size in bytes" + default 524288 + range 1 3257617792 + help + The maximum object size a BLOB Transfer Server can receive. + + config BLE_MESH_BLOB_BLOCK_SIZE_MIN + int "Minimum block size" + default 4096 + range 64 1048576 # 2^6 - 2^20 + help + Minimum acceptable block size in a BLOB transfer. The transfer block + size will be some number that is a power of two, and is between block + size min and block size max. If no such number exists, a compile + time warning will be issued. + + config BLE_MESH_BLOB_BLOCK_SIZE_MAX + int "Maximum block size" + default 4096 + range BLE_MESH_BLOB_BLOCK_SIZE_MIN 1048576 + help + Maximum acceptable block size in a BLOB transfer. The transfer block + size will be some number that is a power of two, and is between block + size min and block size max. If no such number exists, a compile + time warning will be issued. + + config BLE_MESH_BLOB_REPORT_TIMEOUT + int "Partial Block Report interval in Pull mode" + default 10 + range 1 31 + help + The timer value that Pull BLOB Transfer Server uses to report missed chunks. + + config BLE_MESH_RX_BLOB_CHUNK_SIZE + depends on !BLE_MESH_ALIGN_CHUNK_SIZE_TO_MAX_SEGMENT + int "BLOB Server chunk size" + default 8 + range 8 377 + help + Set the chunk size for the BLOB Server. + The actual maximum chunk size depends on how many segments are + possible and will be clamped to the max possible if set above. + see also: BLE_MESH_RX_SEG_MAX, + and the maximum SDU a node can receive. + + endif # BLE_MESH_BLOB_SRV + + config BLE_MESH_BLOB_CLI + bool "Support for BLOB Transfer Client model" + depends on BLE_MESH_V11_SUPPORT + help + Enable the Binary Large Object (BLOB) Transfer Client model. + + if BLE_MESH_BLOB_CLI + + config BLE_MESH_BLOB_CLI_BLOCK_RETRIES + int "Number of retries per block" + default 5 + help + Controls the number of times the client will attempt to resend missing + chunks to the BLOB receivers for every block. + + config BLE_MESH_TX_BLOB_CHUNK_SIZE + depends on !BLE_MESH_ALIGN_CHUNK_SIZE_TO_MAX_SEGMENT + int "BLOB Client chunk size" + default 8 + range 1 377 + help + Set the chunk size for the BLOB Client. + The actual maximum chunk size depends on how many segments are + possible and will be clamped to the max possible if set above. + see also: BLE_MESH_TX_SEG_MAX, + and the maximum SDU a node can receive. + + config BLE_MESH_TX_BLOB_CHUNK_SEND_INTERVAL + int "BLOB Client chunk send interval" + default 0 + range 0 2147483647 + help + Set the interval in milliseconds in which chunks are sent during the BLOB transfer. + Note: Without a delay between each sent chunk, the network might become too busy with the + BLOB transfer for other communications to succeed. + Note: Timing restrictions, like the timeout base, should be considered or changed + accordingly when setting this interval. Otherwise, the interval might be too big for the + timeout settings and cause timeouts. + + endif # BLE_MESH_BLOB_CLI + + menu "BLOB models common configuration" + visible if BLE_MESH_BLOB_SRV || BLE_MESH_BLOB_CLI + + config BLE_MESH_BLOB_CHUNK_COUNT_MAX + int "Maximum chunk count per block" + default 256 + range 1 2992 + help + A BLOB transfer contains several blocks. Each block is made up of + several chunks. This option controls the maximum chunk count per + block. + + config BLE_MESH_ALIGN_CHUNK_SIZE_TO_MAX_SEGMENT + bool "Align chunk size to max segmented message size" + default y + + endmenu #BLOB models common configuration + + endmenu # Binary Larger Object Transfer model + + menu "Device Firmware Update model" + + config BLE_MESH_DFU_SRV + bool "Support for Firmware Update Server model" + depends on BLE_MESH_BLOB_SRV + help + Enable the Firmware Update Server model. + + config BLE_MESH_DFU_CLI + bool "Support for Firmware Update Client model" + depends on BLE_MESH_BLOB_CLI + help + Enable the Firmware Update Client model. + + menu "Firmware Update model configuration" + visible if BLE_MESH_DFU_SRV || BLE_MESH_DFU_CLI + + config BLE_MESH_DFU_FWID_MAXLEN + int "DFU FWID max length" + default 16 + range 0 106 + help + This value defines the maximum length of an image's firmware ID. + + config BLE_MESH_DFU_METADATA_MAXLEN + int "DFU metadata max length" + default 32 + range 18 255 if BLE_MESH_DFU_METADATA + range 0 255 + help + This value defines the maximum length of an image's metadata. + + config BLE_MESH_DFU_METADATA + bool "Support for the default metadata format" + help + This option adds a set of functions that can be used to encode and decode a firmware + metadata using the format defined in the Bluetooth mesh DFU subsystem. + + config BLE_MESH_DFU_URI_MAXLEN + int "DFU URI max length" + default 32 + range 0 255 + help + This value defines the maximum length of an image's URI, not including + a string terminator. + + endmenu #Firmware Update model configuration + + config BLE_MESH_DFU_SLOTS + bool "Firmware image slot manager" + default y if BLE_MESH_DFU_CLI + help + Enable the DFU image slot manager, for managing firmware distribution slots + for the Firmware Update Client model. + + if BLE_MESH_DFU_SLOTS + + config BLE_MESH_DFU_SLOT_CNT + int "Number of firmware image slots" + default 1 + range 1 32767 + help + This value defines the number of firmware slots the DFU image slot manager + can keep simultaneously. + + endif #BLE_MESH_DFU_SLOTS + + endmenu # Device Firmware Update model endmenu #Support for BLE Mesh Client/Server models diff --git a/components/bt/esp_ble_mesh/api/esp_ble_mesh_defs.h b/components/bt/esp_ble_mesh/api/esp_ble_mesh_defs.h index 7d4b0ed170..8b7ff38ed7 100644 --- a/components/bt/esp_ble_mesh/api/esp_ble_mesh_defs.h +++ b/components/bt/esp_ble_mesh/api/esp_ble_mesh_defs.h @@ -1982,8 +1982,12 @@ typedef union { #define ESP_BLE_MESH_MODEL_ID_LIGHT_LC_SRV 0x130f #define ESP_BLE_MESH_MODEL_ID_LIGHT_LC_SETUP_SRV 0x1310 #define ESP_BLE_MESH_MODEL_ID_LIGHT_LC_CLI 0x1311 -#define ESP_BLE_MESH_MODEL_ID_MBT_SRV 0x1400 -#define ESP_BLE_MESH_MODEL_ID_MBT_CLI 0x1401 +#define ESP_BLE_MESH_MODEL_ID_MBT_SRV 0x14fe +#define ESP_BLE_MESH_MODEL_ID_MBT_CLI 0x14ff +#define ESP_BLE_MESH_MODEL_ID_BLOB_SRV 0x1400 +#define ESP_BLE_MESH_MODEL_ID_BLOB_CLI 0x1401 +#define ESP_BLE_MESH_MODEL_ID_DFU_SRV 0x1402 +#define ESP_BLE_MESH_MODEL_ID_DFU_CLI 0x1403 /** * esp_ble_mesh_opcode_config_client_get_t belongs to esp_ble_mesh_opcode_t, this typedef is only 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 72d1774d5e..babbdf5408 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 @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2017-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2017-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -1410,6 +1410,23 @@ typedef bt_mesh_client_user_data_t bt_mesh_mbt_client_t; extern const struct bt_mesh_model_op bt_mesh_mbt_cli_op[]; extern const struct bt_mesh_model_cb bt_mesh_mbt_cli_cb; #endif /* CONFIG_BLE_MESH_MBT_CLI */ +#if CONFIG_BLE_MESH_BLOB_SRV +extern const struct bt_mesh_model_op _bt_mesh_blob_srv_op[]; +extern const struct bt_mesh_model_cb _bt_mesh_blob_srv_cb; +#endif /* CONFIG_BLE_MESH_BLOB_SRV */ +#if CONFIG_BLE_MESH_BLOB_CLI +typedef bt_mesh_client_user_data_t bt_mesh_blob_client_t; +extern const struct bt_mesh_model_op _bt_mesh_blob_cli_op[]; +extern const struct bt_mesh_model_cb _bt_mesh_blob_cli_cb; +#endif /* CONFIG_BLE_MESH_BLOB_CLI */ +#if CONFIG_BLE_MESH_DFU_SRV +extern const struct bt_mesh_model_op _bt_mesh_dfu_srv_op[]; +extern const struct bt_mesh_model_cb _bt_mesh_dfu_srv_cb; +#endif /* CONFIG_BLE_MESH_DFU_SRV */ +#if CONFIG_BLE_MESH_DFU_CLI +extern const struct bt_mesh_model_op _bt_mesh_dfu_cli_op[]; +extern const struct bt_mesh_model_cb _bt_mesh_dfu_cli_cb; +#endif /* CONFIG_BLE_MESH_DFU_CLI */ static void btc_ble_mesh_model_op_set(esp_ble_mesh_model_t *model) { @@ -2205,9 +2222,9 @@ static void btc_ble_mesh_model_op_set(esp_ble_mesh_model_t *model) case BLE_MESH_MODEL_ID_MBT_CLI: model->op = (esp_ble_mesh_model_op_t *)bt_mesh_mbt_cli_op; model->cb = (esp_ble_mesh_model_cbs_t *)&bt_mesh_mbt_cli_cb; - bt_mesh_mbt_client_t *cli = (bt_mesh_mbt_client_t *)model->user_data; - if (cli) { - cli->publish_status = btc_ble_mesh_mbt_client_publish_callback; + bt_mesh_mbt_client_t *mbt_cli = (bt_mesh_mbt_client_t *)model->user_data; + if (mbt_cli) { + mbt_cli->publish_status = btc_ble_mesh_mbt_client_publish_callback; } break; #endif /* CONFIG_BLE_MESH_MBT_CLI */ @@ -2220,6 +2237,43 @@ static void btc_ble_mesh_model_op_set(esp_ble_mesh_model_t *model) } break; #endif /* CONFIG_BLE_MESH_MBT_SRV */ +#if CONFIG_BLE_MESH_BLOB_CLI + case BLE_MESH_MODEL_ID_BLOB_CLI: + model->op = (esp_ble_mesh_model_op_t *)_bt_mesh_blob_cli_op; + model->cb = (esp_ble_mesh_model_cbs_t *)&_bt_mesh_blob_cli_cb; + bt_mesh_blob_client_t *blob_cli = (bt_mesh_blob_client_t *)model->user_data; + if (blob_cli) { + /* TBD: do we need publish callback for Blob Transfer Client model? */ + } + break; +#endif /* CONFIG_BLE_MESH_BLOB_CLI */ +#if CONFIG_BLE_MESH_BLOB_SRV + case BLE_MESH_MODEL_ID_BLOB_SRV: + model->op = (esp_ble_mesh_model_op_t *)_bt_mesh_blob_srv_op; + model->cb = (esp_ble_mesh_model_cbs_t *)&_bt_mesh_blob_srv_cb; + if (model->pub) { + model->pub->update = (esp_ble_mesh_cb_t)btc_ble_mesh_model_publish_update; + } + break; +#endif /* CONFIG_BLE_MESH_BLOB_SRV */ +#if CONFIG_BLE_MESH_DFU_CLI + case BLE_MESH_MODEL_ID_DFU_CLI: + model->op = (esp_ble_mesh_model_op_t *)_bt_mesh_dfu_cli_op; + model->cb = (esp_ble_mesh_model_cbs_t *)&_bt_mesh_dfu_cli_cb; + if (model->pub) { + model->pub->update = (esp_ble_mesh_cb_t)btc_ble_mesh_model_publish_update; + } + break; +#endif /* CONFIG_BLE_MESH_DFU_CLI */ +#if CONFIG_BLE_MESH_DFU_SRV + case BLE_MESH_MODEL_ID_DFU_SRV: + model->op = (esp_ble_mesh_model_op_t *)_bt_mesh_dfu_srv_op; + model->cb = (esp_ble_mesh_model_cbs_t *)&_bt_mesh_dfu_srv_cb; + if (model->pub) { + model->pub->update = (esp_ble_mesh_cb_t)btc_ble_mesh_model_publish_update; + } + break; +#endif /* CONFIG_BLE_MESH_DFU_SRV */ default: goto set_vnd_op; } diff --git a/components/bt/esp_ble_mesh/common/include/mesh/kernel.h b/components/bt/esp_ble_mesh/common/include/mesh/kernel.h index 95624cf736..a24772a47d 100644 --- a/components/bt/esp_ble_mesh/common/include/mesh/kernel.h +++ b/components/bt/esp_ble_mesh/common/include/mesh/kernel.h @@ -37,7 +37,7 @@ extern "C" { #endif #endif -#define BLE_MESH_ADV_TASK_STACK_SIZE 3072 +#define BLE_MESH_ADV_TASK_STACK_SIZE (3072) #define BLE_MESH_ADV_TASK_NAME "mesh_adv_task" #define BLE_MESH_ADV_TASK_PRIO (configMAX_PRIORITIES - 5) diff --git a/components/bt/esp_ble_mesh/common/include/mesh/timer.h b/components/bt/esp_ble_mesh/common/include/mesh/timer.h index e1296aa820..30b400aee4 100644 --- a/components/bt/esp_ble_mesh/common/include/mesh/timer.h +++ b/components/bt/esp_ble_mesh/common/include/mesh/timer.h @@ -1,6 +1,6 @@ /* * SPDX-FileCopyrightText: 2016 Wind River Systems, Inc. - * SPDX-FileContributor: 2018-2021 Espressif Systems (Shanghai) CO LTD + * SPDX-FileContributor: 2018-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -49,6 +49,8 @@ struct k_work; */ typedef void (*k_work_handler_t)(struct k_work *work); +typedef int32_t k_timeout_t; + struct k_work { k_work_handler_t handler; int index; @@ -141,6 +143,65 @@ struct k_work { */ #define K_FOREVER (-1) +/** + * @brief Define for delayable work type. + * + * This macro maps the Zephyr delayable work type `k_work_delayable` + * to the ESP-IDF type `k_delayed_work`. + */ +#define k_work_delayable k_delayed_work + +/** + * @brief Reschedule a delayable work item. + * + * This macro maps to `k_delayed_work_submit`, which cancels + * any existing pending submission of the work item and reschedules + * it with the new timeout delay. + * + * @param work Pointer to delayable work item. + * @param delay Timeout delay value. + * @return See implementation of `k_delayed_work_submit`. + */ +#define k_work_reschedule k_delayed_work_submit + +/** + * @brief Schedule a delayable work item. + * + * This macro maps to `k_delayed_work_submit`, + * which schedules a work item to be processed + * after the specified timeout delay. If the work + * is already pending, the new delay is applied. + * + * @param work Pointer to delayable work item. + * @param delay Timeout delay value. + * @return See implementation of `k_delayed_work_submit`. + */ +#define k_work_schedule k_delayed_work_submit + +/** + * @brief Cancel a delayable work item. + * + * This macro maps to `k_delayed_work_cancel`, + * which cancels a pending work submission + * associated with a delayable work item. + * + * @param work Pointer to delayable work item. + * @return See implementation of `k_delayed_work_cancel`. + */ +#define k_work_cancel_delayable k_delayed_work_cancel + +/** + * @brief Initialize a delayable work item. + * + * This macro maps to `k_delayed_work_init`, + * which initializes a delayable work item with + * the provided handler function. + * + * @param work Pointer to delayable work item. + * @param handler Work item handler function. + */ +#define k_work_init_delayable k_delayed_work_init + /** * @brief Get system uptime (32-bit version). * @@ -160,6 +221,27 @@ struct k_delayed_work { struct k_work work; }; +#define _K_DELAYABLE_WORK_INITIALIZER(work_handler) { \ + .work = { \ + .handler = work_handler, \ + }, \ +} + +/** + * @brief Convert a work item to its containing delayable work structure. + * + * This function uses container_of to derive the address of the containing + * k_work_delayable structure from the address of the embedded k_work structure. + * + * @param work Pointer to the embedded k_work structure within a k_work_delayable. + * @return Pointer to the containing k_work_delayable structure. + */ +static inline struct k_work_delayable * +k_work_delayable_from_work(struct k_work *work) +{ + return CONTAINER_OF(work, struct k_work_delayable, work); +} + /** * @brief Submit a delayed work item to the system workqueue. * @@ -209,6 +291,21 @@ int k_delayed_work_submit_periodic(struct k_delayed_work *work, int32_t period); */ int32_t k_delayed_work_remaining_get(struct k_delayed_work *work); +/** + * @brief Check if a delayable work item is pending execution. + * + * This function checks whether a delayable work item has been scheduled + * and is waiting to be processed. It returns true if the work item is in + * pending state (waiting for timeout expiration or being in work queue). + * + * @param dwork Pointer to delayable work item. + * @return true if work is pending, false otherwise. + */ +static inline bool k_work_delayable_is_pending(struct k_work_delayable *dwork) +{ + return k_delayed_work_remaining_get(dwork); +} + /** * @brief Submit a work item to the system workqueue. * @@ -267,10 +364,45 @@ int k_delayed_work_init(struct k_delayed_work *work, k_work_handler_t handler); * @return Current uptime. */ int64_t k_uptime_get(void); +int64_t k_uptime_delta(int64_t *reftime); void bt_mesh_timer_init(void); void bt_mesh_timer_deinit(void); +/** + * @brief Initialize a statically-defined work item. + * + * This macro can be used to initialize a statically-defined workqueue work + * item, prior to its first use. For example, + * + * @code static K_WORK_DEFINE(, ); @endcode + * + * @param work Symbol name for work item object + * @param work_handler Function to invoke each time work item is processed. + */ +#define K_WORK_DEFINE(work, work_handler) \ + struct k_work work = _K_WORK_INITIALIZER(work_handler) + +/** + * @brief Initialize a statically-defined delayable work item. + * + * This macro can be used to initialize a statically-defined delayable + * work item, prior to its first use. For example, + * + * @code static K_WORK_DELAYABLE_DEFINE(, ); @endcode + * + * Note that if the runtime dependencies support initialization with + * k_work_init_delayable() using that will eliminate the initialized + * object in ROM that is produced by this macro and copied in at + * system startup. + * + * @param work Symbol name for delayable work item object + * @param work_handler Function to invoke each time work item is processed. + */ +#define K_WORK_DELAYABLE_DEFINE(work, work_handler) \ + struct k_delayed_work work \ + = _K_DELAYABLE_WORK_INITIALIZER(work_handler) + #ifdef __cplusplus } #endif diff --git a/components/bt/esp_ble_mesh/common/include/mesh/utils.h b/components/bt/esp_ble_mesh/common/include/mesh/utils.h index 967fed2e0c..4ef522ee32 100644 --- a/components/bt/esp_ble_mesh/common/include/mesh/utils.h +++ b/components/bt/esp_ble_mesh/common/include/mesh/utils.h @@ -15,6 +15,8 @@ #define _BLE_MESH_UTILS_H_ #include +#include +#include #include "esp_bit_defs.h" #include "mesh/types.h" #include "utils_loops.h" @@ -94,6 +96,81 @@ extern "C" { #define WB_DN(x) ROUND_DOWN(x, sizeof(void *)) #endif +/** + * @brief Whether @p ptr is an element of @p array + * + * This macro can be seen as a slightly stricter version of @ref PART_OF_ARRAY + * in that it also ensures that @p ptr is aligned to an array-element boundary + * of @p array. + * + * In C, passing a pointer as @p array causes a compile error. + * + * @param array the array in question + * @param ptr the pointer to check + * + * @return 1 if @p ptr is part of @p array, 0 otherwise + */ +#define IS_ARRAY_ELEMENT(array, ptr) \ + ((ptr) && POINTER_TO_UINT(array) <= POINTER_TO_UINT(ptr) && \ + POINTER_TO_UINT(ptr) < POINTER_TO_UINT(&(array)[ARRAY_SIZE(array)]) && \ + (POINTER_TO_UINT(ptr) - POINTER_TO_UINT(array)) % sizeof((array)[0]) == 0) + +/** + * @brief Index of @p ptr within @p array + * + * With `CONFIG_ASSERT=y`, this macro will trigger a runtime assertion + * when @p ptr does not fall into the range of @p array or when @p ptr + * is not aligned to an array-element boundary of @p array. + * + * In C, passing a pointer as @p array causes a compile error. + * + * @param array the array in question + * @param ptr pointer to an element of @p array + * + * @return the array index of @p ptr within @p array, on success + */ +#define ARRAY_INDEX(array, ptr) \ + ({ \ + __ASSERT_NO_MSG(IS_ARRAY_ELEMENT(array, ptr)); \ + (__typeof__((array)[0]) *)(ptr) - (array); \ + }) + + +/** + * @brief Divide and round up. + * + * Example: + * @code{.c} + * DIV_ROUND_UP(1, 2); // 1 + * DIV_ROUND_UP(3, 2); // 2 + * @endcode + * + * @param n Numerator. + * @param d Denominator. + * + * @return The result of @p n / @p d, rounded up. + */ +#define DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d)) + +/** + * @brief Divide and round to the nearest integer. + * + * Example: + * @code{.c} + * DIV_ROUND_CLOSEST(5, 2); // 3 + * DIV_ROUND_CLOSEST(5, -2); // -3 + * DIV_ROUND_CLOSEST(5, 3); // 2 + * @endcode + * + * @param n Numerator. + * @param d Denominator. + * + * @return The result of @p n / @p d, rounded to the nearest integer. + */ +#define DIV_ROUND_CLOSEST(n, d) \ + ((((n) < 0) ^ ((d) < 0)) ? ((n) - ((d) / 2)) / (d) : \ + ((n) + ((d) / 2)) / (d)) + #ifndef ceiling_fraction #define ceiling_fraction(numerator, divider) \ (((numerator) + ((divider) - 1)) / (divider)) @@ -125,6 +202,21 @@ extern "C" { #define BIT(n) (1UL << (n)) #endif +/** + * @brief Set or clear a bit depending on a boolean value + * + * The argument @p var is a variable whose value is written to as a + * side effect. + * + * @param var Variable to be altered + * @param bit Bit number + * @param set if 0, clears @p bit in @p var; any other value sets @p bit + */ +#ifndef WRITE_BIT +#define WRITE_BIT(var, bit, set) \ + ((var) = (set) ? ((var) | BIT(bit)) : ((var) & ~BIT(bit))) +#endif + #ifndef BIT_MASK #define BIT_MASK(n) (BIT(n) - 1) #endif @@ -219,6 +311,20 @@ const char *bt_hex(const void *buf, size_t len); void mem_rcopy(uint8_t *dst, uint8_t const *src, uint16_t len); +/** + * @brief Checks if a value is within range. + * + * @note @p val is evaluated twice. + * + * @param val Value to be checked. + * @param min Lower bound (inclusive). + * @param max Upper bound (inclusive). + * + * @retval true If value is within range + * @retval false If the value is not within range + */ +#define IN_RANGE(val, min, max) ((val) >= (min) && (val) <= (max)) + #ifdef __cplusplus } #endif diff --git a/components/bt/esp_ble_mesh/common/timer.c b/components/bt/esp_ble_mesh/common/timer.c index 0d65bd3a10..248480e70b 100644 --- a/components/bt/esp_ble_mesh/common/timer.c +++ b/components/bt/esp_ble_mesh/common/timer.c @@ -1,7 +1,7 @@ /* * SPDX-FileCopyrightText: 2016 Intel Corporation * SPDX-FileCopyrightText: 2016 Wind River Systems, Inc. - * SPDX-FileContributor: 2018-2021 Espressif Systems (Shanghai) CO LTD + * SPDX-FileContributor: 2018-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -43,6 +43,28 @@ uint32_t k_uptime_get_32(void) return (uint32_t)(esp_timer_get_time() / 1000); } +/** + * @brief Get elapsed time. + * + * This routine computes the elapsed time between the current system uptime + * and an earlier reference time, in milliseconds. + * + * @param reftime Pointer to a reference time, which is updated to the current + * uptime upon return. + * + * @return Elapsed time. + */ +int64_t k_uptime_delta(int64_t *reftime) +{ + int64_t uptime, delta; + + uptime = k_uptime_get(); + delta = uptime - *reftime; + *reftime = uptime; + + return delta; +} + void bt_mesh_timer_init(void) { bm_alarm_hash_map = hash_map_new(BLE_MESH_ALARM_HASH_MAP_SIZE, diff --git a/components/bt/esp_ble_mesh/core/access.c b/components/bt/esp_ble_mesh/core/access.c index 86d80139d0..73363c2f99 100644 --- a/components/bt/esp_ble_mesh/core/access.c +++ b/components/bt/esp_ble_mesh/core/access.c @@ -37,7 +37,7 @@ extern const struct bt_mesh_comp *comp_0; static uint16_t dev_primary_addr; -static int model_send(struct bt_mesh_model *model, +static int model_send(const struct bt_mesh_model *model, struct bt_mesh_net_tx *tx, bool implicit_bind, struct net_buf_simple *msg, const struct bt_mesh_send_cb *cb, void *cb_data); @@ -300,7 +300,7 @@ static void mod_publish(struct k_work *work) } } -struct bt_mesh_elem *bt_mesh_model_elem(struct bt_mesh_model *mod) +struct bt_mesh_elem *bt_mesh_model_elem(const struct bt_mesh_model *mod) { return &comp_0->elem[mod->elem_idx]; } @@ -548,12 +548,31 @@ struct bt_mesh_elem *bt_mesh_elem_find(uint16_t addr) return NULL; } +bool bt_mesh_has_addr(uint16_t addr) +{ + uint16_t index; + + if (BLE_MESH_ADDR_IS_UNICAST(addr)) { + return bt_mesh_elem_find(addr) != NULL; + } + + for (index = 0; index < comp_0->elem_count; index++) { + struct bt_mesh_elem *elem = &comp_0->elem[index]; + + if (bt_mesh_elem_find_group(elem, addr)) { + return true; + } + } + + return false; +} + uint8_t bt_mesh_elem_count(void) { return comp_0->elem_count; } -static bool model_has_key(struct bt_mesh_model *mod, uint16_t key) +static bool model_has_key(const struct bt_mesh_model *mod, uint16_t key) { int i; @@ -635,7 +654,7 @@ static int get_opcode(struct net_buf_simple *buf, uint32_t *opcode, bool pull_bu if (pull_buf) { *opcode = net_buf_simple_pull_u8(buf) << 16; /* Using LE for the CID since the model layer is defined as - * little-endian in the mesh spec and using BT_MESH_MODEL_OP_3 + * little-endian in the mesh spec and using BLE_MESH_MODEL_OP_3 * will declare the opcode in this way. */ *opcode |= net_buf_simple_pull_le16(buf); @@ -793,7 +812,7 @@ void bt_mesh_model_msg_init(struct net_buf_simple *msg, uint32_t opcode) case 3: net_buf_simple_add_u8(msg, ((opcode >> 16) & 0xff)); /* Using LE for the CID since the model layer is defined as - * little-endian in the mesh spec and using BT_MESH_MODEL_OP_3 + * little-endian in the mesh spec and using BLE_MESH_MODEL_OP_3 * will declare the opcode in this way. */ net_buf_simple_add_le16(msg, opcode & 0xffff); @@ -951,7 +970,7 @@ void bt_mesh_choose_better_security_cred(struct bt_mesh_net_tx *tx) } #endif /* !CONFIG_BLE_MESH_V11_SUPPORT */ -static int model_send(struct bt_mesh_model *model, +static int model_send(const struct bt_mesh_model *model, struct bt_mesh_net_tx *tx, bool implicit_bind, struct net_buf_simple *msg, const struct bt_mesh_send_cb *cb, void *cb_data) @@ -1041,7 +1060,7 @@ int bt_mesh_model_send_implicit(struct bt_mesh_model *model, return model_send(model, &tx, implicit_bind, msg, cb, cb_data); } -int bt_mesh_model_send(struct bt_mesh_model *model, +int bt_mesh_model_send(const struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, struct net_buf_simple *msg, const struct bt_mesh_send_cb *cb, void *cb_data) diff --git a/components/bt/esp_ble_mesh/core/access.h b/components/bt/esp_ble_mesh/core/access.h index 672a9a2c7b..669cff788c 100644 --- a/components/bt/esp_ble_mesh/core/access.h +++ b/components/bt/esp_ble_mesh/core/access.h @@ -29,6 +29,8 @@ uint8_t bt_mesh_elem_count(void); /* Find local element based on unicast or group address */ struct bt_mesh_elem *bt_mesh_elem_find(uint16_t addr); +bool bt_mesh_has_addr(uint16_t addr); + uint16_t *bt_mesh_model_find_group(struct bt_mesh_model *mod, uint16_t addr); int bt_mesh_get_opcode(struct net_buf_simple *buf, diff --git a/components/bt/esp_ble_mesh/core/include/mesh/access.h b/components/bt/esp_ble_mesh/core/include/mesh/access.h index 38be1a6cac..6aa64080bd 100644 --- a/components/bt/esp_ble_mesh/core/include/mesh/access.h +++ b/components/bt/esp_ble_mesh/core/include/mesh/access.h @@ -14,6 +14,7 @@ #include "mesh/config.h" #include "mesh/buf.h" #include "mesh/timer.h" +#include "sys/types.h" /** * @brief Bluetooth Mesh Access Layer @@ -151,8 +152,15 @@ struct bt_mesh_elem { #define BLE_MESH_MODEL_ID_LIGHT_LC_SRV 0x130f #define BLE_MESH_MODEL_ID_LIGHT_LC_SETUP_SRV 0x1310 #define BLE_MESH_MODEL_ID_LIGHT_LC_CLI 0x1311 -#define BLE_MESH_MODEL_ID_MBT_SRV 0x1400 -#define BLE_MESH_MODEL_ID_MBT_CLI 0x1401 +#define BLE_MESH_MODEL_ID_BLOB_SRV 0x1400 +#define BLE_MESH_MODEL_ID_BLOB_CLI 0x1401 +#define BLE_MESH_MODEL_ID_DFU_SRV 0x1402 +#define BLE_MESH_MODEL_ID_DFU_CLI 0x1403 +#define BLE_MESH_MODEL_ID_DFD_SRV 0x1404 +#define BLE_MESH_MODEL_ID_DFD_CLI 0x1405 + +#define BLE_MESH_MODEL_ID_MBT_SRV BLE_MESH_MODEL_ID_BLOB_SRV +#define BLE_MESH_MODEL_ID_MBT_CLI BLE_MESH_MODEL_ID_BLOB_CLI typedef struct { uint32_t adv_itvl; @@ -520,6 +528,8 @@ struct bt_mesh_model_pub { .update = _update, \ } +typedef ssize_t (*settings_read_cb)(void *cb_arg, void *data, size_t len); + /** Model callback functions. */ struct bt_mesh_model_cb { /** @brief Model init callback. @@ -613,7 +623,7 @@ void bt_mesh_model_msg_init(struct net_buf_simple *msg, uint32_t opcode); * * @return 0 on success, or (negative) error code on failure. */ -int bt_mesh_model_send(struct bt_mesh_model *model, +int bt_mesh_model_send(const struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, struct net_buf_simple *msg, const struct bt_mesh_send_cb *cb, @@ -642,7 +652,7 @@ int bt_mesh_model_publish(struct bt_mesh_model *model); * * @return Pointer to the element that the given model belongs to. */ -struct bt_mesh_elem *bt_mesh_model_elem(struct bt_mesh_model *mod); +struct bt_mesh_elem *bt_mesh_model_elem(const struct bt_mesh_model *mod); /** @brief Find a SIG model. * diff --git a/components/bt/esp_ble_mesh/core/storage/settings.c b/components/bt/esp_ble_mesh/core/storage/settings.c index 40cf117f2e..437c3d9b07 100644 --- a/components/bt/esp_ble_mesh/core/storage/settings.c +++ b/components/bt/esp_ble_mesh/core/storage/settings.c @@ -1,6 +1,6 @@ /* * SPDX-FileCopyrightText: 2018 Intel Corporation - * SPDX-FileContributor: 2018-2021 Espressif Systems (Shanghai) CO LTD + * SPDX-FileContributor: 2018-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -45,10 +45,12 @@ * key: "mesh/s/xxxx/b" -> write/read to set/get SIG MODEL Bind AppKey List * key: "mesh/s/xxxx/s" -> write/read to set/get SIG MODEL Subscription List * key: "mesh/s/xxxx/p" -> write/read to set/get SIG MODEL Publication + * key: "mesh/s/xxxx/d" -> write/read to set/get SIG MODEL Data * key: "mesh/vnd" -> write/read to set/get all VENDOR MODEL model_keys. * key: "mesh/v/xxxx/b" -> write/read to set/get VENDOR MODEL Bind AppKey List * key: "mesh/v/xxxx/s" -> write/read to set/get VENDOR MODEL Subscription List * key: "mesh/v/xxxx/p" -> write/read to set/get VENDOR MODEL Publication + * key: "mesh/v/xxxx/d" -> write/read to set/get VENDOR MODEL Data * key: "mesh/vaddr" -> write/read to set/get all virtual addresses * key: "mesh/va/xxxx" -> write/read to set/get the "xxxx" virtual address * key: "mesh/dkca" -> write/read to set/get Device Key Candidate @@ -2731,3 +2733,40 @@ void bt_mesh_settings_reset(bool erase) } #endif /* CONFIG_BLE_MESH_SETTINGS */ + +#define SETTINGS_MAX_DIR_DEPTH 8 + +int bt_mesh_model_data_store(struct bt_mesh_model *mod, bool vnd, + const char *name, const void *data, + size_t data_len) +{ + int err = 0; + + char path[30] = {'\0'}; + uint16_t model_key = 0U; + + model_key = BLE_MESH_GET_MODEL_KEY(mod->elem_idx, mod->model_idx); + sprintf(path, "mesh/%s/%04x/d", vnd ? "v" : "s", model_key); + if (name) { + strcat(path, "/"); + strncat(path, name, SETTINGS_MAX_DIR_DEPTH); + } + + if (data_len) { + err = bt_mesh_save_core_settings(path, data, data_len); + if (err) { + BT_ERR("Failed to store %s", path); + return err; + } + + err = bt_mesh_add_core_settings_item(vnd ? "mesh/vnd" : "mesh/sig", model_key); + if (err) { + BT_ERR("Failed to add model data to %s, model_key 0x%04x", + vnd ? "mesh/vnd" : "mesh/sig", model_key); + } + } else { + bt_mesh_erase_core_settings(path); + bt_mesh_remove_core_settings_item(vnd ? "mesh/vnd" : "mesh/sig", model_key); + } + return err; +} diff --git a/components/bt/esp_ble_mesh/core/storage/settings.h b/components/bt/esp_ble_mesh/core/storage/settings.h index 255eb2c343..7ad8cf3cd6 100644 --- a/components/bt/esp_ble_mesh/core/storage/settings.h +++ b/components/bt/esp_ble_mesh/core/storage/settings.h @@ -35,6 +35,10 @@ void bt_mesh_store_mod_sub(struct bt_mesh_model *mod); void bt_mesh_store_mod_pub(struct bt_mesh_model *mod); void bt_mesh_store_label(void); +int bt_mesh_model_data_store(struct bt_mesh_model *mod, bool vnd, + const char *name, const void *data, + size_t data_len); + void bt_mesh_clear_role(void); void bt_mesh_clear_net(void); void bt_mesh_clear_subnet(struct bt_mesh_subnet *sub); diff --git a/components/bt/esp_ble_mesh/core/transport.h b/components/bt/esp_ble_mesh/core/transport.h index d8d32f81b6..e4487fa9fd 100644 --- a/components/bt/esp_ble_mesh/core/transport.h +++ b/components/bt/esp_ble_mesh/core/transport.h @@ -11,6 +11,7 @@ #define _TRANSPORT_H_ #include "net.h" +#include "access.h" #ifdef __cplusplus extern "C" { 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 new file mode 100644 index 0000000000..feb082cd66 --- /dev/null +++ b/components/bt/esp_ble_mesh/v1.1/api/models/esp_ble_mesh_dfu_model_api.c @@ -0,0 +1,148 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#include "btc/btc_manage.h" + +#include "btc_ble_mesh_dfu_model.h" +#include "esp_ble_mesh_dfu_model_api.h" + +#if CONFIG_BLE_MESH_DFU_CLI + +static bool is_get_param_needed(esp_ble_mesh_opcode_t opcode) +{ + switch (opcode) { + case ESP_BLE_MESH_DFU_OP_UPDATE_INFO_GET: + case ESP_BLE_MESH_DFU_OP_UPDATE_METADATA_CHECK: + case ESP_BLE_MESH_DFU_OP_UPDATE_START: + return true; + default: + return false; + } +} + +esp_err_t esp_ble_mesh_dfu_client_get_state(esp_ble_mesh_client_common_param_t *params, + esp_ble_mesh_dfu_client_get_t *get) +{ + btc_ble_mesh_dfu_client_args_t arg = {0}; + btc_msg_t msg = {0}; + + if (params == NULL || params->model == NULL || + params->ctx.net_idx == ESP_BLE_MESH_KEY_UNUSED || + !ESP_BLE_MESH_ADDR_IS_UNICAST(params->ctx.addr) || + (is_get_param_needed(params->opcode) && get == NULL)) { + return ESP_ERR_INVALID_ARG; + } + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_DFU_CLIENT; + msg.act = BTC_BLE_MESH_ACT_DFU_CLIENT_GET_STATE; + + arg.dfu_get.params = params; + arg.dfu_get.get = get; + + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_mesh_dfu_client_args_t), + btc_ble_mesh_dfu_client_arg_deep_copy, + btc_ble_mesh_dfu_client_arg_deep_free) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_ble_mesh_dfu_cli_img_send(esp_ble_mesh_dfu_cli_t *cli, + esp_ble_mesh_blob_cli_inputs_t *inputs, + esp_ble_mesh_blob_io_t *io, + esp_ble_mesh_dfu_cli_xfer_t *xfer) +{ + btc_ble_mesh_dfu_client_args_t arg = {0}; + btc_msg_t msg = {0}; + + if (cli == NULL || inputs == NULL || + io == NULL || xfer == NULL) { + return ESP_ERR_INVALID_ARG; + } + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_DFU_CLIENT; + msg.act = BTC_BLE_MESH_ACT_DFU_CLIENT_IMG_SEND; + + arg.send_arg.cli = cli; + arg.send_arg.inputs = inputs; + arg.send_arg.io = io; + 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); +} + +uint8_t esp_ble_mesh_dfu_cli_progress(esp_ble_mesh_dfu_cli_t *cli) +{ + if (!cli) { + return 0; + } + return btc_ble_mesh_dfu_cli_progress(cli); +} + +esp_err_t esp_ble_mesh_register_dfu_client_callback(esp_ble_mesh_dfu_client_cb_t callback) +{ + return (btc_profile_cb_set(BTC_PID_DFU_CLIENT, callback) == 0 ? ESP_OK : ESP_FAIL); +} + +#endif /* CONFIG_BLE_MESH_DFU_CLI */ + +#if CONFIG_BLE_MESH_DFU_SRV +void esp_ble_mesh_dfu_srv_verified(esp_ble_mesh_dfu_srv_t *srv) +{ + if (!srv) { + return; + } + + btc_ble_mesh_dfu_srv_verified(srv); +} + +void esp_ble_mesh_dfu_srv_rejected(esp_ble_mesh_dfu_srv_t *srv) +{ + if (!srv) { + return; + } + + btc_ble_mesh_dfu_srv_rejected(srv); +} + +void esp_ble_mesh_dfu_srv_cancel(esp_ble_mesh_dfu_srv_t *srv) +{ + if (!srv) { + return; + } + + btc_ble_mesh_dfu_srv_cancel(srv); +} + +void esp_ble_mesh_dfu_srv_applied(esp_ble_mesh_dfu_srv_t *srv) +{ + if (!srv) { + return; + } + + btc_ble_mesh_dfu_srv_applied(srv); +} + +bool esp_ble_mesh_dfu_srv_is_busy(const esp_ble_mesh_dfu_srv_t *srv) +{ + if (!srv) { + return false; + } + + return btc_ble_mesh_dfu_srv_is_busy(srv); +} + +uint8_t esp_ble_mesh_dfu_srv_progress(const esp_ble_mesh_dfu_srv_t *srv) +{ + if (!srv) { + return 0; + } + + return btc_ble_mesh_dfu_srv_progress(srv); +} +#endif /* CONFIG_BLE_MESH_DFU_SRV */ diff --git a/components/bt/esp_ble_mesh/v1.1/api/models/esp_ble_mesh_dfu_slot_api.c b/components/bt/esp_ble_mesh/v1.1/api/models/esp_ble_mesh_dfu_slot_api.c new file mode 100644 index 0000000000..8cc17d28fa --- /dev/null +++ b/components/bt/esp_ble_mesh/v1.1/api/models/esp_ble_mesh_dfu_slot_api.c @@ -0,0 +1,102 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#include "esp_ble_mesh_dfu_slot_api.h" +#include "btc_ble_mesh_dfu_slot.h" + +int esp_ble_mesh_dfu_slot_count(void) +{ + return btc_ble_mesh_dfu_slot_count(); +} + +esp_ble_mesh_dfu_slot_t *esp_ble_mesh_dfu_slot_reserve(void) +{ + return btc_ble_mesh_dfu_slot_reserve(); +} + +int esp_ble_mesh_dfu_slot_info_set(esp_ble_mesh_dfu_slot_t *dfu_slot, size_t size, + const uint8_t *metadata, size_t metadata_len) +{ + if (!dfu_slot || !metadata || + metadata_len == 0 || + metadata_len > CONFIG_BLE_MESH_DFU_METADATA_MAXLEN) { + return ESP_ERR_INVALID_ARG; + } + + return btc_ble_mesh_dfu_slot_info_set(dfu_slot, size, + metadata, metadata_len); +} + +int esp_ble_mesh_dfu_slot_fwid_set(esp_ble_mesh_dfu_slot_t *dfu_slot, + const uint8_t *fwid, size_t fwid_len) +{ + if (!dfu_slot || !fwid || fwid_len == 0 || + fwid_len > CONFIG_BLE_MESH_DFU_FWID_MAXLEN) { + return ESP_ERR_INVALID_ARG; + } + + return btc_ble_mesh_dfu_slot_fwid_set(dfu_slot, fwid, + fwid_len); +} + +int esp_ble_mesh_dfu_slot_commit(esp_ble_mesh_dfu_slot_t *dfu_slot) +{ + if (!dfu_slot) { + return ESP_ERR_INVALID_ARG; + } + + return btc_ble_mesh_dfu_slot_commit(dfu_slot); +} + +void esp_ble_mesh_dfu_slot_release(const esp_ble_mesh_dfu_slot_t *dfu_slot) +{ + if (!dfu_slot) { + return; + } + + btc_ble_mesh_dfu_slot_release(dfu_slot); +} + +int esp_ble_mesh_dfu_slot_del(const esp_ble_mesh_dfu_slot_t *slot) +{ + if (!slot) { + return ESP_ERR_INVALID_ARG; + } + + return btc_ble_mesh_dfu_slot_del(slot); +} + +void esp_ble_mesh_dfu_slot_del_all(void) +{ + btc_ble_mesh_dfu_slot_del_all(); +} + +const esp_ble_mesh_dfu_slot_t *esp_ble_mesh_dfu_slot_at(uint16_t img_idx) +{ + return btc_ble_mesh_dfu_slot_at(img_idx); +} + +int esp_ble_mesh_dfu_slot_get(const uint8_t *fwid, size_t fwid_len, esp_ble_mesh_dfu_slot_t **slot) +{ + if (!fwid || fwid_len == 0 || + fwid_len > CONFIG_BLE_MESH_DFU_FWID_MAXLEN || + !slot || !*slot) { + return ESP_ERR_INVALID_ARG; + } + + return btc_ble_mesh_dfu_slot_get(fwid, fwid_len, slot); +} + +int esp_ble_mesh_dfu_slot_img_idx_get(const esp_ble_mesh_dfu_slot_t *slot) +{ + if (!slot) { + return ESP_ERR_INVALID_ARG; + } + + return btc_ble_mesh_dfu_slot_img_idx_get(slot); +} diff --git a/components/bt/esp_ble_mesh/v1.1/api/models/esp_ble_mesh_mbt_model_api.c b/components/bt/esp_ble_mesh/v1.1/api/models/esp_ble_mesh_mbt_model_api.c index e5aed197ae..70763ba2ec 100644 --- a/components/bt/esp_ble_mesh/v1.1/api/models/esp_ble_mesh_mbt_model_api.c +++ b/components/bt/esp_ble_mesh/v1.1/api/models/esp_ble_mesh_mbt_model_api.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2020-2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -20,20 +20,20 @@ extern int bt_mesh_get_transfer_progress(void *model, uint16_t unicast_addr, uint8_t *block_percent, uint8_t *chunk_percent); extern int bt_mesh_get_blob_reception_progress(void *model, uint8_t *reception_progress); -esp_err_t esp_ble_mesh_register_mbt_client_callback(esp_ble_mesh_mbt_client_cb_t callback) +esp_err_t esp_ble_mesh_register_mbt_client_callback(esp_ble_mesh_mbt_client_cb_t callback) __attribute__((deprecated)) { ESP_BLE_HOST_STATUS_CHECK(ESP_BLE_HOST_STATUS_ENABLED); return (btc_profile_cb_set(BTC_PID_MBT_CLIENT, callback) == 0 ? ESP_OK : ESP_FAIL); } -esp_err_t esp_ble_mesh_mbt_client_retrieve_capabilities(esp_ble_mesh_retrieve_capabilities_t *input) +esp_err_t esp_ble_mesh_mbt_client_retrieve_capabilities(esp_ble_mesh_retrieve_capabilities_t *input) __attribute__((deprecated)) { btc_ble_mesh_mbt_client_args_t arg = {0}; btc_msg_t msg = {0}; if (input == NULL || input->model == NULL || - (input->unicast_addr_count && input->unicast_addr == NULL)) { + (input->unicast_addr_count && input->unicast_addr == NULL)) { return ESP_ERR_INVALID_ARG; } @@ -51,14 +51,14 @@ esp_err_t esp_ble_mesh_mbt_client_retrieve_capabilities(esp_ble_mesh_retrieve_ca == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); } -esp_err_t esp_ble_mesh_mbt_client_transfer_blob(esp_ble_mesh_transfer_blob_t *input) +esp_err_t esp_ble_mesh_mbt_client_transfer_blob(esp_ble_mesh_transfer_blob_t *input) __attribute__((deprecated)) { btc_ble_mesh_mbt_client_args_t arg = {0}; btc_msg_t msg = {0}; if (input == NULL || input->model == NULL || - (input->unicast_addr_count && input->unicast_addr == NULL) || - input->blob_data == NULL) { + (input->unicast_addr_count && input->unicast_addr == NULL) || + input->blob_data == NULL) { return ESP_ERR_INVALID_ARG; } @@ -76,7 +76,7 @@ esp_err_t esp_ble_mesh_mbt_client_transfer_blob(esp_ble_mesh_transfer_blob_t *in == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); } -esp_err_t esp_ble_mesh_mbt_client_send_block(esp_ble_mesh_send_block_t *input) +esp_err_t esp_ble_mesh_mbt_client_send_block(esp_ble_mesh_send_block_t *input) __attribute__((deprecated)) { btc_ble_mesh_mbt_client_args_t arg = {0}; btc_msg_t msg = {0}; @@ -97,7 +97,7 @@ esp_err_t esp_ble_mesh_mbt_client_send_block(esp_ble_mesh_send_block_t *input) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); } -esp_err_t esp_ble_mesh_mbt_client_send_data(esp_ble_mesh_send_data_t *input) +esp_err_t esp_ble_mesh_mbt_client_send_data(esp_ble_mesh_send_data_t *input) __attribute__((deprecated)) { btc_ble_mesh_mbt_client_args_t arg = {0}; btc_msg_t msg = {0}; @@ -118,7 +118,7 @@ esp_err_t esp_ble_mesh_mbt_client_send_data(esp_ble_mesh_send_data_t *input) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); } -esp_err_t esp_ble_mesh_mbt_client_determine_block_status(esp_ble_mesh_determine_block_status_t *input) +esp_err_t esp_ble_mesh_mbt_client_determine_block_status(esp_ble_mesh_determine_block_status_t *input) __attribute__((deprecated)) { btc_ble_mesh_mbt_client_args_t arg = {0}; btc_msg_t msg = {0}; @@ -139,13 +139,13 @@ esp_err_t esp_ble_mesh_mbt_client_determine_block_status(esp_ble_mesh_determine_ == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); } -esp_err_t esp_ble_mesh_mbt_client_determine_transfer_status(esp_ble_mesh_determine_transfer_status_t *input) +esp_err_t esp_ble_mesh_mbt_client_determine_transfer_status(esp_ble_mesh_determine_transfer_status_t *input) __attribute__((deprecated)) { btc_ble_mesh_mbt_client_args_t arg = {0}; btc_msg_t msg = {0}; if (input == NULL || input->model == NULL || - (input->unicast_addr_count && input->unicast_addr == NULL)) { + (input->unicast_addr_count && input->unicast_addr == NULL)) { return ESP_ERR_INVALID_ARG; } @@ -163,13 +163,13 @@ esp_err_t esp_ble_mesh_mbt_client_determine_transfer_status(esp_ble_mesh_determi == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); } -esp_err_t esp_ble_mesh_mbt_client_cancel_transfer(esp_ble_mesh_cancel_transfer_t *input) +esp_err_t esp_ble_mesh_mbt_client_cancel_transfer(esp_ble_mesh_cancel_transfer_t *input) __attribute__((deprecated)) { btc_ble_mesh_mbt_client_args_t arg = {0}; btc_msg_t msg = {0}; if (input == NULL || input->model == NULL || - (input->unicast_addr_count && input->unicast_addr == NULL)) { + (input->unicast_addr_count && input->unicast_addr == NULL)) { return ESP_ERR_INVALID_ARG; } @@ -188,13 +188,13 @@ esp_err_t esp_ble_mesh_mbt_client_cancel_transfer(esp_ble_mesh_cancel_transfer_t } const esp_ble_mesh_blob_receiver_t *esp_ble_mesh_mbt_client_get_blob_receiver(esp_ble_mesh_model_t *model, - uint16_t unicast_addr) + uint16_t unicast_addr) __attribute__((deprecated)) { return (const esp_ble_mesh_blob_receiver_t *)bt_mesh_get_blob_receiver((struct bt_mesh_model *)model, unicast_addr); } -const esp_ble_mesh_blob_receiver_t **esp_ble_mesh_mbt_client_get_active_blob_receiver(esp_ble_mesh_model_t *model) +const esp_ble_mesh_blob_receiver_t **esp_ble_mesh_mbt_client_get_active_blob_receiver(esp_ble_mesh_model_t *model) __attribute__((deprecated)) { return (const esp_ble_mesh_blob_receiver_t **)bt_mesh_get_active_blob_receiver((struct bt_mesh_model *)model); } @@ -202,14 +202,14 @@ const esp_ble_mesh_blob_receiver_t **esp_ble_mesh_mbt_client_get_active_blob_rec esp_err_t esp_ble_mesh_mbt_client_get_transfer_progress(esp_ble_mesh_model_t *model, uint16_t unicast_addr, uint8_t *block_percent, - uint8_t *chunk_percent) + uint8_t *chunk_percent) __attribute__((deprecated)) { return (bt_mesh_get_transfer_progress((struct bt_mesh_model *)model, unicast_addr, block_percent, chunk_percent) == 0 ? ESP_OK : ESP_FAIL); } esp_err_t esp_ble_mesh_mbt_client_set_transfer_ttl(esp_ble_mesh_model_t *model, - uint8_t transfer_ttl) + uint8_t transfer_ttl) __attribute__((deprecated)) { btc_ble_mesh_mbt_client_args_t arg = {0}; btc_msg_t msg = {0}; @@ -231,7 +231,7 @@ esp_err_t esp_ble_mesh_mbt_client_set_transfer_ttl(esp_ble_mesh_model_t *model, == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); } -esp_err_t esp_ble_mesh_mbt_client_clear_transfer_ttl(esp_ble_mesh_model_t *model) +esp_err_t esp_ble_mesh_mbt_client_clear_transfer_ttl(esp_ble_mesh_model_t *model) __attribute__((deprecated)) { btc_ble_mesh_mbt_client_args_t arg = {0}; btc_msg_t msg = {0}; @@ -253,7 +253,7 @@ esp_err_t esp_ble_mesh_mbt_client_clear_transfer_ttl(esp_ble_mesh_model_t *model } esp_err_t esp_ble_mesh_mbt_client_set_app_idx(esp_ble_mesh_model_t *model, - uint16_t app_idx) + uint16_t app_idx) __attribute__((deprecated)) { btc_ble_mesh_mbt_client_args_t arg = {0}; btc_msg_t msg = {0}; @@ -275,7 +275,7 @@ esp_err_t esp_ble_mesh_mbt_client_set_app_idx(esp_ble_mesh_model_t *model, == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); } -esp_err_t esp_ble_mesh_mbt_client_clear_app_idx(esp_ble_mesh_model_t *model) +esp_err_t esp_ble_mesh_mbt_client_clear_app_idx(esp_ble_mesh_model_t *model) __attribute__((deprecated)) { btc_ble_mesh_mbt_client_args_t arg = {0}; btc_msg_t msg = {0}; @@ -297,7 +297,7 @@ esp_err_t esp_ble_mesh_mbt_client_clear_app_idx(esp_ble_mesh_model_t *model) } esp_err_t esp_ble_mesh_mbt_client_set_multicast_addr(esp_ble_mesh_model_t *model, - uint16_t multicast_addr) + uint16_t multicast_addr) __attribute__((deprecated)) { btc_ble_mesh_mbt_client_args_t arg = {0}; btc_msg_t msg = {0}; @@ -319,7 +319,7 @@ esp_err_t esp_ble_mesh_mbt_client_set_multicast_addr(esp_ble_mesh_model_t *model == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); } -esp_err_t esp_ble_mesh_mbt_client_clear_multicast_addr(esp_ble_mesh_model_t *model) +esp_err_t esp_ble_mesh_mbt_client_clear_multicast_addr(esp_ble_mesh_model_t *model) __attribute__((deprecated)) { btc_ble_mesh_mbt_client_args_t arg = {0}; btc_msg_t msg = {0}; @@ -342,14 +342,14 @@ esp_err_t esp_ble_mesh_mbt_client_clear_multicast_addr(esp_ble_mesh_model_t *mod #endif /* CONFIG_BLE_MESH_MBT_CLI */ #if CONFIG_BLE_MESH_MBT_SRV -esp_err_t esp_ble_mesh_register_mbt_server_callback(esp_ble_mesh_mbt_server_cb_t callback) +esp_err_t esp_ble_mesh_register_mbt_server_callback(esp_ble_mesh_mbt_server_cb_t callback) __attribute__((deprecated)) { ESP_BLE_HOST_STATUS_CHECK(ESP_BLE_HOST_STATUS_ENABLED); return (btc_profile_cb_set(BTC_PID_MBT_SERVER, callback) == 0 ? ESP_OK : ESP_FAIL); } -esp_err_t esp_ble_mesh_mbt_server_initialize_blob_receive(esp_ble_mesh_initialize_blob_receive_t *input) +esp_err_t esp_ble_mesh_mbt_server_initialize_blob_receive(esp_ble_mesh_initialize_blob_receive_t *input) __attribute__((deprecated)) { btc_ble_mesh_mbt_server_args_t arg = {0}; btc_msg_t msg = {0}; @@ -370,7 +370,7 @@ esp_err_t esp_ble_mesh_mbt_server_initialize_blob_receive(esp_ble_mesh_initializ == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); } -esp_err_t esp_ble_mesh_mbt_server_cancel_blob_receive(esp_ble_mesh_cancel_blob_receive_t *input) +esp_err_t esp_ble_mesh_mbt_server_cancel_blob_receive(esp_ble_mesh_cancel_blob_receive_t *input) __attribute__((deprecated)) { btc_ble_mesh_mbt_server_args_t arg = {0}; btc_msg_t msg = {0}; @@ -391,7 +391,7 @@ esp_err_t esp_ble_mesh_mbt_server_cancel_blob_receive(esp_ble_mesh_cancel_blob_r == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); } -esp_err_t esp_ble_mesh_mbt_server_set_blob_capabilities(esp_ble_mesh_set_blob_capabilities_t *input) +esp_err_t esp_ble_mesh_mbt_server_set_blob_capabilities(esp_ble_mesh_set_blob_capabilities_t *input) __attribute__((deprecated)) { btc_ble_mesh_mbt_server_args_t arg = {0}; btc_msg_t msg = {0}; @@ -413,7 +413,7 @@ esp_err_t esp_ble_mesh_mbt_server_set_blob_capabilities(esp_ble_mesh_set_blob_ca } esp_err_t esp_ble_mesh_mbt_server_get_blob_reception_progress(esp_ble_mesh_model_t *model, - uint8_t *reception_progress) + uint8_t *reception_progress) __attribute__((deprecated)) { return (bt_mesh_get_blob_reception_progress((struct bt_mesh_model *)model, reception_progress) == 0 ? ESP_OK : ESP_FAIL); diff --git a/components/bt/esp_ble_mesh/v1.1/api/models/include/esp_ble_mesh_blob_model_api.h b/components/bt/esp_ble_mesh/v1.1/api/models/include/esp_ble_mesh_blob_model_api.h new file mode 100644 index 0000000000..165450ed56 --- /dev/null +++ b/components/bt/esp_ble_mesh/v1.1/api/models/include/esp_ble_mesh_blob_model_api.h @@ -0,0 +1,696 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef _ESP_BLE_MESH_BLOB_MODEL_API_H_ +#define _ESP_BLE_MESH_BLOB_MODEL_API_H_ + +#include "esp_ble_mesh_defs.h" +#include "esp_ble_mesh_blob_model_api.h" + +#if CONFIG_BLE_MESH_BLOB_CLI || CONFIG_BLE_MESH_BLOB_SRV +#ifndef _BLE_MESH_BLOB_DEPRECATE_WARN +#define _BLE_MESH_BLOB_DEPRECATE_WARN +#warning Please note: All APIs published in this document are in Preview version and may undergo significant changes in the future. +#endif +#endif /* CONFIG_BLE_MESH_BLOB_CLI || CONFIG_BLE_MESH_BLOB_SRV */ + +#ifdef __cplusplus +extern "C" { +#endif + +#define ESP_BLE_MESH_BLOB_ID_SIZE 8 + +/** @def ESP_BLE_MESH_MODEL_BLOB_CLI + * + * @brief Define a new BLOB Transfer Client model. + * + * @note This API needs to be called for each element on which + * the application needs to have a BLOB Transfer Client model. + * + * @param cli_pub Pointer to the unique struct esp_ble_mesh_model_pub_t. + * @param cli_data Pointer to the unique struct esp_ble_mesh_client_t. + * + * @return New BLOB Transfer Client model instance. + */ +#define ESP_BLE_MESH_MODEL_BLOB_CLI(cli_pub, cli_data) \ + ESP_BLE_MESH_SIG_MODEL(ESP_BLE_MESH_MODEL_ID_BLOB_CLI, \ + NULL, cli_pub, cli_data) + +/** @def ESP_BLE_MESH_MODEL_BLOB_SRV + * + * @brief Define a new BLOB Transfer Server model. + * + * @note This API needs to be called for each element on which + * the application needs to have a BLOB Transfer Server model. + * + * @param srv_pub Pointer to the unique struct esp_ble_mesh_model_pub_t. + * @param srv_data Pointer to the unique struct esp_ble_mesh_blob_trans_srv_t. + * + * @return New BLOB Transfer Server model instance. + */ +#define ESP_BLE_MESH_MODEL_BLOB_SRV(srv_pub, srv_data) \ + ESP_BLE_MESH_SIG_MODEL(ESP_BLE_MESH_MODEL_ID_BLOB_SRV, \ + NULL, srv_pub, srv_data) + + +/** BLOB stream. */ +typedef struct esp_ble_mesh_blob_io esp_ble_mesh_blob_io_t; + +/** @brief BLOB Transfer Server instance. */ +typedef struct esp_ble_mesh_blob_srv esp_ble_mesh_blob_srv_t; + +/** @brief BLOB Transfer Server model event handlers. */ +typedef struct esp_ble_mesh_blob_srv_cb esp_ble_mesh_blob_srv_cb_t; + +/** Target node's Pull mode (Pull BLOB Transfer Mode) context used + * while sending chunks to the Target node. + */ +typedef struct esp_ble_mesh_blob_target_pull esp_ble_mesh_blob_target_pull_t; + +/** BLOB Transfer Client Target node. */ +typedef struct esp_ble_mesh_blob_target esp_ble_mesh_blob_target_t; + +/** BLOB transfer information. */ +typedef struct esp_ble_mesh_blob_xfer_info esp_ble_mesh_blob_xfer_info_t; + +/** BLOB Transfer Client transfer inputs. */ +typedef struct esp_ble_mesh_blob_cli_inputs esp_ble_mesh_blob_cli_inputs_t; + +/** Transfer capabilities of a Target node. */ +typedef struct esp_ble_mesh_blob_cli_caps esp_ble_mesh_blob_cli_caps_t; + +/** + * @brief BLOB Transfer Client model instance. + * @note Preview version, the contents of this struct may change in the future. + */ +typedef struct esp_ble_mesh_blob_cli esp_ble_mesh_blob_cli_t; + +/** Event handler callbacks for the BLOB Transfer Client model. */ +typedef struct esp_ble_mesh_blob_cli_cb esp_ble_mesh_blob_cli_cb_t; + +/** BLOB transfer. */ +typedef struct esp_ble_mesh_blob_xfer esp_ble_mesh_blob_xfer_t; + +/** BLOB transfer data block. */ +typedef struct esp_ble_mesh_blob_block esp_ble_mesh_blob_block_t; + +/** BLOB data chunk. */ +typedef struct esp_ble_mesh_blob_chunk esp_ble_mesh_blob_chunk_t; + +/** Transfer phase. */ +typedef enum esp_ble_mesh_blob_xfer_phase { + ESP_BLE_MESH_BLOB_XFER_PHASE_INACTIVE, /*!< The BLOB Transfer Server is awaiting configuration. */ + ESP_BLE_MESH_BLOB_XFER_PHASE_WAITING_FOR_START, /*!< The BLOB Transfer Server is ready to receive a BLOB transfer. */ + ESP_BLE_MESH_BLOB_XFER_PHASE_WAITING_FOR_BLOCK, /*!< The BLOB Transfer Server is waiting for the next block of data. */ + ESP_BLE_MESH_BLOB_XFER_PHASE_WAITING_FOR_CHUNK, /*!< The BLOB Transfer Server is waiting for the next chunk of data. */ + ESP_BLE_MESH_BLOB_XFER_PHASE_COMPLETE, /*!< The BLOB was transferred successfully. */ + ESP_BLE_MESH_BLOB_XFER_PHASE_SUSPENDED, /*!< The BLOB transfer is paused. */ +} esp_ble_mesh_blob_xfer_phase_t; + +/** BLOB transfer mode. */ +typedef enum esp_ble_mesh_blob_xfer_mode { + ESP_BLE_MESH_BLOB_XFER_MODE_NONE, /*!< No valid transfer mode. */ + ESP_BLE_MESH_BLOB_XFER_MODE_PUSH, /*!< Push mode (Push BLOB Transfer Mode). */ + ESP_BLE_MESH_BLOB_XFER_MODE_PULL, /*!< Pull mode (Pull BLOB Transfer Mode). */ + ESP_BLE_MESH_BLOB_XFER_MODE_ALL, /*!< Both modes are valid. */ +} esp_ble_mesh_blob_xfer_mode_t; + +/** BLOB model status codes. */ +typedef enum esp_ble_mesh_blob_status { + /** The message was processed successfully. */ + ESP_BLE_MESH_BLOB_SUCCESS, + /** The Block Number field value is not within the range of blocks being + * transferred. + */ + ESP_BLE_MESH_BLOB_ERR_INVALID_BLOCK_NUM, + /** The block size is smaller than the size indicated by the Min Block + * Size Log state or is larger than the size indicated by the Max Block + * Size Log state. + */ + ESP_BLE_MESH_BLOB_ERR_INVALID_BLOCK_SIZE, + /** The chunk size exceeds the size indicated by the Max Chunk Size + * state, or the number of chunks exceeds the number specified by the + * Max Total Chunks state. + */ + ESP_BLE_MESH_BLOB_ERR_INVALID_CHUNK_SIZE, + /** The operation cannot be performed while the server is in the current + * phase. + */ + ESP_BLE_MESH_BLOB_ERR_WRONG_PHASE, + /** A parameter value in the message cannot be accepted. */ + ESP_BLE_MESH_BLOB_ERR_INVALID_PARAM, + /** The message contains a BLOB ID value that is not expected. */ + ESP_BLE_MESH_BLOB_ERR_WRONG_BLOB_ID, + /** There is not enough space available in memory to receive the BLOB. + */ + ESP_BLE_MESH_BLOB_ERR_BLOB_TOO_LARGE, + /** The transfer mode is not supported by the BLOB Transfer Server + * model. + */ + ESP_BLE_MESH_BLOB_ERR_UNSUPPORTED_MODE, + /** An internal error occurred on the node. */ + ESP_BLE_MESH_BLOB_ERR_INTERNAL, + /** The requested information cannot be provided while the server is in + * the current phase. + */ + ESP_BLE_MESH_BLOB_ERR_INFO_UNAVAILABLE, +} esp_ble_mesh_blob_status_t; + +/** BLOB Transfer Client state. */ +typedef enum esp_ble_mesh_blob_cli_state { + ESP_BLE_MESH_BLOB_CLI_STATE_NONE, /*!< No transfer is active. */ + ESP_BLE_MESH_BLOB_CLI_STATE_CAPS_GET, /*!< Retrieving transfer capabilities. */ + ESP_BLE_MESH_BLOB_CLI_STATE_START, /*!< Sending transfer start. */ + ESP_BLE_MESH_BLOB_CLI_STATE_BLOCK_START, /*!< Sending block start. */ + ESP_BLE_MESH_BLOB_CLI_STATE_BLOCK_SEND, /*!< Sending block chunks. */ + ESP_BLE_MESH_BLOB_CLI_STATE_BLOCK_CHECK, /*!< Checking block status. */ + ESP_BLE_MESH_BLOB_CLI_STATE_XFER_CHECK, /*!< Checking transfer status. */ + ESP_BLE_MESH_BLOB_CLI_STATE_CANCEL, /*!< Cancelling transfer. */ + ESP_BLE_MESH_BLOB_CLI_STATE_SUSPENDED, /*!< Transfer is suspended. */ + ESP_BLE_MESH_BLOB_CLI_STATE_XFER_PROGRESS_GET, /*!< Checking transfer progress. */ +} esp_ble_mesh_blob_cli_state_t; + +/** BLOB stream interaction mode. */ +typedef enum esp_ble_mesh_blob_io_mode { + ESP_BLE_MESH_BLOB_READ, /*!< Read data from the stream. */ + ESP_BLE_MESH_BLOB_WRITE, /*!< Write data to the stream. */ +} esp_ble_mesh_blob_io_mode_t; + +/** BLOB transfer. */ +struct esp_ble_mesh_blob_xfer { + uint64_t id; /*!< BLOB ID. */ + size_t size; /*!< Total BLOB size in bytes. */ + esp_ble_mesh_blob_xfer_mode_t mode; /*!< BLOB transfer mode. */ + uint8_t block_size_log; /*!< Logarithmic representation of the block size. */ + uint16_t chunk_size; /*!< Base chunk size. May be smaller for the last chunk. */ +}; + +/** BLOB transfer data block. */ +struct esp_ble_mesh_blob_block { + size_t size; /*!< Block size in bytes */ + off_t offset; /*!< Offset in bytes from the start of the BLOB. */ + uint16_t number; /*!< Block number */ + uint16_t chunk_count; /*!< Number of chunks in block. */ + uint8_t missing[DIV_ROUND_UP(CONFIG_BLE_MESH_BLOB_CHUNK_COUNT_MAX, + 8)]; /*!< Bitmap of missing chunks. */ +}; + +/** BLOB data chunk. */ +struct esp_ble_mesh_blob_chunk { + off_t offset; /*!< Offset of the chunk data from the start of the block. */ + size_t size; /*!< Chunk data size. */ + uint8_t *data; /*!< Chunk data. */ +}; + +/** BLOB stream. */ +struct esp_ble_mesh_blob_io { + /** @brief Open callback. + * + * Called when the reader is opened for reading. + * + * @param io BLOB stream. + * @param xfer BLOB transfer. + * @param mode Direction of the stream (read/write). + * + * @return 0 on success, or (negative) error code otherwise. + */ + int (*open)(const esp_ble_mesh_blob_io_t *io, + const esp_ble_mesh_blob_xfer_t *xfer, + esp_ble_mesh_blob_io_mode_t mode); + + /** @brief Close callback. + * + * Called when the reader is closed. + * + * @param io BLOB stream. + * @param xfer BLOB transfer. + */ + void (*close)(const esp_ble_mesh_blob_io_t *io, + const esp_ble_mesh_blob_xfer_t *xfer); + + /** @brief Block start callback. + * + * Called when a new block is opened for sending. Each block is only + * sent once, and are always sent in increasing order. The data chunks + * inside a single block may be requested out of order and multiple + * times. + * + * @param io BLOB stream. + * @param xfer BLOB transfer. + * @param block Block that was started. + */ + int (*block_start)(const esp_ble_mesh_blob_io_t *io, + const esp_ble_mesh_blob_xfer_t *xfer, + const esp_ble_mesh_blob_block_t *block); + + /** @brief Block end callback. + * + * Called when the current block has been transmitted in full. + * No data from this block will be requested again, and the application + * data associated with this block may be discarded. + * + * @param io BLOB stream. + * @param xfer BLOB transfer. + * @param block Block that finished sending. + */ + void (*block_end)(const esp_ble_mesh_blob_io_t *io, + const esp_ble_mesh_blob_xfer_t *xfer, + const esp_ble_mesh_blob_block_t *block); + + /** @brief Chunk data write callback. + * + * Used by the BLOB Transfer Server on incoming data. + * + * Each block is divided into chunks of data. This callback is called + * when a new chunk of data is received. Chunks may be received in + * any order within their block. + * + * If the callback returns successfully, this chunk will be marked as + * received, and will not be received again unless the block is + * restarted due to a transfer suspension. If the callback returns a + * non-zero value, the chunk remains unreceived, and the BLOB Transfer + * Client will attempt to resend it later. + * + * Note that the Client will only perform a limited number of attempts + * at delivering a chunk before dropping a Target node from the transfer. + * The number of retries performed by the Client is implementation + * specific. + * + * @param io BLOB stream. + * @param xfer BLOB transfer. + * @param block Block the chunk is part of. + * @param chunk Received chunk. + * + * @return 0 on success, or (negative) error code otherwise. + */ + int (*write)(const esp_ble_mesh_blob_io_t *io, + const esp_ble_mesh_blob_xfer_t *xfer, + const esp_ble_mesh_blob_block_t *block, + const esp_ble_mesh_blob_chunk_t *chunk); + + /** @brief Chunk data read callback. + * + * Used by the BLOB Transfer Client to fetch outgoing data. + * + * The Client calls the chunk data request callback to populate a chunk + * message going out to the Target nodes. The data request callback + * may be called out of order and multiple times for each offset, and + * cannot be used as an indication of progress. + * + * Returning a non-zero status code on the chunk data request callback + * results in termination of the transfer. + * + * @param io BLOB stream. + * @param xfer BLOB transfer. + * @param block Block the chunk is part of. + * @param chunk Chunk to get the data of. The buffer pointer to by the + * @c data member should be filled by the callback. + * + * @return 0 on success, or (negative) error code otherwise. + */ + int (*read)(const esp_ble_mesh_blob_io_t *io, + const esp_ble_mesh_blob_xfer_t *xfer, + const esp_ble_mesh_blob_block_t *block, + const esp_ble_mesh_blob_chunk_t *chunk); +}; + +/** @brief BLOB Transfer Server model event handlers. + * + * All callbacks are optional. + */ +struct esp_ble_mesh_blob_srv_cb { + /** @brief Transfer start callback. + * + * Called when the transfer has started with the prepared BLOB ID. + * + * @param srv BLOB Transfer Server instance. + * @param ctx Message context for the incoming start message. The + * entire transfer will be sent from the same source + * address. + * @param xfer Transfer parameters. + * + * @return 0 on success, or (negative) error code to reject the + * transfer. + */ + int (*start)(esp_ble_mesh_blob_srv_t *srv, esp_ble_mesh_msg_ctx_t *ctx, + esp_ble_mesh_blob_xfer_t *xfer); + + /** @brief Transfer end callback. + * + * Called when the transfer ends, either because it was cancelled, or + * because it finished successfully. A new transfer may be prepared. + * + * @note The transfer may end before it's started if the start + * parameters are invalid. + * + * @param srv BLOB Transfer Server instance. + * @param id BLOB ID of the cancelled transfer. + * @param success Whether the transfer was successful. + */ + void (*end)(esp_ble_mesh_blob_srv_t *srv, uint64_t id, bool success); + + /** @brief Transfer suspended callback. + * + * Called if the Server timed out while waiting for a transfer packet. + * A suspended transfer may resume later from the start of the current + * block. Any received chunks in the current block should be discarded, + * they will be received again if the transfer resumes. + * + * The transfer will call @c resumed again when resuming. + * + * @note The BLOB Transfer Server does not run a timer in the suspended state, + * and it's up to the application to determine whether the + * transfer should be permanently cancelled. Without interaction, + * the transfer will be suspended indefinitely, and the BLOB Transfer + * Server will not accept any new transfers. + * + * @param srv BLOB Transfer Server instance. + */ + void (*suspend)(esp_ble_mesh_blob_srv_t *srv); + + /** @brief Transfer resume callback. + * + * Called if the transfer is resumed after being suspended. + * + * @param srv BLOB Transfer Server instance. + */ + void (*resume)(esp_ble_mesh_blob_srv_t *srv); + + /** @brief Transfer recovery callback. + * + * Called when the Bluetooth mesh subsystem is started if the device is rebooted + * in the middle of a transfer. + * + * Transfers will not be resumed after a reboot if this callback is not + * defined. + * + * @param srv BLOB Transfer Server instance. + * @param xfer Transfer to resume. + * @param io BLOB stream return parameter. Must be set to a valid + * BLOB stream by the callback. + * + * @return 0 on success, or (negative) error code to abandon the + * transfer. + */ + int (*recover)(esp_ble_mesh_blob_srv_t *srv, + esp_ble_mesh_blob_xfer_t *xfer, + esp_ble_mesh_blob_io_t **io); +}; + +#if defined(CONFIG_BLE_MESH_BLOB_SRV) +#define ESP_BLE_MESH_BLOB_BLOCKS_MAX \ + (DIV_ROUND_UP(CONFIG_BLE_MESH_BLOB_SIZE_MAX, \ + CONFIG_BLE_MESH_BLOB_BLOCK_SIZE_MIN)) +#else +#define ESP_BLE_MESH_BLOB_BLOCKS_MAX 1 +#endif + +/** @brief BLOB Transfer Server instance. */ +struct esp_ble_mesh_blob_srv { + /** Event handler callbacks. */ + esp_ble_mesh_blob_srv_cb_t *cb; + + /** Runtime state: */ + esp_ble_mesh_blob_io_t *io; + struct k_delayed_work rx_timeout; + esp_ble_mesh_blob_block_t block; + esp_ble_mesh_model_t *mod; + esp_ble_mesh_blob_xfer_phase_t phase; + + /** State of blob serber. */ + struct esp_ble_mesh_blob_srv_state { + esp_ble_mesh_blob_xfer_t xfer; + uint16_t cli; + uint16_t app_idx; + uint16_t timeout_base; + uint16_t mtu_size; + uint8_t ttl; + + /* Bitfield of pending blocks. */ + BLE_MESH_ATOMIC_DEFINE(blocks, ESP_BLE_MESH_BLOB_BLOCKS_MAX); + } state; + + /* Pull mode (Pull BLOB Transfer Mode) behavior. */ + struct { + uint16_t chunk_idx; + struct k_delayed_work report; + } pull; +}; + +/** Target node's Pull mode (Pull BLOB Transfer Mode) context used + * while sending chunks to the Target node. + */ +struct esp_ble_mesh_blob_target_pull { + int64_t block_report_timestamp; /*!< Timestamp when the Block Report Timeout Timer expires for this Target node. */ + uint8_t missing[DIV_ROUND_UP(CONFIG_BLE_MESH_BLOB_CHUNK_COUNT_MAX, 8)]; /*!< Missing chunks reported by this Target node. */ +}; + +/** BLOB Transfer Client Target node. */ +struct esp_ble_mesh_blob_target { + sys_snode_t n; /*!< Linked list node */ + + uint16_t addr; /*!< Target node address. */ + + /** Target node's Pull mode context. + * Needs to be initialized when sending a BLOB in Pull mode. + */ + esp_ble_mesh_blob_target_pull_t *pull; + + uint8_t status; /*!< BLOB transfer status, see esp_ble_mesh_blob_status. */ + + uint8_t procedure_complete: 1, /*!< Procedure has been completed. */ + acked: 1, /*!< Message has been acknowledged. Not used when sending. */ + timedout: 1, /*!< Target node didn't respond after specified timeout. */ + skip: 1; /*!< Skip Target node from broadcast. */ +}; + + +/** BLOB transfer information. */ +struct esp_ble_mesh_blob_xfer_info { + esp_ble_mesh_blob_status_t status; /*!< BLOB transfer status. */ + esp_ble_mesh_blob_xfer_mode_t mode; /*!< BLOB transfer mode. */ + esp_ble_mesh_blob_xfer_phase_t phase; /*!< BLOB transfer phase. */ + uint64_t id; /*!< BLOB ID. */ + uint32_t size; /*!< BLOB size in octets. */ + uint8_t block_size_log; /*!< Logarithmic representation of the block size. */ + uint16_t mtu_size; /*!< MTU size in octets. */ + const uint8_t *missing_blocks; /*!< Bit field indicating blocks that were not received. */ +}; + + +/** BLOB Transfer Client transfer inputs. */ +struct esp_ble_mesh_blob_cli_inputs { + /** Linked list of Target nodes. Each node should point to + * esp_ble_mesh_blob_target_t::n. + */ + sys_slist_t targets; + + /** AppKey index to send with. */ + uint16_t app_idx; + + /** Group address destination for the BLOB transfer, or + * ESP_BLE_MESH_ADDR_UNASSIGNED to send every message to each Target + * node individually. + */ + uint16_t group; + + /** Time to live value of BLOB transfer messages. */ + uint8_t ttl; + + /** Additional response time for the Target nodes, in 10-second increments. + * + * The extra time can be used to give the Target nodes more time to respond + * to messages from the Client. The actual timeout will be calculated + * according to the following formula: + * + * @verbatim + * timeout = 20 seconds + (10 seconds * timeout_base) + (100 ms * TTL) + * @endverbatim + * + * If a Target node fails to respond to a message from the Client within the + * configured transfer timeout, the Target node is dropped. + */ + uint16_t timeout_base; +}; + +/** Transfer capabilities of a Target node. */ +struct esp_ble_mesh_blob_cli_caps { + size_t max_size; /*!< Max BLOB size. */ + uint8_t min_block_size_log; /*!< Logarithmic representation of the minimum block size. */ + uint8_t max_block_size_log; /*!< Logarithmic representation of the maximum block size. */ + uint16_t max_chunks; /*!< Max number of chunks per block. */ + uint16_t max_chunk_size; /*!< Max chunk size. */ + uint16_t mtu_size; /*!< Max MTU size. */ + esp_ble_mesh_blob_xfer_mode_t modes; /*!< Supported transfer modes. */ +}; + +/** Event handler callbacks for the BLOB Transfer Client model. + * + * All handlers are optional. + */ +struct esp_ble_mesh_blob_cli_cb { + /** @brief Capabilities retrieval completion callback. + * + * Called when the capabilities retrieval procedure completes, indicating that + * a common set of acceptable transfer parameters have been established + * for the given list of Target nodes. All compatible Target nodes have + * status code ESP_BLE_MESH_BLOB_SUCCESS. + * + * @param cli BLOB Transfer Client instance. + * @param caps Safe transfer capabilities if the transfer capabilities + * of at least one Target node has satisfied the Client, or NULL otherwise. + */ + void (*caps)(esp_ble_mesh_blob_cli_t *cli, + const esp_ble_mesh_blob_cli_caps_t *caps); + + /** @brief Target node loss callback. + * + * Called whenever a Target node has been lost due to some error in the + * transfer. Losing a Target node is not considered a fatal error for + * the Client until all Target nodes have been lost. + * + * @param cli BLOB Transfer Client instance. + * @param target Target node that was lost. + * @param reason Reason for the Target node loss. + */ + void (*lost_target)(esp_ble_mesh_blob_cli_t *cli, + esp_ble_mesh_blob_target_t *target, + esp_ble_mesh_blob_status_t reason); + + /** @brief Transfer is suspended. + * + * Called when the transfer is suspended due to response timeout from all Target nodes. + * + * @param cli BLOB Transfer Client instance. + */ + void (*suspend)(esp_ble_mesh_blob_cli_t *cli); + + /** @brief Transfer end callback. + * + * Called when the transfer ends. + * + * @param cli BLOB Transfer Client instance. + * @param xfer Completed transfer. + * @param success Status of the transfer. + * Is @c true if at least one Target + * node received the whole transfer. + */ + void (*end)(esp_ble_mesh_blob_cli_t *cli, + const esp_ble_mesh_blob_xfer_t *xfer, bool success); + + /** @brief Transfer progress callback + * + * The content of @c info is invalidated upon exit from the callback. + * Therefore it needs to be copied if it is planned to be used later. + * + * @param cli BLOB Transfer Client instance. + * @param target Target node that responded to the request. + * @param info BLOB transfer information. + */ + void (*xfer_progress)(esp_ble_mesh_blob_cli_t *cli, + esp_ble_mesh_blob_target_t *target, + const esp_ble_mesh_blob_xfer_info_t *info); + + /** @brief End of Get Transfer Progress procedure. + * + * Called when all Target nodes have responded or the procedure timed-out. + * + * @param cli BLOB Transfer Client instance. + */ + void (*xfer_progress_complete)(esp_ble_mesh_blob_cli_t *cli); +}; + +/** @cond INTERNAL_HIDDEN */ +typedef struct { + /** Called for every Target node in unicast mode, or once in case of multicast mode. */ + void (*send)(esp_ble_mesh_blob_cli_t *cli, uint16_t dst); + /** Called after every @ref blob_cli_broadcast_ctx::send callback. */ + void (*send_complete)(esp_ble_mesh_blob_cli_t *cli, uint16_t dst); + /** If @ref blob_cli_broadcast_ctx::acked is true, called after all Target nodes + * have confirmed reception by @ref blob_cli_broadcast_rsp. Otherwise, called + * after transmission has been completed. + */ + void (*next)(esp_ble_mesh_blob_cli_t *cli); + /** If true, every transmission needs to be confirmed by @ref blob_cli_broadcast_rsp before + * @ref blob_cli_broadcast_ctx::next is called. + */ + bool acked; + /** If true, the message is always sent in a unicast way. */ + bool force_unicast; + /** If true, non-responsive Target nodes won't be dropped after transfer has timed out. */ + bool optional; + /** Set to true by the BLOB Transfer Client between blob_cli_broadcast + * and broadcast_complete calls. + */ + bool is_inited; + /* Defines a time in ms by which the broadcast API postpones sending the message to a next + * target or completing the broadcast. + */ + uint32_t post_send_delay_ms; +} esp_blob_cli_broadcast_ctx_t; +/** INTERNAL_HIDDEN @endcond */ + +/** + * @brief BLOB Transfer Client model instance. + * @note Preview version, the contents of this struct may change in the future. + */ +struct esp_ble_mesh_blob_cli { + /** Event handler callbacks */ + const esp_ble_mesh_blob_cli_cb_t *cb; + + /** Runtime state */ + const esp_ble_mesh_model_t *mod; + + /** Firmware transfer information */ + struct { + esp_ble_mesh_blob_target_t *target; + esp_blob_cli_broadcast_ctx_t ctx; + struct k_delayed_work retry; + /* Represents Client Timeout timer in a timestamp. Used in Pull mode only. */ + int64_t cli_timestamp; + struct k_work complete; + uint16_t pending; + uint8_t retries; + uint8_t sending : 1, + cancelled : 1; + } tx; + + /** Firmware IO operation callbacks */ + const esp_ble_mesh_blob_io_t *io; + + /** BLOB Transfer Client transfer inputs. */ + const esp_ble_mesh_blob_cli_inputs_t *inputs; + + /** BLOB transfer. */ + const esp_ble_mesh_blob_xfer_t *xfer; + + /** Interval between chunk transmissions, in milliseconds. */ + uint32_t chunk_interval_ms; + + /** BLOCK count */ + uint16_t block_count; + + /** CHUNK index */ + uint16_t chunk_idx; + + /** Max Transfer Unit Size of BLOB Transfer */ + uint16_t mtu_size; + + /** State of blob client mode transfer */ + esp_ble_mesh_blob_cli_state_t state; + + /** BLOB transfer data block. */ + esp_ble_mesh_blob_block_t block; + + /** Transfer capabilities of a Target node. */ + esp_ble_mesh_blob_cli_caps_t caps; +}; + +#ifdef __cplusplus +} +#endif + +#endif /* _ESP_BLE_MESH_BLOB_MODEL_API_H_ */ diff --git a/components/bt/esp_ble_mesh/v1.1/api/models/include/esp_ble_mesh_dfu_model_api.h b/components/bt/esp_ble_mesh/v1.1/api/models/include/esp_ble_mesh_dfu_model_api.h new file mode 100644 index 0000000000..bf91d9e06f --- /dev/null +++ b/components/bt/esp_ble_mesh/v1.1/api/models/include/esp_ble_mesh_dfu_model_api.h @@ -0,0 +1,999 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef _ESP_BLE_MESH_DFU_MODEL_API_H_ +#define _ESP_BLE_MESH_DFU_MODEL_API_H_ + +#include "esp_ble_mesh_defs.h" +#include "esp_ble_mesh_blob_model_api.h" +#include "esp_ble_mesh_dfu_slot_api.h" + +#if CONFIG_BLE_MESH_DFU_CLI || CONFIG_BLE_MESH_DFU_SRV +#ifndef _BLE_MESH_BLOB_DEPRECATE_WARN +#define _BLE_MESH_BLOB_DEPRECATE_WARN +#warning Please note: All APIs published in this document are in Preview version and may undergo significant changes in the future. +#endif +#endif /* CONFIG_BLE_MESH_DFU_CLI || CONFIG_BLE_MESH_DFU_SRV */ + +#ifdef __cplusplus +extern "C" { +#endif + +#define ESP_BLE_MESH_DFU_OP_UPDATE_INFO_GET ESP_BLE_MESH_MODEL_OP_2(0x83, 0x08) +#define ESP_BLE_MESH_DFU_OP_UPDATE_INFO_STATUS ESP_BLE_MESH_MODEL_OP_2(0x83, 0x09) + +#define ESP_BLE_MESH_DFU_OP_UPDATE_METADATA_CHECK ESP_BLE_MESH_MODEL_OP_2(0x83, 0x0a) +#define ESP_BLE_MESH_DFU_OP_UPDATE_METADATA_STATUS ESP_BLE_MESH_MODEL_OP_2(0x83, 0x0b) + +#define ESP_BLE_MESH_DFU_OP_UPDATE_GET ESP_BLE_MESH_MODEL_OP_2(0x83, 0x0c) +#define ESP_BLE_MESH_DFU_OP_UPDATE_START ESP_BLE_MESH_MODEL_OP_2(0x83, 0x0d) +#define ESP_BLE_MESH_DFU_OP_UPDATE_CANCEL ESP_BLE_MESH_MODEL_OP_2(0x83, 0x0e) +#define ESP_BLE_MESH_DFU_OP_UPDATE_APPLY ESP_BLE_MESH_MODEL_OP_2(0x83, 0x0f) +#define ESP_BLE_MESH_DFU_OP_UPDATE_STATUS ESP_BLE_MESH_MODEL_OP_2(0x83, 0x10) + +#if CONFIG_BLE_MESH_DFU_CLI +/** @def ESP_BLE_MESH_MODEL_DFU_CLI + * + * @brief Define a new DFU Client model. + * + * @note This API needs to be called for each element on which + * the application needs to have a DFU Client model. + * + * @param cli_pub Pointer to the unique struct esp_ble_mesh_model_pub_t. + * @param cli_data Pointer to the unique struct esp_ble_mesh_client_t. + * + * @return New DFU Client model instance. + */ +#define ESP_BLE_MESH_MODEL_DFU_CLI(cli_pub, cli_data) \ + ESP_BLE_MESH_MODEL_BLOB_CLI(NULL, &(cli_data)->blob), \ + ESP_BLE_MESH_SIG_MODEL(ESP_BLE_MESH_MODEL_ID_DFU_CLI, \ + NULL, cli_pub, cli_data) + + +/** @def ESP_BLE_MESH_DFU_CLI_INIT + * + * @brief Initialize a DFU Client model instance. + * + * @note This macro is used to initialize the DFU Client model. + * It sets the callback handlers for the DFU Client. + * + * @param _handlers Pointer to the callback handler structure (esp_ble_mesh_dfu_cli_cb_t). + * + * @return An initialized DFU Client model instance. + */ +#define ESP_BLE_MESH_DFU_CLI_INIT(_handlers) \ +{ \ + .cb = _handlers, \ +} +#endif /* CONFIG_BLE_MESH_DFU_CLI */ + +#if CONFIG_BLE_MESH_DFU_SRV +/** @def ESP_BLE_MESH_MODEL_DFU_SRV + * + * @brief Define a new DFU Server model. + * + * @note This API needs to be called for each element on which + * the application needs to have a DFU Server model. + * + * @param srv_pub Pointer to the unique struct esp_ble_mesh_model_pub_t. + * @param srv_data Pointer to the unique struct esp_ble_mesh_dfu_trans_srv_t. + * + * @return New DFU Server model instance. + */ +#define ESP_BLE_MESH_MODEL_DFU_SRV(srv_pub, srv_data) \ + ESP_BLE_MESH_MODEL_BLOB_SRV(NULL, &(srv_data)->blob), \ + ESP_BLE_MESH_SIG_MODEL(ESP_BLE_MESH_MODEL_ID_DFU_SRV, \ + NULL, srv_pub, srv_data) + + +/** @def ESP_BLE_MESH_DFU_SRV_INIT + * + * @brief Initialize a DFU Server model instance. + * + * @note This macro is used to initialize the DFU Server model. + * It sets the callback handlers and image information for the DFU Server. + * + * @param _handlers Pointer to the callback handler structure (esp_ble_mesh_dfu_srv_cb_t). + * @param _imgs Pointer to the array of DFU images (esp_ble_mesh_dfu_img_t). + * @param _img_count Number of DFU images in the array. + * + * @return An initialized DFU Server model instance. + */ +#define ESP_BLE_MESH_DFU_SRV_INIT(_handlers, _imgs, _img_count) \ + { \ + .cb = _handlers, \ + .imgs = _imgs, .img_count = _img_count, \ + } +#endif /* CONFIG_BLE_MESH_DFU_SRV */ + +/** @brief DFU image structure. **/ +typedef struct esp_ble_mesh_dfu_img esp_ble_mesh_dfu_img_t; + +#if CONFIG_BLE_MESH_DFU_CLI +/** @brief DFU Client model instance structure. + * + * This structure represents a DFU (Device Firmware Update) Client model instance. + * It contains the necessary fields for managing DFU operations as a client. + * Should be initialized with ESP_BLE_MESH_DFU_CLI_INIT. + * + * @note Preview version, the contents of this struct may change in the future. + */ +typedef struct esp_ble_mesh_dfu_cli esp_ble_mesh_dfu_cli_t; + +/** @brief DFU iteration action enumeration. + * + * This enumeration defines the possible actions that can be taken + * during DFU iteration callbacks. It controls whether to continue + * or stop iterating through DFU images. + */ +typedef enum esp_ble_mesh_dfu_iter esp_ble_mesh_dfu_iter_t; + +/** @brief DFU image callback. + * + * The image callback is called for every DFU image on the Target node when + * calling bt_mesh_dfu_cli_imgs_get. + * + * @param cli Firmware Update Client model instance. + * @param ctx Message context of the received message. + * @param idx Image index. + * @param total Total number of images on the Target node. + * @param img Image information for the given image index. + * @param cb_data Callback data. + * + * @retval ESP_BLE_MESH_DFU_ITER_STOP Stop iterating through the image list and + * return from bt_mesh_dfu_cli_imgs_get. + * @retval ESP_BLE_MESH_DFU_ITER_CONTINUE Continue iterating through the image list + * if any images remain. + */ +typedef esp_ble_mesh_dfu_iter_t (*esp_ble_mesh_dfu_img_cb_t)(esp_ble_mesh_dfu_cli_t *cli, + esp_ble_mesh_msg_ctx_t *ctx, uint8_t idx, + uint8_t total, const esp_ble_mesh_dfu_img_t *img, + void *cb_data); +/** @brief DFU image slot structure. + * + * This structure represents a DFU image slot used for DFU distribution. + * It contains information about the firmware image including size, + * firmware ID, metadata, and their respective lengths. + */ +typedef struct esp_ble_mesh_dfu_slot esp_ble_mesh_dfu_slot_t; + +/** @brief DFU Target node structure. + * + * This structure represents a DFU Target node in the mesh network. + * It contains information about the target's BLOB transfer status, + * image index, expected DFU effect, current status, and phase. + */ +typedef struct esp_ble_mesh_dfu_target esp_ble_mesh_dfu_target_t; + +/** @brief DFU Client callback structure. + * + * This structure contains callback function pointers for DFU Client events + * such as transfer suspension, completion, application, confirmation, + * and target node loss. + */ +typedef struct esp_ble_mesh_dfu_cli_cb esp_ble_mesh_dfu_cli_cb_t; + +/** @brief DFU Client model instance structure. + * + * This structure represents a DFU (Device Firmware Update) Client model instance. + * It contains the necessary fields for managing DFU operations as a client. + * Should be initialized with ESP_BLE_MESH_DFU_CLI_INIT. + * + * @note Preview version, the contents of this struct may change in the future. + */ +typedef struct esp_ble_mesh_dfu_cli esp_ble_mesh_dfu_cli_t; + +/** @brief DFU Client BLOB transfer parameters structure. + * + * This structure contains parameters for BLOB transfer during DFU operations, + * including block size logarithmic representation and base chunk size. + */ +typedef struct esp_ble_mesh_dfu_cli_xfer_blob_params esp_ble_mesh_dfu_cli_xfer_blob_params_t; + +/** @brief DFU Client transfer parameters structure. + * + * This structure contains parameters for DFU transfer operations, + * including BLOB ID, image slot, transfer mode, and BLOB parameters. + */ +typedef struct esp_ble_mesh_dfu_cli_xfer esp_ble_mesh_dfu_cli_xfer_t; + +/** @brief Device Firmware Update Info Get parameters structure. + * + * This structure contains parameters for the Firmware Update Information Get message, + * including the first index and entries limit for requesting firmware information. + */ +typedef struct esp_ble_mesh_device_firmware_update_info_get esp_ble_mesh_device_firmware_update_info_get_t; + +/** @brief Device Firmware Update Metadata Check parameters structure. + * + * This structure contains parameters for the Firmware Update Metadata Check message, + * including the update firmware index and vendor-specific metadata. + */ +typedef struct esp_ble_mesh_device_firmware_update_metadata_check esp_ble_mesh_device_firmware_update_metadata_check_t; + +/** @brief DFU Client get message union. + * + * This union contains different parameter structures for various DFU Client + * get messages, allowing type-safe access to message-specific parameters. + */ +typedef union esp_ble_mesh_dfu_client_get esp_ble_mesh_dfu_client_get_t; + +/** @brief DFU Client send arguments structure. + * + * This structure contains arguments for sending firmware via DFU Client, + * including the client instance, transfer inputs, BLOB stream, and transfer parameters. + */ +typedef struct esp_ble_mesh_dfu_cli_send_arg esp_ble_mesh_dfu_cli_send_arg_t; + +/** @brief Firmware information structure. + * + * This structure contains information about firmware including firmware ID + * and update URI for retrieving new firmware. + */ +typedef struct esp_ble_mesh_firmware_info esp_ble_mesh_firmware_info_t; + +/** @brief Device Firmware Update Info Status response structure. + * + * This structure contains the response parameters for Firmware Update Information + * Status message, including firmware information list count and details. + */ +typedef struct esp_ble_mesh_device_firmware_update_info_status esp_ble_mesh_device_firmware_update_info_status_t; + +/** @brief Device Firmware Update Metadata Check Status response structure. + * + * This structure contains the response parameters for Firmware Update Metadata + * Check Status message, including status code and additional information. + */ +typedef struct esp_ble_mesh_device_firmware_metadata_check_status esp_ble_mesh_device_firmware_metadata_check_status_t; + +/** @brief Device Firmware Update Status response structure. + * + * This structure contains the response parameters for various Firmware Update + * Status messages (Get, Start, Cancel, Apply), including status, phase, + * TTL, timeout base, BLOB ID, and firmware image index. + */ +typedef struct esp_ble_mesh_device_firmware_update_status esp_ble_mesh_device_firmware_update_status_t; + +/** @brief DFU Client send callback structure. + * + * This structure contains the result of sending DFU Client messages, + * including error code indicating success or failure. + */ +typedef struct esp_ble_mesh_dfu_client_send_cb esp_ble_mesh_dfu_client_send_cb_t; + +/** @brief DFU Client receive callback union. + * + * This union contains different response structures for various DFU Client + * received status messages, allowing type-safe access to message-specific responses. + */ +typedef union esp_ble_mesh_dfu_client_recv_cb esp_ble_mesh_dfu_client_recv_cb_t; + +/** @brief DFU Client callback parameters structure. + * + * This structure contains parameters for DFU Client callbacks, including + * common client parameters and a union of send/receive callback data. + */ +typedef struct esp_ble_mesh_dfu_client_cb_param esp_ble_mesh_dfu_client_cb_param_t; +#endif /* CONFIG_BLE_MESH_DFU_CLI */ + +#if CONFIG_BLE_MESH_DFU_SRV +/** @brief Firmware Update Server instance structure. + * + * This structure represents a DFU (Device Firmware Update) Server model instance. + * It contains the necessary fields for managing DFU operations as a server. + * Should be initialized with ESP_BLE_MESH_DFU_SRV_INIT. + * + * @note Preview version, the contents of this struct may change in the future. + */ +typedef struct esp_ble_mesh_dfu_srv esp_ble_mesh_dfu_srv_t; + +/** @brief Firmware Update Server callback structure. + * + * This structure contains callback function pointers for DFU Server events + * such as transfer check, start, end, recovery, and apply. + */ +typedef struct esp_ble_mesh_dfu_srv_cb esp_ble_mesh_dfu_srv_cb_t; +#endif /* CONFIG_BLE_MESH_DFU_SRV */ + +/** DFU transfer phase. */ +typedef enum esp_ble_mesh_dfu_phase { + /** Ready to start a Receive Firmware procedure. */ + ESP_BLE_MESH_DFU_PHASE_IDLE, + + /** The Transfer BLOB procedure failed. */ + ESP_BLE_MESH_DFU_PHASE_TRANSFER_ERR, + + /** The Receive Firmware procedure is being executed. */ + ESP_BLE_MESH_DFU_PHASE_TRANSFER_ACTIVE, + + /** The Verify Firmware procedure is being executed. */ + ESP_BLE_MESH_DFU_PHASE_VERIFY, + + /** The Verify Firmware procedure completed successfully. */ + ESP_BLE_MESH_DFU_PHASE_VERIFY_OK, + + /** The Verify Firmware procedure failed. */ + ESP_BLE_MESH_DFU_PHASE_VERIFY_FAIL, + + /** The Apply New Firmware procedure is being executed. */ + ESP_BLE_MESH_DFU_PHASE_APPLYING, + + /** Firmware transfer has been canceled. */ + ESP_BLE_MESH_DFU_PHASE_TRANSFER_CANCELED, + + /** Firmware applying succeeded. */ + ESP_BLE_MESH_DFU_PHASE_APPLY_SUCCESS, + + /** Firmware applying failed. */ + ESP_BLE_MESH_DFU_PHASE_APPLY_FAIL, + + /** Phase of a node was not yet retrieved. */ + ESP_BLE_MESH_DFU_PHASE_UNKNOWN, +} esp_ble_mesh_dfu_phase_t; + +/** Expected effect of a DFU transfer. */ +typedef enum esp_ble_mesh_dfu_effect { + /** No changes to node Composition Data. */ + ESP_BLE_MESH_DFU_EFFECT_NONE, + + /** Node Composition Data changed and the node does not support remote + * provisioning. + */ + ESP_BLE_MESH_DFU_EFFECT_COMP_CHANGE_NO_RPR, + + /** Node Composition Data changed, and remote provisioning is supported. + * The node supports remote provisioning and Composition Data Page + * 0x80. Page 0x80 contains different Composition Data than Page 0x0. + */ + ESP_BLE_MESH_DFU_EFFECT_COMP_CHANGE, + + /** Node will be unprovisioned after the update. */ + ESP_BLE_MESH_DFU_EFFECT_UNPROV, +} esp_ble_mesh_dfu_effect_t; + +/** DFU status. */ +typedef enum esp_ble_mesh_dfu_status { + /** The message was processed successfully. */ + ESP_BLE_MESH_DFU_SUCCESS, + + /** Insufficient resources on the node */ + ESP_BLE_MESH_DFU_ERR_RESOURCES, + + /** The operation cannot be performed while the Server is in the current + * phase. + */ + ESP_BLE_MESH_DFU_ERR_WRONG_PHASE, + + /** An internal error occurred on the node. */ + ESP_BLE_MESH_DFU_ERR_INTERNAL, + + /** The message contains a firmware index value that is not expected. */ + ESP_BLE_MESH_DFU_ERR_FW_IDX, + + /** The metadata check failed. */ + ESP_BLE_MESH_DFU_ERR_METADATA, + + /** The Server cannot start a firmware update. */ + ESP_BLE_MESH_DFU_ERR_TEMPORARILY_UNAVAILABLE, + + /** Another BLOB transfer is in progress. */ + ESP_BLE_MESH_DFU_ERR_BLOB_XFER_BUSY, +} esp_ble_mesh_dfu_status_t; + +#if CONFIG_BLE_MESH_DFU_CLI +/** Action for DFU iteration callbacks. */ +enum esp_ble_mesh_dfu_iter { + /** Stop iterating. */ + ESP_BLE_MESH_DFU_ITER_STOP, + + /** Continue iterating. */ + ESP_BLE_MESH_DFU_ITER_CONTINUE, +}; + +/** @brief DFU transfer states + * + * This enumeration defines the various states of a DFU (Device Firmware Update) transfer. + * These states track the progress of firmware distribution from idle state through + * transfer, verification, application, and confirmation phases. + */ +typedef enum esp_ble_mesh_dfu_xfer_state { + /** Idle state - No DFU transfer is in progress. */ + ESP_BLE_MESH_DFU_XFER_IDLE, + + /** Transfer state - Firmware data is being transferred to target nodes. */ + ESP_BLE_MESH_DFU_XFER_TRANSFER, + + /** Refresh state - Transfer is paused or being refreshed. */ + ESP_BLE_MESH_DFU_XFER_REFRESH, + + /** Verified state - Firmware has been successfully verified on target nodes. */ + ESP_BLE_MESH_DFU_XFER_VERIFIED, + + /** Apply state - Firmware is being applied on target nodes. */ + ESP_BLE_MESH_DFU_XFER_APPLY, + + /** Applied state - Firmware has been successfully applied on target nodes. */ + ESP_BLE_MESH_DFU_XFER_APPLIED, + + /** Confirm state - Waiting for confirmation from target nodes. */ + ESP_BLE_MESH_DFU_XFER_CONFIRM, + + /** Cancel state - DFU transfer has been cancelled. */ + ESP_BLE_MESH_DFU_XFER_CANCEL, + + /** Suspended state - DFU transfer has been suspended. */ + ESP_BLE_MESH_DFU_XFER_SUSPENDED, +} esp_ble_mesh_dfu_xfer_state_t; + +/** @brief Callback events of Device Firmware Update Client Model + * + * This enumeration defines the various callback events that can be generated + * by the DFU (Device Firmware Update) Client model. These events are used + * to notify the application about different stages and states of the DFU process, + * including message sending, timeouts, response reception, and image transfer completion. + */ +typedef enum { + /** DFU Client message sending completed event. + * This event is triggered when a DFU Client message has been successfully sent. + */ + ESP_BLE_MESH_DFU_CLIENT_SEND_COMP_EVT, + + /** DFU Client timeout event. + * This event is triggered when a DFU Client operation times out waiting for a response. + */ + ESP_BLE_MESH_DFU_CLIENT_TIMEOUT_EVT, + + /** DFU Client received Get response event. + * This event is triggered when the DFU Client receives a response to a Get message. + */ + ESP_BLE_MESH_DFU_CLIENT_RECV_GET_RSP_EVT, + + /** DFU Client image send completed event. + * This event is triggered when the DFU Client has completed sending a firmware image. + */ + ESP_BLE_MESH_DFU_CLIENT_IMG_SEND_CMP_EVT, + + /** Maximum DFU Client event value (used for bounds checking). */ + ESP_BLE_MESH_DFU_CLIENT_EVT_MAX, +} esp_ble_mesh_dfu_client_cb_event_t; + +#endif /* CONFIG_BLE_MESH_DFU_CLI */ + +/** DFU image instance. + * + * Each DFU image represents a single updatable firmware image. + */ +struct esp_ble_mesh_dfu_img { + /** Firmware ID. */ + const void *fwid; + + /** Length of the firmware ID. */ + size_t fwid_len; + + /** Update URI, or NULL. */ + const char *uri; +}; + +#if CONFIG_BLE_MESH_DFU_CLI +/** DFU image slot for DFU distribution. */ +struct esp_ble_mesh_dfu_slot { + /** Size of the firmware in bytes. */ + size_t size; + /** Length of the firmware ID. */ + size_t fwid_len; + /** Length of the metadata. */ + size_t metadata_len; + /** Firmware ID. */ + uint8_t fwid[CONFIG_BLE_MESH_DFU_FWID_MAXLEN]; + /** Metadata. */ + uint8_t metadata[CONFIG_BLE_MESH_DFU_METADATA_MAXLEN]; +}; + +/** DFU Target node. */ +struct esp_ble_mesh_dfu_target { + /** BLOB Target node */ + esp_ble_mesh_blob_target_t blob; + /** Image index on the Target node */ + uint8_t img_idx; + /** Expected DFU effect, see bt_mesh_dfu_effect. */ + uint8_t effect; + /** Current DFU status, see bt_mesh_dfu_status. */ + uint8_t status; + /** Current DFU phase, see bt_mesh_dfu_phase. */ + uint8_t phase; +}; + +/** Firmware Update Client event callbacks. */ +struct esp_ble_mesh_dfu_cli_cb { + /** @brief BLOB transfer is suspended. + * + * Called when the BLOB transfer is suspended due to response timeout from all Target nodes. + * + * @param cli Firmware Update Client model instance. + */ + void (*suspended)(esp_ble_mesh_dfu_cli_t *cli); + + /** @brief DFU ended. + * + * Called when the DFU transfer ends, either because all Target nodes were + * lost or because the transfer was completed successfully. + * + * @param cli Firmware Update Client model instance. + * @param reason Reason for ending. + */ + void (*ended)(esp_ble_mesh_dfu_cli_t *cli, + esp_ble_mesh_dfu_status_t reason); + + /** @brief DFU transfer applied on all active Target nodes. + * + * Called at the end of the apply procedure. + * + * @param cli Firmware Update Client model instance. + */ + void (*applied)(esp_ble_mesh_dfu_cli_t *cli); + + /** @brief DFU transfer confirmed on all active Target nodes. + * + * Called at the end of the apply procedure. + * + * @param cli Firmware Update Client model instance. + */ + void (*confirmed)(esp_ble_mesh_dfu_cli_t *cli); + + /** @brief DFU Target node was lost. + * + * A DFU Target node was dropped from the receivers list. The Target node's + * @c status is set to reflect the reason for the failure. + * + * @param cli Firmware Update Client model instance. + * @param target DFU Target node that was lost. + */ + void (*lost_target)(esp_ble_mesh_dfu_cli_t *cli, + esp_ble_mesh_dfu_target_t *target); +}; + +/** Firmware Update Client model instance. + * Should be initialized with ESP_BLE_MESH_DFU_CLI_INIT. + * + * @note Preview version, the contents of this struct may change in the future. + */ +struct esp_ble_mesh_dfu_cli { + /** Callback structure. */ + const esp_ble_mesh_dfu_cli_cb_t *cb; + /** Underlying BLOB Transfer Client. */ + esp_ble_mesh_blob_cli_t blob; + + /** runtime state */ + uint32_t op; + + /** ble mesh model instance. */ + esp_ble_mesh_model_t *mod; + + /** BLOB Transfer information */ + struct { + const esp_ble_mesh_dfu_slot_t *slot; + const esp_ble_mesh_blob_io_t *io; + esp_ble_mesh_blob_xfer_t blob; + uint8_t state; + uint8_t flags; + } xfer; + + /** Current request */ + void *req; +}; + +/** BLOB parameters for Firmware Update Client transfer */ +struct esp_ble_mesh_dfu_cli_xfer_blob_params { + /** Logarithmic representation of the block size. */ + uint8_t block_size_log; + /** Base chunk size. May be smaller for the last chunk. */ + uint16_t chunk_size; +}; + +/** Firmware Update Client transfer parameters */ +struct esp_ble_mesh_dfu_cli_xfer { + /** BLOB ID to use for this transfer, or 0 to set it randomly. */ + uint64_t blob_id; + /** DFU image slot to transfer. */ + const esp_ble_mesh_dfu_slot_t *slot; + /** Transfer mode (Push (Push BLOB Transfer Mode) or Pull (Pull BLOB Transfer Mode)) */ + enum esp_ble_mesh_blob_xfer_mode mode; + /** BLOB parameters to be used for the transfer, or NULL to retrieve Target nodes' + * capabilities before sending a firmware. + */ + const esp_ble_mesh_dfu_cli_xfer_blob_params_t *blob_params; +}; + +/** + * @brief Parameters of Device Firmware Update Info Get. + */ +struct esp_ble_mesh_device_firmware_update_info_get { + uint8_t first_index; /*!< Index of the first requested entry from the Firmware Information List state */ + uint8_t entries_limit; /*!< Maximum number of entries that the server includes in a Firmware Update Information Status message */ + esp_ble_mesh_dfu_img_cb_t img_cb; /*!< Added by Espressif, callback function used to walkthrough recceived images */ +}; + +/** + * @brief Parameters of Device Firmware Update Firmware Metadata Check. + */ +struct esp_ble_mesh_device_firmware_update_metadata_check { + uint8_t update_firmware_index; /*!< Index of the firmware image in the Firmware Information List state to check */ + struct net_buf_simple *metadata; /*!< Vendor-specific metadata */ +}; + +/** + * @brief Device Firmware Update Client model get message union. + * For ESP_BLE_MESH_DFU_OP_UPDATE_INFO_GET + * ESP_BLE_MESH_DFU_OP_UPDATE_METADATA_CHECK + * ESP_BLE_MESH_DFU_OP_UPDATE_START + * the get_state parameter in the `esp_ble_mesh_dfu_client_get_state` function should not be set to NULL. + */ +union esp_ble_mesh_dfu_client_get { + esp_ble_mesh_device_firmware_update_info_get_t dfu_update_info_get; /*!< For ESP_BLE_MESH_DFU_OP_UPDATE_INFO_GET */ + esp_ble_mesh_device_firmware_update_metadata_check_t dfu_metadata_check; /*!< For ESP_BLE_MESH_DFU_OP_UPDATE_METADATA_CHECK */ +}; + +/** + * @brief Parameters of Device Firmware Update Client + * model to send Firmware. + */ +struct esp_ble_mesh_dfu_cli_send_arg { + esp_ble_mesh_dfu_cli_t *cli; /*!< Device Firmware Update Client model instance */ + esp_ble_mesh_blob_cli_inputs_t *inputs; /*!< BLOB Transfer Client transfer inputs. */ + esp_ble_mesh_blob_io_t *io; /*!< BLOB stream. */ + esp_ble_mesh_dfu_cli_xfer_t *xfer; /*!< Firmware Update Client transfer parameters */ +}; + +/** + * @brief Struct of firmware information. + */ +struct esp_ble_mesh_firmware_info { + struct net_buf_simple *fw_id; /*!< Firmware ID */ + struct net_buf_simple *update_uri; /*!< URI used to retrieve a new firmware */ +}; + +/** + * @brief Response of Firmware Update Information Get Message + */ +struct esp_ble_mesh_device_firmware_update_info_status { + uint8_t fw_info_list_cnt; /*!< The number of entries in the Firmware Information List */ + uint8_t first_index; /*!< Index of the first requested entry from the Firmware Information List */ + esp_ble_mesh_firmware_info_t *fw_info_list; /*!< List of firmware information entries */ +}; + +/** + * @brief Response of Firmware Update Firmware Metadata Check Message + */ +struct esp_ble_mesh_device_firmware_metadata_check_status { + uint8_t status : 3; /*!< Status Code from the firmware metadata check */ + uint8_t additional_info : 5; /*!< The Firmware Update Additional Information state from the Firmware Update Server (see Section 4.1.3)*/ + uint8_t update_firmware_image_index; /*!< Index of the firmware image in the Firmware Information List state that was checked*/ +}; + +/** + * @brief Response of Firmware Update Get Message, + * Firmware Update Start Message, + * Firmware Update Cancel Message, + * Firmware Update Apply Message. + */ +struct esp_ble_mesh_device_firmware_update_status { + uint8_t status : 3; /*!< Status Code for the requesting message */ + uint8_t update_phase : 3; /*!< The Update Phase state of the Firmware Update Server */ + uint8_t update_ttl; /*!< TTL value to use during firmware image transfer */ + uint8_t additional_info : 5; /*!< The Firmware Update Additional Information state from the Firmware Update Server (see Section 4.1.3)*/ + uint16_t update_timeout_base; /*!< Used to compute the timeout of the firmware image transfer */ + uint64_t update_blob_id; /*!< BLOB identifier for the firmware image */ + uint8_t update_fw_img_idx; /*!< The index of the firmware image in the Firmware Information List state being updated*/ +}; + +/** Result of sending Device Firmware Update Client messages */ +struct esp_ble_mesh_dfu_client_send_cb { + int err_code; /*!< Result of sending a message */ +}; + +/** + * @brief Device Firmware Update Client model received message union + */ +union esp_ble_mesh_dfu_client_recv_cb { + esp_ble_mesh_device_firmware_update_info_status_t dfu_update_info_status; /*!< ESP_BLE_MESH_MODEL_OP_DIRECTED_CONTROL_STATUS */ + esp_ble_mesh_device_firmware_metadata_check_status_t dfu_metadata_check_status; /*!< ESP_BLE_MESH_MODEL_OP_PATH_METRIC_STATUS */ + esp_ble_mesh_device_firmware_update_status_t dfu_firmware_update_status; /*!< ESP_BLE_MESH_MODEL_OP_DISCOVERY_TABLE_CAPS_STATUS */ +}; + +/** Device Firmware Update Client model callback parameters */ +struct esp_ble_mesh_dfu_client_cb_param { + esp_ble_mesh_client_common_param_t *params; /*!< Client common parameters, used by all events. */ + /** Union of DF Client callback */ + union { + esp_ble_mesh_dfu_client_send_cb_t send; /*!< Result of sending a message */ + esp_ble_mesh_dfu_client_recv_cb_t recv; /*!< Parameters of received status message */ + }; +}; + +/** + * @brief Bluetooth Mesh Device Firmware Update client and server model functions. + */ + +/** + * @brief Device Firmware Update Client model callback function type + * @param event: Event type + * @param param: Pointer to callback parameter + */ +typedef void (* esp_ble_mesh_dfu_client_cb_t)(esp_ble_mesh_dfu_client_cb_event_t event, + esp_ble_mesh_dfu_client_cb_param_t *param); + +/** + * @brief Device Firmware Update Server model callback function type + * @param event: Event type + * @param param: Pointer to callback parameter + */ +typedef void (* esp_ble_mesh_dfu_server_cb_t)(esp_ble_mesh_dfu_client_cb_event_t event, + esp_ble_mesh_dfu_client_cb_param_t *param); + +/** + * @brief Register BLE Mesh Device Firmware Update Client model callback. + * + * @param callback: Pointer to the callback function. + * + * @return ESP_OK on success or error code otherwise. + * + */ +esp_err_t esp_ble_mesh_register_dfu_client_callback(esp_ble_mesh_dfu_client_cb_t callback); + +/** + * @brief Get the value of Device Firmware Server model state with the corresponding get message. + * + * @param params: Pointer to BLE Mesh common client parameters. + * @param get: Pointer to a union, each kind of opcode corresponds to one structure inside. + * + * @return ESP_OK on success or error code otherwise. + * + */ +esp_err_t esp_ble_mesh_dfu_client_get_state(esp_ble_mesh_client_common_param_t *params, + esp_ble_mesh_dfu_client_get_t *get); + +/** @brief Start distributing a DFU. + * + * Starts distribution of the firmware in the given slot to the list of DFU + * Target nodes in @c ctx. The transfer runs in the background, and its end is + * signalled through the bt_mesh_dfu_cli_cb::ended callback. + * + * @note The BLOB Transfer Client transfer inputs @c targets list must point to a list of + * bt_mesh_dfu_target nodes. The contents of `inputs`, `io`, `xfer` must not be changed or + * released from the beginning to the end of the transmission. + * + * @param cli Firmware Update Client model instance. + * @param inputs BLOB Transfer Client transfer inputs. + * @param io BLOB stream to read BLOB from. + * @param xfer Firmware Update Client transfer parameters. + * + * @return 0 on success, or (negative) error code otherwise. + */ +esp_err_t esp_ble_mesh_dfu_cli_img_send(esp_ble_mesh_dfu_cli_t *cli, + esp_ble_mesh_blob_cli_inputs_t *inputs, + esp_ble_mesh_blob_io_t *io, + esp_ble_mesh_dfu_cli_xfer_t *xfer); + +/** @brief Get progress as a percentage of completion. + * + * @param cli Firmware Update Client model instance. + * + * @return The progress of the current transfer in percent, or 0 if no + * transfer is active. + */ +uint8_t esp_ble_mesh_dfu_cli_progress(esp_ble_mesh_dfu_cli_t *cli); +#endif /* CONFIG_BLE_MESH_DFU_CLI */ + +#if CONFIG_BLE_MESH_DFU_SRV +/** @brief Firmware Update Server event callbacks. */ +struct esp_ble_mesh_dfu_srv_cb { + /** @brief Transfer check callback. + * + * The transfer check can be used to validate the incoming transfer + * before it starts. The contents of the metadata is implementation + * specific, and should contain all the information the application + * needs to determine whether this image should be accepted, and what + * the effect of the transfer would be. + * + * If applying the image will have an effect on the provisioning state + * of the mesh stack, this can be communicated through the @c effect + * return parameter. + * + * The metadata check can be performed both as part of starting a new + * transfer and as a separate procedure. + * + * This handler is optional. + * + * @param srv Firmware Update Server instance. + * @param img DFU image the metadata check is performed on. + * @param metadata Image metadata. + * @param effect Return parameter for the image effect on the + * provisioning state of the mesh stack. + * + * @return 0 on success, or (negative) error code otherwise. + */ + int (*check)(esp_ble_mesh_dfu_srv_t *srv, + const esp_ble_mesh_dfu_img_t *img, + struct net_buf_simple *metadata, + esp_ble_mesh_dfu_effect_t *effect); + + /** @brief Transfer start callback. + * + * Called when the Firmware Update Server is ready to start a new DFU transfer. + * The application must provide an initialized BLOB stream to be used + * during the DFU transfer. + * + * The following error codes are treated specially, and should be used + * to communicate these issues: + * - @c -ENOMEM: The device cannot fit this image. + * - @c -EBUSY: The application is temporarily unable to accept the + * transfer. + * - @c -EALREADY: The device has already received and verified this + * image, and there's no need to transfer it again. The Firmware Update model + * will skip the transfer phase, and mark the image as verified. + * + * This handler is mandatory. + * + * @param srv Firmware Update Server instance. + * @param img DFU image being updated. + * @param metadata Image metadata. + * @param io BLOB stream return parameter. Must be set to a + * valid BLOB stream by the callback. + * + * @return 0 on success, or (negative) error code otherwise. Return + * codes @c -ENOMEM, @c -EBUSY @c -EALREADY will be passed to + * the updater, other error codes are reported as internal + * errors. + */ + int (*start)(esp_ble_mesh_dfu_srv_t *srv, + const esp_ble_mesh_dfu_img_t *img, + struct net_buf_simple *metadata, + const esp_ble_mesh_blob_io_t **io); + + /** @brief Transfer end callback. + * + * This handler is optional. + * + * If the transfer is successful, the application should verify the + * firmware image, and call either bt_mesh_dfu_srv_verified or + * bt_mesh_dfu_srv_rejected depending on the outcome. + * + * If the transfer fails, the Firmware Update Server will be available for new + * transfers immediately after this function returns. + * + * @param srv Firmware Update Server instance. + * @param img DFU image that failed the update. + * @param success Whether the DFU transfer was successful. + */ + void (*end)(esp_ble_mesh_dfu_srv_t *srv, + const esp_ble_mesh_dfu_img_t *img, bool success); + + /** @brief Transfer recovery callback. + * + * If the device reboots in the middle of a transfer, the Firmware Update Server + * calls this function when the Bluetooth mesh subsystem is started. + * + * This callback is optional, but transfers will not be recovered after + * a reboot without it. + * + * @param srv Firmware Update Server instance. + * @param img DFU image being updated. + * @param io BLOB stream return parameter. Must be set to a valid BLOB + * stream by the callback. + * + * @return 0 on success, or (negative) error code to abandon the + * transfer. + */ + int (*recover)(esp_ble_mesh_dfu_srv_t *srv, + const esp_ble_mesh_dfu_img_t *img, + const esp_ble_mesh_blob_io_t **io); + + /** @brief Transfer apply callback. + * + * Called after a transfer has been validated, and the updater sends an + * apply message to the Target nodes. + * + * This handler is optional. + * + * @param srv Firmware Update Server instance. + * @param img DFU image that should be applied. + * + * @return 0 on success, or (negative) error code otherwise. + */ + int (*apply)(esp_ble_mesh_dfu_srv_t *srv, + const esp_ble_mesh_dfu_img_t *img); +}; + +/** @brief Firmware Update Server instance. + * + * Should be initialized with ESP_BLE_MESH_DFU_SRV_INIT. + * @note Preview version, the contents of this struct may change in the future. + */ +struct esp_ble_mesh_dfu_srv { + /** Underlying BLOB Transfer Server. */ + esp_ble_mesh_blob_srv_t blob; + /** Callback structure. */ + const esp_ble_mesh_dfu_srv_cb_t *cb; + /** List of updatable images. */ + const esp_ble_mesh_dfu_img_t *imgs; + /** Number of updatable images. */ + size_t img_count; + + /** Runtime state */ + esp_ble_mesh_model_t *mod; + /** Update information */ + struct { + /* Effect of transfer, @see bt_mesh_dfu_effect. */ + uint8_t effect; + /* Current phase, @see bt_mesh_dfu_phase. */ + uint8_t phase; + uint8_t ttl; + uint8_t idx; + uint16_t timeout_base; + uint16_t meta; + } update; +}; + +/** @brief Accept the received DFU transfer. + * + * Should be called at the end of a successful DFU transfer. + * + * If the DFU transfer completes successfully, the application should verify + * the image validity (including any image authentication or integrity checks), + * and call this function if the image is ready to be applied. + * + * @param srv Firmware Update Server instance. + */ +void esp_ble_mesh_dfu_srv_verified(esp_ble_mesh_dfu_srv_t *srv); + +/** @brief Reject the received DFU transfer. + * + * Should be called at the end of a successful DFU transfer. + * + * If the DFU transfer completes successfully, the application should verify + * the image validity (including any image authentication or integrity checks), + * and call this function if one of the checks fail. + * + * @param srv Firmware Update Server instance. + */ +void esp_ble_mesh_dfu_srv_rejected(esp_ble_mesh_dfu_srv_t *srv); + +/** @brief Cancel the ongoing DFU transfer. + * + * @param srv Firmware Update Server instance. + */ +void esp_ble_mesh_dfu_srv_cancel(esp_ble_mesh_dfu_srv_t *srv); + +/** @brief Confirm that the received DFU transfer was applied. + * + * Should be called as a result of the `bt_mesh_dfu_srv_cb.apply` callback. + * + * @param srv Firmware Update Server instance. + */ +void esp_ble_mesh_dfu_srv_applied(esp_ble_mesh_dfu_srv_t *srv); + +/** @brief Check if the Firmware Update Server is busy processing a transfer. + * + * @param srv Firmware Update Server instance. + * + * @return true if a DFU procedure is in progress, false otherwise. + */ +bool esp_ble_mesh_dfu_srv_is_busy(const esp_ble_mesh_dfu_srv_t *srv); + +/** @brief Get the progress of the current DFU procedure, in percent. + * + * @param srv Firmware Update Server instance. + * + * @return The current transfer progress in percent. + */ +uint8_t esp_ble_mesh_dfu_srv_progress(const esp_ble_mesh_dfu_srv_t *srv); +#endif /* CONFIG_BLE_MESH_DFU_SRV */ + +#ifdef __cplusplus +} +#endif + +#endif /* _ESP_BLE_MESH_DFU_MODEL_API_H_ */ diff --git a/components/bt/esp_ble_mesh/v1.1/api/models/include/esp_ble_mesh_dfu_slot_api.h b/components/bt/esp_ble_mesh/v1.1/api/models/include/esp_ble_mesh_dfu_slot_api.h new file mode 100644 index 0000000000..0332e0a0b5 --- /dev/null +++ b/components/bt/esp_ble_mesh/v1.1/api/models/include/esp_ble_mesh_dfu_slot_api.h @@ -0,0 +1,148 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef _ESP_BLE_MESH_DFU_SLOT_API_H_ +#define _ESP_BLE_MESH_DFU_SLOT_API_H_ + +#include "esp_ble_mesh_dfu_model_api.h" + +#if CONFIG_BLE_MESH_DFU_SLOTS +#ifndef _BLE_MESH_BLOB_DEPRECATE_WARN +#define _BLE_MESH_BLOB_DEPRECATE_WARN +#warning Please note: All APIs published in this document are in Preview version and may undergo significant changes in the future. +#endif +#endif /* CONFIG_BLE_MESH_DFU_SLOTS */ + +typedef struct esp_ble_mesh_dfu_slot esp_ble_mesh_dfu_slot_t; + +typedef enum esp_ble_mesh_dfu_iter + (*esp_ble_mesh_dfu_slot_cb_t) (const esp_ble_mesh_dfu_slot_t *slot, void *user_data); + +/** @brief Get the number of slots committed to the firmware list. + * + * @return Number of committed slots. + */ +int esp_ble_mesh_dfu_slot_count(void); + +/** @brief Reserve a new DFU image slot for a distributable image. + * + * A DFU image slot represents a single distributable DFU image with all its + * metadata. The slot data must be set using esp_ble_mesh_dfu_slot_info_set and + * esp_ble_mesh_dfu_slot_fwid_set, and the slot committed using + * esp_ble_mesh_dfu_slot_commit for the slot to be considered part of the slot + * list. + * + * @return + * - slot : A pointer to the reserved slot + * - @c NULL : Slot allocation failed + */ +esp_ble_mesh_dfu_slot_t *esp_ble_mesh_dfu_slot_reserve(void); + +/** @brief Set the size and metadata for a reserved slot. + * + * @param dfu_slot Pointer to the reserved slot for which to set the + * metadata. + * @param size The size of the image. + * @param metadata Metadata or NULL. + * @param metadata_len Length of the metadata, at most @c + * CONFIG_BLE_MESH_DFU_METADATA_MAXLEN. + * + * @return + * - 0 : success + * - @c ESP_ERR_INVALID_ARG : invalid slot parameter or invalid metadata parameter + * - @c ESP_ERR_INVALID_SIZE : metadata length is zero or exceeds @c CONFIG_BLE_MESH_DFU_METADATA_MAXLEN + */ +int esp_ble_mesh_dfu_slot_info_set(esp_ble_mesh_dfu_slot_t *dfu_slot, size_t size, + const uint8_t *metadata, size_t metadata_len); + +/** @brief Set the new fwid for the incoming image for a reserved slot. + * + * @param dfu_slot Pointer to the reserved slot for which to set the fwid. + * @param fwid Fwid to set. + * @param fwid_len Length of the fwid, at most @c + * CONFIG_BLE_MESH_DFU_FWID_MAXLEN. + * + * @return + * - 0 : success + * - @c ESP_ERR_INVALID_ARG : invalid slot parameter or invalid fwid parameter + * - @c ESP_ERR_INVALID_SIZE : fwid length is zero or exceeds @c CONFIG_BLE_MESH_DFU_FWID_MAXLEN + */ +int esp_ble_mesh_dfu_slot_fwid_set(esp_ble_mesh_dfu_slot_t *dfu_slot, + const uint8_t *fwid, size_t fwid_len); + +/** @brief Commit the reserved slot to the list of slots, and store it + * persistently. + * + * If the commit fails for any reason, the slot will still be in the reserved + * state after this call. + * + * @param dfu_slot Pointer to the reserved slot. + * + * @return + * - 0 : success + * - @c ESP_ERR_INVALID_ARG : invalid slot parameter or slot has been committed + * - @c ESP_ERR_INVALID_SIZE : slot size is zero + */ +int esp_ble_mesh_dfu_slot_commit(esp_ble_mesh_dfu_slot_t *dfu_slot); + +/** @brief Release a reserved slot so that it can be reserved again. + * + * @param dfu_slot Pointer to the reserved slot. + */ +void esp_ble_mesh_dfu_slot_release(const esp_ble_mesh_dfu_slot_t *dfu_slot); + +/** @brief Delete a committed DFU image slot. + * + * @param slot Slot to delete. Must be a valid pointer acquired from this + * module. + * + * @return + * - 0 : success + * - @c ESP_ERR_INVALID_ARG : invalid slot parameter + */ +int esp_ble_mesh_dfu_slot_del(const esp_ble_mesh_dfu_slot_t *slot); + +/** @brief Delete all DFU image slots. + */ +void esp_ble_mesh_dfu_slot_del_all(void); + +/** @brief Get the DFU image slot at the given firmware image list index. + * + * @param img_idx DFU image slot index. + * + * @return + * - slot : A pointer to the reserved slot + * - @c NULL : No slot exists with the given index + */ +const esp_ble_mesh_dfu_slot_t *esp_ble_mesh_dfu_slot_at(uint16_t img_idx); + +/** @brief Get the committed DFU image slot for the image with the given + * firmware ID. + * + * @param fwid Firmware ID. + * @param fwid_len Firmware ID length. + * @param slot Slot pointer to fill. + * + * @return + * - index : Slot index + * - @c ESP_ERR_INVALID_ARG : invalid fwid parameter or invalid slot parameter + * - @c ESP_ERR_INVALID_SIZE : fwid length is zero + * - @c ESP_ERR_NOT_FOUND : no slot exists with the given firmware id + */ +int esp_ble_mesh_dfu_slot_get(const uint8_t *fwid, size_t fwid_len, esp_ble_mesh_dfu_slot_t **slot); + +/** @brief Get the index in the firmware image list for the given slot. + * + * @param slot Slot to find. + * + * @return + * - index : Slot index + * - @c ESP_ERR_INVALID_ARG : invalid slot parameter + * - @c ESP_ERR_NOT_FOUND : The slot does not exist + */ +int esp_ble_mesh_dfu_slot_img_idx_get(const esp_ble_mesh_dfu_slot_t *slot); + +#endif /* _ESP_BLE_MESH_FW_SLOT_API_H_ */ diff --git a/components/bt/esp_ble_mesh/v1.1/api/models/include/esp_ble_mesh_mbt_model_api.h b/components/bt/esp_ble_mesh/v1.1/api/models/include/esp_ble_mesh_mbt_model_api.h index 90e948b7d9..54b4f20aa8 100644 --- a/components/bt/esp_ble_mesh/v1.1/api/models/include/esp_ble_mesh_mbt_model_api.h +++ b/components/bt/esp_ble_mesh/v1.1/api/models/include/esp_ble_mesh_mbt_model_api.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2020-2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -9,51 +9,86 @@ #include "esp_ble_mesh_defs.h" +#if CONFIG_BLE_MESH_MBT_SRV || CONFIG_BLE_MESH_MBT_CLI +#ifndef _BLE_MESH_MBT_DEPRECATE_WARN +#define _BLE_MESH_MBT_DEPRECATE_WARN +#warning "warning: 'All content in this document, including data structures and APIs, will be deprecated." +#endif +#endif /* CONFIG_BLE_MESH_MBT_SRV || CONFIG_BLE_MESH_MBT_CLI */ + #ifdef __cplusplus extern "C" { #endif +#if CONFIG_IDF_CI_BUILD +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" +#endif + +/** @cond */ +#if (CONFIG_BLE_MESH_MBT_SRV && CONFIG_BLE_MESH_BLOB_SRV) || \ + (CONFIG_BLE_MESH_MBT_CLI && CONFIG_BLE_MESH_BLOB_CLI) +#error "BLOB Transfer Model and BLOB Model cannot be enabled at the same time" +#endif +/** @endcond */ + +/** @deprecated This macro will be deprecated in future versions. */ #define ESP_BLE_MESH_MODEL_OP_BLOB_TRANSFER_GET ESP_BLE_MESH_MODEL_OP_2(0x83, 0x00) +/** @deprecated This macro will be deprecated in future versions. */ #define ESP_BLE_MESH_MODEL_OP_BLOB_TRANSFER_START ESP_BLE_MESH_MODEL_OP_2(0x83, 0x01) +/** @deprecated This macro will be deprecated in future versions. */ #define ESP_BLE_MESH_MODEL_OP_BLOB_TRANSFER_CANCEL ESP_BLE_MESH_MODEL_OP_2(0x83, 0x02) +/** @deprecated This macro will be deprecated in future versions. */ #define ESP_BLE_MESH_MODEL_OP_BLOB_TRANSFER_STATUS ESP_BLE_MESH_MODEL_OP_2(0x83, 0x03) +/** @deprecated This macro will be deprecated in future versions. */ #define ESP_BLE_MESH_MODEL_OP_BLOB_BLOCK_GET ESP_BLE_MESH_MODEL_OP_2(0x83, 0x05) +/** @deprecated This macro will be deprecated in future versions. */ #define ESP_BLE_MESH_MODEL_OP_BLOB_BLOCK_START ESP_BLE_MESH_MODEL_OP_2(0x83, 0x04) +/** @deprecated This macro will be deprecated in future versions. */ #define ESP_BLE_MESH_MODEL_OP_BLOB_PARTIAL_BLOCK_REPORT ESP_BLE_MESH_MODEL_OP_1(0x65) +/** @deprecated This macro will be deprecated in future versions. */ #define ESP_BLE_MESH_MODEL_OP_BLOB_BLOCK_STATUS ESP_BLE_MESH_MODEL_OP_1(0x67) +/** @deprecated This macro will be deprecated in future versions. */ #define ESP_BLE_MESH_MODEL_OP_BLOB_CHUNK_TRANSFER ESP_BLE_MESH_MODEL_OP_1(0x66) +/** @deprecated This macro will be deprecated in future versions. */ #define ESP_BLE_MESH_MODEL_OP_BLOB_INFORMATION_GET ESP_BLE_MESH_MODEL_OP_2(0x83, 0x06) +/** @deprecated This macro will be deprecated in future versions. */ #define ESP_BLE_MESH_MODEL_OP_BLOB_INFORMATION_STATUS ESP_BLE_MESH_MODEL_OP_2(0x83, 0x07) +/** @deprecated This macro will be deprecated in future versions. */ #define ESP_BLE_MESH_BLOB_ID_SIZE 8 -/** @def ESP_BLE_MESH_MODEL_MBT_CLI +/** @def ESP_BLE_MESH_MODEL_MBT_CLI * - * @brief Define a new BLOB Transfer Client model. + * @brief Define a new BLOB Transfer Client model. * - * @note This API needs to be called for each element on which - * the application needs to have a BLOB Transfer Client model. + * @note This API needs to be called for each element on which + * the application needs to have a BLOB Transfer Client model. * - * @param cli_pub Pointer to the unique struct esp_ble_mesh_model_pub_t. - * @param cli_data Pointer to the unique struct esp_ble_mesh_client_t. + * @deprecated This macro will be deprecated in future versions. * - * @return New BLOB Transfer Client model instance. + * @param cli_pub Pointer to unique struct esp_ble_mesh_model_pub_t. + * @param cli_data Pointer to unique struct esp_ble_mesh_client_t. + * + * @return New BLOB Transfer Client model instance. */ #define ESP_BLE_MESH_MODEL_MBT_CLI(cli_pub, cli_data) \ ESP_BLE_MESH_SIG_MODEL(ESP_BLE_MESH_MODEL_ID_MBT_CLI, \ NULL, cli_pub, cli_data) -/** @def ESP_BLE_MESH_MODEL_MBT_SRV +/** @def ESP_BLE_MESH_MODEL_MBT_SRV * - * @brief Define a new BLOB Transfer Server model. + * @brief Define a new BLOB Transfer Server model. * - * @note This API needs to be called for each element on which - * the application needs to have a BLOB Transfer Server model. + * @note This API needs to be called for each element on which + * the application needs to have a BLOB Transfer Server model. * - * @param srv_pub Pointer to the unique struct esp_ble_mesh_model_pub_t. - * @param srv_data Pointer to the unique struct esp_ble_mesh_blob_trans_srv_t. + * @deprecated This macro will be deprecated in future versions. * - * @return New BLOB Transfer Server model instance. + * @param srv_pub Pointer to the unique struct esp_ble_mesh_model_pub_t. + * @param srv_data Pointer to the unique struct esp_ble_mesh_blob_trans_srv_t. + * + * @return New BLOB Transfer Server model instance. */ #define ESP_BLE_MESH_MODEL_MBT_SRV(srv_pub, srv_data) \ ESP_BLE_MESH_SIG_MODEL(ESP_BLE_MESH_MODEL_ID_MBT_SRV, \ @@ -61,125 +96,125 @@ extern "C" { /** BLOB Transfer Server model context */ typedef struct { - esp_ble_mesh_model_t *model; /*!< Pointer to BLOB Transfer Server model */ -} esp_ble_mesh_mbt_srv_t; + esp_ble_mesh_model_t *model; /*!< Pointer to BLOB Transfer Server model */ +} esp_ble_mesh_mbt_srv_t __attribute__((deprecated)); /** Parameters of BLOB receiver */ typedef struct { - uint16_t unicast_addr; /*!< Unicast address of the server */ - uint8_t retrieved_transfer_phase; /*!< Retrieved transfer phase of the server */ - uint8_t status:4; /*!< Status of the last operation */ - uint16_t blocks_not_received_len; /*!< Indicates the length which blocks were not received by the server. */ - uint8_t *blocks_not_received; /*!< Indicates which blocks were not received by the server. */ - uint16_t missing_chunks_len; /*!< Indicates which chunks were not received in the current block */ - uint8_t *missing_chunks; /*!< Indicates which chunks were not received by the server in the current block */ -/* The followings are the additional information contained in status messages. */ - uint8_t transfer_mode:2; /*!< BLOB transfer mode */ - uint8_t expected_blob_id[ESP_BLE_MESH_BLOB_ID_SIZE]; /*!< Expected BLOB identifier list */ - uint32_t blob_size; /*!< BLOB size in octets */ - uint8_t block_size_log; /*!< Indicates the block size */ - uint16_t transfer_mtu_size; /*!< MTU size in octets */ - bool block_status_recv; /*!< Indicate if Blob Block Status is received as a response. */ -} esp_ble_mesh_blob_receiver_t; /*!< Structure of BLOB receiver */ + uint16_t unicast_addr; /*!< Unicast address of the server */ + uint8_t retrieved_transfer_phase; /*!< Retrieved transfer phase of the server */ + uint8_t status: 4; /*!< Status of the last operation */ + uint16_t blocks_not_received_len; /*!< Indicates the length which blocks were not received by the server. */ + uint8_t *blocks_not_received; /*!< Indicates which blocks were not received by the server. */ + uint16_t missing_chunks_len; /*!< Indicates which chunks were not received in the current block */ + uint8_t *missing_chunks; /*!< Indicates which chunks were not received by the server in the current block */ + /* The following are the additional information contained in status messages. */ + uint8_t transfer_mode: 2; /*!< BLOB transfer mode */ + uint8_t expected_blob_id[ESP_BLE_MESH_BLOB_ID_SIZE]; /*!< Expected BLOB identifier list */ + uint32_t blob_size; /*!< BLOB size in octets */ + uint8_t block_size_log; /*!< Indicates the block size */ + uint16_t transfer_mtu_size; /*!< MTU size in octets */ + bool block_status_recv; /*!< Indicate if Blob Block Status is received as a response. */ +} esp_ble_mesh_blob_receiver_t __attribute__((deprecated)); /*!< Structure of BLOB receiver */ /** Parameters of BLOB Information Status */ typedef struct { - uint8_t min_block_size_log; /*!< Min Block Size Log */ - uint8_t max_block_size_log; /*!< Max Block Size Log */ - uint16_t max_total_chunks; /*!< Max Total Chunks */ - uint16_t max_chunk_size; /*!< Max Chunk Size */ - uint32_t max_blob_size; /*!< Max BLOB Size */ - uint16_t server_mtu_size; /*!< Server MTU size */ - uint8_t supported_transfer_mode; /*!< Supported Transfer Mode */ -} esp_ble_mesh_blob_capabilities_t; /*!< Parameters of BLOB Information Status */ + uint8_t min_block_size_log; /*!< Min Block Size Log */ + uint8_t max_block_size_log; /*!< Max Block Size Log */ + uint16_t max_total_chunks; /*!< Max Total Chunks */ + uint16_t max_chunk_size; /*!< Max Chunk Size */ + uint32_t max_blob_size; /*!< Max BLOB Size */ + uint16_t server_mtu_size; /*!< Server MTU size */ + uint8_t supported_transfer_mode; /*!< Supported Transfer Mode */ +} esp_ble_mesh_blob_capabilities_t __attribute__((deprecated)); /*!< Parameters of BLOB Information Status */ /** Parameters of BLOB retrieve capabilities */ typedef struct { - esp_ble_mesh_model_t *model; /*!< Pointer to BLOB Transfer Server model */ - uint8_t msg_role; /*!< Role of the device - Node/Provisioner */ + esp_ble_mesh_model_t *model; /*!< Pointer to BLOB Transfer Server model */ + uint8_t msg_role; /*!< Role of the device - Node/Provisioner */ - uint16_t multicast_addr; /*!< Multicast Address state */ - uint16_t app_idx; /*!< AppKey Index state */ - uint8_t transfer_ttl; /*!< Transfer TTL state */ - uint8_t unicast_addr_count; /*!< The count of unicast address */ - uint16_t *unicast_addr; /*!< Unicast address list */ -} esp_ble_mesh_retrieve_capabilities_t; /*!< Parameters of BLOB retrieve capabilities */ + uint16_t multicast_addr; /*!< Multicast Address state */ + uint16_t app_idx; /*!< AppKey Index state */ + uint8_t transfer_ttl; /*!< Transfer TTL state */ + uint8_t unicast_addr_count; /*!< The count of unicast address */ + uint16_t *unicast_addr; /*!< Unicast address list */ +} esp_ble_mesh_retrieve_capabilities_t __attribute__((deprecated)); /*!< Parameters of BLOB retrieve capabilities */ /** Parameters of BLOB transfer */ typedef struct { - esp_ble_mesh_model_t *model; /*!< Pointer to BLOB Transfer Server model */ - uint8_t msg_role; /*!< Role of the device - Node/Provisioner */ + esp_ble_mesh_model_t *model; /*!< Pointer to BLOB Transfer Server model */ + uint8_t msg_role; /*!< Role of the device - Node/Provisioner */ - uint8_t unicast_addr_count; /*!< The count of unicast address */ - uint16_t *unicast_addr; /*!< Unicast address list */ - uint16_t multicast_addr; /*!< Multicast Address state */ - uint16_t app_idx; /*!< AppKey Index state */ - uint8_t transfer_ttl; /*!< Transfer TTL state */ - uint8_t blob_id[ESP_BLE_MESH_BLOB_ID_SIZE]; /*!< BLOB identifier list */ - uint32_t blob_size; /*!< BLOB size in octets */ - uint8_t *blob_data; /*!< BLOB data */ - uint8_t transfer_mode; /*!< BLOB transfer mode */ - uint16_t client_timeout_base; /*!< Time wait for messages from the serve */ -} esp_ble_mesh_transfer_blob_t; /*!< Parameters of BLOB transfer */ + uint8_t unicast_addr_count; /*!< The count of unicast address */ + uint16_t *unicast_addr; /*!< Unicast address list */ + uint16_t multicast_addr; /*!< Multicast Address state */ + uint16_t app_idx; /*!< AppKey Index state */ + uint8_t transfer_ttl; /*!< Transfer TTL state */ + uint8_t blob_id[ESP_BLE_MESH_BLOB_ID_SIZE]; /*!< BLOB identifier list */ + uint32_t blob_size; /*!< BLOB size in octets */ + uint8_t *blob_data; /*!< BLOB data */ + uint8_t transfer_mode; /*!< BLOB transfer mode */ + uint16_t client_timeout_base; /*!< Time wait for messages from the serve */ +} esp_ble_mesh_transfer_blob_t __attribute__((deprecated)); /*!< Parameters of BLOB transfer */ /** Parameters of BLOB Block Status message */ typedef struct { - esp_ble_mesh_model_t *model; /*!< Pointer to BLOB Transfer Server model */ - uint8_t msg_role; /*!< Role of the device - Node/Provisioner */ + esp_ble_mesh_model_t *model; /*!< Pointer to BLOB Transfer Server model */ + uint8_t msg_role; /*!< Role of the device - Node/Provisioner */ - uint16_t multicast_addr; /*!< Multicast Address state */ - uint16_t app_idx; /*!< AppKey Index state */ - uint8_t transfer_ttl; /*!< Transfer TTL state */ + uint16_t multicast_addr; /*!< Multicast Address state */ + uint16_t app_idx; /*!< AppKey Index state */ + uint8_t transfer_ttl; /*!< Transfer TTL state */ - uint16_t block_number; /*!< Block number of the current block */ - uint16_t chunk_size; /*!< Chunk Size (in octets) for the current block */ -} esp_ble_mesh_send_block_t; /*!< BLOB Block Status message structure */ + uint16_t block_number; /*!< Block number of the current block */ + uint16_t chunk_size; /*!< Chunk Size (in octets) for the current block */ +} esp_ble_mesh_send_block_t __attribute__((deprecated)); /*!< BLOB Block Status message structure */ /** Parameters of BLOB send message */ typedef struct { - esp_ble_mesh_model_t *model; /*!< Pointer to BLOB Transfer Server model */ - uint8_t msg_role; /*!< Role of the device - Node/Provisioner */ + esp_ble_mesh_model_t *model; /*!< Pointer to BLOB Transfer Server model */ + uint8_t msg_role; /*!< Role of the device - Node/Provisioner */ - uint16_t multicast_addr; /*!< Multicast Address state */ - uint16_t app_idx; /*!< AppKey Index state */ - uint8_t transfer_ttl; /*!< Transfer TTL state */ -} esp_ble_mesh_send_data_t; /*!< Parameters of BLOB send message */ + uint16_t multicast_addr; /*!< Multicast Address state */ + uint16_t app_idx; /*!< AppKey Index state */ + uint8_t transfer_ttl; /*!< Transfer TTL state */ +} esp_ble_mesh_send_data_t __attribute__((deprecated)); /*!< Parameters of BLOB send message */ /** Parameters of determine Block Status message */ typedef struct { - esp_ble_mesh_model_t *model; /*!< Pointer to BLOB Transfer Server model */ - uint8_t msg_role; /*!< Role of the device - Node/Provisioner */ + esp_ble_mesh_model_t *model; /*!< Pointer to BLOB Transfer Server model */ + uint8_t msg_role; /*!< Role of the device - Node/Provisioner */ - uint16_t multicast_addr; /*!< Multicast Address state */ - uint16_t app_idx; /*!< AppKey Index state */ - uint8_t transfer_ttl; /*!< Transfer TTL state */ -} esp_ble_mesh_determine_block_status_t; /*!< Parameters of determine Block Status message */ + uint16_t multicast_addr; /*!< Multicast Address state */ + uint16_t app_idx; /*!< AppKey Index state */ + uint8_t transfer_ttl; /*!< Transfer TTL state */ +} esp_ble_mesh_determine_block_status_t __attribute__((deprecated)); /*!< Parameters of determine Block Status message */ /** Parameters of determine Block Status message */ typedef struct { - esp_ble_mesh_model_t *model; /*!< Pointer to BLOB Transfer Server model */ - uint8_t msg_role; /*!< Role of the device - Node/Provisioner */ + esp_ble_mesh_model_t *model; /*!< Pointer to BLOB Transfer Server model */ + uint8_t msg_role; /*!< Role of the device - Node/Provisioner */ - uint16_t multicast_addr; /*!< Multicast Address state */ - uint16_t app_idx; /*!< AppKey Index state */ - uint8_t transfer_ttl; /*!< Transfer TTL state */ - uint8_t unicast_addr_count; /*!< The count of unicast address */ - uint16_t *unicast_addr; /*!< Unicast address list */ -} esp_ble_mesh_determine_transfer_status_t; /*!< Parameters of determine Block Status message */ + uint16_t multicast_addr; /*!< Multicast Address state */ + uint16_t app_idx; /*!< AppKey Index state */ + uint8_t transfer_ttl; /*!< Transfer TTL state */ + uint8_t unicast_addr_count; /*!< The count of unicast address */ + uint16_t *unicast_addr; /*!< Unicast address list */ +} esp_ble_mesh_determine_transfer_status_t __attribute__((deprecated)); /*!< Parameters of determine Block Status message */ /** Parameters of cancel transfer message */ typedef struct { - esp_ble_mesh_model_t *model; /*!< Pointer to BLOB Transfer Server model */ - uint8_t msg_role; /*!< Role of the device - Node/Provisioner */ + esp_ble_mesh_model_t *model; /*!< Pointer to BLOB Transfer Server model */ + uint8_t msg_role; /*!< Role of the device - Node/Provisioner */ - uint16_t multicast_addr; /*!< Multicast Address state */ - uint16_t app_idx; /*!< AppKey Index state */ - uint8_t transfer_ttl; /*!< Transfer TTL state */ - uint8_t unicast_addr_count; /*!< The count of unicast address */ - uint16_t *unicast_addr; /*!< Unicast address list */ + uint16_t multicast_addr; /*!< Multicast Address state */ + uint16_t app_idx; /*!< AppKey Index state */ + uint8_t transfer_ttl; /*!< Transfer TTL state */ + uint8_t unicast_addr_count; /*!< The count of unicast address */ + uint16_t *unicast_addr; /*!< Unicast address list */ - uint8_t blob_id[ESP_BLE_MESH_BLOB_ID_SIZE]; /*!< BLOB identifier list */ -} esp_ble_mesh_cancel_transfer_t; /*!< Parameters of cancel transfer message */ + uint8_t blob_id[ESP_BLE_MESH_BLOB_ID_SIZE]; /*!< BLOB identifier list */ +} esp_ble_mesh_cancel_transfer_t __attribute__((deprecated)); /*!< Parameters of cancel transfer message */ /** * @brief BLOB Transfer Client model procedure result @@ -193,113 +228,113 @@ typedef struct { typedef union { /** Retrieve capabilities status */ struct { - int error_code; /*!< Result of starting Retrieve Capabilities procedure */ - esp_ble_mesh_retrieve_capabilities_t input; /*!< Input of starting Retrieve Capabilities procedure */ - } retrieve_capabilities_status; /*!< Retrieve capabilities status */ + int error_code; /*!< Result of starting Retrieve Capabilities procedure */ + esp_ble_mesh_retrieve_capabilities_t input; /*!< Input of starting Retrieve Capabilities procedure */ + } retrieve_capabilities_status; /*!< Retrieve capabilities status */ /** Transfer BLOB status */ struct { - int error_code; /*!< Result of starting Transfer BLOB procedure */ - esp_ble_mesh_transfer_blob_t input; /*!< Input of starting Transfer BLOB procedure */ - } transfer_blob_status; /*!< Transfer BLOB status */ + int error_code; /*!< Result of starting Transfer BLOB procedure */ + esp_ble_mesh_transfer_blob_t input; /*!< Input of starting Transfer BLOB procedure */ + } transfer_blob_status; /*!< Transfer BLOB status */ /** Send block status */ struct { - int error_code; /*!< Result of starting Send Block sub-procedure */ - esp_ble_mesh_send_block_t input; /*!< Input of starting Send Block sub-procedure */ - } send_block_status; /*!< Send block status */ + int error_code; /*!< Result of starting Send Block sub-procedure */ + esp_ble_mesh_send_block_t input; /*!< Input of starting Send Block sub-procedure */ + } send_block_status; /*!< Send block status */ /** Send data status */ struct { - int error_code; /*!< Result of starting Send Data sub-procedure */ - esp_ble_mesh_send_data_t input; /*!< Input of starting Send Data sub-procedure */ - } send_data_status; /*!< Send data status */ + int error_code; /*!< Result of starting Send Data sub-procedure */ + esp_ble_mesh_send_data_t input; /*!< Input of starting Send Data sub-procedure */ + } send_data_status; /*!< Send data status */ /** Determine block status */ struct { - int error_code; /*!< Result of starting Determine Block Status sub-procedure */ - esp_ble_mesh_determine_block_status_t input; /*!< Input of starting Determine Block Status sub-procedure */ - } determine_block_status_status; /*!< Determine block status */ + int error_code; /*!< Result of starting Determine Block Status sub-procedure */ + esp_ble_mesh_determine_block_status_t input; /*!< Input of starting Determine Block Status sub-procedure */ + } determine_block_status_status; /*!< Determine block status */ /** Determine transfer status */ struct { - int error_code; /*!< Result of starting Determine Transfer Status procedure */ - esp_ble_mesh_determine_transfer_status_t input; /*!< Input of starting Determine Transfer Status procedure */ - } determine_transfer_status_status; /*!< Determine transfer status */ + int error_code; /*!< Result of starting Determine Transfer Status procedure */ + esp_ble_mesh_determine_transfer_status_t input; /*!< Input of starting Determine Transfer Status procedure */ + } determine_transfer_status_status; /*!< Determine transfer status */ /** Cancel transfer status */ struct { - int error_code; /*!< Result of starting Cancel Transfer procedure */ - esp_ble_mesh_cancel_transfer_t input; /*!< Input of starting Cancel Transfer procedure */ - } cancel_transfer_status; /*!< Cancel transfer status */ + int error_code; /*!< Result of starting Cancel Transfer procedure */ + esp_ble_mesh_cancel_transfer_t input; /*!< Input of starting Cancel Transfer procedure */ + } cancel_transfer_status; /*!< Cancel transfer status */ /** Retrieve capabilities complete */ struct { - esp_ble_mesh_model_t *model; /*!< Pointer to the BLOB Transfer Client model */ - uint8_t result; /*!< Result of Retrieve Capabilities procedure */ - } retrieve_capabilities_comp; /*!< Retrieve capabilities complete */ + esp_ble_mesh_model_t *model; /*!< Pointer to the BLOB Transfer Client model */ + uint8_t result; /*!< Result of Retrieve Capabilities procedure */ + } retrieve_capabilities_comp; /*!< Retrieve capabilities complete */ /** Transfer BLOB complete */ struct { - esp_ble_mesh_model_t *model; /*!< Pointer to the BLOB Transfer Client model */ - uint8_t result; /*!< Result of Transfer BLOB procedure */ - } transfer_blob_comp; /*!< Transfer BLOB complete */ + esp_ble_mesh_model_t *model; /*!< Pointer to the BLOB Transfer Client model */ + uint8_t result; /*!< Result of Transfer BLOB procedure */ + } transfer_blob_comp; /*!< Transfer BLOB complete */ /** Send block complete */ struct { - esp_ble_mesh_model_t *model; /*!< Pointer to the BLOB Transfer Client model */ - uint8_t result; /*!< Result of Send Block sub-procedure */ - } send_block_comp; /*!< Send block complete */ + esp_ble_mesh_model_t *model; /*!< Pointer to the BLOB Transfer Client model */ + uint8_t result; /*!< Result of Send Block sub-procedure */ + } send_block_comp; /*!< Send block complete */ /** Send data complete */ struct { - esp_ble_mesh_model_t *model; /*!< Pointer to the BLOB Transfer Client model */ - uint8_t result; /*!< Result of Send Data sub-procedure */ - } send_data_comp; /*!< Send data complete */ + esp_ble_mesh_model_t *model; /*!< Pointer to the BLOB Transfer Client model */ + uint8_t result; /*!< Result of Send Data sub-procedure */ + } send_data_comp; /*!< Send data complete */ /** Determine block status complete */ struct { - esp_ble_mesh_model_t *model; /*!< Pointer to the BLOB Transfer Client model */ - uint8_t result; /*!< Result of Determine Block Status sub-procedure */ - } determine_block_status_comp; /*!< Determine block status complete */ + esp_ble_mesh_model_t *model; /*!< Pointer to the BLOB Transfer Client model */ + uint8_t result; /*!< Result of Determine Block Status sub-procedure */ + } determine_block_status_comp; /*!< Determine block status complete */ /** Determine transfer status complete */ struct { - esp_ble_mesh_model_t *model; /*!< Pointer to the BLOB Transfer Client model */ - uint8_t result; /*!< Result of Determine Transfer Status procedure */ - } determine_transfer_status_comp; /*!< Determine transfer status complete */ + esp_ble_mesh_model_t *model; /*!< Pointer to the BLOB Transfer Client model */ + uint8_t result; /*!< Result of Determine Transfer Status procedure */ + } determine_transfer_status_comp; /*!< Determine transfer status complete */ /** Cancel transfer complete */ struct { - esp_ble_mesh_model_t *model; /*!< Pointer to the BLOB Transfer Client model */ - uint8_t result; /*!< Result of Cancel Transfer procedure */ - } cancel_transfer_comp; /*!< Cancel transfer complete */ + esp_ble_mesh_model_t *model; /*!< Pointer to the BLOB Transfer Client model */ + uint8_t result; /*!< Result of Cancel Transfer procedure */ + } cancel_transfer_comp; /*!< Cancel transfer complete */ /** Set transfer TTL */ struct { - int error_code; /*!< Result of setting Transfer TTL state */ - esp_ble_mesh_model_t *model; /*!< Pointer to the BLOB Transfer Client model */ - uint8_t transfer_ttl; /*!< Transfer TTL state */ - } set_transfer_ttl; /*!< Set transfer TTL */ + int error_code; /*!< Result of setting Transfer TTL state */ + esp_ble_mesh_model_t *model; /*!< Pointer to the BLOB Transfer Client model */ + uint8_t transfer_ttl; /*!< Transfer TTL state */ + } set_transfer_ttl; /*!< Set transfer TTL */ /** Clear transfer TTL */ struct { - int error_code; /*!< Result of clearing Transfer TTL state */ - esp_ble_mesh_model_t *model; /*!< Pointer to the BLOB Transfer Client model */ - } clear_transfer_ttl; /*!< Clear transfer TTL */ + int error_code; /*!< Result of clearing Transfer TTL state */ + esp_ble_mesh_model_t *model; /*!< Pointer to the BLOB Transfer Client model */ + } clear_transfer_ttl; /*!< Clear transfer TTL */ /** Set application index */ struct { - int error_code; /*!< Result of setting AppKey Index state */ - esp_ble_mesh_model_t *model; /*!< Pointer to the BLOB Transfer Client model */ - uint16_t app_idx; /*!< AppKey Index state */ - } set_app_idx; /*!< Set application index */ + int error_code; /*!< Result of setting AppKey Index state */ + esp_ble_mesh_model_t *model; /*!< Pointer to the BLOB Transfer Client model */ + uint16_t app_idx; /*!< AppKey Index state */ + } set_app_idx; /*!< Set application index */ /** Clear application index */ struct { - int error_code; /*!< Result of clearing AppKey Index state */ - esp_ble_mesh_model_t *model; /*!< Pointer to the BLOB Transfer Client model */ - } clear_app_idx; /*!< Clear application index */ + int error_code; /*!< Result of clearing AppKey Index state */ + esp_ble_mesh_model_t *model; /*!< Pointer to the BLOB Transfer Client model */ + } clear_app_idx; /*!< Clear application index */ /** Set multicast address */ struct { - int error_code; /*!< Result of setting Multicast Address state */ - esp_ble_mesh_model_t *model; /*!< Pointer to the BLOB Transfer Client model */ - uint16_t multicast_addr; /*!< Multicast Address state */ - } set_multicast_addr; /*!< Set multicast address */ + int error_code; /*!< Result of setting Multicast Address state */ + esp_ble_mesh_model_t *model; /*!< Pointer to the BLOB Transfer Client model */ + uint16_t multicast_addr; /*!< Multicast Address state */ + } set_multicast_addr; /*!< Set multicast address */ /** Clear multicast address */ struct { - int error_code; /*!< Result of clearing Multicast Address state */ - esp_ble_mesh_model_t *model; /*!< Pointer to the BLOB Transfer Client model */ - } clear_multicast_addr; /*!< Clear multicast address */ -} esp_ble_mesh_mbt_client_cb_value_t; /*!< BLOB Transfer Client model callback values union */ + int error_code; /*!< Result of clearing Multicast Address state */ + esp_ble_mesh_model_t *model; /*!< Pointer to the BLOB Transfer Client model */ + } clear_multicast_addr; /*!< Clear multicast address */ +} esp_ble_mesh_mbt_client_cb_value_t; /*!< BLOB Transfer Client model callback values union */ /** BLOB Transfer Client model callback parameters */ typedef struct { - esp_ble_mesh_mbt_client_cb_value_t value; /*!< BLOB Transfer Client model callback values */ -} esp_ble_mesh_mbt_client_cb_param_t; /*!< BLOB Transfer Client model callback parameters */ + esp_ble_mesh_mbt_client_cb_value_t value; /*!< BLOB Transfer Client model callback values */ +} esp_ble_mesh_mbt_client_cb_param_t __attribute__((deprecated)); /*!< BLOB Transfer Client model callback parameters */ /** * This enum value is the event of BLOB Transfer Client model. @@ -327,30 +362,30 @@ typedef enum { ESP_BLE_MESH_MBT_CLIENT_SET_MULTICAST_ADDR_COMP_EVT, ESP_BLE_MESH_MBT_CLIENT_CLEAR_MULTICAST_ADDR_COMP_EVT, ESP_BLE_MESH_MBT_CLIENT_EVT_MAX, -} esp_ble_mesh_mbt_client_cb_event_t; +} esp_ble_mesh_mbt_client_cb_event_t __attribute__((deprecated)); /** Parameters of initialize BLOB receive */ typedef struct { - esp_ble_mesh_model_t *model; /*!< Pointer to the BLOB Transfer Client model */ + esp_ble_mesh_model_t *model; /*!< Pointer to the BLOB Transfer Client model */ - uint8_t blob_id[ESP_BLE_MESH_BLOB_ID_SIZE]; /*!< BLOB identifier list */ - uint16_t timeout; /*!< Timeout */ - uint8_t transfer_ttl; /*!< Transfer TTL state */ -} esp_ble_mesh_initialize_blob_receive_t; /*!< Structure of initialize BLOB receive */ + uint8_t blob_id[ESP_BLE_MESH_BLOB_ID_SIZE]; /*!< BLOB identifier list */ + uint16_t timeout; /*!< Timeout */ + uint8_t transfer_ttl; /*!< Transfer TTL state */ +} esp_ble_mesh_initialize_blob_receive_t __attribute__((deprecated)); /*!< Structure of initialize BLOB receive */ /** Parameters of cancel BLOB receive */ typedef struct { esp_ble_mesh_model_t *model; /*!< Pointer to the BLOB Transfer Client model */ uint8_t blob_id[ESP_BLE_MESH_BLOB_ID_SIZE]; /*!< BLOB identifier list */ -} esp_ble_mesh_cancel_blob_receive_t;/*!< */ +} esp_ble_mesh_cancel_blob_receive_t __attribute__((deprecated));/*!< */ /** Parameters of cancel BLOB receive */ typedef struct { esp_ble_mesh_model_t *model; /*!< Pointer to the BLOB Transfer Client model */ esp_ble_mesh_blob_capabilities_t caps; /*!< Parameters of BLOB Information Status */ -} esp_ble_mesh_set_blob_capabilities_t;/*!< */ +} esp_ble_mesh_set_blob_capabilities_t __attribute__((deprecated)); /** * @brief BLOB Transfer Server model callback value union @@ -358,61 +393,61 @@ typedef struct { typedef union { /** Initialize BLOB receive complete */ struct { - int error_code; /*!< Result of initializing BLOB receive */ - esp_ble_mesh_initialize_blob_receive_t input; /*!< Input of initializing BLOB receive */ - } initialize_blob_receive_comp; /*!< Initialize BLOB receive complete */ + int error_code; /*!< Result of initializing BLOB receive */ + esp_ble_mesh_initialize_blob_receive_t input; /*!< Input of initializing BLOB receive */ + } initialize_blob_receive_comp; /*!< Initialize BLOB receive complete */ /** Cancel BLOB receive complete */ struct { - int error_code; /*!< Result of canceling BLOB receive */ - esp_ble_mesh_cancel_blob_receive_t input; /*!< Input of canceling BLOB receive */ - } cancel_blob_receive_comp; /*!< Cancel BLOB receive complete */ + int error_code; /*!< Result of canceling BLOB receive */ + esp_ble_mesh_cancel_blob_receive_t input; /*!< Input of canceling BLOB receive */ + } cancel_blob_receive_comp; /*!< Cancel BLOB receive complete */ /** Set BLOB capabilities complete */ struct { - int error_code; /*!< Result of setting BLOB capabilities */ - esp_ble_mesh_set_blob_capabilities_t input; /*!< Input of setting BLOB capabilities */ - } set_blob_capabilities_comp; /*!< Set BLOB capabilities complete */ + int error_code; /*!< Result of setting BLOB capabilities */ + esp_ble_mesh_set_blob_capabilities_t input; /*!< Input of setting BLOB capabilities */ + } set_blob_capabilities_comp; /*!< Set BLOB capabilities complete */ /** BLOB transfer get */ struct { - esp_ble_mesh_msg_ctx_t ctx; /*!< BLOB Transfer Get message context */ - } blob_transfer_get; /*!< BLOB transfer get */ + esp_ble_mesh_msg_ctx_t ctx; /*!< BLOB Transfer Get message context */ + } blob_transfer_get; /*!< BLOB transfer get */ /** BLOB transfer start */ struct { - esp_ble_mesh_msg_ctx_t ctx; /*!< BLOB Transfer Start message context */ - } blob_transfer_start; /*!< BLOB transfer start */ + esp_ble_mesh_msg_ctx_t ctx; /*!< BLOB Transfer Start message context */ + } blob_transfer_start; /*!< BLOB transfer start */ /** BLOB transfer cancel */ struct { - esp_ble_mesh_msg_ctx_t ctx; /*!< BLOB Transfer Cancel message context */ - } blob_transfer_cancel; /*!< BLOB transfer cancel */ + esp_ble_mesh_msg_ctx_t ctx; /*!< BLOB Transfer Cancel message context */ + } blob_transfer_cancel; /*!< BLOB transfer cancel */ /** BLOB block get */ struct { - esp_ble_mesh_msg_ctx_t ctx; /*!< BLOB Block Get message context */ - } blob_block_get; /*!< BLOB block get */ + esp_ble_mesh_msg_ctx_t ctx; /*!< BLOB Block Get message context */ + } blob_block_get; /*!< BLOB block get */ /** BLOB block start */ struct { - esp_ble_mesh_msg_ctx_t ctx; /*!< BLOB Block Start message context */ - } blob_block_start; /*!< BLOB block start */ + esp_ble_mesh_msg_ctx_t ctx; /*!< BLOB Block Start message context */ + } blob_block_start; /*!< BLOB block start */ /** BLOB chunk transfer */ struct { - esp_ble_mesh_msg_ctx_t ctx; /*!< BLOB Chunk Transfer message context */ - } blob_chunk_transfer; /*!< BLOB chunk transfer */ + esp_ble_mesh_msg_ctx_t ctx; /*!< BLOB Chunk Transfer message context */ + } blob_chunk_transfer; /*!< BLOB chunk transfer */ /** BLOB information get */ struct { - esp_ble_mesh_msg_ctx_t ctx; /*!< BLOB Information Get message context */ - } blob_information_get; /*!< BLOB information get */ + esp_ble_mesh_msg_ctx_t ctx; /*!< BLOB Information Get message context */ + } blob_information_get; /*!< BLOB information get */ /** Block receive complete */ struct { - esp_ble_mesh_msg_ctx_t ctx; /*!< Information of receiving BLOB block completely */ - } block_receive_comp; /*!< Block receive complete */ + esp_ble_mesh_msg_ctx_t ctx; /*!< Information of receiving BLOB block completely */ + } block_receive_comp; /*!< Block receive complete */ /** BLOB receive complete */ struct { - esp_ble_mesh_msg_ctx_t ctx; /*!< Information of receiving BLOB completely */ - } blob_receive_comp; /*!< BLOB receive complete */ -} esp_ble_mesh_mbt_server_cb_value_t; /*!< BLOB Transfer Server model callback value union */ + esp_ble_mesh_msg_ctx_t ctx; /*!< Information of receiving BLOB completely */ + } blob_receive_comp; /*!< BLOB receive complete */ +} esp_ble_mesh_mbt_server_cb_value_t __attribute__((deprecated)); /*!< BLOB Transfer Server model callback value union */ /** BLOB Transfer Server model callback parameters */ typedef struct { - esp_ble_mesh_mbt_server_cb_value_t value; /*!< Value of the received blob transfer messages */ -} esp_ble_mesh_mbt_server_cb_param_t; /*!< BLOB Transfer Server model callback parameters */ + esp_ble_mesh_mbt_server_cb_value_t value; /*!< Value of the received blob transfer messages */ +} esp_ble_mesh_mbt_server_cb_param_t __attribute__((deprecated)); /*!< BLOB Transfer Server model callback parameters */ /** This enum value is the event of BLOB Transfer Server model */ typedef enum { @@ -430,7 +465,7 @@ typedef enum { ESP_BLE_MESH_MBT_SERVER_BLOB_RECEIVE_COMP_EVT, ESP_BLE_MESH_MBT_SERVER_BLOB_RECEIVE_TIMEOUT_EVT, ESP_BLE_MESH_MBT_SERVER_EVT_MAX, -} esp_ble_mesh_mbt_server_cb_event_t; +} esp_ble_mesh_mbt_server_cb_event_t __attribute__((deprecated)); /** * @brief BLOB Transfer Client model callback function type @@ -453,96 +488,116 @@ typedef void (* esp_ble_mesh_mbt_server_cb_t)(esp_ble_mesh_mbt_server_cb_event_t /** * @brief Register BLE Mesh BLOB Transfer Client model callback. * + * @deprecated This function will be deprecated in future versions. + * * @param[in] callback: Pointer to the callback function. * * @return ESP_OK on success or error code otherwise. * */ -esp_err_t esp_ble_mesh_register_mbt_client_callback(esp_ble_mesh_mbt_client_cb_t callback); +esp_err_t esp_ble_mesh_register_mbt_client_callback(esp_ble_mesh_mbt_client_cb_t callback) __attribute__((deprecated)); /** * @brief Register BLE Mesh BLOB Transfer Server model callback. * + * @deprecated This function will be deprecated in future versions. + * * @param[in] callback: Pointer to the callback function. * * @return ESP_OK on success or error code otherwise. * */ -esp_err_t esp_ble_mesh_register_mbt_server_callback(esp_ble_mesh_mbt_server_cb_t callback); +esp_err_t esp_ble_mesh_register_mbt_server_callback(esp_ble_mesh_mbt_server_cb_t callback) __attribute__((deprecated)); /** * @brief BLOB Transfer Client starts Retrieve Capabilities procedure. * + * @deprecated This function will be deprecated in future versions. + * * @param[in] input: The input of Retrieve Capabilities procedure. * * @return ESP_OK on success or error code otherwise. * */ -esp_err_t esp_ble_mesh_mbt_client_retrieve_capabilities(esp_ble_mesh_retrieve_capabilities_t *input); +esp_err_t esp_ble_mesh_mbt_client_retrieve_capabilities(esp_ble_mesh_retrieve_capabilities_t *input) __attribute__((deprecated)); /** * @brief BLOB Transfer Client starts Transfer BLOB procedure. * + * @deprecated This function will be deprecated in future versions. + * * @param[in] input: The input of Transfer BLOB procedure. * * @return ESP_OK on success or error code otherwise. * */ -esp_err_t esp_ble_mesh_mbt_client_transfer_blob(esp_ble_mesh_transfer_blob_t *input); +esp_err_t esp_ble_mesh_mbt_client_transfer_blob(esp_ble_mesh_transfer_blob_t *input) __attribute__((deprecated)); /** * @brief BLOB Transfer Client starts Send Block sub-procedure. * + * @deprecated This function will be deprecated in future versions. + * * @param[in] input: The input of Send Block sub-procedure. * * @return ESP_OK on success or error code otherwise. * */ -esp_err_t esp_ble_mesh_mbt_client_send_block(esp_ble_mesh_send_block_t *input); +esp_err_t esp_ble_mesh_mbt_client_send_block(esp_ble_mesh_send_block_t *input) __attribute__((deprecated)); /** * @brief BLOB Transfer Client starts Send Data sub-procedure. * + * @deprecated This function will be deprecated in future versions. + * * @param[in] input: The input of Send Data sub-procedure. * * @return ESP_OK on success or error code otherwise. * */ -esp_err_t esp_ble_mesh_mbt_client_send_data(esp_ble_mesh_send_data_t *input); +esp_err_t esp_ble_mesh_mbt_client_send_data(esp_ble_mesh_send_data_t *input) __attribute__((deprecated)); /** * @brief BLOB Transfer Client starts Determine Block Status sub-procedure. * + * @deprecated This function will be deprecated in future versions. + * * @param[in] input: The input of Determine Block Status sub-procedure. * * @return ESP_OK on success or error code otherwise. * */ -esp_err_t esp_ble_mesh_mbt_client_determine_block_status(esp_ble_mesh_determine_block_status_t *input); +esp_err_t esp_ble_mesh_mbt_client_determine_block_status(esp_ble_mesh_determine_block_status_t *input) __attribute__((deprecated)); /** * @brief BLOB Transfer Client starts Determine Transfer Status procedure. * + * @deprecated This function will be deprecated in future versions. + * * @param[in] input: The input of Determine Transfer Status procedure. * * @return ESP_OK on success or error code otherwise. * */ -esp_err_t esp_ble_mesh_mbt_client_determine_transfer_status(esp_ble_mesh_determine_transfer_status_t *input); +esp_err_t esp_ble_mesh_mbt_client_determine_transfer_status(esp_ble_mesh_determine_transfer_status_t *input) __attribute__((deprecated)); /** * @brief BLOB Transfer Client starts Cancel Transfer procedure. * + * @deprecated This function will be deprecated in future versions. + * * @param[in] input: The input of Cancel Transfer procedure. * * @return ESP_OK on success or error code otherwise. * */ -esp_err_t esp_ble_mesh_mbt_client_cancel_transfer(esp_ble_mesh_cancel_transfer_t *input); +esp_err_t esp_ble_mesh_mbt_client_cancel_transfer(esp_ble_mesh_cancel_transfer_t *input) __attribute__((deprecated)); /** * @brief BLOB Transfer Client gets BLOB receiver. * + * @deprecated This function will be deprecated in future versions. + * * @param[in] model: BLOB Transfer Client model. * @param[in] unicast_addr: Unicast address of the BLOB receiver. * @@ -550,21 +605,25 @@ esp_err_t esp_ble_mesh_mbt_client_cancel_transfer(esp_ble_mesh_cancel_transfer_t * */ const esp_ble_mesh_blob_receiver_t *esp_ble_mesh_mbt_client_get_blob_receiver(esp_ble_mesh_model_t *model, - uint16_t unicast_addr); + uint16_t unicast_addr) __attribute__((deprecated)); /** * @brief BLOB Transfer Client gets active BLOB receiver list. * + * @deprecated This function will be deprecated in future versions. + * * @param[in] model: BLOB Transfer Client model. * * @return Active BLOB receiver list on success or NULL on failure. * */ -const esp_ble_mesh_blob_receiver_t **esp_ble_mesh_mbt_client_get_active_blob_receiver(esp_ble_mesh_model_t *model); +const esp_ble_mesh_blob_receiver_t **esp_ble_mesh_mbt_client_get_active_blob_receiver(esp_ble_mesh_model_t *model) __attribute__((deprecated)); /** * @brief BLOB Transfer Client gets BLOB transfer progress. * + * @deprecated This function will be deprecated in future versions. + * * @param[in] model: BLOB Transfer Client model. * @param[in] unicast_addr: Unicast address of the BLOB receiver. * @param[in] block_percent: Block reception percent to be updated. @@ -576,11 +635,13 @@ const esp_ble_mesh_blob_receiver_t **esp_ble_mesh_mbt_client_get_active_blob_rec esp_err_t esp_ble_mesh_mbt_client_get_transfer_progress(esp_ble_mesh_model_t *model, uint16_t unicast_addr, uint8_t *block_percent, - uint8_t *chunk_percent); + uint8_t *chunk_percent) __attribute__((deprecated)); /** * @brief BLOB Transfer Client sets Transfer TTL state. * + * @deprecated This function will be deprecated in future versions. + * * @param[in] model: BLOB Transfer Client model. * @param[in] transfer_ttl: Transfer TTL state value. * @@ -588,21 +649,25 @@ esp_err_t esp_ble_mesh_mbt_client_get_transfer_progress(esp_ble_mesh_model_t *mo * */ esp_err_t esp_ble_mesh_mbt_client_set_transfer_ttl(esp_ble_mesh_model_t *model, - uint8_t transfer_ttl); + uint8_t transfer_ttl) __attribute__((deprecated)); /** * @brief BLOB Transfer Client clear Transfer TTL state. * + * @deprecated This function will be deprecated in future versions. + * * @param[in] model: BLOB Transfer Client model. * * @return ESP_OK on success or error code otherwise. * */ -esp_err_t esp_ble_mesh_mbt_client_clear_transfer_ttl(esp_ble_mesh_model_t *model); +esp_err_t esp_ble_mesh_mbt_client_clear_transfer_ttl(esp_ble_mesh_model_t *model) __attribute__((deprecated)); /** * @brief BLOB Transfer Client sets AppKey Index state. * + * @deprecated This function will be deprecated in future versions. + * * @param[in] model: BLOB Transfer Client model. * @param[in] app_idx: AppKey Index state value. * @@ -610,21 +675,25 @@ esp_err_t esp_ble_mesh_mbt_client_clear_transfer_ttl(esp_ble_mesh_model_t *model * */ esp_err_t esp_ble_mesh_mbt_client_set_app_idx(esp_ble_mesh_model_t *model, - uint16_t app_idx); + uint16_t app_idx) __attribute__((deprecated)); /** * @brief BLOB Transfer Client clear AppKey Index state. * + * @deprecated This function will be deprecated in future versions. + * * @param[in] model: BLOB Transfer Client model. * * @return ESP_OK on success or error code otherwise. * */ -esp_err_t esp_ble_mesh_mbt_client_clear_app_idx(esp_ble_mesh_model_t *model); +esp_err_t esp_ble_mesh_mbt_client_clear_app_idx(esp_ble_mesh_model_t *model) __attribute__((deprecated)); /** * @brief BLOB Transfer Client sets Multicast Address state. * + * @deprecated This function will be deprecated in future versions. + * * @param[in] model: BLOB Transfer Client model. * @param[in] multicast_addr: Multicast Address state value. * @@ -632,51 +701,61 @@ esp_err_t esp_ble_mesh_mbt_client_clear_app_idx(esp_ble_mesh_model_t *model); * */ esp_err_t esp_ble_mesh_mbt_client_set_multicast_addr(esp_ble_mesh_model_t *model, - uint16_t multicast_addr); + uint16_t multicast_addr) __attribute__((deprecated)); /** * @brief BLOB Transfer Client clear Multicast Address state. * + * @deprecated This function will be deprecated in future versions. + * * @param[in] model: BLOB Transfer Client model. * * @return ESP_OK on success or error code otherwise. * */ -esp_err_t esp_ble_mesh_mbt_client_clear_multicast_addr(esp_ble_mesh_model_t *model); +esp_err_t esp_ble_mesh_mbt_client_clear_multicast_addr(esp_ble_mesh_model_t *model) __attribute__((deprecated)); /** * @brief BLOB Transfer Server initializes BLOB receive. * + * @deprecated This function will be deprecated in future versions. + * * @param[in] input: The input of initializing BLOB receive. * * @return ESP_OK on success or error code otherwise. * */ -esp_err_t esp_ble_mesh_mbt_server_initialize_blob_receive(esp_ble_mesh_initialize_blob_receive_t *input); +esp_err_t esp_ble_mesh_mbt_server_initialize_blob_receive(esp_ble_mesh_initialize_blob_receive_t *input) __attribute__((deprecated)); /** * @brief BLOB Transfer Server cancels BLOB receive. * + * @deprecated This function will be deprecated in future versions. + * * @param[in] input: The input of cancelling BLOB receive. * * @return ESP_OK on success or error code otherwise. * */ -esp_err_t esp_ble_mesh_mbt_server_cancel_blob_receive(esp_ble_mesh_cancel_blob_receive_t *input); +esp_err_t esp_ble_mesh_mbt_server_cancel_blob_receive(esp_ble_mesh_cancel_blob_receive_t *input) __attribute__((deprecated)); /** * @brief BLOB Transfer Server sets BLOB capabilities. * + * @deprecated This function will be deprecated in future versions. + * * @param[in] input: The input of setting BLOB capabilities. * * @return ESP_OK on success or error code otherwise. * */ -esp_err_t esp_ble_mesh_mbt_server_set_blob_capabilities(esp_ble_mesh_set_blob_capabilities_t *input); +esp_err_t esp_ble_mesh_mbt_server_set_blob_capabilities(esp_ble_mesh_set_blob_capabilities_t *input) __attribute__((deprecated)); /** * @brief BLOB Transfer Server gets current BLOB reception progress. * + * @deprecated This function will be deprecated in future versions. + * * @param[in] model: BLOB Transfer Server model. * @param[in] reception_progress: Reception progress to be updated. * @@ -684,7 +763,11 @@ esp_err_t esp_ble_mesh_mbt_server_set_blob_capabilities(esp_ble_mesh_set_blob_ca * */ esp_err_t esp_ble_mesh_mbt_server_get_blob_reception_progress(esp_ble_mesh_model_t *model, - uint8_t *reception_progress); + uint8_t *reception_progress) __attribute__((deprecated)); + +#if CONFIG_IDF_CI_BUILD +#pragma GCC diagnostic pop +#endif #ifdef __cplusplus } 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 new file mode 100644 index 0000000000..91f0ab9d83 --- /dev/null +++ b/components/bt/esp_ble_mesh/v1.1/btc/btc_ble_mesh_dfu_model.c @@ -0,0 +1,414 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#include "btc_ble_mesh_model_common.h" +#include "btc_ble_mesh_dfu_model.h" +#include "esp_ble_mesh_dfu_model_api.h" +#include "mesh_v1.1/dfu/dfu_cli.h" +#include "mesh_v1.1/dfu/dfu_srv.h" + +#if CONFIG_BLE_MESH_DFU_CLI +/* Device Firmware Update Client model related functions */ + +static inline void btc_ble_mesh_dfu_client_cb_to_app(btc_ble_mesh_dfu_client_cb_evt_t event, + esp_ble_mesh_dfu_client_cb_param_t *param) +{ + esp_ble_mesh_dfu_client_cb_t btc_ble_mesh_cb = + btc_profile_cb_get(BTC_PID_DFU_CLIENT); + if (btc_ble_mesh_cb) { + btc_ble_mesh_cb(event, param); + } +} + +void btc_ble_mesh_dfu_client_arg_deep_copy(btc_msg_t *msg, void *p_dest, void *p_src) +{ + btc_ble_mesh_dfu_client_args_t *dst = p_dest; + btc_ble_mesh_dfu_client_args_t *src = p_src; + + if (!msg || !dst || !src) { + BT_ERR("%s, Invalid parameter", __func__); + return; + } + + switch (msg->act) { + case BTC_BLE_MESH_ACT_DFU_CLIENT_GET_STATE: { + dst->dfu_get.params = bt_mesh_calloc(sizeof(esp_ble_mesh_client_common_param_t)); + if (dst->dfu_get.params) { + memcpy(dst->dfu_get.params, src->dfu_get.params, + sizeof(esp_ble_mesh_client_common_param_t)); + } else { + BT_ERR("%s, Out of memory, act %d", __func__, msg->act); + break; + } + if (src->dfu_get.get) { + dst->dfu_get.get = bt_mesh_calloc(sizeof(esp_ble_mesh_dfu_client_get_t)); + if (dst->dfu_get.get) { + memcpy(dst->dfu_get.get, src->dfu_get.get, + sizeof(esp_ble_mesh_dfu_client_get_t)); + if (src->dfu_get.params->opcode == ESP_BLE_MESH_DFU_OP_UPDATE_METADATA_CHECK) { + if (src->dfu_get.get->dfu_metadata_check.metadata) { + dst->dfu_get.get->dfu_metadata_check.metadata = bt_mesh_alloc_buf(src->dfu_get.get->dfu_metadata_check.metadata->len); + if (dst->dfu_get.get->dfu_metadata_check.metadata) { + net_buf_simple_add_mem(dst->dfu_get.get->dfu_metadata_check.metadata, + src->dfu_get.get->dfu_metadata_check.metadata->data, + src->dfu_get.get->dfu_metadata_check.metadata->len); + } else { + BT_ERR("Out of memory for metadata"); + return; + } + } else { + BT_ERR("Metadata should be exists"); + return; + } + } + } else { + BT_ERR("%s, Out of memory, act %d", __func__, msg->act); + } + } + break; + } + default: + BT_DBG("%s, Unknown act %d", __func__, msg->act); + break; + } +} + +void btc_ble_mesh_dfu_client_arg_deep_free(btc_msg_t *msg) +{ + btc_ble_mesh_dfu_client_args_t *arg = NULL; + + if (!msg) { + BT_ERR("%s, Invalid parameter", __func__); + return; + } + + arg = (btc_ble_mesh_dfu_client_args_t *)msg->arg; + + switch (msg->act) { + case BTC_BLE_MESH_ACT_DFU_CLIENT_GET_STATE: + if (arg->dfu_get.params) { + bt_mesh_free(arg->dfu_get.params); + } + if (arg->dfu_get.get) { + bt_mesh_free(arg->dfu_get.get); + } + break; + default: + break; + } +} + +static void btc_ble_mesh_dfu_client_copy_req_data(btc_msg_t *msg, void *p_dest, void *p_src) +{ + esp_ble_mesh_dfu_client_cb_param_t *p_dest_data = p_dest; + esp_ble_mesh_dfu_client_cb_param_t *p_src_data = p_src; + uint16_t length = 0; + + if (!msg || !p_src_data || !p_dest_data) { + BT_ERR("%s, Invalid parameter", __func__); + return; + } + + if (p_src_data->params) { + p_dest_data->params = bt_mesh_calloc(sizeof(esp_ble_mesh_client_common_param_t)); + if (!p_dest_data->params) { + BT_ERR("%s, Out of memory, act %d", __func__, msg->act); + return; + } + + memcpy(p_dest_data->params, p_src_data->params, sizeof(esp_ble_mesh_client_common_param_t)); + } + + switch (msg->act) { + case ESP_BLE_MESH_DFU_CLIENT_RECV_GET_RSP_EVT: + if (p_src_data->params) { + switch (p_src_data->params->opcode) { + case ESP_BLE_MESH_DFU_OP_UPDATE_INFO_GET: + if (p_src_data->recv.dfu_update_info_status.fw_info_list_cnt) { + length = p_src_data->recv.dfu_update_info_status.fw_info_list_cnt * sizeof(esp_ble_mesh_firmware_info_t); + p_dest_data->recv.dfu_update_info_status.fw_info_list = bt_mesh_calloc(length); + if (!p_dest_data->recv.dfu_update_info_status.fw_info_list) { + BT_ERR("%s, Out of memory, act %d", __func__, msg->act); + return; + } + memcpy(p_dest_data->recv.dfu_update_info_status.fw_info_list, + p_src_data->recv.dfu_update_info_status.fw_info_list, + length); + } + break; + default: + break; + } + } + case ESP_BLE_MESH_DFU_CLIENT_SEND_COMP_EVT: + case ESP_BLE_MESH_DFU_CLIENT_TIMEOUT_EVT: + case ESP_BLE_MESH_DFU_CLIENT_IMG_SEND_CMP_EVT: + break; + default: + break; + } +} + +static void btc_ble_mesh_dfu_client_free_req_data(btc_msg_t *msg) +{ + esp_ble_mesh_dfu_client_cb_param_t *arg = NULL; + + if (!msg) { + BT_ERR("%s, Invalid parameter", __func__); + return; + } + + if (msg->act >= ESP_BLE_MESH_DFU_CLIENT_EVT_MAX) { + BT_ERR("%s, Invalid event %d", msg->act); + return; + } + + arg = (esp_ble_mesh_dfu_client_cb_param_t *)msg->arg; + + switch (msg->act) { + case ESP_BLE_MESH_DFU_CLIENT_RECV_GET_RSP_EVT: + if (arg->params) { + switch (arg->params->opcode) { + case ESP_BLE_MESH_DFU_OP_UPDATE_INFO_GET: + bt_mesh_free(arg->recv.dfu_update_info_status.fw_info_list); + break; + default: + break; + } + } + case ESP_BLE_MESH_DFU_CLIENT_SEND_COMP_EVT: + case ESP_BLE_MESH_DFU_CLIENT_TIMEOUT_EVT: + case ESP_BLE_MESH_DFU_CLIENT_IMG_SEND_CMP_EVT: + break; + default: + BT_WARN("Unprocessed event %d", msg->act); + break; + } + + if (arg->params) { + bt_mesh_free(arg->params); + } + + return; +} + +static void btc_ble_mesh_dfu_client_cb(esp_ble_mesh_dfu_client_cb_param_t *cb_params, uint8_t act) +{ + btc_msg_t msg = {0}; + + /* If corresponding callback is not registered, event will not be posted. */ + if (!btc_profile_cb_get(BTC_PID_DFU_CLIENT)) { + return; + } + + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_DFU_CLIENT; + msg.act = act; + + btc_transfer_context(&msg, cb_params, sizeof(esp_ble_mesh_dfu_client_cb_param_t), + btc_ble_mesh_dfu_client_copy_req_data, + btc_ble_mesh_dfu_client_free_req_data); +} + +void bt_mesh_dfu_client_cb_evt_to_btc(uint32_t opcode, uint8_t event, + struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + const uint8_t *val, size_t len) +{ + esp_ble_mesh_dfu_client_cb_param_t cb_params = {0}; + esp_ble_mesh_client_common_param_t params = {0}; + uint8_t act = 0; + + if (!model || !ctx || len > sizeof(cb_params.recv)) { + BT_ERR("%s, Invalid parameter", __func__); + return; + } + + switch (event) { + case BTC_BLE_MESH_EVT_DFU_CLIENT_TIMEOUT: + act = ESP_BLE_MESH_DFU_CLIENT_TIMEOUT_EVT; + break; + case BTC_BLE_MESH_EVT_DFU_CLIENT_RECV_GET_RSP: + act = ESP_BLE_MESH_DFU_CLIENT_RECV_GET_RSP_EVT; + break; + case BTC_BLE_MESH_EVT_DFU_CLIENT_IMG_SEND_CMP: + act = ESP_BLE_MESH_DFU_CLIENT_IMG_SEND_CMP_EVT; + break; + default: + BT_ERR("Unknown Device Firmware Update client event type %d", event); + return; + } + + params.opcode = opcode; + params.model = (esp_ble_mesh_model_t *)model; + params.ctx.net_idx = ctx->net_idx; + params.ctx.app_idx = ctx->app_idx; + params.ctx.addr = ctx->addr; + params.ctx.recv_ttl = ctx->recv_ttl; + params.ctx.recv_op = ctx->recv_op; + params.ctx.recv_dst = ctx->recv_dst; + params.ctx.recv_rssi = ctx->recv_rssi; + params.ctx.send_ttl = ctx->send_ttl; + + cb_params.params = ¶ms; + + if (val && len) { + memcpy(&cb_params.recv, val, len); + } + + btc_ble_mesh_dfu_client_cb(&cb_params, act); +} + +static int btc_ble_mesh_dfu_client_get_state(esp_ble_mesh_client_common_param_t *params, + esp_ble_mesh_dfu_client_get_t *get) +{ + bt_mesh_client_common_param_t param = {0}; + struct bt_mesh_dfu_cli *dfu_cli = NULL; + + if (params == NULL || + params->model == NULL || + params->model->user_data == NULL) { + BT_ERR("%s, Invalid parameter %d %p %p", __func__, params == NULL, params->model, params->model->user_data); + return -EINVAL; + } + + dfu_cli = (struct bt_mesh_dfu_cli *)params->model->user_data; + + switch (params->opcode) { + case ESP_BLE_MESH_DFU_OP_UPDATE_INFO_GET: + case ESP_BLE_MESH_DFU_OP_UPDATE_METADATA_CHECK: + case ESP_BLE_MESH_DFU_OP_UPDATE_START: + if (get == NULL) { + BT_ERR("Invalid Device Firmware Update Get"); + return -EINVAL; + } + break; + default: + break; + } + + btc_ble_mesh_set_client_common_param(params, ¶m, false); + + switch (param.opcode) { + case ESP_BLE_MESH_DFU_OP_UPDATE_INFO_GET: + return bt_mesh_dfu_cli_imgs_get(dfu_cli, ¶m.ctx, (bt_mesh_dfu_img_cb_t) get->dfu_update_info_get.img_cb, + NULL, get->dfu_update_info_get.first_index, + get->dfu_update_info_get.entries_limit); + case ESP_BLE_MESH_DFU_OP_UPDATE_METADATA_CHECK: + return bt_mesh_dfu_cli_metadata_check(dfu_cli, ¶m.ctx, get->dfu_metadata_check.update_firmware_index, + get->dfu_metadata_check.metadata); + case ESP_BLE_MESH_DFU_OP_UPDATE_GET: + return bt_mesh_dfu_cli_status_get(dfu_cli, ¶m.ctx); + case ESP_BLE_MESH_DFU_OP_UPDATE_START: + // automatic start + return 0; + case ESP_BLE_MESH_DFU_OP_UPDATE_CANCEL: + return bt_mesh_dfu_cli_cancel(dfu_cli, ¶m.ctx); + case ESP_BLE_MESH_DFU_OP_UPDATE_APPLY: + return bt_mesh_dfu_cli_apply(dfu_cli); + default: + BT_ERR("Invalid Device firmware Get opcode 0x%04x", param.opcode); + return -EINVAL; + } +} + +void btc_ble_mesh_dfu_client_call_handler(btc_msg_t *msg) +{ + esp_ble_mesh_dfu_client_cb_param_t cb = {0}; + btc_ble_mesh_dfu_client_args_t *arg = NULL; + + if (!msg) { + BT_ERR("%s, Invalid parameter", __func__); + return; + } + + arg = (btc_ble_mesh_dfu_client_args_t *)msg->arg; + + switch (msg->act) { + case BTC_BLE_MESH_ACT_DFU_CLIENT_GET_STATE: + cb.params = arg->dfu_get.params; + cb.send.err_code = btc_ble_mesh_dfu_client_get_state(arg->dfu_get.params, + arg->dfu_get.get); + btc_ble_mesh_dfu_client_cb(&cb, + ESP_BLE_MESH_DFU_CLIENT_SEND_COMP_EVT); + break; + case BTC_BLE_MESH_ACT_DFU_CLIENT_IMG_SEND: + cb.send.err_code = bt_mesh_dfu_cli_send((struct bt_mesh_dfu_cli *)arg->send_arg.cli, + (struct bt_mesh_blob_cli_inputs *)arg->send_arg.inputs, + (struct bt_mesh_blob_io *)arg->send_arg.io, + (struct bt_mesh_dfu_cli_xfer *)arg->send_arg.xfer); + btc_ble_mesh_dfu_client_cb(&cb, + ESP_BLE_MESH_DFU_CLIENT_IMG_SEND_CMP_EVT); + break; + default: + break; + } + + btc_ble_mesh_dfu_client_arg_deep_free(msg); +} + +void btc_ble_mesh_dfu_client_cb_handler(btc_msg_t *msg) +{ + esp_ble_mesh_dfu_client_cb_param_t *arg = NULL; + + if (!msg) { + BT_ERR("%s, Invalid parameter", __func__); + return; + } + + arg = (esp_ble_mesh_dfu_client_cb_param_t *)msg->arg; + + if (msg->act < ESP_BLE_MESH_DFU_CLIENT_EVT_MAX) { + btc_ble_mesh_dfu_client_cb_to_app(msg->act, arg); + } else { + BT_ERR("%s, Unknown act %d", __func__, msg->act); + } + + btc_ble_mesh_dfu_client_free_req_data(msg); +} + +uint8_t btc_ble_mesh_dfu_cli_progress(struct esp_ble_mesh_dfu_cli *cli) +{ + return bt_mesh_dfu_cli_progress((struct bt_mesh_dfu_cli *)cli); +} + +#endif /* CONFIG_BLE_MESH_DFU_CLI */ + +#if CONFIG_BLE_MESH_DFU_SRV + +void btc_ble_mesh_dfu_srv_verified(esp_ble_mesh_dfu_srv_t *srv) +{ + bt_mesh_dfu_srv_verified((struct bt_mesh_dfu_srv *)srv); +} + +void btc_ble_mesh_dfu_srv_rejected(esp_ble_mesh_dfu_srv_t *srv) +{ + bt_mesh_dfu_srv_rejected((struct bt_mesh_dfu_srv *)srv); +} + +void btc_ble_mesh_dfu_srv_cancel(esp_ble_mesh_dfu_srv_t *srv) +{ + bt_mesh_dfu_srv_cancel((struct bt_mesh_dfu_srv *)srv); +} + +void btc_ble_mesh_dfu_srv_applied(esp_ble_mesh_dfu_srv_t *srv) +{ + bt_mesh_dfu_srv_applied((struct bt_mesh_dfu_srv *)srv); +} + +bool btc_ble_mesh_dfu_srv_is_busy(const esp_ble_mesh_dfu_srv_t *srv) +{ + return bt_mesh_dfu_srv_is_busy((struct bt_mesh_dfu_srv *)srv); +} + +uint8_t btc_ble_mesh_dfu_srv_progress(const esp_ble_mesh_dfu_srv_t *srv) +{ + return bt_mesh_dfu_srv_progress((struct bt_mesh_dfu_srv *)srv); +} +#endif /* CONFIG_BLE_MESH_DFU_SRV */ diff --git a/components/bt/esp_ble_mesh/v1.1/btc/btc_ble_mesh_dfu_slot.c b/components/bt/esp_ble_mesh/v1.1/btc/btc_ble_mesh_dfu_slot.c new file mode 100644 index 0000000000..1c92a05567 --- /dev/null +++ b/components/bt/esp_ble_mesh/v1.1/btc/btc_ble_mesh_dfu_slot.c @@ -0,0 +1,79 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#include "dfu_slot.h" +#include "esp_ble_mesh_dfu_model_api.h" +#include "esp_ble_mesh_dfu_slot_api.h" +#include "btc_ble_mesh_dfu_slot.h" + +#if CONFIG_BLE_MESH_DFU_SLOTS +int btc_ble_mesh_dfu_slot_count(void) +{ + return bt_mesh_dfu_slot_count(); +} + +struct esp_ble_mesh_dfu_slot *btc_ble_mesh_dfu_slot_reserve(void) +{ + return (struct esp_ble_mesh_dfu_slot *)bt_mesh_dfu_slot_reserve(); +} + +int btc_ble_mesh_dfu_slot_info_set(struct esp_ble_mesh_dfu_slot *dfu_slot, size_t size, + const uint8_t *metadata, size_t metadata_len) +{ + return bt_mesh_dfu_slot_info_set((struct bt_mesh_dfu_slot *) dfu_slot, size, + metadata, metadata_len); +} + +int btc_ble_mesh_dfu_slot_fwid_set(struct esp_ble_mesh_dfu_slot *dfu_slot, + const uint8_t *fwid, size_t fwid_len) +{ + return bt_mesh_dfu_slot_fwid_set((struct bt_mesh_dfu_slot *) dfu_slot, fwid, + fwid_len); +} + +int btc_ble_mesh_dfu_slot_commit(struct esp_ble_mesh_dfu_slot *dfu_slot) +{ + return bt_mesh_dfu_slot_commit((struct bt_mesh_dfu_slot *) dfu_slot); +} + +void btc_ble_mesh_dfu_slot_release(const struct esp_ble_mesh_dfu_slot *dfu_slot) +{ + bt_mesh_dfu_slot_release((const struct bt_mesh_dfu_slot *)dfu_slot); +} + +int btc_ble_mesh_dfu_slot_del(const struct esp_ble_mesh_dfu_slot *slot) +{ + return bt_mesh_dfu_slot_del((const struct bt_mesh_dfu_slot *)slot); +} + +void btc_ble_mesh_dfu_slot_del_all(void) +{ + bt_mesh_dfu_slot_del_all(); +} + +const struct esp_ble_mesh_dfu_slot *btc_ble_mesh_dfu_slot_at(uint16_t img_idx) +{ + return (const struct esp_ble_mesh_dfu_slot *)bt_mesh_dfu_slot_at(img_idx); +} + +int btc_ble_mesh_dfu_slot_get(const uint8_t *fwid, size_t fwid_len, struct esp_ble_mesh_dfu_slot **slot) +{ + return bt_mesh_dfu_slot_get(fwid, fwid_len, (struct bt_mesh_dfu_slot **)slot); +} + +int btc_ble_mesh_dfu_slot_img_idx_get(const struct esp_ble_mesh_dfu_slot *slot) +{ + return bt_mesh_dfu_slot_img_idx_get((const struct bt_mesh_dfu_slot *)slot); +} + +size_t btc_ble_mesh_dfu_slot_foreach(esp_ble_mesh_dfu_slot_cb_t cb, void *user_data) +{ + return bt_mesh_dfu_slot_foreach((bt_mesh_dfu_slot_cb_t)cb, user_data); +} +#endif /* CONFIG_BLE_MESH_DFU_SLOTS */ diff --git a/components/bt/esp_ble_mesh/v1.1/btc/include/btc_ble_mesh_dfu_model.h b/components/bt/esp_ble_mesh/v1.1/btc/include/btc_ble_mesh_dfu_model.h new file mode 100644 index 0000000000..a74aeb5453 --- /dev/null +++ b/components/bt/esp_ble_mesh/v1.1/btc/include/btc_ble_mesh_dfu_model.h @@ -0,0 +1,74 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef _BTC_BLE_MESH_DFU_MODEL_H_ +#define _BTC_BLE_MESH_DFU_MODEL_H_ + +#include "btc/btc_manage.h" +#include "esp_ble_mesh_dfu_model_api.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#if CONFIG_BLE_MESH_DFU_CLI +typedef enum { + BTC_BLE_MESH_ACT_DFU_CLIENT_GET_STATE, + BTC_BLE_MESH_ACT_DFU_CLIENT_IMG_SEND, + BTC_BLE_MESH_ACT_DFU_CLIENT_MAX, +} btc_ble_mesh_dfu_client_act_t; + +typedef union { + struct { + esp_ble_mesh_client_common_param_t *params; + esp_ble_mesh_dfu_client_get_t *get; + } dfu_get; + struct { + struct esp_ble_mesh_dfu_cli *cli; + struct esp_ble_mesh_blob_cli_inputs *inputs; + struct esp_ble_mesh_blob_io *io; + struct esp_ble_mesh_dfu_cli_xfer *xfer; + } send_arg; +} btc_ble_mesh_dfu_client_args_t; + +typedef enum { + BTC_BLE_MESH_EVT_DFU_CLIENT_TIMEOUT, + BTC_BLE_MESH_EVT_DFU_CLIENT_RECV_GET_RSP, + BTC_BLE_MESH_EVT_DFU_CLIENT_IMG_SEND_CMP, + BTC_BLE_MESH_EVT_DFU_CLIENT_MAX, +} btc_ble_mesh_dfu_client_cb_evt_t; + +void btc_ble_mesh_dfu_client_arg_deep_copy(btc_msg_t *msg, void *p_dest, void *p_src); +void btc_ble_mesh_dfu_client_arg_deep_free(btc_msg_t *msg); +void bt_mesh_dfu_client_cb_evt_to_btc(uint32_t opcode, uint8_t event, + struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + const uint8_t *val, size_t len); +void btc_ble_mesh_dfu_client_call_handler(btc_msg_t *msg); +void btc_ble_mesh_dfu_client_cb_handler(btc_msg_t *msg); +uint8_t btc_ble_mesh_dfu_cli_progress(struct esp_ble_mesh_dfu_cli *cli); +#endif /* CONFIG_BLE_MESH_DFU_CLI */ + +#if CONFIG_BLE_MESH_DFU_SRV + +void btc_ble_mesh_dfu_srv_verified(esp_ble_mesh_dfu_srv_t *srv); + +void btc_ble_mesh_dfu_srv_rejected(esp_ble_mesh_dfu_srv_t *srv); + +void btc_ble_mesh_dfu_srv_cancel(esp_ble_mesh_dfu_srv_t *srv); + +void btc_ble_mesh_dfu_srv_applied(esp_ble_mesh_dfu_srv_t *srv); + +bool btc_ble_mesh_dfu_srv_is_busy(const esp_ble_mesh_dfu_srv_t *srv); + +uint8_t btc_ble_mesh_dfu_srv_progress(const esp_ble_mesh_dfu_srv_t *srv); +#endif /* CONFIG_BLE_MESH_DFU_SRV */ + +#ifdef __cplusplus +} +#endif + +#endif /* _BTC_BLE_MESH_DFU_MODEL_H_ */ diff --git a/components/bt/esp_ble_mesh/v1.1/btc/include/btc_ble_mesh_dfu_slot.h b/components/bt/esp_ble_mesh/v1.1/btc/include/btc_ble_mesh_dfu_slot.h new file mode 100644 index 0000000000..90867586c9 --- /dev/null +++ b/components/bt/esp_ble_mesh/v1.1/btc/include/btc_ble_mesh_dfu_slot.h @@ -0,0 +1,36 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef _BTC_BLE_MESH_DFU_SLOT_H_ +#define _BTC_BLE_MESH_DFU_SLOT_H_ + +int btc_ble_mesh_dfu_slot_count(void); + +struct esp_ble_mesh_dfu_slot *btc_ble_mesh_dfu_slot_reserve(void); + +int btc_ble_mesh_dfu_slot_info_set(struct esp_ble_mesh_dfu_slot *dfu_slot, size_t size, + const uint8_t *metadata, size_t metadata_len); + +int btc_ble_mesh_dfu_slot_fwid_set(struct esp_ble_mesh_dfu_slot *dfu_slot, + const uint8_t *fwid, size_t fwid_len); + +int btc_ble_mesh_dfu_slot_commit(struct esp_ble_mesh_dfu_slot *dfu_slot); + +void btc_ble_mesh_dfu_slot_release(const struct esp_ble_mesh_dfu_slot *dfu_slot); + +int btc_ble_mesh_dfu_slot_del(const struct esp_ble_mesh_dfu_slot *slot); + +void btc_ble_mesh_dfu_slot_del_all(void); + +const struct esp_ble_mesh_dfu_slot *btc_ble_mesh_dfu_slot_at(uint16_t img_idx); + +int btc_ble_mesh_dfu_slot_get(const uint8_t *fwid, size_t fwid_len, struct esp_ble_mesh_dfu_slot **slot); + +int btc_ble_mesh_dfu_slot_img_idx_get(const struct esp_ble_mesh_dfu_slot *slot); + +size_t btc_ble_mesh_dfu_slot_foreach(esp_ble_mesh_dfu_slot_cb_t cb, void *user_data); + +#endif /* _BTC_BLE_MESH_DFU_SLOT_H_ */ diff --git a/components/bt/esp_ble_mesh/v1.1/btc/include/btc_ble_mesh_mbt_model.h b/components/bt/esp_ble_mesh/v1.1/btc/include/btc_ble_mesh_mbt_model.h index 070888ca55..4bb82ed1a3 100644 --- a/components/bt/esp_ble_mesh/v1.1/btc/include/btc_ble_mesh_mbt_model.h +++ b/components/bt/esp_ble_mesh/v1.1/btc/include/btc_ble_mesh_mbt_model.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2020-2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2020-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -14,6 +14,11 @@ extern "C" { #endif +#if CONFIG_IDF_CI_BUILD +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" +#endif + typedef enum { BTC_BLE_MESH_ACT_MBT_CLIENT_RETRIEVE_CAPABILITIES, BTC_BLE_MESH_ACT_MBT_CLIENT_TRANSFER_BLOB, @@ -136,6 +141,10 @@ void bt_mesh_mbt_server_cb_evt_to_btc(uint8_t event, struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx); +#if CONFIG_IDF_CI_BUILD +#pragma GCC diagnostic pop +#endif + #ifdef __cplusplus } #endif diff --git a/components/bt/esp_ble_mesh/v1.1/dfu/dfd.h b/components/bt/esp_ble_mesh/v1.1/dfu/dfd.h index 3af9af7214..ee94be24e6 100644 --- a/components/bt/esp_ble_mesh/v1.1/dfu/dfd.h +++ b/components/bt/esp_ble_mesh/v1.1/dfu/dfd.h @@ -1,29 +1,35 @@ /* - * Copyright (c) 2020 Nordic Semiconductor ASA + * SPDX-FileCopyrightText: 2020 Nordic Semiconductor ASA + * SPDX-FileContributor: 2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ -#define BT_MESH_DFD_OP_RECEIVERS_ADD BT_MESH_MODEL_OP_2(0x83, 0x11) -#define BT_MESH_DFD_OP_RECEIVERS_DELETE_ALL BT_MESH_MODEL_OP_2(0x83, 0x12) -#define BT_MESH_DFD_OP_RECEIVERS_STATUS BT_MESH_MODEL_OP_2(0x83, 0x13) -#define BT_MESH_DFD_OP_RECEIVERS_GET BT_MESH_MODEL_OP_2(0x83, 0x14) -#define BT_MESH_DFD_OP_RECEIVERS_LIST BT_MESH_MODEL_OP_2(0x83, 0x15) -#define BT_MESH_DFD_OP_CAPABILITIES_GET BT_MESH_MODEL_OP_2(0x83, 0x16) -#define BT_MESH_DFD_OP_CAPABILITIES_STATUS BT_MESH_MODEL_OP_2(0x83, 0x17) -#define BT_MESH_DFD_OP_GET BT_MESH_MODEL_OP_2(0x83, 0x18) -#define BT_MESH_DFD_OP_START BT_MESH_MODEL_OP_2(0x83, 0x19) -#define BT_MESH_DFD_OP_SUSPEND BT_MESH_MODEL_OP_2(0x83, 0x1a) -#define BT_MESH_DFD_OP_CANCEL BT_MESH_MODEL_OP_2(0x83, 0x1b) -#define BT_MESH_DFD_OP_APPLY BT_MESH_MODEL_OP_2(0x83, 0x1c) -#define BT_MESH_DFD_OP_STATUS BT_MESH_MODEL_OP_2(0x83, 0x1d) -#define BT_MESH_DFD_OP_UPLOAD_GET BT_MESH_MODEL_OP_2(0x83, 0x1e) -#define BT_MESH_DFD_OP_UPLOAD_START BT_MESH_MODEL_OP_2(0x83, 0x1f) -#define BT_MESH_DFD_OP_UPLOAD_START_OOB BT_MESH_MODEL_OP_2(0x83, 0x20) -#define BT_MESH_DFD_OP_UPLOAD_CANCEL BT_MESH_MODEL_OP_2(0x83, 0x21) -#define BT_MESH_DFD_OP_UPLOAD_STATUS BT_MESH_MODEL_OP_2(0x83, 0x22) -#define BT_MESH_DFD_OP_FW_GET BT_MESH_MODEL_OP_2(0x83, 0x23) -#define BT_MESH_DFD_OP_FW_GET_BY_INDEX BT_MESH_MODEL_OP_2(0x83, 0x24) -#define BT_MESH_DFD_OP_FW_DELETE BT_MESH_MODEL_OP_2(0x83, 0x25) -#define BT_MESH_DFD_OP_FW_DELETE_ALL BT_MESH_MODEL_OP_2(0x83, 0x26) -#define BT_MESH_DFD_OP_FW_STATUS BT_MESH_MODEL_OP_2(0x83, 0x27) +#include "access.h" + +/* @todo: adapt to dfd client */ +#if CONFIG_BLE_MESH_DFD_SRV +#define BT_MESH_DFD_OP_RECEIVERS_ADD BLE_MESH_MODEL_OP_2(0x83, 0x11) +#define BT_MESH_DFD_OP_RECEIVERS_DELETE_ALL BLE_MESH_MODEL_OP_2(0x83, 0x12) +#define BT_MESH_DFD_OP_RECEIVERS_STATUS BLE_MESH_MODEL_OP_2(0x83, 0x13) +#define BT_MESH_DFD_OP_RECEIVERS_GET BLE_MESH_MODEL_OP_2(0x83, 0x14) +#define BT_MESH_DFD_OP_RECEIVERS_LIST BLE_MESH_MODEL_OP_2(0x83, 0x15) +#define BT_MESH_DFD_OP_CAPABILITIES_GET BLE_MESH_MODEL_OP_2(0x83, 0x16) +#define BT_MESH_DFD_OP_CAPABILITIES_STATUS BLE_MESH_MODEL_OP_2(0x83, 0x17) +#define BT_MESH_DFD_OP_GET BLE_MESH_MODEL_OP_2(0x83, 0x18) +#define BT_MESH_DFD_OP_START BLE_MESH_MODEL_OP_2(0x83, 0x19) +#define BT_MESH_DFD_OP_SUSPEND BLE_MESH_MODEL_OP_2(0x83, 0x1a) +#define BT_MESH_DFD_OP_CANCEL BLE_MESH_MODEL_OP_2(0x83, 0x1b) +#define BT_MESH_DFD_OP_APPLY BLE_MESH_MODEL_OP_2(0x83, 0x1c) +#define BT_MESH_DFD_OP_STATUS BLE_MESH_MODEL_OP_2(0x83, 0x1d) +#define BT_MESH_DFD_OP_UPLOAD_GET BLE_MESH_MODEL_OP_2(0x83, 0x1e) +#define BT_MESH_DFD_OP_UPLOAD_START BLE_MESH_MODEL_OP_2(0x83, 0x1f) +#define BT_MESH_DFD_OP_UPLOAD_START_OOB BLE_MESH_MODEL_OP_2(0x83, 0x20) +#define BT_MESH_DFD_OP_UPLOAD_CANCEL BLE_MESH_MODEL_OP_2(0x83, 0x21) +#define BT_MESH_DFD_OP_UPLOAD_STATUS BLE_MESH_MODEL_OP_2(0x83, 0x22) +#define BT_MESH_DFD_OP_FW_GET BLE_MESH_MODEL_OP_2(0x83, 0x23) +#define BT_MESH_DFD_OP_FW_GET_BY_INDEX BLE_MESH_MODEL_OP_2(0x83, 0x24) +#define BT_MESH_DFD_OP_FW_DELETE BLE_MESH_MODEL_OP_2(0x83, 0x25) +#define BT_MESH_DFD_OP_FW_DELETE_ALL BLE_MESH_MODEL_OP_2(0x83, 0x26) +#define BT_MESH_DFD_OP_FW_STATUS BLE_MESH_MODEL_OP_2(0x83, 0x27) +#endif diff --git a/components/bt/esp_ble_mesh/v1.1/dfu/dfd_srv.c b/components/bt/esp_ble_mesh/v1.1/dfu/dfd_srv.c index 4a395a175b..7821b21b88 100644 --- a/components/bt/esp_ble_mesh/v1.1/dfu/dfd_srv.c +++ b/components/bt/esp_ble_mesh/v1.1/dfu/dfd_srv.c @@ -1,1306 +1,1311 @@ /* - * Copyright (c) 2020 Nordic Semiconductor ASA + * SPDX-FileCopyrightText: 2020 Nordic Semiconductor ASA + * SPDX-FileContributor: 2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ - #include -#include -#include #include "dfu_slot.h" #include "dfd.h" #include "dfu.h" #include "dfd_srv_internal.h" #include "net.h" #include "transport.h" +#include "mesh_v1.1/dfu/dfd.h" +#include "mesh_v1.1/dfu/dfu_cli.h" +#include "mesh_v1.1/dfu/dfd_srv.h" +#include "mesh_v1.1/mbt/blob_srv.h" -#define LOG_LEVEL CONFIG_BT_MESH_DFU_LOG_LEVEL -#include -LOG_MODULE_REGISTER(bt_mesh_dfd_srv); +#if CONFIG_BLE_MESH_DFD_SRV +#define DFD_UPLOAD_STATUS_MSG_MAXLEN (5 + CONFIG_BLE_MESH_DFU_FWID_MAXLEN) -#define ERASE_BLOCK_SIZE DT_PROP(DT_CHOSEN(zephyr_flash), erase_block_size) +_Static_assert((DFD_UPLOAD_STATUS_MSG_MAXLEN + BLE_MESH_MODEL_OP_LEN(BT_MESH_DFD_OP_UPLOAD_STATUS) + + BLE_MESH_MIC_SHORT) <= BLE_MESH_TX_SDU_MAX, + "The Firmware Distribution Upload Status message does not fit into the maximum " + "outgoing SDU size."); -#define DFD_UPLOAD_STATUS_MSG_MAXLEN (5 + CONFIG_BT_MESH_DFU_FWID_MAXLEN) +#define DFD_UPLOAD_START_MSG_MAXLEN (16 + CONFIG_BLE_MESH_DFU_FWID_MAXLEN + \ + CONFIG_BLE_MESH_DFU_METADATA_MAXLEN) -BUILD_ASSERT((DFD_UPLOAD_STATUS_MSG_MAXLEN + BT_MESH_MODEL_OP_LEN(BT_MESH_DFD_OP_UPLOAD_STATUS) + - BT_MESH_MIC_SHORT) <= BT_MESH_TX_SDU_MAX, - "The Firmware Distribution Upload Status message does not fit into the maximum " - "outgoing SDU size."); +_Static_assert((DFD_UPLOAD_START_MSG_MAXLEN + BLE_MESH_MODEL_OP_LEN(BT_MESH_DFD_OP_UPLOAD_START) + + BLE_MESH_MIC_SHORT) <= BLE_MESH_RX_SDU_MAX, + "The Firmware Distribution Upload Start message does not fit into the maximum " + "incoming SDU size."); -#define DFD_UPLOAD_START_MSG_MAXLEN (16 + CONFIG_BT_MESH_DFU_FWID_MAXLEN + \ - CONFIG_BT_MESH_DFU_METADATA_MAXLEN) +#define DFD_RECEIVERS_LIST_MSG_MAXLEN (4 + CONFIG_BLE_MESH_DFD_SRV_TARGETS_MAX * 5) -BUILD_ASSERT((DFD_UPLOAD_START_MSG_MAXLEN + BT_MESH_MODEL_OP_LEN(BT_MESH_DFD_OP_UPLOAD_START) + - BT_MESH_MIC_SHORT) <= BT_MESH_RX_SDU_MAX, - "The Firmware Distribution Upload Start message does not fit into the maximum " - "incoming SDU size."); +_Static_assert((DFD_RECEIVERS_LIST_MSG_MAXLEN + BLE_MESH_MODEL_OP_LEN(BT_MESH_DFD_OP_RECEIVERS_LIST) + + BLE_MESH_MIC_SHORT) <= BLE_MESH_TX_SDU_MAX, + "The Firmware Distribution Receivers List message does not fit into the maximum " + "outgoing SDU size."); -#define DFD_RECEIVERS_LIST_MSG_MAXLEN (4 + CONFIG_BT_MESH_DFD_SRV_TARGETS_MAX * 5) +#define DFD_RECEIVERS_ADD_MSG_MAXLEN (CONFIG_BLE_MESH_DFD_SRV_TARGETS_MAX * 3) -BUILD_ASSERT((DFD_RECEIVERS_LIST_MSG_MAXLEN + BT_MESH_MODEL_OP_LEN(BT_MESH_DFD_OP_RECEIVERS_LIST) + - BT_MESH_MIC_SHORT) <= BT_MESH_TX_SDU_MAX, - "The Firmware Distribution Receivers List message does not fit into the maximum " - "outgoing SDU size."); - -#define DFD_RECEIVERS_ADD_MSG_MAXLEN (CONFIG_BT_MESH_DFD_SRV_TARGETS_MAX * 3) - -BUILD_ASSERT((DFD_RECEIVERS_ADD_MSG_MAXLEN + BT_MESH_MODEL_OP_LEN(BT_MESH_DFD_OP_RECEIVERS_ADD) + - BT_MESH_MIC_SHORT) <= BT_MESH_RX_SDU_MAX, - "The Firmware Distribution Receivers Add message does not fit into the maximum " - "incoming SDU size."); +_Static_assert((DFD_RECEIVERS_ADD_MSG_MAXLEN + BLE_MESH_MODEL_OP_LEN(BT_MESH_DFD_OP_RECEIVERS_ADD) + + BLE_MESH_MIC_SHORT) <= BLE_MESH_RX_SDU_MAX, + "The Firmware Distribution Receivers Add message does not fit into the maximum " + "incoming SDU size."); struct slot_search_ctx { - off_t offset; - size_t size; - bool failed; + off_t offset; + size_t size; + bool failed; }; static void dfd_phase_set(struct bt_mesh_dfd_srv *srv, - enum bt_mesh_dfd_phase new_phase) + enum bt_mesh_dfd_phase new_phase) { - srv->phase = new_phase; + srv->phase = new_phase; - if (srv->cb && srv->cb->phase) { - srv->cb->phase(srv, srv->phase); - } + if (srv->cb && srv->cb->phase) { + srv->cb->phase(srv, srv->phase); + } } static struct bt_mesh_dfu_target *target_get(struct bt_mesh_dfd_srv *srv, - uint16_t addr) + uint16_t addr) { - for (int i = 0; i < srv->target_cnt; ++i) { - if (addr == srv->targets[i].blob.addr) { - return &srv->targets[i]; - } - } + for (int i = 0; i < srv->target_cnt; ++i) { + if (addr == srv->targets[i].blob.addr) { + return &srv->targets[i]; + } + } - return NULL; + return NULL; } static bool is_busy(const struct bt_mesh_dfd_srv *srv) { - return srv->phase == BT_MESH_DFD_PHASE_TRANSFER_ACTIVE || - srv->phase == BT_MESH_DFD_PHASE_TRANSFER_SUCCESS || - srv->phase == BT_MESH_DFD_PHASE_APPLYING_UPDATE; + return srv->phase == BT_MESH_DFD_PHASE_TRANSFER_ACTIVE || + srv->phase == BT_MESH_DFD_PHASE_TRANSFER_SUCCESS || + srv->phase == BT_MESH_DFD_PHASE_APPLYING_UPDATE; } static bool upload_is_busy(const struct bt_mesh_dfd_srv *srv) { - return bt_mesh_blob_srv_is_busy(&srv->upload.blob) || - srv->upload.phase == BT_MESH_DFD_UPLOAD_PHASE_TRANSFER_ACTIVE; + return bt_mesh_blob_srv_is_busy(&srv->upload.blob) || + srv->upload.phase == BT_MESH_DFD_UPLOAD_PHASE_TRANSFER_ACTIVE; } static int slot_del(struct bt_mesh_dfd_srv *srv, const struct bt_mesh_dfu_slot *slot) { - if (srv->cb && srv->cb->del) { - srv->cb->del(srv, slot); - } + if (srv->cb && srv->cb->del) { + srv->cb->del(srv, slot); + } - return bt_mesh_dfu_slot_del(slot); + return bt_mesh_dfu_slot_del(slot); } static void receivers_status_rsp(struct bt_mesh_dfd_srv *srv, - struct bt_mesh_msg_ctx *ctx, - enum bt_mesh_dfd_status status) + struct bt_mesh_msg_ctx *ctx, + enum bt_mesh_dfd_status status) { - BT_MESH_MODEL_BUF_DEFINE(buf, BT_MESH_DFD_OP_RECEIVERS_STATUS, 3); - bt_mesh_model_msg_init(&buf, BT_MESH_DFD_OP_RECEIVERS_STATUS); + BLE_MESH_MODEL_BUF_DEFINE(buf, BT_MESH_DFD_OP_RECEIVERS_STATUS, 3); + bt_mesh_model_msg_init(&buf, BT_MESH_DFD_OP_RECEIVERS_STATUS); - net_buf_simple_add_u8(&buf, status); - net_buf_simple_add_le16(&buf, srv->target_cnt); + net_buf_simple_add_u8(&buf, status); + net_buf_simple_add_le16(&buf, srv->target_cnt); - bt_mesh_model_send(srv->mod, ctx, &buf, NULL, NULL); + bt_mesh_model_send(srv->mod, ctx, &buf, NULL, NULL); } static int handle_receivers_add(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx, - struct net_buf_simple *buf) + struct net_buf_simple *buf) { - enum bt_mesh_dfd_status status = BT_MESH_DFD_SUCCESS; - struct bt_mesh_dfd_srv *srv = mod->rt->user_data; + enum bt_mesh_dfd_status status = BT_MESH_DFD_SUCCESS; + struct bt_mesh_dfd_srv *srv = mod->user_data; - if (buf->len % 3) { - return -EINVAL; - } + if (buf->len % 3) { + return -EINVAL; + } - if (bt_mesh_dfu_cli_is_busy(&srv->dfu)) { - receivers_status_rsp(srv, ctx, - BT_MESH_DFD_ERR_BUSY_WITH_DISTRIBUTION); - return 0; - } + if (bt_mesh_dfu_cli_is_busy(&srv->dfu)) { + receivers_status_rsp(srv, ctx, + BT_MESH_DFD_ERR_BUSY_WITH_DISTRIBUTION); + return 0; + } - while (buf->len >= 3 && status == BT_MESH_DFD_SUCCESS) { - uint8_t img_idx; - uint16_t addr; + while (buf->len >= 3 && status == BT_MESH_DFD_SUCCESS) { + uint8_t img_idx; + uint16_t addr; - addr = net_buf_simple_pull_le16(buf); - img_idx = net_buf_simple_pull_u8(buf); + addr = net_buf_simple_pull_le16(buf); + img_idx = net_buf_simple_pull_u8(buf); - status = bt_mesh_dfd_srv_receiver_add(srv, addr, img_idx); - } + status = bt_mesh_dfd_srv_receiver_add(srv, addr, img_idx); + } - receivers_status_rsp(srv, ctx, status); + receivers_status_rsp(srv, ctx, status); - return 0; + return 0; } static int handle_receivers_delete_all(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx, - struct net_buf_simple *buf) + struct net_buf_simple *buf) { - struct bt_mesh_dfd_srv *srv = mod->rt->user_data; + struct bt_mesh_dfd_srv *srv = mod->user_data; - receivers_status_rsp(srv, ctx, bt_mesh_dfd_srv_receivers_delete_all(srv)); + receivers_status_rsp(srv, ctx, bt_mesh_dfd_srv_receivers_delete_all(srv)); - return 0; + return 0; } static int handle_receivers_get(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx, - struct net_buf_simple *buf) + struct net_buf_simple *buf) { - struct bt_mesh_dfd_srv *srv = mod->rt->user_data; - uint16_t first, cnt; - uint8_t progress; - int i; + struct bt_mesh_dfd_srv *srv = mod->user_data; + uint16_t first, cnt; + uint8_t progress; + int i; - first = net_buf_simple_pull_le16(buf); - cnt = net_buf_simple_pull_le16(buf); - if (cnt == 0) { - return -EINVAL; - } + first = net_buf_simple_pull_le16(buf); + cnt = net_buf_simple_pull_le16(buf); + if (cnt == 0) { + return -EINVAL; + } - /* Create a buffer that can fit the full target list, maxing out at TX_SDU_MAX: */ - NET_BUF_SIMPLE_DEFINE( - rsp, BT_MESH_MODEL_BUF_LEN(BT_MESH_DFD_OP_RECEIVERS_LIST, - DFD_RECEIVERS_LIST_MSG_MAXLEN)); - bt_mesh_model_msg_init(&rsp, BT_MESH_DFD_OP_RECEIVERS_LIST); + /* Create a buffer that can fit the full target list, maxing out at TX_SDU_MAX: */ + NET_BUF_SIMPLE_DEFINE( + rsp, BLE_MESH_MODEL_BUF_LEN(BT_MESH_DFD_OP_RECEIVERS_LIST, + DFD_RECEIVERS_LIST_MSG_MAXLEN)); + bt_mesh_model_msg_init(&rsp, BT_MESH_DFD_OP_RECEIVERS_LIST); - net_buf_simple_add_le16(&rsp, srv->target_cnt); - net_buf_simple_add_le16(&rsp, first); + net_buf_simple_add_le16(&rsp, srv->target_cnt); + net_buf_simple_add_le16(&rsp, first); - cnt = MIN(cnt, srv->target_cnt - first); - progress = bt_mesh_dfu_cli_progress(&srv->dfu) / 2; + cnt = MIN(cnt, srv->target_cnt - first); + progress = bt_mesh_dfu_cli_progress(&srv->dfu) / 2; - for (i = 0; i < cnt && net_buf_simple_tailroom(&rsp) >= 5 + BT_MESH_MIC_SHORT; i++) { - const struct bt_mesh_dfu_target *t = &srv->targets[i + first]; + for (i = 0; i < cnt && net_buf_simple_tailroom(&rsp) >= 5 + BLE_MESH_MIC_SHORT; i++) { + const struct bt_mesh_dfu_target *t = &srv->targets[i + first]; - net_buf_simple_add_le32( - &rsp, ((t->blob.addr & BIT_MASK(15)) | - ((t->phase & BIT_MASK(4)) << 15U) | - ((t->status & BIT_MASK(3)) << 19U) | - ((t->blob.status & BIT_MASK(4)) << 22U) | - ((progress & BIT_MASK(6)) << 26U))); - net_buf_simple_add_u8(&rsp, t->img_idx); - } + net_buf_simple_add_le32( + &rsp, ((t->blob.addr & BIT_MASK(15)) | + ((t->phase & BIT_MASK(4)) << 15U) | + ((t->status & BIT_MASK(3)) << 19U) | + ((t->blob.status & BIT_MASK(4)) << 22U) | + ((progress & BIT_MASK(6)) << 26U))); + net_buf_simple_add_u8(&rsp, t->img_idx); + } - bt_mesh_model_send(srv->mod, ctx, &rsp, NULL, NULL); + bt_mesh_model_send(srv->mod, ctx, &rsp, NULL, NULL); - return 0; + return 0; } static enum bt_mesh_dfu_iter slot_space_cb(const struct bt_mesh_dfu_slot *slot, - void *user_data) + void *user_data) { - size_t *total = user_data; + size_t *total = user_data; - *total += slot->size; + *total += slot->size; - return BT_MESH_DFU_ITER_CONTINUE; + return BLE_MESH_DFU_ITER_CONTINUE; } static int handle_capabilities_get(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx, - struct net_buf_simple *buf) + struct net_buf_simple *buf) { - size_t size = 0; + size_t size = 0; - BT_MESH_MODEL_BUF_DEFINE(rsp, BT_MESH_DFD_OP_CAPABILITIES_STATUS, 17); - bt_mesh_model_msg_init(&rsp, BT_MESH_DFD_OP_CAPABILITIES_STATUS); + BLE_MESH_MODEL_BUF_DEFINE(rsp, BT_MESH_DFD_OP_CAPABILITIES_STATUS, 17); + bt_mesh_model_msg_init(&rsp, BT_MESH_DFD_OP_CAPABILITIES_STATUS); - net_buf_simple_add_le16(&rsp, CONFIG_BT_MESH_DFD_SRV_TARGETS_MAX); - net_buf_simple_add_le16(&rsp, CONFIG_BT_MESH_DFU_SLOT_CNT); - net_buf_simple_add_le32(&rsp, CONFIG_BT_MESH_DFD_SRV_SLOT_MAX_SIZE); - net_buf_simple_add_le32(&rsp, CONFIG_BT_MESH_DFD_SRV_SLOT_SPACE); + net_buf_simple_add_le16(&rsp, CONFIG_BLE_MESH_DFD_SRV_TARGETS_MAX); + net_buf_simple_add_le16(&rsp, CONFIG_BLE_MESH_DFU_SLOT_CNT); + net_buf_simple_add_le32(&rsp, CONFIG_BLE_MESH_DFD_SRV_SLOT_MAX_SIZE); + net_buf_simple_add_le32(&rsp, CONFIG_BLE_MESH_DFD_SRV_SLOT_SPACE); - /* Remaining size */ - (void)bt_mesh_dfu_slot_foreach(slot_space_cb, &size); - size = MIN(size, CONFIG_BT_MESH_DFD_SRV_SLOT_SPACE); + /* Remaining size */ + (void)bt_mesh_dfu_slot_foreach(slot_space_cb, &size); + size = MIN(size, CONFIG_BLE_MESH_DFD_SRV_SLOT_SPACE); - net_buf_simple_add_le32(&rsp, CONFIG_BT_MESH_DFD_SRV_SLOT_SPACE - size); + net_buf_simple_add_le32(&rsp, CONFIG_BLE_MESH_DFD_SRV_SLOT_SPACE - size); -#ifdef CONFIG_BT_MESH_DFD_SRV_OOB_UPLOAD - struct bt_mesh_dfd_srv *srv = mod->rt->user_data; +#ifdef CONFIG_BLE_MESH_DFD_SRV_OOB_UPLOAD + struct bt_mesh_dfd_srv *srv = mod->user_data; - if (srv->oob_schemes.count > 0) { - net_buf_simple_add_u8(&rsp, 1); - net_buf_simple_add_mem(&rsp, srv->oob_schemes.schemes, - srv->oob_schemes.count); - } else + if (srv->oob_schemes.count > 0) { + net_buf_simple_add_u8(&rsp, 1); + net_buf_simple_add_mem(&rsp, srv->oob_schemes.schemes, + srv->oob_schemes.count); + } else #endif - { - net_buf_simple_add_u8(&rsp, 0); - } + { + net_buf_simple_add_u8(&rsp, 0); + } - bt_mesh_model_send(mod, ctx, &rsp, NULL, NULL); + bt_mesh_model_send(mod, ctx, &rsp, NULL, NULL); - return 0; + return 0; } static void status_rsp(struct bt_mesh_dfd_srv *srv, struct bt_mesh_msg_ctx *ctx, - enum bt_mesh_dfd_status status) + enum bt_mesh_dfd_status status) { - BT_MESH_MODEL_BUF_DEFINE(rsp, BT_MESH_DFD_OP_STATUS, 12); - bt_mesh_model_msg_init(&rsp, BT_MESH_DFD_OP_STATUS); + BLE_MESH_MODEL_BUF_DEFINE(rsp, BT_MESH_DFD_OP_STATUS, 12); + bt_mesh_model_msg_init(&rsp, BT_MESH_DFD_OP_STATUS); - net_buf_simple_add_u8(&rsp, status); - net_buf_simple_add_u8(&rsp, srv->phase); + net_buf_simple_add_u8(&rsp, status); + net_buf_simple_add_u8(&rsp, srv->phase); - if (srv->phase == BT_MESH_DFD_PHASE_IDLE || !srv->dfu.xfer.slot) { - bt_mesh_model_send(srv->mod, ctx, &rsp, NULL, NULL); - return; - } + if (srv->phase == BT_MESH_DFD_PHASE_IDLE || !srv->dfu.xfer.slot) { + bt_mesh_model_send(srv->mod, ctx, &rsp, NULL, NULL); + return; + } - net_buf_simple_add_le16(&rsp, srv->inputs.group); - net_buf_simple_add_le16(&rsp, srv->inputs.app_idx); - net_buf_simple_add_u8(&rsp, srv->inputs.ttl); - net_buf_simple_add_le16(&rsp, srv->inputs.timeout_base); - net_buf_simple_add_u8(&rsp, ((srv->dfu.xfer.blob.mode & BIT_MASK(2)) | - ((srv->apply & BIT_MASK(1)) << 2))); - net_buf_simple_add_le16(&rsp, srv->slot_idx); + net_buf_simple_add_le16(&rsp, srv->inputs.group); + net_buf_simple_add_le16(&rsp, srv->inputs.app_idx); + net_buf_simple_add_u8(&rsp, srv->inputs.ttl); + net_buf_simple_add_le16(&rsp, srv->inputs.timeout_base); + net_buf_simple_add_u8(&rsp, ((srv->dfu.xfer.blob.mode & BIT_MASK(2)) | + ((srv->apply & BIT_MASK(1)) << 2))); + net_buf_simple_add_le16(&rsp, srv->slot_idx); - bt_mesh_model_send(srv->mod, ctx, &rsp, NULL, NULL); + bt_mesh_model_send(srv->mod, ctx, &rsp, NULL, NULL); } static int handle_get(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx, - struct net_buf_simple *buf) + struct net_buf_simple *buf) { - struct bt_mesh_dfd_srv *srv = mod->rt->user_data; + struct bt_mesh_dfd_srv *srv = mod->user_data; - status_rsp(srv, ctx, BT_MESH_DFD_SUCCESS); + status_rsp(srv, ctx, BT_MESH_DFD_SUCCESS); - return 0; + return 0; } static int handle_start(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx, - struct net_buf_simple *buf) + struct net_buf_simple *buf) { - struct bt_mesh_dfd_srv *srv = mod->rt->user_data; - struct bt_mesh_dfd_start_params params; - uint8_t byte; + struct bt_mesh_dfd_srv *srv = mod->user_data; + struct bt_mesh_dfd_start_params params; + uint8_t byte; - params.app_idx = net_buf_simple_pull_le16(buf); - params.ttl = net_buf_simple_pull_u8(buf); - params.timeout_base = net_buf_simple_pull_le16(buf); - byte = net_buf_simple_pull_u8(buf); - params.xfer_mode = byte & BIT_MASK(2); - params.apply = (byte >> 2U) & BIT_MASK(1); - params.slot_idx = net_buf_simple_pull_le16(buf); + params.app_idx = net_buf_simple_pull_le16(buf); + params.ttl = net_buf_simple_pull_u8(buf); + params.timeout_base = net_buf_simple_pull_le16(buf); + byte = net_buf_simple_pull_u8(buf); + params.xfer_mode = byte & BIT_MASK(2); + params.apply = (byte >> 2U) & BIT_MASK(1); + params.slot_idx = net_buf_simple_pull_le16(buf); - if (buf->len == 16) { - /* TODO: Virtual addresses not supported. */ - status_rsp(srv, ctx, BT_MESH_DFD_ERR_INTERNAL); - return 0; - } + if (buf->len == 16) { + /* TODO: Virtual addresses not supported. */ + status_rsp(srv, ctx, BT_MESH_DFD_ERR_INTERNAL); + return 0; + } - if (buf->len != 2) { - return -EINVAL; - } + if (buf->len != 2) { + return -EINVAL; + } - params.group = net_buf_simple_pull_le16(buf); + params.group = net_buf_simple_pull_le16(buf); - status_rsp(srv, ctx, bt_mesh_dfd_srv_start(srv, ¶ms)); + status_rsp(srv, ctx, bt_mesh_dfd_srv_start(srv, ¶ms)); - return 0; + return 0; } static int handle_suspend(const struct bt_mesh_model *mod, - struct bt_mesh_msg_ctx *ctx, - struct net_buf_simple *buf) + struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) { - struct bt_mesh_dfd_srv *srv = mod->rt->user_data; + struct bt_mesh_dfd_srv *srv = mod->user_data; - status_rsp(srv, ctx, bt_mesh_dfd_srv_suspend(srv)); + status_rsp(srv, ctx, bt_mesh_dfd_srv_suspend(srv)); - return 0; + return 0; } static int handle_cancel(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx, - struct net_buf_simple *buf) + struct net_buf_simple *buf) { - struct bt_mesh_dfd_srv *srv = mod->rt->user_data; + struct bt_mesh_dfd_srv *srv = mod->user_data; - bt_mesh_dfd_srv_cancel(srv, ctx); + bt_mesh_dfd_srv_cancel(srv, ctx); - return 0; + return 0; } static int handle_apply(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx, - struct net_buf_simple *buf) + struct net_buf_simple *buf) { - struct bt_mesh_dfd_srv *srv = mod->rt->user_data; + struct bt_mesh_dfd_srv *srv = mod->user_data; - status_rsp(srv, ctx, bt_mesh_dfd_srv_apply(srv)); + status_rsp(srv, ctx, bt_mesh_dfd_srv_apply(srv)); - return 0; + return 0; } static void upload_status_rsp_with_progress(struct bt_mesh_dfd_srv *srv, - struct bt_mesh_msg_ctx *ctx, - enum bt_mesh_dfd_status status, - uint8_t progress) + struct bt_mesh_msg_ctx *ctx, + enum bt_mesh_dfd_status status, + uint8_t progress) { - BT_MESH_MODEL_BUF_DEFINE(rsp, BT_MESH_DFD_OP_UPLOAD_STATUS, - DFD_UPLOAD_STATUS_MSG_MAXLEN); - bt_mesh_model_msg_init(&rsp, BT_MESH_DFD_OP_UPLOAD_STATUS); + BLE_MESH_MODEL_BUF_DEFINE(rsp, BT_MESH_DFD_OP_UPLOAD_STATUS, + DFD_UPLOAD_STATUS_MSG_MAXLEN); + bt_mesh_model_msg_init(&rsp, BT_MESH_DFD_OP_UPLOAD_STATUS); - net_buf_simple_add_u8(&rsp, status); - net_buf_simple_add_u8(&rsp, srv->upload.phase); + net_buf_simple_add_u8(&rsp, status); + net_buf_simple_add_u8(&rsp, srv->upload.phase); - if (srv->upload.phase == BT_MESH_DFD_UPLOAD_PHASE_IDLE || - !srv->upload.slot) { - bt_mesh_model_send(srv->mod, ctx, &rsp, NULL, NULL); - return; - } + if (srv->upload.phase == BT_MESH_DFD_UPLOAD_PHASE_IDLE || + !srv->upload.slot) { + bt_mesh_model_send(srv->mod, ctx, &rsp, NULL, NULL); + return; + } -#ifdef CONFIG_BT_MESH_DFD_SRV_OOB_UPLOAD - if (srv->upload.is_oob) { - net_buf_simple_add_u8(&rsp, progress | BIT(7)); - net_buf_simple_add_mem(&rsp, srv->upload.oob.current_fwid, - srv->upload.oob.current_fwid_len); - } else +#ifdef CONFIG_BLE_MESH_DFD_SRV_OOB_UPLOAD + if (srv->upload.is_oob) { + net_buf_simple_add_u8(&rsp, progress | BIT(7)); + net_buf_simple_add_mem(&rsp, srv->upload.oob.current_fwid, + srv->upload.oob.current_fwid_len); + } else #endif - { - net_buf_simple_add_u8(&rsp, progress); - net_buf_simple_add_mem(&rsp, srv->upload.slot->fwid, - srv->upload.slot->fwid_len); - } + { + net_buf_simple_add_u8(&rsp, progress); + net_buf_simple_add_mem(&rsp, srv->upload.slot->fwid, + srv->upload.slot->fwid_len); + } - bt_mesh_model_send(srv->mod, ctx, &rsp, NULL, NULL); + bt_mesh_model_send(srv->mod, ctx, &rsp, NULL, NULL); } static void upload_status_rsp(struct bt_mesh_dfd_srv *srv, - struct bt_mesh_msg_ctx *ctx, - enum bt_mesh_dfd_status status) + struct bt_mesh_msg_ctx *ctx, + enum bt_mesh_dfd_status status) { - uint8_t progress; + uint8_t progress; -#ifdef CONFIG_BT_MESH_DFD_SRV_OOB_UPLOAD - if (srv->upload.is_oob) { - progress = srv->cb->oob_progress_get(srv, srv->upload.slot); - } else +#ifdef CONFIG_BLE_MESH_DFD_SRV_OOB_UPLOAD + if (srv->upload.is_oob) { + progress = srv->cb->oob_progress_get(srv, srv->upload.slot); + } else #endif - { - progress = bt_mesh_blob_srv_progress(&srv->upload.blob); - } + { + progress = bt_mesh_blob_srv_progress(&srv->upload.blob); + } - upload_status_rsp_with_progress(srv, ctx, status, progress); + upload_status_rsp_with_progress(srv, ctx, status, progress); } static int handle_upload_get(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx, - struct net_buf_simple *buf) + struct net_buf_simple *buf) { - struct bt_mesh_dfd_srv *srv = mod->rt->user_data; + struct bt_mesh_dfd_srv *srv = mod->user_data; - upload_status_rsp(srv, ctx, BT_MESH_DFD_SUCCESS); + upload_status_rsp(srv, ctx, BT_MESH_DFD_SUCCESS); - return 0; + return 0; } static inline int set_upload_fwid(struct bt_mesh_dfd_srv *srv, struct bt_mesh_msg_ctx *ctx, - const uint8_t *fwid, size_t fwid_len) + const uint8_t *fwid, size_t fwid_len) { - int err = bt_mesh_dfu_slot_fwid_set(srv->upload.slot, fwid, fwid_len); + int err = bt_mesh_dfu_slot_fwid_set(srv->upload.slot, fwid, fwid_len); - switch (err) { - case -EFBIG: /* Fwid too long */ - case -EALREADY: /* Other server is in progress with this fwid */ - bt_mesh_dfu_slot_release(srv->upload.slot); - srv->upload.slot = NULL; - upload_status_rsp(srv, ctx, BT_MESH_DFD_ERR_INTERNAL); - break; - case -EEXIST: /* Img with this fwid already is in list */ - srv->upload.phase = BT_MESH_DFD_UPLOAD_PHASE_TRANSFER_SUCCESS; - bt_mesh_dfu_slot_release(srv->upload.slot); + switch (err) { + case -EFBIG: /* Fwid too long */ + case -EALREADY: /* Other server is in progress with this fwid */ + bt_mesh_dfu_slot_release(srv->upload.slot); + srv->upload.slot = NULL; + upload_status_rsp(srv, ctx, BT_MESH_DFD_ERR_INTERNAL); + break; + case -EEXIST: /* Img with this fwid already is in list */ + srv->upload.phase = BT_MESH_DFD_UPLOAD_PHASE_TRANSFER_SUCCESS; + bt_mesh_dfu_slot_release(srv->upload.slot); - err = bt_mesh_dfu_slot_get(fwid, fwid_len, &srv->upload.slot); - if (!err) { - upload_status_rsp_with_progress(srv, ctx, BT_MESH_DFD_SUCCESS, 100); - } else { - srv->upload.slot = NULL; - upload_status_rsp(srv, ctx, BT_MESH_DFD_ERR_INTERNAL); - } - break; - case 0: - srv->upload.phase = BT_MESH_DFD_UPLOAD_PHASE_TRANSFER_ACTIVE; - break; - case -EINVAL: /* Slot in wrong state. */ - default: - break; - } + err = bt_mesh_dfu_slot_get(fwid, fwid_len, &srv->upload.slot); + if (!err) { + upload_status_rsp_with_progress(srv, ctx, BT_MESH_DFD_SUCCESS, 100); + } else { + srv->upload.slot = NULL; + upload_status_rsp(srv, ctx, BT_MESH_DFD_ERR_INTERNAL); + } + break; + case 0: + srv->upload.phase = BT_MESH_DFD_UPLOAD_PHASE_TRANSFER_ACTIVE; + break; + case -EINVAL: /* Slot in wrong state. */ + default: + break; + } - return err; + return err; } static int handle_upload_start(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx, - struct net_buf_simple *buf) + struct net_buf_simple *buf) { - struct bt_mesh_dfd_srv *srv = mod->rt->user_data; - size_t meta_len, fwid_len, size; - const uint8_t *meta, *fwid; - uint16_t timeout_base; - uint64_t blob_id; - int err; - uint8_t ttl; + struct bt_mesh_dfd_srv *srv = mod->user_data; + size_t meta_len, fwid_len, size; + const uint8_t *meta, *fwid; + uint16_t timeout_base; + uint64_t blob_id; + int err; + uint8_t ttl; - ttl = net_buf_simple_pull_u8(buf); - timeout_base = net_buf_simple_pull_le16(buf); - blob_id = net_buf_simple_pull_le64(buf); - size = net_buf_simple_pull_le32(buf); - meta_len = net_buf_simple_pull_u8(buf); - if (buf->len < meta_len) { - return -EINVAL; - } + ttl = net_buf_simple_pull_u8(buf); + timeout_base = net_buf_simple_pull_le16(buf); + blob_id = net_buf_simple_pull_le64(buf); + size = net_buf_simple_pull_le32(buf); + meta_len = net_buf_simple_pull_u8(buf); + if (buf->len < meta_len) { + return -EINVAL; + } - meta = net_buf_simple_pull_mem(buf, meta_len); - fwid_len = buf->len; - fwid = net_buf_simple_pull_mem(buf, fwid_len); + meta = net_buf_simple_pull_mem(buf, meta_len); + fwid_len = buf->len; + fwid = net_buf_simple_pull_mem(buf, fwid_len); - LOG_DBG("Upload Start: size: %d, fwid: %s, metadata: %s", size, bt_hex(fwid, fwid_len), - bt_hex(meta, meta_len)); + BT_DBG("Upload Start: size: %d, fwid: %s, metadata: %s", size, bt_hex(fwid, fwid_len), + bt_hex(meta, meta_len)); - if (size > CONFIG_BT_MESH_DFD_SRV_SLOT_MAX_SIZE) { - upload_status_rsp(srv, ctx, - BT_MESH_DFD_ERR_INSUFFICIENT_RESOURCES); - return 0; - } + if (size > CONFIG_BLE_MESH_DFD_SRV_SLOT_MAX_SIZE) { + upload_status_rsp(srv, ctx, + BT_MESH_DFD_ERR_INSUFFICIENT_RESOURCES); + return 0; + } - if (upload_is_busy(srv)) { - if (!srv->upload.slot) { - LOG_WRN("Busy with no upload slot"); - upload_status_rsp(srv, ctx, BT_MESH_DFD_ERR_INTERNAL); - return 0; - } + if (upload_is_busy(srv)) { + if (!srv->upload.slot) { + BT_WARN("Busy with no upload slot"); + upload_status_rsp(srv, ctx, BT_MESH_DFD_ERR_INTERNAL); + return 0; + } - if (srv->upload.slot->fwid_len == fwid_len && - !memcmp(srv->upload.slot->fwid, fwid, fwid_len) && - srv->upload.slot->metadata_len == meta_len && - !memcmp(srv->upload.slot->metadata, meta, meta_len) && - srv->upload.blob.state.xfer.id == blob_id && - srv->upload.blob.state.ttl == ttl && - srv->upload.blob.state.timeout_base == timeout_base -#ifdef CONFIG_BT_MESH_DFD_SRV_OOB_UPLOAD - && !srv->upload.is_oob + if (srv->upload.slot->fwid_len == fwid_len && + !memcmp(srv->upload.slot->fwid, fwid, fwid_len) && + srv->upload.slot->metadata_len == meta_len && + !memcmp(srv->upload.slot->metadata, meta, meta_len) && + srv->upload.blob.state.xfer.id == blob_id && + srv->upload.blob.state.ttl == ttl && + srv->upload.blob.state.timeout_base == timeout_base +#ifdef CONFIG_BLE_MESH_DFD_SRV_OOB_UPLOAD + && !srv->upload.is_oob #endif - ) { - LOG_DBG("Duplicate upload start"); - upload_status_rsp(srv, ctx, BT_MESH_DFD_SUCCESS); - return 0; - } + ) { + BT_DBG("Duplicate upload start"); + upload_status_rsp(srv, ctx, BT_MESH_DFD_SUCCESS); + return 0; + } - LOG_WRN("Upload already in progress"); - upload_status_rsp(srv, ctx, BT_MESH_DFD_ERR_BUSY_WITH_UPLOAD); - return 0; - } + BT_WARN("Upload already in progress"); + upload_status_rsp(srv, ctx, BT_MESH_DFD_ERR_BUSY_WITH_UPLOAD); + return 0; + } - /* This will be a no-op if the slot state isn't RESERVED, which is - * what we want. - */ - if (srv->upload.slot) { - bt_mesh_dfu_slot_release(srv->upload.slot); - } + /* This will be a no-op if the slot state isn't RESERVED, which is + * what we want. + */ + if (srv->upload.slot) { + bt_mesh_dfu_slot_release(srv->upload.slot); + } -#ifdef CONFIG_BT_MESH_DFD_SRV_OOB_UPLOAD - srv->upload.is_oob = false; +#ifdef CONFIG_BLE_MESH_DFD_SRV_OOB_UPLOAD + srv->upload.is_oob = false; #endif - srv->upload.slot = bt_mesh_dfu_slot_reserve(); + srv->upload.slot = bt_mesh_dfu_slot_reserve(); - if (!srv->upload.slot) { - LOG_WRN("No space for slot"); - upload_status_rsp(srv, ctx, - BT_MESH_DFD_ERR_INSUFFICIENT_RESOURCES); - return 0; - } + if (!srv->upload.slot) { + BT_WARN("No space for slot"); + upload_status_rsp(srv, ctx, + BT_MESH_DFD_ERR_INSUFFICIENT_RESOURCES); + return 0; + } - err = set_upload_fwid(srv, ctx, fwid, fwid_len); - if (err) { - return err; - } + err = set_upload_fwid(srv, ctx, fwid, fwid_len); + if (err) { + return err; + } - err = bt_mesh_dfu_slot_info_set(srv->upload.slot, size, meta, meta_len); - switch (err) { - case -EFBIG: - upload_status_rsp(srv, ctx, BT_MESH_DFD_ERR_INTERNAL); - break; - case 0: - break; - default: - return err; - } + err = bt_mesh_dfu_slot_info_set(srv->upload.slot, size, meta, meta_len); + switch (err) { + case -EFBIG: + upload_status_rsp(srv, ctx, BT_MESH_DFD_ERR_INTERNAL); + break; + case 0: + break; + default: + return err; + } - srv->io = NULL; - err = srv->cb->recv(srv, srv->upload.slot, &srv->io); - if (err || !srv->io) { - LOG_ERR("App rejected upload. err: %d io: %p", err, srv->io); - bt_mesh_dfu_slot_release(srv->upload.slot); - upload_status_rsp(srv, ctx, BT_MESH_DFD_ERR_INTERNAL); - return 0; - } + srv->io = NULL; + err = srv->cb->recv(srv, srv->upload.slot, &srv->io); + if (err || !srv->io) { + BT_ERR("App rejected upload. err: %d io: %p", err, srv->io); + bt_mesh_dfu_slot_release(srv->upload.slot); + upload_status_rsp(srv, ctx, BT_MESH_DFD_ERR_INTERNAL); + return 0; + } - err = bt_mesh_blob_srv_recv(&srv->upload.blob, blob_id, srv->io, ttl, - timeout_base); - if (err) { - LOG_ERR("BLOB Server rejected upload (err: %d)", err); - bt_mesh_dfu_slot_release(srv->upload.slot); - upload_status_rsp(srv, ctx, BT_MESH_DFD_ERR_INTERNAL); - return 0; - } + err = bt_mesh_blob_srv_recv(&srv->upload.blob, blob_id, srv->io, ttl, + timeout_base); + if (err) { + BT_ERR("BLOB Server rejected upload (err: %d)", err); + bt_mesh_dfu_slot_release(srv->upload.slot); + upload_status_rsp(srv, ctx, BT_MESH_DFD_ERR_INTERNAL); + return 0; + } - LOG_DBG("%s", bt_hex(fwid, fwid_len)); + BT_DBG("%s", bt_hex(fwid, fwid_len)); - srv->upload.phase = BT_MESH_DFD_UPLOAD_PHASE_TRANSFER_ACTIVE; - upload_status_rsp(srv, ctx, BT_MESH_DFD_SUCCESS); + srv->upload.phase = BT_MESH_DFD_UPLOAD_PHASE_TRANSFER_ACTIVE; + upload_status_rsp(srv, ctx, BT_MESH_DFD_SUCCESS); - return 0; + return 0; } static int handle_upload_start_oob(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx, - struct net_buf_simple *buf) + struct net_buf_simple *buf) { - struct bt_mesh_dfd_srv *srv = mod->rt->user_data; - uint8_t uri_len; - uint8_t *uri; - uint16_t fwid_len; - uint8_t *fwid; + struct bt_mesh_dfd_srv *srv = mod->user_data; + uint8_t uri_len; + uint8_t *uri; + uint16_t fwid_len; + uint8_t *fwid; - uri_len = net_buf_simple_pull_u8(buf); + uri_len = net_buf_simple_pull_u8(buf); - if (uri_len > buf->len) { - return -EINVAL; - } + if (uri_len > buf->len) { + return -EINVAL; + } - uri = net_buf_simple_pull_mem(buf, uri_len); - fwid_len = buf->len; - fwid = net_buf_simple_pull_mem(buf, fwid_len); + uri = net_buf_simple_pull_mem(buf, uri_len); + fwid_len = buf->len; + fwid = net_buf_simple_pull_mem(buf, fwid_len); - LOG_DBG("Upload OOB Start"); - LOG_HEXDUMP_DBG(uri, uri_len, "URI"); - LOG_HEXDUMP_DBG(fwid, fwid_len, "FWID"); + BT_DBG("Upload OOB Start"); + BT_INFO("URI %s", bt_hex(uri, uri_len)); + BT_INFO("FWID %s", bt_hex(fwid, fwid_len)); - if (upload_is_busy(srv)) { -#ifdef CONFIG_BT_MESH_DFD_SRV_OOB_UPLOAD - if (srv->upload.is_oob && - uri_len == srv->upload.oob.uri_len && - fwid_len == srv->upload.oob.current_fwid_len && - !memcmp(uri, srv->upload.oob.uri, uri_len) && - !memcmp(fwid, srv->upload.oob.current_fwid, fwid_len)) { - /* Same image, return SUCCESS for idempotency */ - upload_status_rsp(srv, ctx, BT_MESH_DFD_SUCCESS); - return 0; - } + if (upload_is_busy(srv)) { +#ifdef CONFIG_BLE_MESH_DFD_SRV_OOB_UPLOAD + if (srv->upload.is_oob && + uri_len == srv->upload.oob.uri_len && + fwid_len == srv->upload.oob.current_fwid_len && + !memcmp(uri, srv->upload.oob.uri, uri_len) && + !memcmp(fwid, srv->upload.oob.current_fwid, fwid_len)) { + /* Same image, return SUCCESS for idempotency */ + upload_status_rsp(srv, ctx, BT_MESH_DFD_SUCCESS); + return 0; + } #endif - upload_status_rsp(srv, ctx, BT_MESH_DFD_ERR_BUSY_WITH_UPLOAD); - return 0; -#ifdef CONFIG_BT_MESH_DFD_SRV_OOB_UPLOAD - } else if (srv->upload.is_oob && srv->upload.is_pending_oob_check) { - /* Ignore the request if we didn't confirm the previous one. */ - return 0; + upload_status_rsp(srv, ctx, BT_MESH_DFD_ERR_BUSY_WITH_UPLOAD); + return 0; +#ifdef CONFIG_BLE_MESH_DFD_SRV_OOB_UPLOAD + } else if (srv->upload.is_oob && srv->upload.is_pending_oob_check) { + /* Ignore the request if we didn't confirm the previous one. */ + return 0; #endif - } + } -#ifdef CONFIG_BT_MESH_DFD_SRV_OOB_UPLOAD - if (uri_len > CONFIG_BT_MESH_DFU_URI_MAXLEN || - fwid_len > CONFIG_BT_MESH_DFU_FWID_MAXLEN) { - upload_status_rsp(srv, ctx, BT_MESH_DFD_ERR_INTERNAL); - return 0; - } +#ifdef CONFIG_BLE_MESH_DFD_SRV_OOB_UPLOAD + if (uri_len > CONFIG_BLE_MESH_DFU_URI_MAXLEN || + fwid_len > CONFIG_BLE_MESH_DFU_FWID_MAXLEN) { + upload_status_rsp(srv, ctx, BT_MESH_DFD_ERR_INTERNAL); + return 0; + } - struct bt_mesh_dfu_slot *slot = bt_mesh_dfu_slot_reserve(); + struct bt_mesh_dfu_slot *slot = bt_mesh_dfu_slot_reserve(); - if (slot == NULL) { - upload_status_rsp(srv, ctx, BT_MESH_DFD_ERR_INSUFFICIENT_RESOURCES); - return 0; - } + if (slot == NULL) { + upload_status_rsp(srv, ctx, BT_MESH_DFD_ERR_INSUFFICIENT_RESOURCES); + return 0; + } - /* This will be a no-op if the slot state isn't RESERVED, which is - * what we want. - */ - if (srv->upload.slot) { - bt_mesh_dfu_slot_release(srv->upload.slot); - } + /* This will be a no-op if the slot state isn't RESERVED, which is + * what we want. + */ + if (srv->upload.slot) { + bt_mesh_dfu_slot_release(srv->upload.slot); + } - srv->upload.is_oob = true; - srv->upload.slot = slot; - memcpy(srv->upload.oob.uri, uri, uri_len); - srv->upload.oob.uri_len = uri_len; - memcpy(srv->upload.oob.current_fwid, fwid, fwid_len); - srv->upload.oob.current_fwid_len = fwid_len; - memcpy(&srv->upload.oob.ctx, ctx, sizeof(struct bt_mesh_msg_ctx)); + srv->upload.is_oob = true; + srv->upload.slot = slot; + memcpy(srv->upload.oob.uri, uri, uri_len); + srv->upload.oob.uri_len = uri_len; + memcpy(srv->upload.oob.current_fwid, fwid, fwid_len); + srv->upload.oob.current_fwid_len = fwid_len; + memcpy(&srv->upload.oob.ctx, ctx, sizeof(struct bt_mesh_msg_ctx)); - int status = srv->cb->start_oob_upload(srv, srv->upload.slot, srv->upload.oob.uri, - srv->upload.oob.uri_len, - srv->upload.oob.current_fwid, - srv->upload.oob.current_fwid_len); + int status = srv->cb->start_oob_upload(srv, srv->upload.slot, srv->upload.oob.uri, + srv->upload.oob.uri_len, + srv->upload.oob.current_fwid, + srv->upload.oob.current_fwid_len); - if (status != BT_MESH_DFD_SUCCESS) { - upload_status_rsp(srv, ctx, status); - bt_mesh_dfu_slot_release(srv->upload.slot); - } else { - srv->upload.is_pending_oob_check = true; - } + if (status != BT_MESH_DFD_SUCCESS) { + upload_status_rsp(srv, ctx, status); + bt_mesh_dfu_slot_release(srv->upload.slot); + } else { + srv->upload.is_pending_oob_check = true; + } #else - upload_status_rsp(srv, ctx, BT_MESH_DFD_ERR_URI_NOT_SUPPORTED); -#endif /* CONFIG_BT_MESH_DFD_SRV_OOB_UPLOAD */ + upload_status_rsp(srv, ctx, BT_MESH_DFD_ERR_URI_NOT_SUPPORTED); +#endif /* CONFIG_BLE_MESH_DFD_SRV_OOB_UPLOAD */ - return 0; + return 0; } static int handle_upload_cancel(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx, - struct net_buf_simple *buf) + struct net_buf_simple *buf) { - struct bt_mesh_dfd_srv *srv = mod->rt->user_data; + struct bt_mesh_dfd_srv *srv = mod->user_data; - srv->upload.phase = BT_MESH_DFD_UPLOAD_PHASE_IDLE; -#ifdef CONFIG_BT_MESH_DFD_SRV_OOB_UPLOAD - if (srv->upload.is_oob) { - srv->cb->cancel_oob_upload(srv, srv->upload.slot); - } else + srv->upload.phase = BT_MESH_DFD_UPLOAD_PHASE_IDLE; +#ifdef CONFIG_BLE_MESH_DFD_SRV_OOB_UPLOAD + if (srv->upload.is_oob) { + srv->cb->cancel_oob_upload(srv, srv->upload.slot); + } else #endif - { - (void)bt_mesh_blob_srv_cancel(&srv->upload.blob); - } - upload_status_rsp(srv, ctx, BT_MESH_DFD_SUCCESS); + { + (void)bt_mesh_blob_srv_cancel(&srv->upload.blob); + } + upload_status_rsp(srv, ctx, BT_MESH_DFD_SUCCESS); - return 0; + return 0; } static void fw_status_rsp(struct bt_mesh_dfd_srv *srv, - struct bt_mesh_msg_ctx *ctx, - enum bt_mesh_dfd_status status, uint16_t idx, - const uint8_t *fwid, size_t fwid_len) + struct bt_mesh_msg_ctx *ctx, + enum bt_mesh_dfd_status status, uint16_t idx, + const uint8_t *fwid, size_t fwid_len) { - BT_MESH_MODEL_BUF_DEFINE(rsp, BT_MESH_DFD_OP_FW_STATUS, - 7 + CONFIG_BT_MESH_DFU_FWID_MAXLEN); - bt_mesh_model_msg_init(&rsp, BT_MESH_DFD_OP_FW_STATUS); + BLE_MESH_MODEL_BUF_DEFINE(rsp, BT_MESH_DFD_OP_FW_STATUS, + 7 + CONFIG_BLE_MESH_DFU_FWID_MAXLEN); + bt_mesh_model_msg_init(&rsp, BT_MESH_DFD_OP_FW_STATUS); - net_buf_simple_add_u8(&rsp, status); - net_buf_simple_add_le16(&rsp, bt_mesh_dfu_slot_count()); + net_buf_simple_add_u8(&rsp, status); + net_buf_simple_add_le16(&rsp, bt_mesh_dfu_slot_count()); - net_buf_simple_add_le16(&rsp, idx); - if (fwid) { - net_buf_simple_add_mem(&rsp, fwid, fwid_len); - } + net_buf_simple_add_le16(&rsp, idx); + if (fwid) { + net_buf_simple_add_mem(&rsp, fwid, fwid_len); + } - bt_mesh_model_send(srv->mod, ctx, &rsp, NULL, NULL); + bt_mesh_model_send(srv->mod, ctx, &rsp, NULL, NULL); } static int handle_fw_get(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx, - struct net_buf_simple *buf) + struct net_buf_simple *buf) { - struct bt_mesh_dfd_srv *srv = mod->rt->user_data; - struct bt_mesh_dfu_slot *slot; - const uint8_t *fwid; - size_t fwid_len; - int idx; + struct bt_mesh_dfd_srv *srv = mod->user_data; + struct bt_mesh_dfu_slot *slot; + const uint8_t *fwid; + size_t fwid_len; + int idx; - fwid_len = buf->len; - fwid = net_buf_simple_pull_mem(buf, fwid_len); + fwid_len = buf->len; + fwid = net_buf_simple_pull_mem(buf, fwid_len); - idx = bt_mesh_dfu_slot_get(fwid, fwid_len, &slot); - if (idx >= 0) { - fw_status_rsp(srv, ctx, BT_MESH_DFD_SUCCESS, idx, fwid, - fwid_len); - } else { - fw_status_rsp(srv, ctx, BT_MESH_DFD_ERR_FW_NOT_FOUND, 0xffff, - fwid, fwid_len); - } + idx = bt_mesh_dfu_slot_get(fwid, fwid_len, &slot); + if (idx >= 0) { + fw_status_rsp(srv, ctx, BT_MESH_DFD_SUCCESS, idx, fwid, + fwid_len); + } else { + fw_status_rsp(srv, ctx, BT_MESH_DFD_ERR_FW_NOT_FOUND, 0xffff, + fwid, fwid_len); + } - return 0; + return 0; } static int handle_fw_get_by_index(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx, - struct net_buf_simple *buf) + struct net_buf_simple *buf) { - struct bt_mesh_dfd_srv *srv = mod->rt->user_data; - const struct bt_mesh_dfu_slot *slot; - uint16_t idx; + struct bt_mesh_dfd_srv *srv = mod->user_data; + const struct bt_mesh_dfu_slot *slot; + uint16_t idx; - idx = net_buf_simple_pull_le16(buf); + idx = net_buf_simple_pull_le16(buf); - slot = bt_mesh_dfu_slot_at(idx); - if (slot) { - fw_status_rsp(srv, ctx, BT_MESH_DFD_SUCCESS, idx, slot->fwid, - slot->fwid_len); - } else { - fw_status_rsp(srv, ctx, BT_MESH_DFD_ERR_FW_NOT_FOUND, idx, - NULL, 0); - } + slot = bt_mesh_dfu_slot_at(idx); + if (slot) { + fw_status_rsp(srv, ctx, BT_MESH_DFD_SUCCESS, idx, slot->fwid, + slot->fwid_len); + } else { + fw_status_rsp(srv, ctx, BT_MESH_DFD_ERR_FW_NOT_FOUND, idx, + NULL, 0); + } - return 0; + return 0; } static int handle_fw_delete(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx, - struct net_buf_simple *buf) + struct net_buf_simple *buf) { - struct bt_mesh_dfd_srv *srv = mod->rt->user_data; - const uint8_t *fwid; - size_t fwid_len; + struct bt_mesh_dfd_srv *srv = mod->user_data; + const uint8_t *fwid; + size_t fwid_len; - fwid_len = buf->len; - fwid = net_buf_simple_pull_mem(buf, fwid_len); + fwid_len = buf->len; + fwid = net_buf_simple_pull_mem(buf, fwid_len); - enum bt_mesh_dfd_status status = bt_mesh_dfd_srv_fw_delete(srv, &fwid_len, &fwid); + enum bt_mesh_dfd_status status = bt_mesh_dfd_srv_fw_delete(srv, &fwid_len, &fwid); - fw_status_rsp(srv, ctx, status, 0xffff, fwid, fwid_len); + fw_status_rsp(srv, ctx, status, 0xffff, fwid, fwid_len); - return 0; + return 0; } static enum bt_mesh_dfu_iter slot_del_cb(const struct bt_mesh_dfu_slot *slot, - void *user_data) + void *user_data) { - struct bt_mesh_dfd_srv *srv = user_data; + struct bt_mesh_dfd_srv *srv = user_data; - if (srv->cb && srv->cb->del) { - srv->cb->del(srv, slot); - } + if (srv->cb && srv->cb->del) { + srv->cb->del(srv, slot); + } - return BT_MESH_DFU_ITER_CONTINUE; + return BLE_MESH_DFU_ITER_CONTINUE; } static int handle_fw_delete_all(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx, - struct net_buf_simple *buf) + struct net_buf_simple *buf) { - struct bt_mesh_dfd_srv *srv = mod->rt->user_data; + struct bt_mesh_dfd_srv *srv = mod->user_data; - fw_status_rsp(srv, ctx, bt_mesh_dfd_srv_fw_delete_all(srv), 0xffff, NULL, 0); + fw_status_rsp(srv, ctx, bt_mesh_dfd_srv_fw_delete_all(srv), 0xffff, NULL, 0); - return 0; + return 0; } const struct bt_mesh_model_op _bt_mesh_dfd_srv_op[] = { - { BT_MESH_DFD_OP_RECEIVERS_ADD, BT_MESH_LEN_MIN(3), handle_receivers_add }, - { BT_MESH_DFD_OP_RECEIVERS_DELETE_ALL, BT_MESH_LEN_EXACT(0), handle_receivers_delete_all }, - { BT_MESH_DFD_OP_RECEIVERS_GET, BT_MESH_LEN_EXACT(4), handle_receivers_get }, - { BT_MESH_DFD_OP_CAPABILITIES_GET, BT_MESH_LEN_EXACT(0), handle_capabilities_get }, - { BT_MESH_DFD_OP_GET, BT_MESH_LEN_EXACT(0), handle_get }, - { BT_MESH_DFD_OP_START, BT_MESH_LEN_MIN(10), handle_start }, - { BT_MESH_DFD_OP_SUSPEND, BT_MESH_LEN_EXACT(0), handle_suspend }, - { BT_MESH_DFD_OP_CANCEL, BT_MESH_LEN_EXACT(0), handle_cancel }, - { BT_MESH_DFD_OP_APPLY, BT_MESH_LEN_EXACT(0), handle_apply }, - { BT_MESH_DFD_OP_UPLOAD_GET, BT_MESH_LEN_EXACT(0), handle_upload_get }, - { BT_MESH_DFD_OP_UPLOAD_START, BT_MESH_LEN_MIN(16), handle_upload_start }, - { BT_MESH_DFD_OP_UPLOAD_START_OOB, BT_MESH_LEN_MIN(2), handle_upload_start_oob }, - { BT_MESH_DFD_OP_UPLOAD_CANCEL, BT_MESH_LEN_EXACT(0), handle_upload_cancel }, - { BT_MESH_DFD_OP_FW_GET, BT_MESH_LEN_MIN(0), handle_fw_get }, - { BT_MESH_DFD_OP_FW_GET_BY_INDEX, BT_MESH_LEN_EXACT(2), handle_fw_get_by_index }, - { BT_MESH_DFD_OP_FW_DELETE, BT_MESH_LEN_MIN(0), handle_fw_delete }, - { BT_MESH_DFD_OP_FW_DELETE_ALL, BT_MESH_LEN_EXACT(0), handle_fw_delete_all }, + { BT_MESH_DFD_OP_RECEIVERS_ADD, 3, (void *)handle_receivers_add }, + { BT_MESH_DFD_OP_RECEIVERS_DELETE_ALL, 0, (void *)handle_receivers_delete_all }, + { BT_MESH_DFD_OP_RECEIVERS_GET, 4, (void *)handle_receivers_get }, + { BT_MESH_DFD_OP_CAPABILITIES_GET, 0, (void *)handle_capabilities_get }, + { BT_MESH_DFD_OP_GET, 0, (void *)handle_get }, + { BT_MESH_DFD_OP_START, 10, (void *)handle_start }, + { BT_MESH_DFD_OP_SUSPEND, 0, (void *)handle_suspend }, + { BT_MESH_DFD_OP_CANCEL, 0, (void *)handle_cancel }, + { BT_MESH_DFD_OP_APPLY, 0, (void *)handle_apply }, + { BT_MESH_DFD_OP_UPLOAD_GET, 0, (void *)handle_upload_get }, + { BT_MESH_DFD_OP_UPLOAD_START, 16, (void *)handle_upload_start }, + { BT_MESH_DFD_OP_UPLOAD_START_OOB, 2, (void *)handle_upload_start_oob }, + { BT_MESH_DFD_OP_UPLOAD_CANCEL, 0, (void *)handle_upload_cancel }, + { BT_MESH_DFD_OP_FW_GET, 0, (void *)handle_fw_get }, + { BT_MESH_DFD_OP_FW_GET_BY_INDEX, 2, (void *)handle_fw_get_by_index }, + { BT_MESH_DFD_OP_FW_DELETE, 0, (void *)handle_fw_delete }, + { BT_MESH_DFD_OP_FW_DELETE_ALL, 0, (void *)handle_fw_delete_all }, - BT_MESH_MODEL_OP_END + BLE_MESH_MODEL_OP_END }; static void dfu_suspended(struct bt_mesh_dfu_cli *cli) { - struct bt_mesh_dfd_srv *srv = - CONTAINER_OF(cli, struct bt_mesh_dfd_srv, dfu); + struct bt_mesh_dfd_srv *srv = + CONTAINER_OF(cli, struct bt_mesh_dfd_srv, dfu); - dfd_phase_set(srv, BT_MESH_DFD_PHASE_TRANSFER_SUSPENDED); + dfd_phase_set(srv, BT_MESH_DFD_PHASE_TRANSFER_SUSPENDED); } static void dfu_ended(struct bt_mesh_dfu_cli *cli, - enum bt_mesh_dfu_status reason) + enum bt_mesh_dfu_status reason) { - struct bt_mesh_dfd_srv *srv = - CONTAINER_OF(cli, struct bt_mesh_dfd_srv, dfu); - int err; + struct bt_mesh_dfd_srv *srv = + CONTAINER_OF(cli, struct bt_mesh_dfd_srv, dfu); + int err; - LOG_DBG("reason: %u, phase: %u, apply: %u", reason, srv->phase, srv->apply); + BT_DBG("reason: %u, phase: %u, apply: %u", reason, srv->phase, srv->apply); - if (srv->phase == BT_MESH_DFD_PHASE_IDLE) { - return; - } + if (srv->phase == BT_MESH_DFD_PHASE_IDLE) { + return; + } - if (srv->phase == BT_MESH_DFD_PHASE_CANCELING_UPDATE) { - dfd_phase_set(srv, BT_MESH_DFD_PHASE_IDLE); - return; - } + if (srv->phase == BT_MESH_DFD_PHASE_CANCELING_UPDATE) { + dfd_phase_set(srv, BT_MESH_DFD_PHASE_IDLE); + return; + } - if (reason != BT_MESH_DFU_SUCCESS) { - dfd_phase_set(srv, BT_MESH_DFD_PHASE_FAILED); - return; - } + if (reason != BLE_MESH_DFU_SUCCESS) { + dfd_phase_set(srv, BT_MESH_DFD_PHASE_FAILED); + return; + } - if (!srv->apply) { - dfd_phase_set(srv, BT_MESH_DFD_PHASE_TRANSFER_SUCCESS); - return; - } + if (!srv->apply) { + dfd_phase_set(srv, BT_MESH_DFD_PHASE_TRANSFER_SUCCESS); + return; + } - dfd_phase_set(srv, BT_MESH_DFD_PHASE_APPLYING_UPDATE); + dfd_phase_set(srv, BT_MESH_DFD_PHASE_APPLYING_UPDATE); - err = bt_mesh_dfu_cli_apply(cli); - if (err) { - LOG_ERR("Apply failed: %d", err); - dfd_phase_set(srv, BT_MESH_DFD_PHASE_FAILED); - } + err = bt_mesh_dfu_cli_apply(cli); + if (err) { + BT_ERR("Apply failed: %d", err); + dfd_phase_set(srv, BT_MESH_DFD_PHASE_FAILED); + } } static void dfu_applied(struct bt_mesh_dfu_cli *cli) { - struct bt_mesh_dfd_srv *srv = - CONTAINER_OF(cli, struct bt_mesh_dfd_srv, dfu); - int err; + struct bt_mesh_dfd_srv *srv = + CONTAINER_OF(cli, struct bt_mesh_dfd_srv, dfu); + int err; - if (srv->phase == BT_MESH_DFD_PHASE_CANCELING_UPDATE) { - dfd_phase_set(srv, BT_MESH_DFD_PHASE_FAILED); - return; - } + if (srv->phase == BT_MESH_DFD_PHASE_CANCELING_UPDATE) { + dfd_phase_set(srv, BT_MESH_DFD_PHASE_FAILED); + return; + } - if (srv->phase != BT_MESH_DFD_PHASE_APPLYING_UPDATE) { - return; - } + if (srv->phase != BT_MESH_DFD_PHASE_APPLYING_UPDATE) { + return; + } - err = bt_mesh_dfu_cli_confirm(cli); - if (err) { - LOG_ERR("Confirm failed: %d", err); - dfd_phase_set(srv, BT_MESH_DFD_PHASE_FAILED); - } + err = bt_mesh_dfu_cli_confirm(cli); + if (err) { + BT_ERR("Confirm failed: %d", err); + dfd_phase_set(srv, BT_MESH_DFD_PHASE_FAILED); + } } static void dfu_confirmed(struct bt_mesh_dfu_cli *cli) { - struct bt_mesh_dfd_srv *srv = - CONTAINER_OF(cli, struct bt_mesh_dfd_srv, dfu); + struct bt_mesh_dfd_srv *srv = + CONTAINER_OF(cli, struct bt_mesh_dfd_srv, dfu); - if (srv->phase != BT_MESH_DFD_PHASE_APPLYING_UPDATE && - srv->phase != BT_MESH_DFD_PHASE_CANCELING_UPDATE) { - return; - } + if (srv->phase != BT_MESH_DFD_PHASE_APPLYING_UPDATE && + srv->phase != BT_MESH_DFD_PHASE_CANCELING_UPDATE) { + return; + } - dfd_phase_set(srv, BT_MESH_DFD_PHASE_COMPLETED); + dfd_phase_set(srv, BT_MESH_DFD_PHASE_COMPLETED); } const struct bt_mesh_dfu_cli_cb _bt_mesh_dfd_srv_dfu_cb = { - .suspended = dfu_suspended, - .ended = dfu_ended, - .applied = dfu_applied, - .confirmed = dfu_confirmed, + .suspended = dfu_suspended, + .ended = dfu_ended, + .applied = dfu_applied, + .confirmed = dfu_confirmed, }; static int upload_start(struct bt_mesh_blob_srv *b, struct bt_mesh_msg_ctx *ctx, - struct bt_mesh_blob_xfer *xfer) + struct bt_mesh_blob_xfer *xfer) { - LOG_DBG(""); - return 0; + BT_DBG(""); + return 0; } static void upload_end(struct bt_mesh_blob_srv *b, uint64_t id, bool success) { - struct bt_mesh_dfd_srv *srv = - CONTAINER_OF(b, struct bt_mesh_dfd_srv, upload.blob); + struct bt_mesh_dfd_srv *srv = + CONTAINER_OF(b, struct bt_mesh_dfd_srv, upload.blob); - if (srv->upload.phase != BT_MESH_DFD_UPLOAD_PHASE_TRANSFER_ACTIVE) { - return; - } + if (srv->upload.phase != BT_MESH_DFD_UPLOAD_PHASE_TRANSFER_ACTIVE) { + return; + } - LOG_DBG("%u", success); + BT_DBG("%u", success); - if (success && (bt_mesh_dfu_slot_commit(srv->upload.slot) == 0)) { - srv->upload.phase = BT_MESH_DFD_UPLOAD_PHASE_TRANSFER_SUCCESS; - return; - } + if (success && (bt_mesh_dfu_slot_commit(srv->upload.slot) == 0)) { + srv->upload.phase = BT_MESH_DFD_UPLOAD_PHASE_TRANSFER_SUCCESS; + return; + } - /* Will delete slot when we start a new upload */ - srv->upload.phase = BT_MESH_DFD_UPLOAD_PHASE_TRANSFER_ERROR; + /* Will delete slot when we start a new upload */ + srv->upload.phase = BT_MESH_DFD_UPLOAD_PHASE_TRANSFER_ERROR; } static void upload_timeout(struct bt_mesh_blob_srv *b) { - LOG_DBG(""); + BT_DBG(""); - upload_end(b, b->state.xfer.id, false); + upload_end(b, b->state.xfer.id, false); } const struct bt_mesh_blob_srv_cb _bt_mesh_dfd_srv_blob_cb = { - .start = upload_start, - .end = upload_end, - .suspended = upload_timeout, + .start = upload_start, + .end = upload_end, + .suspended = upload_timeout, }; -static int dfd_srv_init(const struct bt_mesh_model *mod) +static int dfd_srv_init(struct bt_mesh_model *mod) { - struct bt_mesh_dfd_srv *srv = mod->rt->user_data; + struct bt_mesh_dfd_srv *srv = mod->user_data; - srv->mod = mod; + srv->mod = mod; - if (IS_ENABLED(CONFIG_BT_MESH_MODEL_EXTENSIONS)) { - bt_mesh_model_extend(mod, srv->upload.blob.mod); - } - - return 0; + return 0; } -static void dfd_srv_reset(const struct bt_mesh_model *mod) +static void dfd_srv_reset(struct bt_mesh_model *mod) { - struct bt_mesh_dfd_srv *srv = mod->rt->user_data; + struct bt_mesh_dfd_srv *srv = mod->user_data; - dfd_phase_set(srv, BT_MESH_DFD_PHASE_IDLE); - srv->upload.phase = BT_MESH_DFD_UPLOAD_PHASE_IDLE; + dfd_phase_set(srv, BT_MESH_DFD_PHASE_IDLE); - sys_slist_init(&srv->inputs.targets); - srv->target_cnt = 0; + srv->upload.phase = BT_MESH_DFD_UPLOAD_PHASE_IDLE; - bt_mesh_dfu_slot_foreach(slot_del_cb, srv); - bt_mesh_dfu_slot_del_all(); + sys_slist_init(&srv->inputs.targets); + srv->target_cnt = 0; + + bt_mesh_dfu_slot_foreach(slot_del_cb, srv); + bt_mesh_dfu_slot_del_all(); } +static int dfd_srv_deinit(struct bt_mesh_model *mod) +{ + dfd_srv_reset(mod); + return 0; +} const struct bt_mesh_model_cb _bt_mesh_dfd_srv_cb = { - .init = dfd_srv_init, - .reset = dfd_srv_reset, + .init = dfd_srv_init, +#if CONFIG_BLE_MESH_DEINIT + .deinit = dfd_srv_deinit, +#endif }; enum bt_mesh_dfd_status bt_mesh_dfd_srv_receiver_add(struct bt_mesh_dfd_srv *srv, uint16_t addr, - uint8_t img_idx) + uint8_t img_idx) { - struct bt_mesh_dfu_target *t; - struct bt_mesh_blob_target_pull *p; + struct bt_mesh_dfu_target *t; + struct bt_mesh_blob_target_pull *p; - if (!BT_MESH_ADDR_IS_UNICAST(addr)) { - return BT_MESH_DFD_SUCCESS; - } + if (!BLE_MESH_ADDR_IS_UNICAST(addr)) { + return BT_MESH_DFD_SUCCESS; + } - t = target_get(srv, addr); - if (t) { - t->img_idx = img_idx; - return BT_MESH_DFD_SUCCESS; - } + t = target_get(srv, addr); + if (t) { + t->img_idx = img_idx; + return BT_MESH_DFD_SUCCESS; + } - /* New target node, add it to the list */ + /* New target node, add it to the list */ - if (srv->target_cnt == ARRAY_SIZE(srv->targets)) { - return BT_MESH_DFD_ERR_INSUFFICIENT_RESOURCES; - } + if (srv->target_cnt == ARRAY_SIZE(srv->targets)) { + return BT_MESH_DFD_ERR_INSUFFICIENT_RESOURCES; + } - t = &srv->targets[srv->target_cnt]; - p = &srv->pull_ctxs[srv->target_cnt]; - srv->target_cnt++; + t = &srv->targets[srv->target_cnt]; + p = &srv->pull_ctxs[srv->target_cnt]; + srv->target_cnt++; - memset(t, 0, sizeof(*t)); - memset(p, 0, sizeof(*p)); - t->blob.addr = addr; - t->blob.pull = p; - t->img_idx = img_idx; + memset(t, 0, sizeof(*t)); + memset(p, 0, sizeof(*p)); + t->blob.addr = addr; + t->blob.pull = p; + t->img_idx = img_idx; - LOG_DBG("Added receiver 0x%04x img: %u", t->blob.addr, - t->img_idx); + BT_DBG("Added receiver 0x%04x img: %u", t->blob.addr, + t->img_idx); - return BT_MESH_DFD_SUCCESS; + return BT_MESH_DFD_SUCCESS; } enum bt_mesh_dfd_status bt_mesh_dfd_srv_receivers_delete_all(struct bt_mesh_dfd_srv *srv) { - if (bt_mesh_dfu_cli_is_busy(&srv->dfu)) { - return BT_MESH_DFD_ERR_BUSY_WITH_DISTRIBUTION; - } + if (bt_mesh_dfu_cli_is_busy(&srv->dfu)) { + return BT_MESH_DFD_ERR_BUSY_WITH_DISTRIBUTION; + } - sys_slist_init(&srv->inputs.targets); - srv->target_cnt = 0; + sys_slist_init(&srv->inputs.targets); + srv->target_cnt = 0; - return BT_MESH_DFD_SUCCESS; + return BT_MESH_DFD_SUCCESS; } enum bt_mesh_dfd_status bt_mesh_dfd_srv_start(struct bt_mesh_dfd_srv *srv, - struct bt_mesh_dfd_start_params *params) + struct bt_mesh_dfd_start_params *params) { - int err, i; - struct bt_mesh_dfu_cli_xfer xfer = { 0 }; + int err, i; + struct bt_mesh_dfu_cli_xfer xfer = { 0 }; - if (!srv->target_cnt) { - return BT_MESH_DFD_ERR_RECEIVERS_LIST_EMPTY; - } + if (!srv->target_cnt) { + return BT_MESH_DFD_ERR_RECEIVERS_LIST_EMPTY; + } - if (!bt_mesh_app_key_exists(params->app_idx)) { - return BT_MESH_DFD_ERR_INVALID_APPKEY_INDEX; - } + if (!bt_mesh_app_key_get(params->app_idx)) { + return BT_MESH_DFD_ERR_INVALID_APPKEY_INDEX; + } - xfer.mode = params->xfer_mode; - xfer.slot = bt_mesh_dfu_slot_at(params->slot_idx); - if (!xfer.slot) { - return BT_MESH_DFD_ERR_FW_NOT_FOUND; - } + xfer.mode = params->xfer_mode; + xfer.slot = bt_mesh_dfu_slot_at(params->slot_idx); + if (!xfer.slot) { + return BT_MESH_DFD_ERR_FW_NOT_FOUND; + } - if (srv->inputs.app_idx == params->app_idx && - srv->inputs.timeout_base == params->timeout_base && - srv->inputs.group == params->group && srv->inputs.ttl == params->ttl && - srv->dfu.xfer.blob.mode == xfer.mode && srv->apply == params->apply && - srv->slot_idx == params->slot_idx) { - if (is_busy(srv) || - srv->phase == BT_MESH_DFD_PHASE_COMPLETED) { - LOG_WRN("Already completed or in progress"); - return BT_MESH_DFD_SUCCESS; - } else if (srv->phase == BT_MESH_DFD_PHASE_TRANSFER_SUSPENDED) { - bt_mesh_dfu_cli_resume(&srv->dfu); - dfd_phase_set(srv, BT_MESH_DFD_PHASE_TRANSFER_ACTIVE); - return BT_MESH_DFD_SUCCESS; - } - } else if (is_busy(srv) || - srv->phase == BT_MESH_DFD_PHASE_TRANSFER_SUSPENDED) { - LOG_WRN("Busy with distribution"); - return BT_MESH_DFD_ERR_BUSY_WITH_DISTRIBUTION; - } + if (srv->inputs.app_idx == params->app_idx && + srv->inputs.timeout_base == params->timeout_base && + srv->inputs.group == params->group && srv->inputs.ttl == params->ttl && + srv->dfu.xfer.blob.mode == xfer.mode && srv->apply == params->apply && + srv->slot_idx == params->slot_idx) { + if (is_busy(srv) || + srv->phase == BT_MESH_DFD_PHASE_COMPLETED) { + BT_WARN("Already completed or in progress"); + return BT_MESH_DFD_SUCCESS; + } else if (srv->phase == BT_MESH_DFD_PHASE_TRANSFER_SUSPENDED) { + bt_mesh_dfu_cli_resume(&srv->dfu); + dfd_phase_set(srv, BT_MESH_DFD_PHASE_TRANSFER_ACTIVE); + return BT_MESH_DFD_SUCCESS; + } + } else if (is_busy(srv) || + srv->phase == BT_MESH_DFD_PHASE_TRANSFER_SUSPENDED) { + BT_WARN("Busy with distribution"); + return BT_MESH_DFD_ERR_BUSY_WITH_DISTRIBUTION; + } - if (srv->phase == BT_MESH_DFD_PHASE_CANCELING_UPDATE) { - LOG_WRN("Canceling distribution"); - return BT_MESH_DFD_ERR_BUSY_WITH_DISTRIBUTION; - } + if (srv->phase == BT_MESH_DFD_PHASE_CANCELING_UPDATE) { + BT_WARN("Canceling distribution"); + return BT_MESH_DFD_ERR_BUSY_WITH_DISTRIBUTION; + } - srv->io = NULL; - err = srv->cb->send(srv, xfer.slot, &srv->io); - if (err || !srv->io) { - return BT_MESH_DFD_ERR_INTERNAL; - } + srv->io = NULL; + err = srv->cb->send(srv, xfer.slot, &srv->io); + if (err || !srv->io) { + return BT_MESH_DFD_ERR_INTERNAL; + } - sys_slist_init(&srv->inputs.targets); - for (i = 0; i < srv->target_cnt; i++) { - uint16_t addr = srv->targets[i].blob.addr; + sys_slist_init(&srv->inputs.targets); + for (i = 0; i < srv->target_cnt; i++) { + uint16_t addr = srv->targets[i].blob.addr; - memset(&srv->targets[i].blob, 0, sizeof(struct bt_mesh_blob_target)); - memset(&srv->pull_ctxs[i], 0, sizeof(struct bt_mesh_blob_target_pull)); - srv->targets[i].blob.addr = addr; - srv->targets[i].blob.pull = &srv->pull_ctxs[i]; + memset(&srv->targets[i].blob, 0, sizeof(struct bt_mesh_blob_target)); + memset(&srv->pull_ctxs[i], 0, sizeof(struct bt_mesh_blob_target_pull)); + srv->targets[i].blob.addr = addr; + srv->targets[i].blob.pull = &srv->pull_ctxs[i]; - sys_slist_append(&srv->inputs.targets, &srv->targets[i].blob.n); - } + sys_slist_append(&srv->inputs.targets, &srv->targets[i].blob.n); + } - srv->slot_idx = params->slot_idx; - srv->inputs.app_idx = params->app_idx; - srv->inputs.timeout_base = params->timeout_base; - srv->inputs.group = params->group; - srv->inputs.ttl = params->ttl; - srv->apply = params->apply; + srv->slot_idx = params->slot_idx; + srv->inputs.app_idx = params->app_idx; + srv->inputs.timeout_base = params->timeout_base; + srv->inputs.group = params->group; + srv->inputs.ttl = params->ttl; + srv->apply = params->apply; - LOG_DBG("Distribution Start: slot: %d, appidx: %d, tb: %d, addr: %04X, ttl: %d, apply: %d", - params->slot_idx, params->app_idx, params->timeout_base, params->group, params->ttl, - params->apply); + BT_DBG("Distribution Start: slot: %d, appidx: %d, tb: %d, addr: %04X, ttl: %d, apply: %d", + params->slot_idx, params->app_idx, params->timeout_base, params->group, params->ttl, + params->apply); - /* DFD Server will always retrieve targets' capabilities before distributing a firmware.*/ - xfer.blob_params = NULL; + /* DFD Server will always retrieve targets' capabilities before distributing a firmware.*/ + xfer.blob_params = NULL; - dfd_phase_set(srv, BT_MESH_DFD_PHASE_TRANSFER_ACTIVE); - err = bt_mesh_dfu_cli_send(&srv->dfu, &srv->inputs, srv->io, &xfer); - if (err) { - dfd_phase_set(srv, BT_MESH_DFD_PHASE_IDLE); - return BT_MESH_DFD_ERR_INTERNAL; - } + dfd_phase_set(srv, BT_MESH_DFD_PHASE_TRANSFER_ACTIVE); + err = bt_mesh_dfu_cli_send(&srv->dfu, &srv->inputs, srv->io, &xfer); + if (err) { + dfd_phase_set(srv, BT_MESH_DFD_PHASE_IDLE); + return BT_MESH_DFD_ERR_INTERNAL; + } - return BT_MESH_DFD_SUCCESS; + return BT_MESH_DFD_SUCCESS; } enum bt_mesh_dfd_status bt_mesh_dfd_srv_suspend(struct bt_mesh_dfd_srv *srv) { - int err; + int err; - if (srv->phase == BT_MESH_DFD_PHASE_TRANSFER_SUSPENDED) { - return BT_MESH_DFD_SUCCESS; - } + if (srv->phase == BT_MESH_DFD_PHASE_TRANSFER_SUSPENDED) { + return BT_MESH_DFD_SUCCESS; + } - if (srv->phase != BT_MESH_DFD_PHASE_TRANSFER_ACTIVE) { - return BT_MESH_DFD_ERR_WRONG_PHASE; - } + if (srv->phase != BT_MESH_DFD_PHASE_TRANSFER_ACTIVE) { + return BT_MESH_DFD_ERR_WRONG_PHASE; + } - err = bt_mesh_dfu_cli_suspend(&srv->dfu); - if (err) { - return BT_MESH_DFD_ERR_SUSPEND_FAILED; - } + err = bt_mesh_dfu_cli_suspend(&srv->dfu); + if (err) { + return BT_MESH_DFD_ERR_SUSPEND_FAILED; + } - srv->phase = BT_MESH_DFD_PHASE_TRANSFER_SUSPENDED; - return BT_MESH_DFD_SUCCESS; + srv->phase = BT_MESH_DFD_PHASE_TRANSFER_SUSPENDED; + return BT_MESH_DFD_SUCCESS; } enum bt_mesh_dfd_status bt_mesh_dfd_srv_cancel(struct bt_mesh_dfd_srv *srv, - struct bt_mesh_msg_ctx *ctx) + struct bt_mesh_msg_ctx *ctx) { - enum bt_mesh_dfd_phase prev_phase; - int err; + enum bt_mesh_dfd_phase prev_phase; + int err; - if (srv->phase == BT_MESH_DFD_PHASE_CANCELING_UPDATE || - srv->phase == BT_MESH_DFD_PHASE_IDLE) { - if (ctx != NULL) { - status_rsp(srv, ctx, BT_MESH_DFD_SUCCESS); - } - return BT_MESH_DFD_SUCCESS; - } + if (srv->phase == BT_MESH_DFD_PHASE_CANCELING_UPDATE || + srv->phase == BT_MESH_DFD_PHASE_IDLE) { + if (ctx != NULL) { + status_rsp(srv, ctx, BT_MESH_DFD_SUCCESS); + } + return BT_MESH_DFD_SUCCESS; + } - if (srv->phase == BT_MESH_DFD_PHASE_COMPLETED || - srv->phase == BT_MESH_DFD_PHASE_FAILED) { - dfd_phase_set(srv, BT_MESH_DFD_PHASE_IDLE); - if (ctx != NULL) { - status_rsp(srv, ctx, BT_MESH_DFD_SUCCESS); - } - return BT_MESH_DFD_SUCCESS; - } + if (srv->phase == BT_MESH_DFD_PHASE_COMPLETED || + srv->phase == BT_MESH_DFD_PHASE_FAILED) { + dfd_phase_set(srv, BT_MESH_DFD_PHASE_IDLE); + if (ctx != NULL) { + status_rsp(srv, ctx, BT_MESH_DFD_SUCCESS); + } + return BT_MESH_DFD_SUCCESS; + } - /* Phase TRANSFER_ACTIVE, TRANSFER_SUSPENDED, TRANSFER_SUCCESS, APPLYING_UPDATE: */ + /* Phase TRANSFER_ACTIVE, TRANSFER_SUSPENDED, TRANSFER_SUCCESS, APPLYING_UPDATE: */ - prev_phase = srv->phase; - dfd_phase_set(srv, BT_MESH_DFD_PHASE_CANCELING_UPDATE); - err = bt_mesh_dfu_cli_cancel(&srv->dfu, NULL); - if (err) { - if (ctx != NULL) { - status_rsp(srv, ctx, BT_MESH_DFD_ERR_INTERNAL); - } - return BT_MESH_DFD_ERR_INTERNAL; - } + prev_phase = srv->phase; + dfd_phase_set(srv, BT_MESH_DFD_PHASE_CANCELING_UPDATE); + err = bt_mesh_dfu_cli_cancel(&srv->dfu, NULL); + if (err) { + if (ctx != NULL) { + status_rsp(srv, ctx, BT_MESH_DFD_ERR_INTERNAL); + } + return BT_MESH_DFD_ERR_INTERNAL; + } - if (prev_phase == BT_MESH_DFD_PHASE_APPLYING_UPDATE && ctx) { - /* Disable randomization for the Firmware Distribution State message to avoid - * reordering when Firmware Distribution Server sends 2 messages in a row when - * cancelling the update (see section 6.2.3.10 of MshDFUv1.0). - */ - ctx->rnd_delay = false; - } + if (prev_phase == BT_MESH_DFD_PHASE_APPLYING_UPDATE && ctx) { + /* Disable randomization for the Firmware Distribution State message to avoid + * reordering when Firmware Distribution Server sends 2 messages in a row when + * cancelling the update (see section 6.2.3.10 of MshDFUv1.0). + */ + /* + * Changed by Espressif: Access Message random delay is not currently supported in IDF. + */ + // ctx->rnd_delay = false; + } - if (ctx != NULL) { - status_rsp(srv, ctx, BT_MESH_DFD_SUCCESS); - } + if (ctx != NULL) { + status_rsp(srv, ctx, BT_MESH_DFD_SUCCESS); + } - if (prev_phase == BT_MESH_DFD_PHASE_APPLYING_UPDATE) { - dfd_phase_set(srv, BT_MESH_DFD_PHASE_IDLE); - if (ctx != NULL) { - status_rsp(srv, ctx, BT_MESH_DFD_SUCCESS); - } - } + if (prev_phase == BT_MESH_DFD_PHASE_APPLYING_UPDATE) { + dfd_phase_set(srv, BT_MESH_DFD_PHASE_IDLE); + if (ctx != NULL) { + status_rsp(srv, ctx, BT_MESH_DFD_SUCCESS); + } + } - return BT_MESH_DFD_SUCCESS; + return BT_MESH_DFD_SUCCESS; } enum bt_mesh_dfd_status bt_mesh_dfd_srv_apply(struct bt_mesh_dfd_srv *srv) { - int err; + int err; - if (srv->phase == BT_MESH_DFD_PHASE_IDLE || - srv->phase == BT_MESH_DFD_PHASE_CANCELING_UPDATE || - srv->phase == BT_MESH_DFD_PHASE_TRANSFER_ACTIVE || - srv->phase == BT_MESH_DFD_PHASE_TRANSFER_SUSPENDED || - srv->phase == BT_MESH_DFD_PHASE_FAILED) { - return BT_MESH_DFD_ERR_WRONG_PHASE; - } + if (srv->phase == BT_MESH_DFD_PHASE_IDLE || + srv->phase == BT_MESH_DFD_PHASE_CANCELING_UPDATE || + srv->phase == BT_MESH_DFD_PHASE_TRANSFER_ACTIVE || + srv->phase == BT_MESH_DFD_PHASE_TRANSFER_SUSPENDED || + srv->phase == BT_MESH_DFD_PHASE_FAILED) { + return BT_MESH_DFD_ERR_WRONG_PHASE; + } - if (srv->phase == BT_MESH_DFD_PHASE_APPLYING_UPDATE || - srv->phase == BT_MESH_DFD_PHASE_COMPLETED) { - return BT_MESH_DFD_SUCCESS; - } + if (srv->phase == BT_MESH_DFD_PHASE_APPLYING_UPDATE || + srv->phase == BT_MESH_DFD_PHASE_COMPLETED) { + return BT_MESH_DFD_SUCCESS; + } - err = bt_mesh_dfu_cli_apply(&srv->dfu); - if (err) { - return BT_MESH_DFD_ERR_INTERNAL; - } + err = bt_mesh_dfu_cli_apply(&srv->dfu); + if (err) { + return BT_MESH_DFD_ERR_INTERNAL; + } - dfd_phase_set(srv, BT_MESH_DFD_PHASE_APPLYING_UPDATE); - return BT_MESH_DFD_SUCCESS; + dfd_phase_set(srv, BT_MESH_DFD_PHASE_APPLYING_UPDATE); + return BT_MESH_DFD_SUCCESS; } enum bt_mesh_dfd_status bt_mesh_dfd_srv_fw_delete(struct bt_mesh_dfd_srv *srv, size_t *fwid_len, - const uint8_t **fwid) + const uint8_t **fwid) { - struct bt_mesh_dfu_slot *slot; - int idx, err; + struct bt_mesh_dfu_slot *slot; + int idx, err; - if (srv->phase != BT_MESH_DFD_PHASE_IDLE) { - *fwid = NULL; - *fwid_len = 0; - return BT_MESH_DFD_ERR_BUSY_WITH_DISTRIBUTION; - } + if (srv->phase != BT_MESH_DFD_PHASE_IDLE) { + *fwid = NULL; + *fwid_len = 0; + return BT_MESH_DFD_ERR_BUSY_WITH_DISTRIBUTION; + } - idx = bt_mesh_dfu_slot_get(*fwid, *fwid_len, &slot); - if (idx < 0) { - return BT_MESH_DFD_SUCCESS; - } + idx = bt_mesh_dfu_slot_get(*fwid, *fwid_len, &slot); + if (idx < 0) { + return BT_MESH_DFD_SUCCESS; + } - err = slot_del(srv, slot); - if (err) { - *fwid = NULL; - *fwid_len = 0; - return BT_MESH_DFD_ERR_INTERNAL; - } else { - return BT_MESH_DFD_SUCCESS; - } + err = slot_del(srv, slot); + if (err) { + *fwid = NULL; + *fwid_len = 0; + return BT_MESH_DFD_ERR_INTERNAL; + } else { + return BT_MESH_DFD_SUCCESS; + } } enum bt_mesh_dfd_status bt_mesh_dfd_srv_fw_delete_all(struct bt_mesh_dfd_srv *srv) { - if (srv->phase != BT_MESH_DFD_PHASE_IDLE) { - return BT_MESH_DFD_ERR_BUSY_WITH_DISTRIBUTION; - } + if (srv->phase != BT_MESH_DFD_PHASE_IDLE) { + return BT_MESH_DFD_ERR_BUSY_WITH_DISTRIBUTION; + } - bt_mesh_dfu_slot_foreach(slot_del_cb, srv); + bt_mesh_dfu_slot_foreach(slot_del_cb, srv); - bt_mesh_dfu_slot_del_all(); + bt_mesh_dfu_slot_del_all(); - return BT_MESH_DFD_SUCCESS; + return BT_MESH_DFD_SUCCESS; } -#ifdef CONFIG_BT_MESH_DFD_SRV_OOB_UPLOAD +#ifdef CONFIG_BLE_MESH_DFD_SRV_OOB_UPLOAD int bt_mesh_dfd_srv_oob_check_complete(struct bt_mesh_dfd_srv *srv, - const struct bt_mesh_dfu_slot *slot, int status, - uint8_t *fwid, size_t fwid_len) + const struct bt_mesh_dfu_slot *slot, int status, + uint8_t *fwid, size_t fwid_len) { - int err; + int err; - if (slot != srv->upload.slot || !srv->upload.is_oob || - srv->upload.phase == BT_MESH_DFD_UPLOAD_PHASE_TRANSFER_ACTIVE || - !srv->upload.is_pending_oob_check) { - /* This should not happen, unless the application calls the function with a - * "wrong" pointer or at a wrong time. - */ - return -EINVAL; - } + if (slot != srv->upload.slot || !srv->upload.is_oob || + srv->upload.phase == BT_MESH_DFD_UPLOAD_PHASE_TRANSFER_ACTIVE || + !srv->upload.is_pending_oob_check) { + /* This should not happen, unless the application calls the function with a + * "wrong" pointer or at a wrong time. + */ + return -EINVAL; + } - srv->upload.is_pending_oob_check = false; + srv->upload.is_pending_oob_check = false; - if (status != BT_MESH_DFD_SUCCESS) { - bt_mesh_dfu_slot_release(srv->upload.slot); - upload_status_rsp(srv, &srv->upload.oob.ctx, status); - return -ECANCELED; - } + if (status != BT_MESH_DFD_SUCCESS) { + bt_mesh_dfu_slot_release(srv->upload.slot); + upload_status_rsp(srv, &srv->upload.oob.ctx, status); + return -ECANCELED; + } - err = set_upload_fwid(srv, &srv->upload.oob.ctx, fwid, fwid_len); + err = set_upload_fwid(srv, &srv->upload.oob.ctx, fwid, fwid_len); - if (err) { - return err; - } + if (err) { + return err; + } - upload_status_rsp(srv, &srv->upload.oob.ctx, BT_MESH_DFD_SUCCESS); - return 0; + upload_status_rsp(srv, &srv->upload.oob.ctx, BT_MESH_DFD_SUCCESS); + return 0; } int bt_mesh_dfd_srv_oob_store_complete(struct bt_mesh_dfd_srv *srv, - const struct bt_mesh_dfu_slot *slot, bool success, - size_t size, const uint8_t *metadata, size_t metadata_len) + const struct bt_mesh_dfu_slot *slot, bool success, + size_t size, const uint8_t *metadata, size_t metadata_len) { - int err = 0; + int err = 0; - if (srv->upload.phase != BT_MESH_DFD_UPLOAD_PHASE_TRANSFER_ACTIVE || - srv->upload.slot != slot || !srv->upload.is_oob) { - return -EINVAL; - } + if (srv->upload.phase != BT_MESH_DFD_UPLOAD_PHASE_TRANSFER_ACTIVE || + srv->upload.slot != slot || !srv->upload.is_oob) { + return -EINVAL; + } - if (!success) { - goto error; - } + if (!success) { + goto error; + } - err = bt_mesh_dfu_slot_info_set(srv->upload.slot, size, metadata, metadata_len); - if (err) { - goto error; - } + err = bt_mesh_dfu_slot_info_set(srv->upload.slot, size, metadata, metadata_len); + if (err) { + goto error; + } - err = bt_mesh_dfu_slot_commit(srv->upload.slot); - if (err) { - goto error; - } + err = bt_mesh_dfu_slot_commit(srv->upload.slot); + if (err) { + goto error; + } - srv->upload.phase = BT_MESH_DFD_UPLOAD_PHASE_TRANSFER_SUCCESS; - return 0; + srv->upload.phase = BT_MESH_DFD_UPLOAD_PHASE_TRANSFER_SUCCESS; + return 0; error: - srv->upload.phase = BT_MESH_DFD_UPLOAD_PHASE_TRANSFER_ERROR; - bt_mesh_dfu_slot_release(srv->upload.slot); - return err; + srv->upload.phase = BT_MESH_DFD_UPLOAD_PHASE_TRANSFER_ERROR; + bt_mesh_dfu_slot_release(srv->upload.slot); + return err; } -#endif /* CONFIG_BT_MESH_DFD_SRV_OOB_UPLOAD */ +#endif /* CONFIG_BLE_MESH_DFD_SRV_OOB_UPLOAD */ +#endif /* CONFIG_BLE_MESH_DFD_SRV */ diff --git a/components/bt/esp_ble_mesh/v1.1/dfu/dfd_srv_internal.h b/components/bt/esp_ble_mesh/v1.1/dfu/dfd_srv_internal.h index 12c7d943f6..403b8b3d17 100644 --- a/components/bt/esp_ble_mesh/v1.1/dfu/dfd_srv_internal.h +++ b/components/bt/esp_ble_mesh/v1.1/dfu/dfd_srv_internal.h @@ -1,47 +1,49 @@ /* - * Copyright (c) 2022 Nordic Semiconductor ASA + * SPDX-FileCopyrightText: 2020 Nordic Semiconductor ASA + * SPDX-FileContributor: 2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ -#ifndef ZEPHYR_INCLUDE_BLUETOOTH_MESH_DFD_SRV_INTERNAL_H__ -#define ZEPHYR_INCLUDE_BLUETOOTH_MESH_DFD_SRV_INTERNAL_H__ +#ifndef _BLE_MESH_v11_DFD_SRV_INTERNAL_H__ +#define _BLE_MESH_v11_DFD_SRV_INTERNAL_H__ -#include +#include "mesh_v1.1/dfu/dfd_srv.h" +#if CONFIG_BLE_MESH_DFD_SRV #ifdef __cplusplus extern "C" { #endif struct bt_mesh_dfd_start_params { - uint16_t app_idx, timeout_base, slot_idx, group; - enum bt_mesh_blob_xfer_mode xfer_mode; - uint8_t ttl; - bool apply; + uint16_t app_idx, timeout_base, slot_idx, group; + enum bt_mesh_blob_xfer_mode xfer_mode; + uint8_t ttl; + bool apply; }; enum bt_mesh_dfd_status bt_mesh_dfd_srv_receiver_add(struct bt_mesh_dfd_srv *srv, uint16_t addr, - uint8_t img_idx); + uint8_t img_idx); enum bt_mesh_dfd_status bt_mesh_dfd_srv_receivers_delete_all(struct bt_mesh_dfd_srv *srv); enum bt_mesh_dfd_status bt_mesh_dfd_srv_start(struct bt_mesh_dfd_srv *srv, - struct bt_mesh_dfd_start_params *params); + struct bt_mesh_dfd_start_params *params); enum bt_mesh_dfd_status bt_mesh_dfd_srv_suspend(struct bt_mesh_dfd_srv *srv); enum bt_mesh_dfd_status bt_mesh_dfd_srv_cancel(struct bt_mesh_dfd_srv *srv, - struct bt_mesh_msg_ctx *ctx); + struct bt_mesh_msg_ctx *ctx); enum bt_mesh_dfd_status bt_mesh_dfd_srv_apply(struct bt_mesh_dfd_srv *srv); enum bt_mesh_dfd_status bt_mesh_dfd_srv_fw_delete(struct bt_mesh_dfd_srv *srv, size_t *fwid_len, - const uint8_t **fwid); + const uint8_t **fwid); enum bt_mesh_dfd_status bt_mesh_dfd_srv_fw_delete_all(struct bt_mesh_dfd_srv *srv); #ifdef __cplusplus } #endif - -#endif /* ZEPHYR_INCLUDE_BLUETOOTH_MESH_DFD_SRV_INTERNAL_H__ */ +#endif +#endif /* _BLE_MESH_v11_DFD_SRV_INTERNAL_H__ */ diff --git a/components/bt/esp_ble_mesh/v1.1/dfu/dfu.h b/components/bt/esp_ble_mesh/v1.1/dfu/dfu.h index c1c5aca46c..17a2059803 100644 --- a/components/bt/esp_ble_mesh/v1.1/dfu/dfu.h +++ b/components/bt/esp_ble_mesh/v1.1/dfu/dfu.h @@ -1,44 +1,47 @@ /* - * Copyright (c) 2020 Nordic Semiconductor ASA + * SPDX-FileCopyrightText: 2020 Nordic Semiconductor ASA + * SPDX-FileContributor: 2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ +#include "mesh/buf.h" -#define BT_MESH_DFU_OP_UPDATE_INFO_GET BT_MESH_MODEL_OP_2(0x83, 0x08) -#define BT_MESH_DFU_OP_UPDATE_INFO_STATUS BT_MESH_MODEL_OP_2(0x83, 0x09) +#if CONFIG_BLE_MESH_DFU_SRV || CONFIG_BLE_MESH_DFU_CLI +#define BLE_MESH_DFU_OP_UPDATE_INFO_GET BLE_MESH_MODEL_OP_2(0x83, 0x08) +#define BLE_MESH_DFU_OP_UPDATE_INFO_STATUS BLE_MESH_MODEL_OP_2(0x83, 0x09) -#define BT_MESH_DFU_OP_UPDATE_METADATA_CHECK BT_MESH_MODEL_OP_2(0x83, 0x0a) -#define BT_MESH_DFU_OP_UPDATE_METADATA_STATUS BT_MESH_MODEL_OP_2(0x83, 0x0b) +#define BLE_MESH_DFU_OP_UPDATE_METADATA_CHECK BLE_MESH_MODEL_OP_2(0x83, 0x0a) +#define BLE_MESH_DFU_OP_UPDATE_METADATA_STATUS BLE_MESH_MODEL_OP_2(0x83, 0x0b) -#define BT_MESH_DFU_OP_UPDATE_GET BT_MESH_MODEL_OP_2(0x83, 0x0c) -#define BT_MESH_DFU_OP_UPDATE_START BT_MESH_MODEL_OP_2(0x83, 0x0d) -#define BT_MESH_DFU_OP_UPDATE_CANCEL BT_MESH_MODEL_OP_2(0x83, 0x0e) -#define BT_MESH_DFU_OP_UPDATE_APPLY BT_MESH_MODEL_OP_2(0x83, 0x0f) -#define BT_MESH_DFU_OP_UPDATE_STATUS BT_MESH_MODEL_OP_2(0x83, 0x10) +#define BLE_MESH_DFU_OP_UPDATE_GET BLE_MESH_MODEL_OP_2(0x83, 0x0c) +#define BLE_MESH_DFU_OP_UPDATE_START BLE_MESH_MODEL_OP_2(0x83, 0x0d) +#define BLE_MESH_DFU_OP_UPDATE_CANCEL BLE_MESH_MODEL_OP_2(0x83, 0x0e) +#define BLE_MESH_DFU_OP_UPDATE_APPLY BLE_MESH_MODEL_OP_2(0x83, 0x0f) +#define BLE_MESH_DFU_OP_UPDATE_STATUS BLE_MESH_MODEL_OP_2(0x83, 0x10) -#define DFU_UPDATE_INFO_STATUS_MSG_MINLEN (4 + CONFIG_BT_MESH_DFU_FWID_MAXLEN + \ - CONFIG_BT_MESH_DFU_URI_MAXLEN) -#define DFU_UPDATE_START_MSG_MAXLEN (12 + CONFIG_BT_MESH_DFU_METADATA_MAXLEN) +#define DFU_UPDATE_INFO_STATUS_MSG_MINLEN (4 + CONFIG_BLE_MESH_DFU_FWID_MAXLEN + \ + CONFIG_BLE_MESH_DFU_URI_MAXLEN) +#define DFU_UPDATE_START_MSG_MAXLEN (12 + CONFIG_BLE_MESH_DFU_METADATA_MAXLEN) static inline uint16_t dfu_metadata_checksum(struct net_buf_simple *buf) { - /* Simple Fletcher-16 checksum to ensure duplicate start messages don't - * have different metadata. - */ - struct net_buf_simple_state state; - uint8_t sum[2] = {0, 0}; + /* Simple Fletcher-16 checksum to ensure duplicate start messages don't + * have different metadata. + */ + struct net_buf_simple_state state; + uint8_t sum[2] = {0, 0}; - net_buf_simple_save(buf, &state); + net_buf_simple_save(buf, &state); + while (buf->len) { + uint8_t byte = net_buf_simple_pull_u8(buf); - while (buf->len) { - uint8_t byte = net_buf_simple_pull_u8(buf); + sum[0] += byte; + sum[1] += sum[0]; + } - sum[0] += byte; - sum[1] += sum[0]; - } + net_buf_simple_restore(buf, &state); - net_buf_simple_restore(buf, &state); - - return (sum[0] << 8U) | sum[1]; + return (sum[0] << 8U) | sum[1]; } +#endif /* CONFIG_BLE_MESH_DFU_SRV || CONFIG_BLE_MESH_DFU_CLI */ 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 f02b884d22..8ce345831d 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,174 +1,325 @@ /* - * Copyright (c) 2020 Nordic Semiconductor ASA + * SPDX-FileCopyrightText: 2020 Nordic Semiconductor ASA + * SPDX-FileContributor: 2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ #include -#include -#include -#include #include "access.h" #include "dfu.h" +#include "btc_ble_mesh_dfu_model.h" #include "blob.h" -#include +#include "mesh/slist.h" +#include "mesh/mutex.h" +#include "mesh_v1.1/dfu/dfu.h" +#include "mesh_v1.1/dfu/dfu_cli.h" -#define LOG_LEVEL CONFIG_BT_MESH_DFU_LOG_LEVEL -#include -LOG_MODULE_REGISTER(bt_mesh_dfu_cli); +#if CONFIG_BLE_MESH_DFU_CLI +#define TARGETS_FOR_EACH(cli, target) \ + SYS_SLIST_FOR_EACH_CONTAINER( \ + (sys_slist_t *)&((cli)->blob.inputs)->targets, target, blob.n) -#define TARGETS_FOR_EACH(cli, target) \ - SYS_SLIST_FOR_EACH_CONTAINER( \ - (sys_slist_t *)&((cli)->blob.inputs)->targets, target, blob.n) - -#define MSG_CTX(cli, dst) \ - { \ - .app_idx = (cli)->blob.inputs->app_idx, .addr = dst, \ - .send_ttl = (cli)->blob.inputs->ttl, \ - } +#define MSG_CTX(cli, dst) \ + { \ + .app_idx = (cli)->blob.inputs->app_idx, .addr = dst, \ + .send_ttl = (cli)->blob.inputs->ttl, \ + } #define DFU_CLI(blob_cli) CONTAINER_OF(blob_cli, struct bt_mesh_dfu_cli, blob) -BUILD_ASSERT((DFU_UPDATE_START_MSG_MAXLEN + BT_MESH_MODEL_OP_LEN(BT_MESH_DFU_OP_UPDATE_START) + - BT_MESH_MIC_SHORT) <= BT_MESH_TX_SDU_MAX, - "The Firmware Update Start message does not fit into the maximum outgoing SDU size."); +_Static_assert((DFU_UPDATE_START_MSG_MAXLEN + BLE_MESH_MODEL_OP_LEN(BLE_MESH_DFU_OP_UPDATE_START) + + BLE_MESH_MIC_SHORT) <= BLE_MESH_TX_SDU_MAX, + "The Firmware Update Start message does not fit into the maximum outgoing SDU size."); -BUILD_ASSERT((DFU_UPDATE_INFO_STATUS_MSG_MINLEN + - BT_MESH_MODEL_OP_LEN(BT_MESH_DFU_OP_UPDATE_INFO_STATUS) + BT_MESH_MIC_SHORT) - <= BT_MESH_RX_SDU_MAX, - "The Firmware Update Info Status message does not fit into the maximum incoming SDU " - "size."); +_Static_assert((DFU_UPDATE_INFO_STATUS_MSG_MINLEN + + BLE_MESH_MODEL_OP_LEN(BLE_MESH_DFU_OP_UPDATE_INFO_STATUS) + BLE_MESH_MIC_SHORT) + <= BLE_MESH_RX_SDU_MAX, + "The Firmware Update Info Status message does not fit into the maximum incoming SDU " + "size."); enum req { - REQ_NONE, - REQ_METADATA, - REQ_IMG, - REQ_STATUS, + REQ_NONE, + REQ_METADATA, + REQ_IMG, + REQ_STATUS, + INTERNAL_REQ_IMG, }; enum { - FLAG_FAILED = BIT(0), - FLAG_CANCELLED = BIT(1), - FLAG_SKIP_CAPS_GET = BIT(2), - FLAG_RESUME = BIT(3), - FLAG_COMPLETED = BIT(4), + FLAG_FAILED = BIT(0), + FLAG_CANCELLED = BIT(1), + FLAG_SKIP_CAPS_GET = BIT(2), + FLAG_RESUME = BIT(3), + FLAG_COMPLETED = BIT(4), }; -enum { - STATE_IDLE, - STATE_TRANSFER, - STATE_REFRESH, - STATE_VERIFIED, - STATE_APPLY, - STATE_APPLIED, - STATE_CONFIRM, - STATE_CANCEL, - STATE_SUSPENDED, -}; +static void bt_mesh_dfu_cli_rm_req_from_list(bt_mesh_dfu_cli_req_t *req); +static int req_free(bt_mesh_dfu_cli_req_t *req); static int32_t dfu_cli_timeout = (10 * MSEC_PER_SEC); +static struct { + bt_mesh_mutex_t op_lock; + sys_slist_t list; +} dfu_req_list; + static struct bt_mesh_dfu_target *target_get(struct bt_mesh_dfu_cli *cli, - uint16_t addr) + uint16_t addr) { - struct bt_mesh_dfu_target *target; + struct bt_mesh_dfu_target *target; - TARGETS_FOR_EACH(cli, target) { - if (addr == target->blob.addr) { - return target; - } - } + TARGETS_FOR_EACH(cli, target) { + if (addr == target->blob.addr) { + return target; + } + } - return NULL; + return NULL; +} + +static void req_timeout_handler(struct k_work *work) +{ + struct k_delayed_work *timer = NULL; + bt_mesh_dfu_cli_req_t *req = NULL; + struct bt_mesh_msg_ctx ctx = {0}; + + BT_WARN("Receive dfu status message timeout"); + + bt_mesh_r_mutex_lock(&dfu_req_list.op_lock); + + timer = CONTAINER_OF(work, struct k_delayed_work, work); + + if (timer && !k_delayed_work_free(timer)) { + req = CONTAINER_OF(work, bt_mesh_dfu_cli_req_t, timer.work); + memcpy(&ctx, &req->ctx, sizeof(struct bt_mesh_msg_ctx)); + if (req->type == REQ_NONE) { + assert(0); + } + if (req == NULL) { + assert(0); + } + + bt_mesh_dfu_client_cb_evt_to_btc(req->opcode, BTC_BLE_MESH_EVT_DFU_CLIENT_TIMEOUT, + req->dfu_cli->mod, &ctx, NULL, 0); + } + + bt_mesh_dfu_cli_rm_req_from_list(req); + req_free(req); + + bt_mesh_r_mutex_unlock(&dfu_req_list.op_lock); +} + +static bt_mesh_dfu_cli_req_t* req_alloc(void) +{ + bt_mesh_dfu_cli_req_t *req = bt_mesh_calloc(sizeof(bt_mesh_dfu_cli_req_t)); + if (!req) { + BT_ERR("device firmware client allocl failed"); + return NULL; + } + + req->ctx.addr = BLE_MESH_ADDR_UNASSIGNED; + req->type = REQ_NONE; + k_delayed_work_init(&req->timer, req_timeout_handler); + return req; +} + +static int req_free(bt_mesh_dfu_cli_req_t *req) +{ + if (req->node.next) { + BT_ERR("req still in list"); + return -EINVAL; + } + + k_delayed_work_free(&req->timer); + bt_mesh_free(req); + return 0; +} + +static int is_valid_req(bt_mesh_dfu_cli_req_t *req) +{ + if (!req || + req->ctx.addr == BLE_MESH_ADDR_UNASSIGNED) { + return false; + } + + return true; +} + +static void bt_mesh_dfu_cli_req_list_init(void) +{ + sys_slist_init(&dfu_req_list.list); + bt_mesh_r_mutex_create(&dfu_req_list.op_lock); +} + +static void bt_mesh_dfu_cli_add_req_to_list(bt_mesh_dfu_cli_req_t *req) +{ + if (!is_valid_req(req)) { + return; + } + + bt_mesh_r_mutex_lock(&dfu_req_list.op_lock); + + sys_slist_append(&dfu_req_list.list, &req->node); + + bt_mesh_r_mutex_unlock(&dfu_req_list.op_lock); +} + +static void bt_mesh_dfu_cli_rm_req_from_list(bt_mesh_dfu_cli_req_t *req) +{ + if (!is_valid_req(req)) { + return; + } + + bt_mesh_r_mutex_lock(&dfu_req_list.op_lock); + + sys_slist_find_and_remove(&dfu_req_list.list, &req->node); + + bt_mesh_r_mutex_unlock(&dfu_req_list.op_lock); + + k_delayed_work_cancel(&req->timer); +} + +static bt_mesh_dfu_cli_req_t* bt_mesh_dfu_req_find_by_addr(uint16_t addr) +{ + bt_mesh_dfu_cli_req_t *req = NULL; + sys_snode_t *peek = NULL; + + if (addr == BLE_MESH_ADDR_UNASSIGNED) { + return NULL; + } + + bt_mesh_r_mutex_lock(&dfu_req_list.op_lock); + + SYS_SLIST_FOR_EACH_NODE(&dfu_req_list.list, peek) { + req = (bt_mesh_dfu_cli_req_t*)peek; + if (req->ctx.addr == addr) { + bt_mesh_r_mutex_unlock(&dfu_req_list.op_lock); + return req; + } + } + + bt_mesh_r_mutex_unlock(&dfu_req_list.op_lock); + return NULL; +} + +static void bt_mesh_dfu_req_list_free(void) +{ + bt_mesh_dfu_cli_req_t *req = NULL; + sys_snode_t *peek = NULL; + sys_snode_t *nxt = NULL; + + bt_mesh_r_mutex_lock(&dfu_req_list.op_lock); + + SYS_SLIST_FOR_EACH_NODE_SAFE(&dfu_req_list.list, peek, nxt) { + req = (bt_mesh_dfu_cli_req_t*)peek; + if (req->params) { + bt_mesh_free(req->params); + } + bt_mesh_dfu_cli_rm_req_from_list(req); + k_delayed_work_cancel(&req->timer); + req_free(req); + } + + bt_mesh_r_mutex_unlock(&dfu_req_list.op_lock); + return; } static void target_failed(struct bt_mesh_dfu_cli *cli, - struct bt_mesh_dfu_target *target, - enum bt_mesh_dfu_status status) + struct bt_mesh_dfu_target *target, + enum bt_mesh_dfu_status status) { - target->status = status; + target->status = status; - LOG_ERR("Target 0x%04x failed: %u", target->blob.addr, status); + BT_ERR("Target 0x%04x failed: %u", target->blob.addr, status); - /* Invalidate blob status to prevent the target from being included in - * future sending: - */ - if (target->blob.status == BT_MESH_BLOB_SUCCESS) { - target->blob.status = BT_MESH_BLOB_ERR_INTERNAL; - } + /* Invalidate blob status to prevent the target from being included in + * future sending: + */ + if (target->blob.status == BT_MESH_BLOB_SUCCESS) { + target->blob.status = BT_MESH_BLOB_ERR_INTERNAL; + } - if (cli->cb && cli->cb->lost_target) { - cli->cb->lost_target(cli, target); - } + if (cli->cb && cli->cb->lost_target) { + cli->cb->lost_target(cli, target); + } } static void dfu_complete(struct bt_mesh_dfu_cli *cli) { - LOG_DBG(""); + BT_DBG(""); - if (cli->cb && cli->cb->ended) { - cli->cb->ended(cli, BT_MESH_DFU_SUCCESS); - } + if (cli->cb && cli->cb->ended) { + cli->cb->ended(cli, BLE_MESH_DFU_SUCCESS); + } } static void dfu_applied(struct bt_mesh_dfu_cli *cli) { - LOG_DBG(""); + BT_DBG(""); - cli->xfer.state = STATE_APPLIED; + cli->xfer.state = STATE_APPLIED; - if (cli->cb && cli->cb->applied) { - cli->cb->applied(cli); - } + if (cli->cb && cli->cb->applied) { + cli->cb->applied(cli); + } } static void dfu_failed(struct bt_mesh_dfu_cli *cli, - enum bt_mesh_dfu_status reason) + enum bt_mesh_dfu_status reason) { - LOG_DBG("%u", reason); + BT_DBG("%u", reason); - cli->xfer.flags |= FLAG_FAILED; + cli->xfer.flags |= FLAG_FAILED; - if (cli->cb && cli->cb->ended) { - cli->cb->ended(cli, reason); - } + if (cli->cb && cli->cb->ended) { + cli->cb->ended(cli, reason); + } } -static int req_setup(struct bt_mesh_dfu_cli *cli, enum req type, uint16_t addr, - void *params) +static int req_setup(struct bt_mesh_dfu_cli *cli, enum req type, + uint32_t opcode, struct bt_mesh_msg_ctx *ctx, + void *params) { - if (cli->req.type != REQ_NONE) { - return -EBUSY; - } + if (!cli->req || + cli->req->type != REQ_NONE) { + return -EBUSY; + } - cli->req.addr = addr; - cli->req.params = params; - cli->req.type = type; + cli->req->params = params; + cli->req->type = type; + cli->req->dfu_cli = cli; + cli->req->opcode = opcode; - return 0; + if (ctx) { + memcpy(&cli->req->ctx, ctx, sizeof(struct bt_mesh_msg_ctx)); + } + + return 0; } static int req_wait(struct bt_mesh_dfu_cli *cli, k_timeout_t timeout) { - int err; + int err = ESP_OK; + bt_mesh_dfu_cli_req_t *req = cli->req; - err = k_sem_take(&cli->req.sem, timeout); - cli->req.type = REQ_NONE; - - return err; + req->timeout = timeout; + k_delayed_work_submit(&req->timer, timeout); + bt_mesh_dfu_cli_add_req_to_list(req); + return err; } static bool targets_active(struct bt_mesh_dfu_cli *cli) { - struct bt_mesh_dfu_target *target; + struct bt_mesh_dfu_target *target; - TARGETS_FOR_EACH(cli, target) { - if (target->status == BT_MESH_DFU_SUCCESS) { - return true; - } - } + TARGETS_FOR_EACH(cli, target) { + if (target->status == BLE_MESH_DFU_SUCCESS) { + return true; + } + } - return false; + return false; } /******************************************************************************* @@ -177,106 +328,112 @@ static bool targets_active(struct bt_mesh_dfu_cli *cli) static void refresh(struct bt_mesh_dfu_cli *cli); static void blob_caps(struct bt_mesh_blob_cli *b, - const struct bt_mesh_blob_cli_caps *caps) + const struct bt_mesh_blob_cli_caps *caps) { - struct bt_mesh_dfu_cli *cli = DFU_CLI(b); - int err; + struct bt_mesh_dfu_cli *cli = DFU_CLI(b); + int err; - if (!caps) { - dfu_failed(cli, BT_MESH_DFU_ERR_RESOURCES); - return; - } + if (!caps) { + dfu_failed(cli, BLE_MESH_DFU_ERR_RESOURCES); + return; + } - cli->xfer.blob.block_size_log = caps->max_block_size_log; - cli->xfer.blob.chunk_size = caps->max_chunk_size; + cli->xfer.blob.block_size_log = caps->max_block_size_log; + cli->xfer.blob.chunk_size = caps->max_chunk_size; - /* 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 - * reported value otherwise. - */ - if (!(cli->xfer.blob.mode & BT_MESH_BLOB_XFER_MODE_ALL)) { - cli->xfer.blob.mode = - caps->modes == BT_MESH_BLOB_XFER_MODE_ALL ? - BT_MESH_BLOB_XFER_MODE_PUSH : caps->modes; - } else { - cli->xfer.blob.mode = - caps->modes == BT_MESH_BLOB_XFER_MODE_ALL ? - cli->xfer.blob.mode : caps->modes; - } + /* 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 + * reported value otherwise. + */ + if (!(cli->xfer.blob.mode & BT_MESH_BLOB_XFER_MODE_ALL)) { + cli->xfer.blob.mode = + caps->modes == BT_MESH_BLOB_XFER_MODE_ALL ? + BT_MESH_BLOB_XFER_MODE_PUSH : caps->modes; + } else { + cli->xfer.blob.mode = + caps->modes == BT_MESH_BLOB_XFER_MODE_ALL ? + cli->xfer.blob.mode : caps->modes; + } - err = bt_mesh_blob_cli_send(b, b->inputs, &cli->xfer.blob, cli->xfer.io); - if (err) { - LOG_ERR("Starting BLOB xfer failed: %d", err); - dfu_failed(cli, BT_MESH_DFU_ERR_BLOB_XFER_BUSY); - } + err = bt_mesh_blob_cli_send(b, b->inputs, &cli->xfer.blob, cli->xfer.io); + if (err) { + BT_ERR("Starting BLOB xfer failed: %d", err); + dfu_failed(cli, BLE_MESH_DFU_ERR_BLOB_XFER_BUSY); + } } static void blob_lost_target(struct bt_mesh_blob_cli *b, - struct bt_mesh_blob_target *blobt, - enum bt_mesh_blob_status reason) + struct bt_mesh_blob_target *blobt, + enum bt_mesh_blob_status reason) { - struct bt_mesh_dfu_target *target = - CONTAINER_OF(blobt, struct bt_mesh_dfu_target, blob); - struct bt_mesh_dfu_cli *cli = DFU_CLI(b); + struct bt_mesh_dfu_target *target = + CONTAINER_OF(blobt, struct bt_mesh_dfu_target, blob); + struct bt_mesh_dfu_cli *cli = DFU_CLI(b); - if ((cli->xfer.state == STATE_CONFIRM || cli->xfer.state == STATE_APPLY) && - target->effect == BT_MESH_DFU_EFFECT_UNPROV) { - /* Reset status for such targets to use them in consequent procedures. See sections - * 7.1.2.6 and 7.1.2.9 of the MeshDFU. - */ - target->blob.status = BT_MESH_BLOB_SUCCESS; - return; - } + if ((cli->xfer.state == STATE_CONFIRM || cli->xfer.state == STATE_APPLY) && + target->effect == BLE_MESH_DFU_EFFECT_UNPROV) { + /* Reset status for such targets to use them in consequent procedures. See sections + * 7.1.2.6 and 7.1.2.9 of the MeshDFU. + */ + target->blob.status = BT_MESH_BLOB_SUCCESS; + return; + } - target_failed(cli, target, BT_MESH_DFU_ERR_INTERNAL); + target_failed(cli, target, BLE_MESH_DFU_ERR_INTERNAL); } static void blob_suspended(struct bt_mesh_blob_cli *b) { - struct bt_mesh_dfu_cli *cli = DFU_CLI(b); + struct bt_mesh_dfu_cli *cli = DFU_CLI(b); - LOG_DBG("BLOB transfer suspended"); + BT_DBG("BLOB transfer suspended"); - cli->xfer.state = STATE_SUSPENDED; + cli->xfer.state = STATE_SUSPENDED; - if (cli->cb && cli->cb->suspended) { - cli->cb->suspended(cli); - } + if (cli->cb && cli->cb->suspended) { + cli->cb->suspended(cli); + } } static void blob_end(struct bt_mesh_blob_cli *b, - const struct bt_mesh_blob_xfer *xfer, bool success) + const struct bt_mesh_blob_xfer *xfer, bool success) { - struct bt_mesh_dfu_cli *cli = DFU_CLI(b); + struct bt_mesh_dfu_cli *cli = DFU_CLI(b); + bt_mesh_dfu_cli_req_t *req = NULL; - cli->req.img_cb = NULL; + if (b->tx.target) { + req = bt_mesh_dfu_req_find_by_addr(b->tx.target->addr); + if (req) { + req->img_cb = NULL; + } + } - if (success) { - refresh(cli); - return; - } + if (success) { + refresh(cli); + return; + } - if (cli->xfer.state == STATE_CANCEL) { - /* The user cancelled the transfer, DFU will end when all - * targets have been notified. - */ - return; - } + if (cli->xfer.state == STATE_CANCEL) { + /* The user cancelled the transfer, DFU will end when all + * targets have been notified. + */ + return; + } - if (cli->xfer.state != STATE_TRANSFER) { - LOG_ERR("Blob failed in invalid state %u", cli->xfer.state); - return; - } + if (cli->xfer.state != STATE_TRANSFER) { + BT_ERR("Blob failed in invalid state %u", cli->xfer.state); + return; + } - dfu_failed(cli, BT_MESH_DFU_ERR_INTERNAL); + dfu_failed(cli, BLE_MESH_DFU_ERR_INTERNAL); } const struct bt_mesh_blob_cli_cb _bt_mesh_dfu_cli_blob_handlers = { - .caps = blob_caps, - .lost_target = blob_lost_target, - .suspended = blob_suspended, - .end = blob_end, + .caps = blob_caps, + .lost_target = blob_lost_target, + .suspended = blob_suspended, + .end = blob_end, }; /******************************************************************************* @@ -287,123 +444,123 @@ static void tx_start(uint16_t dur, int err, void *cb_data); static void tx_end(int err, void *cb_data); static const struct bt_mesh_send_cb send_cb = { - .start = tx_start, - .end = tx_end, + .start = tx_start, + .end = tx_end, }; static void tx_start(uint16_t dur, int err, void *cb_data) { - if (err) { - tx_end(err, cb_data); - } + if (err) { + tx_end(err, cb_data); + } } static void tx_end(int err, void *cb_data) { - struct bt_mesh_dfu_cli *cli = cb_data; + struct bt_mesh_dfu_cli *cli = cb_data; - blob_cli_broadcast_tx_complete(&cli->blob); + blob_cli_broadcast_tx_complete(&cli->blob); } static int tx(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx, - struct net_buf_simple *buf, const struct bt_mesh_send_cb *cb, - struct bt_mesh_dfu_cli *cli) + struct net_buf_simple *buf, const struct bt_mesh_send_cb *cb, + struct bt_mesh_dfu_cli *cli) { - int err; + int err; - err = bt_mesh_model_send(mod, ctx, buf, cb, cli); - if (err) { - LOG_ERR("Send err: %d", err); - if (cb) { - cb->end(err, cli); - } - return err; - } + err = bt_mesh_model_send(mod, ctx, buf, cb, cli); + if (err) { + BT_ERR("Send err: %d", err); + if (cb) { + cb->end(err, cli); + } + return err; + } - return 0; + return 0; } static int info_get(struct bt_mesh_dfu_cli *cli, struct bt_mesh_msg_ctx *ctx, - uint8_t idx, uint8_t max_count, - const struct bt_mesh_send_cb *cb) + uint8_t idx, uint8_t max_count, + const struct bt_mesh_send_cb *cb) { - BT_MESH_MODEL_BUF_DEFINE(buf, BT_MESH_DFU_OP_UPDATE_INFO_GET, 2); - bt_mesh_model_msg_init(&buf, BT_MESH_DFU_OP_UPDATE_INFO_GET); - net_buf_simple_add_u8(&buf, idx); - net_buf_simple_add_u8(&buf, max_count); + BLE_MESH_MODEL_BUF_DEFINE(buf, BLE_MESH_DFU_OP_UPDATE_INFO_GET, 2); + bt_mesh_model_msg_init(&buf, BLE_MESH_DFU_OP_UPDATE_INFO_GET); + net_buf_simple_add_u8(&buf, idx); + net_buf_simple_add_u8(&buf, max_count); - return tx(cli->mod, ctx, &buf, cb, cli); + return tx(cli->mod, ctx, &buf, cb, cli); } static void send_info_get(struct bt_mesh_blob_cli *b, uint16_t dst) { - struct bt_mesh_dfu_cli *cli = DFU_CLI(b); - struct bt_mesh_msg_ctx ctx = MSG_CTX(cli, dst); + struct bt_mesh_dfu_cli *cli = DFU_CLI(b); + struct bt_mesh_msg_ctx ctx = MSG_CTX(cli, dst); - cli->req.img_cnt = 0xff; + cli->req->img_cnt = 0xff; - info_get(cli, &ctx, 0, cli->req.img_cnt, &send_cb); + info_get(cli, &ctx, 0, cli->req->img_cnt, &send_cb); } static void send_update_start(struct bt_mesh_blob_cli *b, uint16_t dst) { - struct bt_mesh_dfu_cli *cli = DFU_CLI(b); - struct bt_mesh_msg_ctx ctx = MSG_CTX(cli, dst); - struct bt_mesh_dfu_target *target; + struct bt_mesh_dfu_cli *cli = DFU_CLI(b); + struct bt_mesh_msg_ctx ctx = MSG_CTX(cli, dst); + struct bt_mesh_dfu_target *target; - if (b->tx.ctx.force_unicast) { - target = target_get(cli, dst); - } else { - target = SYS_SLIST_PEEK_HEAD_CONTAINER( - (sys_slist_t *)&((cli)->blob.inputs)->targets, - target, blob.n); - } + if (b->tx.ctx.force_unicast) { + target = target_get(cli, dst); + } else { + target = SYS_SLIST_PEEK_HEAD_CONTAINER( + (sys_slist_t *) & ((cli)->blob.inputs)->targets, + target, blob.n); + } - BT_MESH_MODEL_BUF_DEFINE(buf, BT_MESH_DFU_OP_UPDATE_START, - DFU_UPDATE_START_MSG_MAXLEN); - bt_mesh_model_msg_init(&buf, BT_MESH_DFU_OP_UPDATE_START); + BLE_MESH_MODEL_BUF_DEFINE(buf, BLE_MESH_DFU_OP_UPDATE_START, + DFU_UPDATE_START_MSG_MAXLEN); + bt_mesh_model_msg_init(&buf, BLE_MESH_DFU_OP_UPDATE_START); - net_buf_simple_add_u8(&buf, cli->blob.inputs->ttl); - net_buf_simple_add_le16(&buf, cli->blob.inputs->timeout_base); - net_buf_simple_add_le64(&buf, cli->xfer.blob.id); - net_buf_simple_add_u8(&buf, target->img_idx); - net_buf_simple_add_mem(&buf, cli->xfer.slot->metadata, - cli->xfer.slot->metadata_len); + net_buf_simple_add_u8(&buf, cli->blob.inputs->ttl); + net_buf_simple_add_le16(&buf, cli->blob.inputs->timeout_base); + net_buf_simple_add_le64(&buf, cli->xfer.blob.id); + net_buf_simple_add_u8(&buf, target->img_idx); + net_buf_simple_add_mem(&buf, cli->xfer.slot->metadata, + cli->xfer.slot->metadata_len); - (void)tx(cli->mod, &ctx, &buf, &send_cb, cli); + (void)tx(cli->mod, &ctx, &buf, &send_cb, cli); } static void send_update_get(struct bt_mesh_blob_cli *b, uint16_t dst) { - struct bt_mesh_dfu_cli *cli = DFU_CLI(b); - struct bt_mesh_msg_ctx ctx = MSG_CTX(cli, dst); + struct bt_mesh_dfu_cli *cli = DFU_CLI(b); + struct bt_mesh_msg_ctx ctx = MSG_CTX(cli, dst); - BT_MESH_MODEL_BUF_DEFINE(buf, BT_MESH_DFU_OP_UPDATE_GET, 0); - bt_mesh_model_msg_init(&buf, BT_MESH_DFU_OP_UPDATE_GET); + BLE_MESH_MODEL_BUF_DEFINE(buf, BLE_MESH_DFU_OP_UPDATE_GET, 0); + bt_mesh_model_msg_init(&buf, BLE_MESH_DFU_OP_UPDATE_GET); - (void)tx(cli->mod, &ctx, &buf, &send_cb, cli); + (void)tx(cli->mod, &ctx, &buf, &send_cb, cli); } static void send_update_cancel(struct bt_mesh_blob_cli *b, uint16_t dst) { - struct bt_mesh_dfu_cli *cli = DFU_CLI(b); - struct bt_mesh_msg_ctx ctx = MSG_CTX(cli, dst); + struct bt_mesh_dfu_cli *cli = DFU_CLI(b); + struct bt_mesh_msg_ctx ctx = MSG_CTX(cli, dst); - BT_MESH_MODEL_BUF_DEFINE(buf, BT_MESH_DFU_OP_UPDATE_CANCEL, 0); - bt_mesh_model_msg_init(&buf, BT_MESH_DFU_OP_UPDATE_CANCEL); + BLE_MESH_MODEL_BUF_DEFINE(buf, BLE_MESH_DFU_OP_UPDATE_CANCEL, 0); + bt_mesh_model_msg_init(&buf, BLE_MESH_DFU_OP_UPDATE_CANCEL); - (void)tx(cli->mod, &ctx, &buf, &send_cb, cli); + (void)tx(cli->mod, &ctx, &buf, &send_cb, cli); } static void send_update_apply(struct bt_mesh_blob_cli *b, uint16_t dst) { - struct bt_mesh_dfu_cli *cli = DFU_CLI(b); - struct bt_mesh_msg_ctx ctx = MSG_CTX(cli, dst); + struct bt_mesh_dfu_cli *cli = DFU_CLI(b); + struct bt_mesh_msg_ctx ctx = MSG_CTX(cli, dst); - BT_MESH_MODEL_BUF_DEFINE(buf, BT_MESH_DFU_OP_UPDATE_APPLY, 0); - bt_mesh_model_msg_init(&buf, BT_MESH_DFU_OP_UPDATE_APPLY); + BLE_MESH_MODEL_BUF_DEFINE(buf, BLE_MESH_DFU_OP_UPDATE_APPLY, 0); + bt_mesh_model_msg_init(&buf, BLE_MESH_DFU_OP_UPDATE_APPLY); - (void)tx(cli->mod, &ctx, &buf, &send_cb, cli); + (void)tx(cli->mod, &ctx, &buf, &send_cb, cli); } /******************************************************************************* @@ -417,282 +574,282 @@ static void cancelled(struct bt_mesh_blob_cli *b); static void initiate(struct bt_mesh_dfu_cli *cli) { - struct blob_cli_broadcast_ctx tx = { - .send = send_update_start, - .next = transfer, - .acked = true, - }; - struct bt_mesh_dfu_target *target; - int img_idx = -1; + struct blob_cli_broadcast_ctx tx = { + .send = send_update_start, + .next = transfer, + .acked = true, + }; + struct bt_mesh_dfu_target *target; + int img_idx = -1; - /** If firmware img index is the same for all targets, we can send Firmware Update Start - * message using multicast address. Otherwise, it has to be send in a unicast way. - */ - TARGETS_FOR_EACH(cli, target) { - if (img_idx == -1) { - img_idx = target->img_idx; - } else if (target->img_idx != img_idx) { - tx.force_unicast = true; - break; - } - } + /** If firmware img index is the same for all targets, we can send Firmware Update Start + * message using multicast address. Otherwise, it has to be send in a unicast way. + */ + TARGETS_FOR_EACH(cli, target) { + if (img_idx == -1) { + img_idx = target->img_idx; + } else if (target->img_idx != img_idx) { + tx.force_unicast = true; + break; + } + } - LOG_DBG(""); + BT_DBG(""); - cli->op = BT_MESH_DFU_OP_UPDATE_STATUS; - cli->xfer.state = STATE_TRANSFER; + cli->op = BLE_MESH_DFU_OP_UPDATE_STATUS; + cli->xfer.state = STATE_TRANSFER; - blob_cli_broadcast(&cli->blob, &tx); + blob_cli_broadcast(&cli->blob, &tx); } static void skip_targets_from_broadcast(struct bt_mesh_dfu_cli *cli, bool skip) { - struct bt_mesh_dfu_target *target; + struct bt_mesh_dfu_target *target; - TARGETS_FOR_EACH(cli, target) { - /* If distributor is in the targets list, or target is in Verify phase, - * disable it until Retrieve Capabilities and BLOB Transfer procedures - * are completed. - */ - if (bt_mesh_has_addr(target->blob.addr) || - target->phase == BT_MESH_DFU_PHASE_VERIFY) { - target->blob.skip = skip; - break; - } - } + TARGETS_FOR_EACH(cli, target) { + /* If distributor is in the targets list, or target is in Verify phase, + * disable it until Retrieve Capabilities and BLOB Transfer procedures + * are completed. + */ + if (bt_mesh_has_addr(target->blob.addr) || + target->phase == BLE_MESH_DFU_PHASE_VERIFY) { + target->blob.skip = skip; + break; + } + } } static bool transfer_skip(struct bt_mesh_dfu_cli *cli) { - struct bt_mesh_dfu_target *target; + struct bt_mesh_dfu_target *target; - TARGETS_FOR_EACH(cli, target) { - if (!bt_mesh_has_addr(target->blob.addr) || !target->blob.skip) { - return false; - } - } + TARGETS_FOR_EACH(cli, target) { + if (!bt_mesh_has_addr(target->blob.addr) || !target->blob.skip) { + return false; + } + } - return true; + return true; } static void transfer(struct bt_mesh_blob_cli *b) { - struct bt_mesh_dfu_cli *cli = DFU_CLI(b); - int err; + struct bt_mesh_dfu_cli *cli = DFU_CLI(b); + int err; - LOG_DBG(""); + BT_DBG(""); - if (!targets_active(cli)) { - dfu_failed(cli, BT_MESH_DFU_ERR_INTERNAL); - return; - } + if (!targets_active(cli)) { + dfu_failed(cli, BLE_MESH_DFU_ERR_INTERNAL); + return; + } - skip_targets_from_broadcast(cli, true); + skip_targets_from_broadcast(cli, true); - if (transfer_skip(cli)) { - /* If distributor only updates itself, or all targets are in Verify phase, - * proceed to the refresh step immediately. - */ - refresh(cli); - return; - } + if (transfer_skip(cli)) { + /* If distributor only updates itself, or all targets are in Verify phase, + * proceed to the refresh step immediately. + */ + refresh(cli); + return; + } - if (cli->xfer.flags & FLAG_RESUME) { - cli->xfer.flags ^= FLAG_RESUME; - err = bt_mesh_blob_cli_resume(b); - if (err) { - LOG_ERR("Resuming BLOB xfer failed: %d", err); - dfu_failed(cli, BT_MESH_DFU_ERR_BLOB_XFER_BUSY); - } - } else if (cli->xfer.flags & FLAG_SKIP_CAPS_GET) { - cli->xfer.flags ^= FLAG_SKIP_CAPS_GET; - err = bt_mesh_blob_cli_send(b, b->inputs, &cli->xfer.blob, cli->xfer.io); - if (err) { - LOG_ERR("Starting BLOB xfer failed: %d", err); - dfu_failed(cli, BT_MESH_DFU_ERR_BLOB_XFER_BUSY); - } - } else { - err = bt_mesh_blob_cli_caps_get(&cli->blob, cli->blob.inputs); - if (err) { - LOG_ERR("Failed starting blob xfer: %d", err); - dfu_failed(cli, BT_MESH_DFU_ERR_BLOB_XFER_BUSY); - } - } + if (cli->xfer.flags & FLAG_RESUME) { + cli->xfer.flags ^= FLAG_RESUME; + err = bt_mesh_blob_cli_resume(b); + if (err) { + BT_ERR("Resuming BLOB xfer failed: %d", err); + dfu_failed(cli, BLE_MESH_DFU_ERR_BLOB_XFER_BUSY); + } + } else if (cli->xfer.flags & FLAG_SKIP_CAPS_GET) { + cli->xfer.flags ^= FLAG_SKIP_CAPS_GET; + err = bt_mesh_blob_cli_send(b, b->inputs, &cli->xfer.blob, cli->xfer.io); + if (err) { + BT_ERR("Starting BLOB xfer failed: %d", err); + dfu_failed(cli, BLE_MESH_DFU_ERR_BLOB_XFER_BUSY); + } + } else { + err = bt_mesh_blob_cli_caps_get(&cli->blob, cli->blob.inputs); + if (err) { + BT_ERR("Failed starting blob xfer: %d", err); + dfu_failed(cli, BLE_MESH_DFU_ERR_BLOB_XFER_BUSY); + } + } } static void refreshed(struct bt_mesh_blob_cli *b) { - struct bt_mesh_dfu_cli *cli = DFU_CLI(b); + struct bt_mesh_dfu_cli *cli = DFU_CLI(b); - if (!targets_active(cli)) { - dfu_failed(cli, BT_MESH_DFU_ERR_INTERNAL); - return; - } + if (!targets_active(cli)) { + dfu_failed(cli, BLE_MESH_DFU_ERR_INTERNAL); + return; + } - cli->xfer.state = STATE_VERIFIED; - dfu_complete(cli); + cli->xfer.state = STATE_VERIFIED; + dfu_complete(cli); } static void refresh(struct bt_mesh_dfu_cli *cli) { - const struct blob_cli_broadcast_ctx tx = { - .send = send_update_get, - .next = refreshed, - .acked = true - }; + const struct blob_cli_broadcast_ctx tx = { + .send = send_update_get, + .next = refreshed, + .acked = true + }; - LOG_DBG(""); + BT_DBG(""); - cli->xfer.state = STATE_REFRESH; - cli->op = BT_MESH_DFU_OP_UPDATE_STATUS; + cli->xfer.state = STATE_REFRESH; + cli->op = BLE_MESH_DFU_OP_UPDATE_STATUS; - /* If distributor is in the targets list, enable it again so it participates in Distribute - * Firmware procedure. - */ - skip_targets_from_broadcast(cli, false); + /* If distributor is in the targets list, enable it again so it participates in Distribute + * Firmware procedure. + */ + skip_targets_from_broadcast(cli, false); - blob_cli_broadcast(&cli->blob, &tx); + blob_cli_broadcast(&cli->blob, &tx); } static void apply(struct bt_mesh_dfu_cli *cli) { - const struct blob_cli_broadcast_ctx tx = { - .send = send_update_apply, - .next = applied, - .acked = true - }; + const struct blob_cli_broadcast_ctx tx = { + .send = send_update_apply, + .next = applied, + .acked = true + }; - LOG_DBG(""); + BT_DBG(""); - cli->xfer.state = STATE_APPLY; - cli->op = BT_MESH_DFU_OP_UPDATE_STATUS; + cli->xfer.state = STATE_APPLY; + cli->op = BLE_MESH_DFU_OP_UPDATE_STATUS; - blob_cli_broadcast(&cli->blob, &tx); + blob_cli_broadcast(&cli->blob, &tx); } static void applied(struct bt_mesh_blob_cli *b) { - struct bt_mesh_dfu_cli *cli = DFU_CLI(b); + struct bt_mesh_dfu_cli *cli = DFU_CLI(b); - if (!targets_active(cli)) { - dfu_failed(cli, BT_MESH_DFU_ERR_INTERNAL); - return; - } + if (!targets_active(cli)) { + dfu_failed(cli, BLE_MESH_DFU_ERR_INTERNAL); + return; + } - dfu_applied(cli); + dfu_applied(cli); } static enum bt_mesh_dfu_iter target_img_cb(struct bt_mesh_dfu_cli *cli, - struct bt_mesh_msg_ctx *ctx, - uint8_t idx, uint8_t cnt, - const struct bt_mesh_dfu_img *img, - void *cb_data) + struct bt_mesh_msg_ctx *ctx, + uint8_t idx, uint8_t cnt, + const struct bt_mesh_dfu_img *img, + void *cb_data) { - struct bt_mesh_dfu_target *target; + struct bt_mesh_dfu_target *target; - if ((img->fwid_len != cli->xfer.slot->fwid_len) || - memcmp(cli->xfer.slot->fwid, img->fwid, img->fwid_len)) { - return BT_MESH_DFU_ITER_CONTINUE; - } + if ((img->fwid_len != cli->xfer.slot->fwid_len) || + memcmp(cli->xfer.slot->fwid, img->fwid, img->fwid_len)) { + return BLE_MESH_DFU_ITER_CONTINUE; + } - target = target_get(cli, ctx->addr); - if (target) { - LOG_DBG("SUCCESS: 0x%04x applied dfu (as image %u)", ctx->addr, - idx); - target->phase = BT_MESH_DFU_PHASE_APPLY_SUCCESS; - blob_cli_broadcast_rsp(&cli->blob, &target->blob); - } else { - LOG_WRN("Target 0x%04x not found", ctx->addr); - } + target = target_get(cli, ctx->addr); + if (target) { + BT_DBG("SUCCESS: 0x%04x applied dfu (as image %u)", ctx->addr, + idx); + target->phase = BLE_MESH_DFU_PHASE_APPLY_SUCCESS; + blob_cli_broadcast_rsp(&cli->blob, &target->blob); + } else { + BT_WARN("Target 0x%04x not found", ctx->addr); + } - return BT_MESH_DFU_ITER_STOP; + return BLE_MESH_DFU_ITER_STOP; } static void confirm(struct bt_mesh_dfu_cli *cli) { - const struct blob_cli_broadcast_ctx tx = { - .send = send_info_get, - .next = confirmed, - .acked = true, - .optional = true, - }; + const struct blob_cli_broadcast_ctx tx = { + .send = send_info_get, + .next = confirmed, + .acked = true, + .optional = true, + }; - LOG_DBG(""); + BT_DBG(""); - cli->op = BT_MESH_DFU_OP_UPDATE_INFO_STATUS; - cli->req.img_cb = target_img_cb; - cli->req.ttl = cli->blob.inputs->ttl; + cli->op = BLE_MESH_DFU_OP_UPDATE_INFO_STATUS; + cli->req->img_cb = target_img_cb; + cli->req->ttl = cli->blob.inputs->ttl; - blob_cli_broadcast(&cli->blob, &tx); + blob_cli_broadcast(&cli->blob, &tx); } static void confirmed(struct bt_mesh_blob_cli *b) { - struct bt_mesh_dfu_cli *cli = DFU_CLI(b); - struct bt_mesh_dfu_target *target; - bool success = false; + struct bt_mesh_dfu_cli *cli = DFU_CLI(b); + struct bt_mesh_dfu_target *target; + bool success = false; - cli->req.img_cb = NULL; + cli->req->img_cb = NULL; - TARGETS_FOR_EACH(cli, target) { - if (target->status != BT_MESH_DFU_SUCCESS) { - /* Target either failed at earlier stage or during confirmation. In any - * case, the app is already notified. Don't consider the target here. - */ - continue; - } + TARGETS_FOR_EACH(cli, target) { + if (target->status != BLE_MESH_DFU_SUCCESS) { + /* Target either failed at earlier stage or during confirmation. In any + * case, the app is already notified. Don't consider the target here. + */ + continue; + } - if (target->effect == BT_MESH_DFU_EFFECT_UNPROV) { - if (!target->blob.acked) { - success = true; - continue; - } + if (target->effect == BLE_MESH_DFU_EFFECT_UNPROV) { + if (!target->blob.acked) { + success = true; + continue; + } - LOG_DBG("Target 0x%04x still provisioned", target->blob.addr); - target->phase = BT_MESH_DFU_PHASE_APPLY_FAIL; - target_failed(cli, target, BT_MESH_DFU_ERR_INTERNAL); - } else if (!target->blob.acked) { - LOG_DBG("Target 0x%04x failed to respond", target->blob.addr); - target->phase = BT_MESH_DFU_PHASE_APPLY_FAIL; - target_failed(cli, target, BT_MESH_DFU_ERR_INTERNAL); - } else if (target->status == BT_MESH_DFU_SUCCESS) { - success = true; - } - } + BT_DBG("Target 0x%04x still provisioned", target->blob.addr); + target->phase = BLE_MESH_DFU_PHASE_APPLY_FAIL; + target_failed(cli, target, BLE_MESH_DFU_ERR_INTERNAL); + } else if (!target->blob.acked) { + BT_DBG("Target 0x%04x failed to respond", target->blob.addr); + target->phase = BLE_MESH_DFU_PHASE_APPLY_FAIL; + target_failed(cli, target, BLE_MESH_DFU_ERR_INTERNAL); + } else if (target->status == BLE_MESH_DFU_SUCCESS) { + success = true; + } + } - if (success) { - cli->xfer.state = STATE_IDLE; - cli->xfer.flags = FLAG_COMPLETED; + if (success) { + cli->xfer.state = STATE_IDLE; + cli->xfer.flags = FLAG_COMPLETED; - if (cli->cb && cli->cb->confirmed) { - cli->cb->confirmed(cli); - } - } else { - dfu_failed(cli, BT_MESH_DFU_ERR_INTERNAL); - } + if (cli->cb && cli->cb->confirmed) { + cli->cb->confirmed(cli); + } + } else { + dfu_failed(cli, BLE_MESH_DFU_ERR_INTERNAL); + } } static void cancel(struct bt_mesh_dfu_cli *cli) { - const struct blob_cli_broadcast_ctx tx = { - .send = send_update_cancel, - .next = cancelled, - .acked = true - }; + const struct blob_cli_broadcast_ctx tx = { + .send = send_update_cancel, + .next = cancelled, + .acked = true + }; - LOG_DBG(""); + BT_DBG(""); - cli->op = BT_MESH_DFU_OP_UPDATE_STATUS; + cli->op = BLE_MESH_DFU_OP_UPDATE_STATUS; - blob_cli_broadcast(&cli->blob, &tx); + blob_cli_broadcast(&cli->blob, &tx); } static void cancelled(struct bt_mesh_blob_cli *b) { - struct bt_mesh_dfu_cli *cli = DFU_CLI(b); + struct bt_mesh_dfu_cli *cli = DFU_CLI(b); - cli->xfer.flags |= FLAG_CANCELLED; - dfu_failed(cli, BT_MESH_DFU_ERR_INTERNAL); + cli->xfer.flags |= FLAG_CANCELLED; + dfu_failed(cli, BLE_MESH_DFU_ERR_INTERNAL); } /******************************************************************************* @@ -700,302 +857,351 @@ static void cancelled(struct bt_mesh_blob_cli *b) ******************************************************************************/ static int handle_status(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx, - struct net_buf_simple *buf) + struct net_buf_simple *buf) { - struct bt_mesh_dfu_cli *cli = mod->rt->user_data; - enum bt_mesh_dfu_status status; - enum bt_mesh_dfu_phase phase; - struct bt_mesh_dfu_target *target; - uint8_t byte; + struct bt_mesh_dfu_cli *cli = mod->user_data; + bt_mesh_dfu_cli_req_t *req = NULL; + enum bt_mesh_dfu_status status; + enum bt_mesh_dfu_phase phase; + struct bt_mesh_dfu_target *target; + uint8_t byte; - byte = net_buf_simple_pull_u8(buf); - status = byte & BIT_MASK(3); - phase = byte >> 5; + byte = net_buf_simple_pull_u8(buf); + status = byte & BIT_MASK(3); + phase = byte >> 5; - if (cli->req.type == REQ_STATUS && cli->req.addr == ctx->addr) { - if (cli->req.params) { - struct bt_mesh_dfu_target_status *rsp = cli->req.params; + req = bt_mesh_dfu_req_find_by_addr(ctx->addr); - rsp->status = status; - rsp->phase = phase; - if (buf->len == 13) { - rsp->ttl = net_buf_simple_pull_u8(buf); - rsp->effect = net_buf_simple_pull_u8(buf) & BIT_MASK(5); - 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) { - return -EINVAL; - } + // The req will be NULL if the target is a group address. + if (req && req->type == REQ_STATUS) { + if (req->params) { + struct bt_mesh_dfu_target_status *rsp = req->params; - rsp->ttl = 0U; - rsp->effect = BT_MESH_DFU_EFFECT_NONE; - rsp->timeout_base = 0U; - rsp->blob_id = 0U; - rsp->img_idx = 0U; - } - k_sem_give(&cli->req.sem); - } - if (cli->op != BT_MESH_DFU_OP_UPDATE_STATUS) { - return 0; - } + rsp->status = status; + rsp->phase = phase; + if (buf->len == 13) { + rsp->ttl = net_buf_simple_pull_u8(buf); + rsp->effect = net_buf_simple_pull_u8(buf) & BIT_MASK(5); + 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) { + return -EINVAL; + } - target = target_get(cli, ctx->addr); - if (!target) { - LOG_WRN("Unknown target 0x%04x", ctx->addr); - return -ENOENT; - } + 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, + sizeof(struct bt_mesh_dfu_metadata_status)); + bt_mesh_free(req->params); + bt_mesh_dfu_cli_rm_req_from_list(req); + k_delayed_work_cancel(&req->timer); + req_free(req); + } - LOG_DBG("%u phase: %u, cur state: %u", status, phase, cli->xfer.state); + if (cli->op != BLE_MESH_DFU_OP_UPDATE_STATUS) { + return 0; + } - target->phase = phase; + target = target_get(cli, ctx->addr); + if (!target) { + BT_WARN("Unknown target 0x%04x", ctx->addr); + return -ENOENT; + } - if (cli->xfer.state == STATE_APPLY && phase == BT_MESH_DFU_PHASE_IDLE && - status == BT_MESH_DFU_ERR_WRONG_PHASE) { - LOG_DBG("Response received with Idle phase"); - blob_cli_broadcast_rsp(&cli->blob, &target->blob); - return 0; - } + BT_DBG("%u phase: %u, cur state: %u", status, phase, cli->xfer.state); - if (status != BT_MESH_DFU_SUCCESS) { - target_failed(cli, target, status); - blob_cli_broadcast_rsp(&cli->blob, &target->blob); - return 0; - } + target->phase = phase; - if (buf->len == 13) { - net_buf_simple_pull_u8(buf); /* ttl */ - target->effect = net_buf_simple_pull_u8(buf) & BIT_MASK(5); - net_buf_simple_pull_le16(buf); /* timeout */ + if (cli->xfer.state == STATE_APPLY && phase == BLE_MESH_DFU_PHASE_IDLE && + status == BLE_MESH_DFU_ERR_WRONG_PHASE) { + BT_DBG("Response received with Idle phase"); + blob_cli_broadcast_rsp(&cli->blob, &target->blob); + return 0; + } - if (net_buf_simple_pull_le64(buf) != cli->xfer.blob.id) { - LOG_WRN("Invalid BLOB ID"); - target_failed(cli, target, BT_MESH_DFU_ERR_BLOB_XFER_BUSY); - blob_cli_broadcast_rsp(&cli->blob, &target->blob); - return 0; - } + if (status != BLE_MESH_DFU_SUCCESS) { + target_failed(cli, target, status); + blob_cli_broadcast_rsp(&cli->blob, &target->blob); + return 0; + } - target->img_idx = net_buf_simple_pull_u8(buf); + if (buf->len == 13) { + net_buf_simple_pull_u8(buf); /* ttl */ + target->effect = net_buf_simple_pull_u8(buf) & BIT_MASK(5); + net_buf_simple_pull_le16(buf); /* timeout */ - LOG_DBG("Target 0x%04x receiving transfer", ctx->addr); - } else if (buf->len) { - return -EINVAL; - } + if (net_buf_simple_pull_le64(buf) != cli->xfer.blob.id) { + BT_WARN("Invalid BLOB ID"); + target_failed(cli, target, BLE_MESH_DFU_ERR_BLOB_XFER_BUSY); + blob_cli_broadcast_rsp(&cli->blob, &target->blob); + return 0; + } - if (cli->xfer.state == STATE_REFRESH) { - if (phase == BT_MESH_DFU_PHASE_VERIFY) { - LOG_DBG("Still pending..."); - return 0; - } else if (phase == BT_MESH_DFU_PHASE_VERIFY_FAIL) { - LOG_WRN("Verification failed on target 0x%04x", - target->blob.addr); - target_failed(cli, target, BT_MESH_DFU_ERR_WRONG_PHASE); - } - } else if (cli->xfer.state == STATE_APPLY) { - if (phase != BT_MESH_DFU_PHASE_APPLYING && - (target->effect == BT_MESH_DFU_EFFECT_UNPROV || - phase != BT_MESH_DFU_PHASE_IDLE)) { - LOG_WRN("Target 0x%04x in phase %u after apply", - target->blob.addr, phase); - target_failed(cli, target, BT_MESH_DFU_ERR_WRONG_PHASE); - blob_cli_broadcast_rsp(&cli->blob, &target->blob); - return 0; - } - return 0; - } else if (cli->xfer.state == STATE_CONFIRM) { - if (phase == BT_MESH_DFU_PHASE_APPLYING) { - LOG_DBG("Still pending..."); - return 0; - } + target->img_idx = net_buf_simple_pull_u8(buf); - if (phase != BT_MESH_DFU_PHASE_IDLE) { - LOG_WRN("Target 0x%04x in phase %u after apply", - target->blob.addr, phase); - target->phase = BT_MESH_DFU_PHASE_APPLY_FAIL; - target_failed(cli, target, BT_MESH_DFU_ERR_WRONG_PHASE); - blob_cli_broadcast_rsp(&cli->blob, &target->blob); - return 0; - } - } else if (cli->xfer.state == STATE_CANCEL) { - target->phase = BT_MESH_DFU_PHASE_TRANSFER_CANCELED; - } + BT_DBG("Target 0x%04x receiving transfer", ctx->addr); + } else if (buf->len) { + return -EINVAL; + } - blob_cli_broadcast_rsp(&cli->blob, &target->blob); + if (cli->xfer.state == STATE_REFRESH) { + if (phase == BLE_MESH_DFU_PHASE_VERIFY) { + BT_DBG("Still pending..."); + return 0; + } else if (phase == BLE_MESH_DFU_PHASE_VERIFY_FAIL) { + BT_WARN("Verification failed on target 0x%04x", + target->blob.addr); + target_failed(cli, target, BLE_MESH_DFU_ERR_WRONG_PHASE); + } + } else if (cli->xfer.state == STATE_APPLY) { + if (phase != BLE_MESH_DFU_PHASE_APPLYING && + (target->effect == BLE_MESH_DFU_EFFECT_UNPROV || + phase != BLE_MESH_DFU_PHASE_IDLE)) { + BT_WARN("Target 0x%04x in phase %u after apply", + target->blob.addr, phase); + target_failed(cli, target, BLE_MESH_DFU_ERR_WRONG_PHASE); + blob_cli_broadcast_rsp(&cli->blob, &target->blob); + return 0; + } + return 0; + } else if (cli->xfer.state == STATE_CONFIRM) { + if (phase == BLE_MESH_DFU_PHASE_APPLYING) { + BT_DBG("Still pending..."); + return 0; + } - return 0; + if (phase != BLE_MESH_DFU_PHASE_IDLE) { + BT_WARN("Target 0x%04x in phase %u after apply", + target->blob.addr, phase); + target->phase = BLE_MESH_DFU_PHASE_APPLY_FAIL; + target_failed(cli, target, BLE_MESH_DFU_ERR_WRONG_PHASE); + blob_cli_broadcast_rsp(&cli->blob, &target->blob); + return 0; + } + } else if (cli->xfer.state == STATE_CANCEL) { + target->phase = BLE_MESH_DFU_PHASE_TRANSFER_CANCELED; + } + + blob_cli_broadcast_rsp(&cli->blob, &target->blob); + + return 0; } static int handle_info_status(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx, - struct net_buf_simple *buf) + struct net_buf_simple *buf) { - struct bt_mesh_dfu_cli *cli = mod->rt->user_data; - struct bt_mesh_dfu_target *target; - enum bt_mesh_dfu_iter it = BT_MESH_DFU_ITER_CONTINUE; - uint8_t img_cnt, idx; + struct bt_mesh_dfu_cli *cli = mod->user_data; + struct bt_mesh_dfu_target *target; + bt_mesh_dfu_cli_req_t *req = NULL; + enum bt_mesh_dfu_iter it = BLE_MESH_DFU_ITER_CONTINUE; + uint8_t img_cnt, idx; - if (!cli->req.img_cb || - (cli->req.type == REQ_IMG && cli->req.addr != ctx->addr)) { - LOG_WRN("Unexpected info status from 0x%04x", ctx->addr); - return 0; - } + req = bt_mesh_dfu_req_find_by_addr(ctx->addr); + if (!req) { + return 0; + } - img_cnt = net_buf_simple_pull_u8(buf); - if (img_cnt < cli->req.img_cnt) { - cli->req.img_cnt = img_cnt; - } + if (req->type == INTERNAL_REQ_IMG) { + goto confirm_procedure; + } - idx = net_buf_simple_pull_u8(buf); - if (idx >= img_cnt) { - LOG_WRN("Invalid idx %u", idx); - return -ENOENT; - } + if (!req->img_cb || + req->type != REQ_IMG) { + BT_WARN("Unexpected info status from 0x%04x req %p type %d", ctx->addr, req, req->type); + return 0; + } - LOG_DBG("Image list from 0x%04x from index %u", ctx->addr, idx); + img_cnt = net_buf_simple_pull_u8(buf); + if (img_cnt < req->img_cnt) { + req->img_cnt = img_cnt; + } - while (buf->len && cli->req.img_cb && idx < cli->req.img_cnt) { - char uri_buf[CONFIG_BT_MESH_DFU_URI_MAXLEN + 1]; - struct bt_mesh_dfu_img img; - size_t uri_len; + idx = net_buf_simple_pull_u8(buf); + if (idx >= img_cnt) { + BT_WARN("Invalid idx %u", idx); + return -ENOENT; + } - img.fwid_len = net_buf_simple_pull_u8(buf); - if (buf->len < img.fwid_len + 1) { - LOG_WRN("Invalid format: fwid"); - return -EINVAL; - } + BT_DBG("Image list from 0x%04x from index %u", ctx->addr, idx); - img.fwid = net_buf_simple_pull_mem(buf, img.fwid_len); + while (buf->len && req->img_cb && idx < req->img_cnt) { + char uri_buf[CONFIG_BLE_MESH_DFU_URI_MAXLEN + 1]; + struct bt_mesh_dfu_img img; + size_t uri_len; - uri_len = net_buf_simple_pull_u8(buf); - if (buf->len < uri_len) { - LOG_WRN("Invalid format: uri"); - return -EINVAL; - } + img.fwid_len = net_buf_simple_pull_u8(buf); + if (buf->len < img.fwid_len + 1) { + BT_WARN("Invalid format: fwid"); + return -EINVAL; + } - LOG_DBG("\tImage %u\n\r\tfwid: %s", idx, bt_hex(img.fwid, img.fwid_len)); + img.fwid = net_buf_simple_pull_mem(buf, img.fwid_len); - if (uri_len) { - size_t uri_buf_len = - MIN(CONFIG_BT_MESH_DFU_URI_MAXLEN, uri_len); + uri_len = net_buf_simple_pull_u8(buf); + if (buf->len < uri_len) { + BT_WARN("Invalid format: uri"); + return -EINVAL; + } - memcpy(uri_buf, net_buf_simple_pull_mem(buf, uri_len), - uri_buf_len); - uri_buf[uri_buf_len] = '\0'; - img.uri = uri_buf; - } else { - img.uri = NULL; - } + BT_DBG("\tImage %u\n\r\tfwid: %s", idx, bt_hex(img.fwid, img.fwid_len)); - it = cli->req.img_cb(cli, ctx, idx, img_cnt, &img, - cli->req.params); - if (it != BT_MESH_DFU_ITER_CONTINUE) { - if (cli->req.type == REQ_IMG) { - k_sem_give(&cli->req.sem); - } + if (uri_len) { + size_t uri_buf_len = + MIN(CONFIG_BLE_MESH_DFU_URI_MAXLEN, uri_len); - return 0; - } + memcpy(uri_buf, net_buf_simple_pull_mem(buf, uri_len), + uri_buf_len); + uri_buf[uri_buf_len] = '\0'; + img.uri = uri_buf; + } else { + img.uri = NULL; + } - idx++; - } + it = req->img_cb(cli, ctx, idx, img_cnt, &img, + req->params); + if (it != BLE_MESH_DFU_ITER_CONTINUE) { + if (req->type == REQ_IMG) { + bt_mesh_dfu_cli_rm_req_from_list(req); + req_free(req); + } - if (idx < cli->req.img_cnt) { - LOG_DBG("Fetching more images (%u/%u)", idx, cli->req.img_cnt); - ctx->send_ttl = cli->req.ttl; - info_get(cli, ctx, idx, cli->req.img_cnt - idx, - (cli->req.type == REQ_IMG) ? NULL : &send_cb); - return 0; - } + return 0; + } - if (cli->req.type == REQ_IMG) { - k_sem_give(&cli->req.sem); - return 0; - } + idx++; + } - /* Confirm-procedure termination: */ - target = target_get(cli, ctx->addr); - if (target) { - LOG_WRN("Target 0x%04x failed to apply image: %s", ctx->addr, - bt_hex(cli->xfer.slot->fwid, cli->xfer.slot->fwid_len)); - target->phase = BT_MESH_DFU_PHASE_APPLY_FAIL; - target_failed(cli, target, BT_MESH_DFU_ERR_INTERNAL); - blob_cli_broadcast_rsp(&cli->blob, &target->blob); - } + if (idx < req->img_cnt) { + BT_DBG("Fetching more images (%u/%u)", idx, req->img_cnt); + ctx->send_ttl = req->ttl; + info_get(cli, ctx, idx, req->img_cnt - idx, + (req->type == REQ_IMG) ? NULL : &send_cb); + return 0; + } else { + if (req->img_cb) { + // used to notify cb function the image output finished + req->img_cb(cli, ctx, idx, img_cnt, NULL, NULL); + } + } - return 0; + bt_mesh_dfu_cli_rm_req_from_list(req); + req_free(req); + return 0; + +confirm_procedure: + /* Confirm-procedure termination: */ + target = target_get(cli, ctx->addr); + if (target) { + BT_WARN("Target 0x%04x failed to apply image: %s", ctx->addr, + bt_hex(cli->xfer.slot->fwid, cli->xfer.slot->fwid_len)); + target->phase = BLE_MESH_DFU_PHASE_APPLY_FAIL; + target_failed(cli, target, BLE_MESH_DFU_ERR_INTERNAL); + blob_cli_broadcast_rsp(&cli->blob, &target->blob); + } + + bt_mesh_dfu_cli_rm_req_from_list(req); + req_free(req); + + return 0; } static int handle_metadata_status(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx, - struct net_buf_simple *buf) + struct net_buf_simple *buf) { - struct bt_mesh_dfu_cli *cli = mod->rt->user_data; - struct bt_mesh_dfu_metadata_status *rsp = cli->req.params; - uint8_t hdr, idx; + bt_mesh_dfu_cli_req_t *req = NULL; + struct bt_mesh_dfu_metadata_status *rsp = NULL; + uint8_t hdr, idx; - hdr = net_buf_simple_pull_u8(buf); - idx = net_buf_simple_pull_u8(buf); + hdr = net_buf_simple_pull_u8(buf); + idx = net_buf_simple_pull_u8(buf); - if (cli->req.type != REQ_METADATA || ctx->addr != cli->req.addr || - idx != rsp->idx) { - LOG_WRN("Unexpected metadata status from 0x%04x img %u", - ctx->addr, idx); - if (cli->req.type != REQ_METADATA) { - LOG_WRN("Expected %u", cli->req.type); - } else { - LOG_WRN("Expected 0x%04x img %u", cli->req.addr, idx); - } + req = bt_mesh_dfu_req_find_by_addr(ctx->addr); + if (!req) { + BT_WARN("Unexcept metadata status form 0x%04x", ctx->addr); + return 0; + } - return 0; - } + rsp = req->params; - rsp->status = hdr & BIT_MASK(3); - rsp->effect = (hdr >> 3); - k_sem_give(&cli->req.sem); + if (req->type != REQ_METADATA || + idx != rsp->idx) { + BT_WARN("Unexpected metadata status from 0x%04x img %u", + ctx->addr, idx); + if (req->type != REQ_METADATA) { + BT_WARN("Expected %u", req->type); + } else { + BT_WARN("Expected 0x%04x img %u", req->ctx.addr, idx); + } - return 0; + return 0; + } + + rsp->status = hdr & BIT_MASK(3); + rsp->effect = (hdr >> 3); + + 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, + sizeof(struct bt_mesh_dfu_metadata_status)); + + bt_mesh_free(rsp); + bt_mesh_dfu_cli_rm_req_from_list(req); + req_free(req); + + return 0; } const struct bt_mesh_model_op _bt_mesh_dfu_cli_op[] = { - {BT_MESH_DFU_OP_UPDATE_STATUS, BT_MESH_LEN_MIN(1), handle_status}, - {BT_MESH_DFU_OP_UPDATE_INFO_STATUS, BT_MESH_LEN_MIN(2), handle_info_status}, - {BT_MESH_DFU_OP_UPDATE_METADATA_STATUS, BT_MESH_LEN_EXACT(2), handle_metadata_status}, - BT_MESH_MODEL_OP_END, + {BLE_MESH_DFU_OP_UPDATE_STATUS, 1, (void *)handle_status}, + {BLE_MESH_DFU_OP_UPDATE_INFO_STATUS, 2, (void *)handle_info_status}, + {BLE_MESH_DFU_OP_UPDATE_METADATA_STATUS, 2, (void *)handle_metadata_status}, + BLE_MESH_MODEL_OP_END, }; -static int dfu_cli_init(const struct bt_mesh_model *mod) +static int dfu_cli_init(struct bt_mesh_model *mod) { - struct bt_mesh_dfu_cli *cli = mod->rt->user_data; + struct bt_mesh_dfu_cli *cli = mod->user_data; - if (mod->rt->elem_idx != 0) { - LOG_ERR("DFU update client must be instantiated on first elem"); - return -EINVAL; - } + if (mod->elem_idx != 0) { + BT_ERR("DFU update client must be instantiated on first elem"); + return -EINVAL; + } - cli->mod = mod; + cli->mod = mod; - if (IS_ENABLED(CONFIG_BT_MESH_MODEL_EXTENSIONS)) { - bt_mesh_model_extend(mod, cli->blob.mod); - } + cli->blob.cb = &_bt_mesh_dfu_cli_blob_handlers; - k_sem_init(&cli->req.sem, 0, 1); + bt_mesh_dfu_cli_req_list_init(); - return 0; + return 0; } -static void dfu_cli_reset(const struct bt_mesh_model *mod) +static void dfu_cli_reset(struct bt_mesh_model *mod) { - struct bt_mesh_dfu_cli *cli = mod->rt->user_data; + struct bt_mesh_dfu_cli *cli = mod->user_data; - cli->req.type = REQ_NONE; - cli->req.addr = BT_MESH_ADDR_UNASSIGNED; - cli->req.img_cnt = 0; - cli->req.img_cb = NULL; - cli->xfer.state = STATE_IDLE; - cli->xfer.flags = 0; + bt_mesh_dfu_req_list_free(); + cli->req = NULL; + cli->xfer.state = STATE_IDLE; + cli->xfer.flags = 0; +} + +static int dfu_cli_deinit(struct bt_mesh_model *mod) +{ + dfu_cli_reset(mod); + return 0; } const struct bt_mesh_model_cb _bt_mesh_dfu_cli_cb = { - .init = dfu_cli_init, - .reset = dfu_cli_reset, + .init = dfu_cli_init, +#if CONFIG_BLE_MESH_DEINIT + .deinit = dfu_cli_deinit, +#endif }; /******************************************************************************* @@ -1003,263 +1209,303 @@ const struct bt_mesh_model_cb _bt_mesh_dfu_cli_cb = { ******************************************************************************/ int bt_mesh_dfu_cli_send(struct bt_mesh_dfu_cli *cli, - const struct bt_mesh_blob_cli_inputs *inputs, - const struct bt_mesh_blob_io *io, - const struct bt_mesh_dfu_cli_xfer *xfer) + const struct bt_mesh_blob_cli_inputs *inputs, + const struct bt_mesh_blob_io *io, + const struct bt_mesh_dfu_cli_xfer *xfer) { - struct bt_mesh_dfu_target *target; + struct bt_mesh_dfu_target *target; - if (bt_mesh_dfu_cli_is_busy(cli)) { - return -EBUSY; - } + if (bt_mesh_dfu_cli_is_busy(cli)) { + return -EBUSY; + } - cli->xfer.blob.mode = xfer->mode; - cli->xfer.blob.size = xfer->slot->size; + cli->xfer.blob.mode = xfer->mode; + cli->xfer.blob.size = xfer->slot->size; - if (xfer->blob_id == 0) { - int err = bt_rand(&cli->xfer.blob.id, sizeof(cli->xfer.blob.id)); + if (xfer->blob_id == 0) { + bt_mesh_rand(&cli->xfer.blob.id, sizeof(cli->xfer.blob.id)); + } else { + cli->xfer.blob.id = xfer->blob_id; + } - if (err) { - return err; - } - } else { - cli->xfer.blob.id = xfer->blob_id; - } + cli->xfer.io = io; + cli->blob.inputs = inputs; + cli->xfer.slot = xfer->slot; + cli->xfer.flags = 0U; - cli->xfer.io = io; - cli->blob.inputs = inputs; - cli->xfer.slot = xfer->slot; - cli->xfer.flags = 0U; + if (xfer->blob_params) { + cli->xfer.flags |= FLAG_SKIP_CAPS_GET; + cli->xfer.blob.block_size_log = xfer->blob_params->block_size_log; + cli->xfer.blob.chunk_size = xfer->blob_params->chunk_size; + } - if (xfer->blob_params) { - cli->xfer.flags |= FLAG_SKIP_CAPS_GET; - cli->xfer.blob.block_size_log = xfer->blob_params->block_size_log; - cli->xfer.blob.chunk_size = xfer->blob_params->chunk_size; - } + /* Phase will be set based on target status messages: */ + TARGETS_FOR_EACH(cli, target) { + target->status = BLE_MESH_DFU_SUCCESS; + target->phase = BLE_MESH_DFU_PHASE_UNKNOWN; + } - /* Phase will be set based on target status messages: */ - TARGETS_FOR_EACH(cli, target) { - target->status = BT_MESH_DFU_SUCCESS; - target->phase = BT_MESH_DFU_PHASE_UNKNOWN; - } - - initiate(cli); - return 0; + initiate(cli); + return 0; } int bt_mesh_dfu_cli_suspend(struct bt_mesh_dfu_cli *cli) { - int err; + int err; - err = bt_mesh_blob_cli_suspend(&cli->blob); - if (!err) { - cli->xfer.state = STATE_SUSPENDED; - } + err = bt_mesh_blob_cli_suspend(&cli->blob); + if (!err) { + cli->xfer.state = STATE_SUSPENDED; + } - return err; + return err; } int bt_mesh_dfu_cli_resume(struct bt_mesh_dfu_cli *cli) { - struct bt_mesh_dfu_target *target; + struct bt_mesh_dfu_target *target; - if (cli->xfer.state != STATE_SUSPENDED) { - return -EINVAL; - } + if (cli->xfer.state != STATE_SUSPENDED) { + return -EINVAL; + } - cli->xfer.flags = FLAG_RESUME; + cli->xfer.flags = FLAG_RESUME; - /* Restore timed out targets. */ - TARGETS_FOR_EACH(cli, target) { - if (!!target->blob.timedout) { - target->status = BT_MESH_DFU_SUCCESS; - target->phase = BT_MESH_DFU_PHASE_UNKNOWN; - } - } + /* Restore timed out targets. */ + TARGETS_FOR_EACH(cli, target) { + if (!!target->blob.timedout) { + target->status = BLE_MESH_DFU_SUCCESS; + target->phase = BLE_MESH_DFU_PHASE_UNKNOWN; + } + } - initiate(cli); - return 0; + initiate(cli); + return 0; } int bt_mesh_dfu_cli_cancel(struct bt_mesh_dfu_cli *cli, - struct bt_mesh_msg_ctx *ctx) + struct bt_mesh_msg_ctx *ctx) { - if (ctx) { - int err; + bt_mesh_dfu_cli_req_t *req = NULL; + if (ctx) { + int err; + req = req_alloc(); + if (!req) { + return -ENOMEM; + } + cli->req = req; + err = req_setup(cli, REQ_STATUS, BLE_MESH_DFU_OP_UPDATE_CANCEL, ctx, NULL); + if (err) { + return err; + } + bt_mesh_dfu_cli_add_req_to_list(req); - err = req_setup(cli, REQ_STATUS, ctx->addr, NULL); - if (err) { - return err; - } + BLE_MESH_MODEL_BUF_DEFINE(buf, BLE_MESH_DFU_OP_UPDATE_CANCEL, 0); + bt_mesh_model_msg_init(&buf, BLE_MESH_DFU_OP_UPDATE_CANCEL); - BT_MESH_MODEL_BUF_DEFINE(buf, BT_MESH_DFU_OP_UPDATE_CANCEL, 0); - bt_mesh_model_msg_init(&buf, BT_MESH_DFU_OP_UPDATE_CANCEL); + err = bt_mesh_model_send(cli->mod, ctx, &buf, NULL, NULL); + if (err) { + cli->req->type = REQ_NONE; + return err; + } - err = bt_mesh_model_send(cli->mod, ctx, &buf, NULL, NULL); - if (err) { - cli->req.type = REQ_NONE; - return err; - } + return req_wait(cli, K_MSEC(dfu_cli_timeout)); + } - return req_wait(cli, K_MSEC(dfu_cli_timeout)); - } + if (cli->xfer.state == STATE_IDLE) { + return -EALREADY; + } - if (cli->xfer.state == STATE_IDLE) { - return -EALREADY; - } - - cli->xfer.state = STATE_CANCEL; - blob_cli_broadcast_abort(&cli->blob); - cancel(cli); - return 0; + cli->xfer.state = STATE_CANCEL; + blob_cli_broadcast_abort(&cli->blob); + cancel(cli); + return 0; } int bt_mesh_dfu_cli_apply(struct bt_mesh_dfu_cli *cli) { - if (cli->xfer.state != STATE_VERIFIED) { - return -EBUSY; - } + if (cli->xfer.state != STATE_VERIFIED) { + return -EBUSY; + } - apply(cli); + apply(cli); - return 0; + return 0; } int bt_mesh_dfu_cli_confirm(struct bt_mesh_dfu_cli *cli) { - if (cli->xfer.state != STATE_APPLIED) { - return -EBUSY; - } + bt_mesh_dfu_cli_req_t *req = NULL; - cli->xfer.state = STATE_CONFIRM; - confirm(cli); + if (cli->xfer.state != STATE_APPLIED) { + return -EBUSY; + } - return 0; + req = req_alloc(); + if (!req) { + BT_ERR("%s: alloc req failed", req); + return -ENOMEM; + } + + cli->req = req; + req_setup(cli, INTERNAL_REQ_IMG, BLE_MESH_DFU_OP_UPDATE_INFO_GET, NULL, NULL); + bt_mesh_dfu_cli_add_req_to_list(req); + + cli->xfer.state = STATE_CONFIRM; + confirm(cli); + + return 0; } uint8_t bt_mesh_dfu_cli_progress(struct bt_mesh_dfu_cli *cli) { - if (cli->xfer.state == STATE_TRANSFER) { - return bt_mesh_blob_cli_xfer_progress_active_get(&cli->blob); - } + if (cli->xfer.state == STATE_TRANSFER) { + return bt_mesh_blob_cli_xfer_progress_active_get(&cli->blob); + } - if (cli->xfer.state == STATE_IDLE) { - if (cli->xfer.flags & FLAG_COMPLETED) { - return 100U; - } - return 0U; - } + if (cli->xfer.state == STATE_IDLE) { + if (cli->xfer.flags & FLAG_COMPLETED) { + return 100U; + } + return 0U; + } - return 100U; + return 100U; } bool bt_mesh_dfu_cli_is_busy(struct bt_mesh_dfu_cli *cli) { - return (cli->xfer.state == STATE_TRANSFER || - cli->xfer.state == STATE_REFRESH || - cli->xfer.state == STATE_APPLY || - cli->xfer.state == STATE_CONFIRM) && - !(cli->xfer.flags & FLAG_FAILED); + return (cli->xfer.state == STATE_TRANSFER || + cli->xfer.state == STATE_REFRESH || + cli->xfer.state == STATE_APPLY || + cli->xfer.state == STATE_CONFIRM) && + !(cli->xfer.flags & FLAG_FAILED); } int bt_mesh_dfu_cli_imgs_get(struct bt_mesh_dfu_cli *cli, - struct bt_mesh_msg_ctx *ctx, - bt_mesh_dfu_img_cb_t cb, void *cb_data, - uint8_t max_count) + struct bt_mesh_msg_ctx *ctx, + bt_mesh_dfu_img_cb_t cb, void *cb_data, + uint8_t first_idx, uint8_t max_count) { - int err; + int err; - if (cli->req.img_cb) { - return -EBUSY; - } + if (bt_mesh_dfu_req_find_by_addr(ctx->addr)) { + BT_WARN("Exists waiting request"); + return -EBUSY; + } - err = req_setup(cli, REQ_IMG, ctx->addr, NULL); - if (err) { - return err; - } + cli->req = req_alloc(); + if (!cli->req) { + return -ENOMEM; + } - cli->req.img_cb = cb; - cli->req.params = cb_data; - cli->req.ttl = ctx->send_ttl; - cli->req.img_cnt = max_count; + err = req_setup(cli, REQ_IMG, BLE_MESH_DFU_OP_UPDATE_INFO_GET, ctx, cb_data); + if (err) { + req_free(cli->req); + return err; + } - err = info_get(cli, ctx, 0, cli->req.img_cnt, NULL); - if (err) { - cli->req.img_cb = NULL; - cli->req.type = REQ_NONE; - return err; - } + cli->req->img_cb = cb; + cli->req->ttl = ctx->send_ttl; + cli->req->img_cnt = max_count; - err = req_wait(cli, K_MSEC(dfu_cli_timeout)); + err = info_get(cli, ctx, first_idx, cli->req->img_cnt, NULL); + if (err) { + cli->req->img_cb = NULL; + cli->req->type = REQ_NONE; + req_free(cli->req); + return err; + } - cli->req.img_cb = NULL; + err = req_wait(cli, K_MSEC(dfu_cli_timeout)); - return err; + return err; } int bt_mesh_dfu_cli_metadata_check(struct bt_mesh_dfu_cli *cli, - struct bt_mesh_msg_ctx *ctx, uint8_t img_idx, - const struct bt_mesh_dfu_slot *slot, - struct bt_mesh_dfu_metadata_status *rsp) + struct bt_mesh_msg_ctx *ctx, uint8_t img_idx, + struct net_buf_simple *metadata) { - int err; + int err; + bt_mesh_dfu_cli_req_t *req = NULL; - err = req_setup(cli, REQ_METADATA, ctx->addr, rsp); - if (err) { - return err; - } + struct bt_mesh_dfu_metadata_status *rsp = bt_mesh_calloc(sizeof(struct bt_mesh_dfu_metadata_status)); + if (!rsp) { + BT_ERR("Alloc metadata status failed"); + return -ENOMEM; + } + req = req_alloc(); + if (!req) { + return -ENOMEM; + } + cli->req = req; + err = req_setup(cli, REQ_METADATA, BLE_MESH_DFU_OP_UPDATE_METADATA_CHECK, ctx, rsp); + if (err) { + return err; + } - BT_MESH_MODEL_BUF_DEFINE(buf, BT_MESH_DFU_OP_UPDATE_METADATA_CHECK, - 1 + CONFIG_BT_MESH_DFU_METADATA_MAXLEN); - bt_mesh_model_msg_init(&buf, BT_MESH_DFU_OP_UPDATE_METADATA_CHECK); + BLE_MESH_MODEL_BUF_DEFINE(buf, BLE_MESH_DFU_OP_UPDATE_METADATA_CHECK, + 1 + CONFIG_BLE_MESH_DFU_METADATA_MAXLEN); + bt_mesh_model_msg_init(&buf, BLE_MESH_DFU_OP_UPDATE_METADATA_CHECK); - net_buf_simple_add_u8(&buf, img_idx); + net_buf_simple_add_u8(&buf, img_idx); - if (slot->metadata_len) { - net_buf_simple_add_mem(&buf, slot->metadata, - slot->metadata_len); - } + if (metadata) { + net_buf_simple_add_mem(&buf, metadata->data, metadata->len); + } - rsp->idx = img_idx; + rsp->idx = img_idx; - err = bt_mesh_model_send(cli->mod, ctx, &buf, NULL, NULL); - if (err) { - cli->req.type = REQ_NONE; - return err; - } + err = bt_mesh_model_send(cli->mod, ctx, &buf, NULL, NULL); + if (err) { + cli->req->type = REQ_NONE; + return err; + } - return req_wait(cli, K_MSEC(dfu_cli_timeout)); + return req_wait(cli, K_MSEC(dfu_cli_timeout)); } int bt_mesh_dfu_cli_status_get(struct bt_mesh_dfu_cli *cli, - struct bt_mesh_msg_ctx *ctx, - struct bt_mesh_dfu_target_status *rsp) + struct bt_mesh_msg_ctx *ctx) { - int err; + int err; + bt_mesh_dfu_cli_req_t *req = NULL; - err = req_setup(cli, REQ_STATUS, ctx->addr, rsp); - if (err) { - return err; - } + struct bt_mesh_dfu_target_status *rsp = bt_mesh_calloc(sizeof(struct bt_mesh_dfu_target_status)); + if (!rsp) { + BT_ERR("dfu target status alloc failed"); + return -ENOMEM; + } - BT_MESH_MODEL_BUF_DEFINE(buf, BT_MESH_DFU_OP_UPDATE_GET, 0); - bt_mesh_model_msg_init(&buf, BT_MESH_DFU_OP_UPDATE_GET); + req = req_alloc(); + if (!req) { + return -ENOMEM; + } + cli->req = req; + err = req_setup(cli, REQ_STATUS, BLE_MESH_DFU_OP_UPDATE_GET, ctx, rsp); + if (err) { + return err; + } - err = bt_mesh_model_send(cli->mod, ctx, &buf, NULL, NULL); - if (err) { - cli->req.type = REQ_NONE; - return err; - } + BLE_MESH_MODEL_BUF_DEFINE(buf, BLE_MESH_DFU_OP_UPDATE_GET, 0); + bt_mesh_model_msg_init(&buf, BLE_MESH_DFU_OP_UPDATE_GET); - return req_wait(cli, K_MSEC(dfu_cli_timeout)); + err = bt_mesh_model_send(cli->mod, ctx, &buf, NULL, NULL); + if (err) { + cli->req->type = REQ_NONE; + return err; + } + + return req_wait(cli, K_MSEC(dfu_cli_timeout)); } int32_t bt_mesh_dfu_cli_timeout_get(void) { - return dfu_cli_timeout; + return dfu_cli_timeout; } void bt_mesh_dfu_cli_timeout_set(int32_t t) { - dfu_cli_timeout = t; + dfu_cli_timeout = t; } +#endif /* CONFIG_BLE_MESH_DFU_CLI */ diff --git a/components/bt/esp_ble_mesh/v1.1/dfu/dfu_metadata.c b/components/bt/esp_ble_mesh/v1.1/dfu/dfu_metadata.c index 51a2cd0306..9c966c5e38 100644 --- a/components/bt/esp_ble_mesh/v1.1/dfu/dfu_metadata.c +++ b/components/bt/esp_ble_mesh/v1.1/dfu/dfu_metadata.c @@ -1,102 +1,105 @@ /* - * Copyright (c) 2021 Nordic Semiconductor ASA + * SPDX-FileCopyrightText: 2020 Nordic Semiconductor ASA + * SPDX-FileContributor: 2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ -#include -#include #include -#include #include "crypto.h" #include "access.h" +#include "mesh/byteorder.h" +#include "mesh/buf.h" +#include "mesh_v1.1/dfu/dfu_metadata.h" +#if CONFIG_BLE_MESH_DFU_METADATA int bt_mesh_dfu_metadata_decode(struct net_buf_simple *buf, - struct bt_mesh_dfu_metadata *metadata) + struct bt_mesh_dfu_metadata *metadata) { - if (buf->len < 12) { - return -EMSGSIZE; - } + if (buf->len < 12) { + return -EMSGSIZE; + } - metadata->fw_ver.major = net_buf_simple_pull_u8(buf); - metadata->fw_ver.minor = net_buf_simple_pull_u8(buf); - metadata->fw_ver.revision = net_buf_simple_pull_le16(buf); - metadata->fw_ver.build_num = net_buf_simple_pull_le32(buf); - metadata->fw_size = net_buf_simple_pull_le24(buf); - metadata->fw_core_type = net_buf_simple_pull_u8(buf); + metadata->fw_ver.major = net_buf_simple_pull_u8(buf); + metadata->fw_ver.minor = net_buf_simple_pull_u8(buf); + metadata->fw_ver.revision = net_buf_simple_pull_le16(buf); + metadata->fw_ver.build_num = net_buf_simple_pull_le32(buf); + metadata->fw_size = net_buf_simple_pull_le24(buf); + metadata->fw_core_type = net_buf_simple_pull_u8(buf); - if (metadata->fw_core_type & BT_MESH_DFU_FW_CORE_TYPE_APP) { - if (buf->len < 6) { - return -EMSGSIZE; - } + if (metadata->fw_core_type & BLE_MESH_DFU_FW_CORE_TYPE_APP) { + if (buf->len < 6) { + return -EMSGSIZE; + } - metadata->comp_hash = net_buf_simple_pull_le32(buf); - metadata->elems = net_buf_simple_pull_le16(buf); - } + metadata->comp_hash = net_buf_simple_pull_le32(buf); + metadata->elems = net_buf_simple_pull_le16(buf); + } - metadata->user_data = buf->len > 0 ? buf->data : NULL; - metadata->user_data_len = buf->len; + metadata->user_data = buf->len > 0 ? buf->data : NULL; + metadata->user_data_len = buf->len; - return 0; + return 0; } - int bt_mesh_dfu_metadata_encode(const struct bt_mesh_dfu_metadata *metadata, - struct net_buf_simple *buf) + struct net_buf_simple *buf) { - size_t md_len_min = 12 + metadata->user_data_len; + size_t md_len_min = 12 + metadata->user_data_len; - if (metadata->fw_core_type & BT_MESH_DFU_FW_CORE_TYPE_APP) { - md_len_min += 6; - } + if (metadata->fw_core_type & BLE_MESH_DFU_FW_CORE_TYPE_APP) { + md_len_min += 6; + } - if (net_buf_simple_tailroom(buf) < md_len_min) { - return -EMSGSIZE; - } + if (net_buf_simple_tailroom(buf) < md_len_min) { + return -EMSGSIZE; + } - net_buf_simple_add_u8(buf, metadata->fw_ver.major); - net_buf_simple_add_u8(buf, metadata->fw_ver.minor); - net_buf_simple_add_le16(buf, metadata->fw_ver.revision); - net_buf_simple_add_le32(buf, metadata->fw_ver.build_num); - net_buf_simple_add_le24(buf, metadata->fw_size); - net_buf_simple_add_u8(buf, metadata->fw_core_type); - net_buf_simple_add_le32(buf, metadata->comp_hash); - net_buf_simple_add_le16(buf, metadata->elems); + net_buf_simple_add_u8(buf, metadata->fw_ver.major); + net_buf_simple_add_u8(buf, metadata->fw_ver.minor); + net_buf_simple_add_le16(buf, metadata->fw_ver.revision); + net_buf_simple_add_le32(buf, metadata->fw_ver.build_num); + net_buf_simple_add_le24(buf, metadata->fw_size); + net_buf_simple_add_u8(buf, metadata->fw_core_type); + net_buf_simple_add_le32(buf, metadata->comp_hash); + net_buf_simple_add_le16(buf, metadata->elems); - if (metadata->user_data_len > 0) { - net_buf_simple_add_mem(buf, metadata->user_data, metadata->user_data_len); - } + if (metadata->user_data_len > 0) { + net_buf_simple_add_mem(buf, metadata->user_data, metadata->user_data_len); + } - return 0; + return 0; } int bt_mesh_dfu_metadata_comp_hash_get(struct net_buf_simple *buf, uint8_t *key, uint32_t *hash) { - uint8_t mac[16]; - int err; - struct bt_mesh_sg sg = {.data = buf->data, .len = buf->len}; + uint8_t mac[16]; + int err; + struct bt_mesh_sg sg = {.data = buf->data, .len = buf->len}; - err = bt_mesh_aes_cmac_raw_key(key, &sg, 1, mac); - if (err) { - return err; - } + /* The implementation of this function is the same as function `bt_mesh_aes_cmac_raw_key` in Zephyr. */ + err = bt_mesh_aes_cmac(key, &sg, 1, mac); + if (err) { + return err; + } - *hash = sys_get_le32(mac); + *hash = sys_get_le32(mac); - return 0; + return 0; } int bt_mesh_dfu_metadata_comp_hash_local_get(uint8_t *key, uint32_t *hash) { - NET_BUF_SIMPLE_DEFINE(buf, BT_MESH_TX_SDU_MAX); - int err; + NET_BUF_SIMPLE_DEFINE(buf, BLE_MESH_TX_SDU_MAX); + int err; - err = bt_mesh_comp_data_get_page_0(&buf, 0); - if (err) { - return err; - } + err = bt_mesh_get_comp_data(&buf, 0, 0, true); + if (err) { + return err; + } - err = bt_mesh_dfu_metadata_comp_hash_get(&buf, key, hash); - return err; + err = bt_mesh_dfu_metadata_comp_hash_get(&buf, key, hash); + return err; } +#endif /* CONFIG_BLE_MESH_DFU_METADATA */ diff --git a/components/bt/esp_ble_mesh/v1.1/dfu/dfu_slot.c b/components/bt/esp_ble_mesh/v1.1/dfu/dfu_slot.c index c11c16d44f..76f3a87e81 100644 --- a/components/bt/esp_ble_mesh/v1.1/dfu/dfu_slot.c +++ b/components/bt/esp_ble_mesh/v1.1/dfu/dfu_slot.c @@ -1,402 +1,444 @@ /* - * Copyright (c) 2020 Nordic Semiconductor ASA + * SPDX-FileCopyrightText: 2020 Nordic Semiconductor ASA + * SPDX-FileContributor: 2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ -#include #include "dfu_slot.h" #include #include #include -#include -#include -#define LOG_LEVEL CONFIG_BT_MESH_DFU_LOG_LEVEL -#include -LOG_MODULE_REGISTER(bt_mesh_dfu_slot); +#include "mesh/common.h" +#include "mesh/utils.h" +#include "settings_nvs.h" +#include "settings.h" +#if CONFIG_BLE_MESH_DFU_SLOTS #define SLOT_ENTRY_BUFLEN 25 -#define DFU_SLOT_SETTINGS_PATH "bt/mesh-dfu/slot" +#define DFU_SLOT_SETTINGS_PATH "mesh/dfu/s" #define HEADER_SIZE offsetof(struct slot, slot.fwid) #define PROP_HEADER "h" #define PROP_FWID "id" #define PROP_METADATA "m" +#define SETTINGS_NAME_END '=' +#define SETTINGS_NAME_SEPARATOR '/' static sys_slist_t list; static struct slot { - uint32_t idx; - struct bt_mesh_dfu_slot slot; - sys_snode_t n; -} slots[CONFIG_BT_MESH_DFU_SLOT_CNT]; + uint32_t idx; + struct bt_mesh_dfu_slot slot; + sys_snode_t n; +} slots[CONFIG_BLE_MESH_DFU_SLOT_CNT]; static uint32_t slot_index; -static char *slot_entry_encode(uint16_t idx, char buf[SLOT_ENTRY_BUFLEN], - const char *property) +inline int settings_name_next(const char *name, const char **next) { - snprintf(buf, SLOT_ENTRY_BUFLEN, DFU_SLOT_SETTINGS_PATH "/%x/%s", idx, - property); + int rc = 0; - return buf; + if (next) { + *next = NULL; + } + + if (!name) { + return 0; + } + + /* name might come from flash directly, in flash the name would end + * with '=' or '\0' depending how storage is done. Flash reading is + * limited to what can be read + */ + while ((*name != '\0') && (*name != SETTINGS_NAME_END) && + (*name != SETTINGS_NAME_SEPARATOR)) { + rc++; + name++; + } + + if (*name == SETTINGS_NAME_SEPARATOR) { + if (next) { + *next = name + 1; + } + return rc; + } + + return rc; } -static bool slot_eq(const struct bt_mesh_dfu_slot *slot, - const uint8_t *fwid, size_t fwid_len) +#if CONFIG_BLE_MESH_SETTINGS +static char *slot_entry_encode(uint16_t idx, char buf[SLOT_ENTRY_BUFLEN], + const char *property) { - return (slot->fwid_len == fwid_len) && - !memcmp(fwid, slot->fwid, fwid_len); + snprintf(buf, SLOT_ENTRY_BUFLEN, DFU_SLOT_SETTINGS_PATH "/%x/%s", idx, + property); + + return buf; +} +#endif + +static bool slot_eq(const struct bt_mesh_dfu_slot *slot, + const uint8_t *fwid, size_t fwid_len) +{ + return (slot->fwid_len == fwid_len) && + !memcmp(fwid, slot->fwid, fwid_len); } static bool is_slot_committed(struct slot *slot_to_check) { - struct slot *s; + struct slot *s; - SYS_SLIST_FOR_EACH_CONTAINER(&list, s, n) { - if (s == slot_to_check) { - return true; - } - } + SYS_SLIST_FOR_EACH_CONTAINER(&list, s, n) { + if (s == slot_to_check) { + return true; + } + } - return false; + return false; } static int slot_store(const struct slot *slot_to_store) { - uint16_t idx = ARRAY_INDEX(slots, slot_to_store); - char buf[SLOT_ENTRY_BUFLEN]; - int err; + int err = 0; +#if CONFIG_BLE_MESH_SETTINGS + uint16_t idx = ARRAY_INDEX(slots, slot_to_store); + char buf[SLOT_ENTRY_BUFLEN]; - err = settings_save_one(slot_entry_encode(idx, buf, PROP_HEADER), - slot_to_store, HEADER_SIZE); - if (err) { - return err; - } + err = bt_mesh_save_core_settings(slot_entry_encode(idx, buf, PROP_HEADER), + (uint8_t *)slot_to_store, HEADER_SIZE); + if (err) { + return err; + } - err = settings_save_one(slot_entry_encode(idx, buf, PROP_FWID), - slot_to_store->slot.fwid, slot_to_store->slot.fwid_len); - if (err) { - return err; - } + err = bt_mesh_save_core_settings(slot_entry_encode(idx, buf, PROP_FWID), + slot_to_store->slot.fwid, slot_to_store->slot.fwid_len); + if (err) { + return err; + } - err = settings_save_one(slot_entry_encode(idx, buf, - PROP_METADATA), - slot_to_store->slot.metadata, slot_to_store->slot.metadata_len); + err = bt_mesh_save_core_settings(slot_entry_encode(idx, buf, PROP_METADATA), + slot_to_store->slot.metadata, slot_to_store->slot.metadata_len); - return err; +#endif + return err; } static void slot_erase(struct slot *slot_to_erase) { - uint16_t idx = ARRAY_INDEX(slots, slot_to_erase); - char buf[SLOT_ENTRY_BUFLEN]; +#if CONFIG_BLE_MESH_SETTINGS + uint16_t idx = ARRAY_INDEX(slots, slot_to_erase); + char buf[SLOT_ENTRY_BUFLEN]; - (void)settings_delete(slot_entry_encode(idx, buf, PROP_HEADER)); - (void)settings_delete(slot_entry_encode(idx, buf, PROP_FWID)); - (void)settings_delete(slot_entry_encode(idx, buf, PROP_METADATA)); + bt_mesh_erase_core_settings(slot_entry_encode(idx, buf, PROP_HEADER)); + bt_mesh_erase_core_settings(slot_entry_encode(idx, buf, PROP_FWID)); + bt_mesh_erase_core_settings(slot_entry_encode(idx, buf, PROP_METADATA)); +#endif } static void slot_index_defrag(void) { - slot_index = 0; - struct slot *s; + slot_index = 0; + struct slot *s; - SYS_SLIST_FOR_EACH_CONTAINER(&list, s, n) { - s->idx = ++slot_index; - slot_store(s); - } + SYS_SLIST_FOR_EACH_CONTAINER(&list, s, n) { + s->idx = ++slot_index; + slot_store(s); + } } int bt_mesh_dfu_slot_count(void) { - int cnt = 0; - sys_snode_t *n; + int cnt = 0; + sys_snode_t *n; - SYS_SLIST_FOR_EACH_NODE(&list, n) { - cnt++; - } + SYS_SLIST_FOR_EACH_NODE(&list, n) { + cnt++; + } - return cnt; + return cnt; } struct bt_mesh_dfu_slot *bt_mesh_dfu_slot_reserve(void) { - struct slot *slot = NULL; + struct slot *slot = NULL; - for (int i = 0; i < ARRAY_SIZE(slots); ++i) { - if (slots[i].idx == 0) { - slot = &slots[i]; - break; - } - } + for (int i = 0; i < ARRAY_SIZE(slots); ++i) { + if (slots[i].idx == 0) { + slot = &slots[i]; + break; + } + } - if (!slot) { - LOG_WRN("No space"); - return NULL; - } + if (!slot) { + BT_WARN("No space"); + return NULL; + } - if (slot_index == UINT32_MAX) { - slot_index_defrag(); - } + if (slot_index == UINT32_MAX) { + slot_index_defrag(); + } - slot->slot.fwid_len = 0; - slot->slot.metadata_len = 0; - slot->slot.size = 0; - slot->idx = ++slot_index; + slot->slot.fwid_len = 0; + slot->slot.metadata_len = 0; + slot->slot.size = 0; + slot->idx = ++slot_index; - LOG_DBG("Reserved slot #%u", slot - &slots[0]); + BT_DBG("Reserved slot #%u", slot - &slots[0]); - return &slot->slot; + return &slot->slot; } int bt_mesh_dfu_slot_info_set(struct bt_mesh_dfu_slot *dfu_slot, size_t size, - const uint8_t *metadata, size_t metadata_len) + const uint8_t *metadata, size_t metadata_len) { - struct slot *slot = CONTAINER_OF(dfu_slot, struct slot, slot); + struct slot *slot = CONTAINER_OF(dfu_slot, struct slot, slot); - if (metadata_len > CONFIG_BT_MESH_DFU_METADATA_MAXLEN) { - return -EFBIG; - } + if (metadata_len > CONFIG_BLE_MESH_DFU_METADATA_MAXLEN) { + return -EFBIG; + } - if (slot->idx == 0 || is_slot_committed(slot)) { - return -EINVAL; - } + if (slot->idx == 0 || is_slot_committed(slot)) { + return -EINVAL; + } - slot->slot.size = size; - slot->slot.metadata_len = metadata_len; - memcpy(slot->slot.metadata, metadata, metadata_len); - return 0; + slot->slot.size = size; + slot->slot.metadata_len = metadata_len; + memcpy(slot->slot.metadata, metadata, metadata_len); + return 0; } int bt_mesh_dfu_slot_fwid_set(struct bt_mesh_dfu_slot *dfu_slot, - const uint8_t *fwid, size_t fwid_len) + const uint8_t *fwid, size_t fwid_len) { - struct slot *slot = CONTAINER_OF(dfu_slot, struct slot, slot); + struct slot *slot = CONTAINER_OF(dfu_slot, struct slot, slot); - if (fwid_len > CONFIG_BT_MESH_DFU_FWID_MAXLEN) { - return -EFBIG; - } + if (fwid_len > CONFIG_BLE_MESH_DFU_FWID_MAXLEN) { + return -EFBIG; + } - if (slot->idx == 0 || is_slot_committed(slot)) { - return -EINVAL; - } + if (slot->idx == 0) { + return -EINVAL; + } - for (int i = 0; i < ARRAY_SIZE(slots); i++) { - if (slots[i].idx != 0 && - slot_eq(&slots[i].slot, fwid, fwid_len)) { - return is_slot_committed(&slots[i]) ? - -EEXIST : -EALREADY; - } - } + if (is_slot_committed(slot)) { + return -EEXIST; + } - slot->slot.fwid_len = fwid_len; - memcpy(slot->slot.fwid, fwid, fwid_len); - return 0; + for (int i = 0; i < ARRAY_SIZE(slots); i++) { + if (slots[i].idx != 0 && + slot_eq(&slots[i].slot, fwid, fwid_len)) { + return -EALREADY; + } + } + + slot->slot.fwid_len = fwid_len; + memcpy(slot->slot.fwid, fwid, fwid_len); + return 0; } int bt_mesh_dfu_slot_commit(struct bt_mesh_dfu_slot *dfu_slot) { - int err; - struct slot *slot = CONTAINER_OF(dfu_slot, struct slot, slot); + int err; + struct slot *slot = CONTAINER_OF(dfu_slot, struct slot, slot); - if (slot->idx == 0 || - slot->slot.fwid_len == 0 || - slot->slot.size == 0 || - is_slot_committed(slot)) { - return -EINVAL; - } + if (slot->idx == 0 || + slot->slot.fwid_len == 0 || + slot->slot.size == 0 || + is_slot_committed(slot)) { + return -EINVAL; + } - err = slot_store(slot); - if (err) { - LOG_WRN("Store failed (err: %d)", err); - return err; - } + err = slot_store(slot); + if (err) { + BT_WARN("Store failed (err: %d)", err); + return err; + } - sys_slist_append(&list, &slot->n); + sys_slist_append(&list, &slot->n); - LOG_DBG("Stored slot #%u: %s", ARRAY_INDEX(slots, slot), - bt_hex(slot->slot.fwid, slot->slot.fwid_len)); - return 0; + BT_DBG("Stored slot #%u: %s", ARRAY_INDEX(slots, slot), + bt_hex(slot->slot.fwid, slot->slot.fwid_len)); + return 0; } void bt_mesh_dfu_slot_release(const struct bt_mesh_dfu_slot *dfu_slot) { - struct slot *slot = CONTAINER_OF(dfu_slot, struct slot, slot); + struct slot *slot = CONTAINER_OF(dfu_slot, struct slot, slot); - if (is_slot_committed(slot)) { - return; - } + if (is_slot_committed(slot)) { + return; + } - slot->idx = 0; + slot->idx = 0; } int bt_mesh_dfu_slot_del(const struct bt_mesh_dfu_slot *dfu_slot) { - struct slot *slot = CONTAINER_OF(dfu_slot, struct slot, slot); + struct slot *slot = CONTAINER_OF(dfu_slot, struct slot, slot); - if (!sys_slist_find_and_remove(&list, &slot->n)) { - return -EINVAL; - } + if (!sys_slist_find_and_remove(&list, &slot->n)) { + return -EINVAL; + } - int idx = ARRAY_INDEX(slots, slot); + int idx = ARRAY_INDEX(slots, slot); - LOG_DBG("%u", idx); + BT_DBG("%u", idx); - slot_erase(slot); - slot->idx = 0; + slot_erase(slot); + slot->idx = 0; - return 0; + return 0; } void bt_mesh_dfu_slot_del_all(void) { - struct slot *s; - SYS_SLIST_FOR_EACH_CONTAINER(&list, s, n) { - slot_erase(s); - s->idx = 0; - } + struct slot *s; + SYS_SLIST_FOR_EACH_CONTAINER(&list, s, n) { + slot_erase(s); + s->idx = 0; + } - sys_slist_init(&list); + sys_slist_init(&list); } const struct bt_mesh_dfu_slot *bt_mesh_dfu_slot_at(uint16_t img_idx) { - struct slot *s; + struct slot *s; - SYS_SLIST_FOR_EACH_CONTAINER(&list, s, n) { - if (!img_idx--) { - return &s->slot; - } - } + SYS_SLIST_FOR_EACH_CONTAINER(&list, s, n) { + if (!img_idx--) { + return &s->slot; + } + } - return NULL; + return NULL; } int bt_mesh_dfu_slot_get(const uint8_t *fwid, size_t fwid_len, struct bt_mesh_dfu_slot **slot) { - struct slot *s; - int idx = 0; + struct slot *s; + int idx = 0; - SYS_SLIST_FOR_EACH_CONTAINER(&list, s, n) { - if (slot_eq(&s->slot, fwid, fwid_len)) { - if (slot) { - *slot = &s->slot; - } - return idx; - } - idx++; - } + SYS_SLIST_FOR_EACH_CONTAINER(&list, s, n) { + if (slot_eq(&s->slot, fwid, fwid_len)) { + if (slot) { + *slot = &s->slot; + } + return idx; + } + idx++; + } - return -ENOENT; + return -ENOENT; } int bt_mesh_dfu_slot_img_idx_get(const struct bt_mesh_dfu_slot *dfu_slot) { - struct slot *s; - int idx = 0; + struct slot *s; + int idx = 0; - SYS_SLIST_FOR_EACH_CONTAINER(&list, s, n) { - if (&s->slot == dfu_slot) { - return idx; - } - idx++; - } + SYS_SLIST_FOR_EACH_CONTAINER(&list, s, n) { + if (&s->slot == dfu_slot) { + return idx; + } + idx++; + } - return -ENOENT; + return -ENOENT; } size_t bt_mesh_dfu_slot_foreach(bt_mesh_dfu_slot_cb_t cb, void *user_data) { - enum bt_mesh_dfu_iter iter; - size_t cnt = 0; - struct slot *s; + enum bt_mesh_dfu_iter iter; + size_t cnt = 0; + struct slot *s; - SYS_SLIST_FOR_EACH_CONTAINER(&list, s, n) { - cnt++; + SYS_SLIST_FOR_EACH_CONTAINER(&list, s, n) { + cnt++; - if (!cb) { - continue; - } + if (!cb) { + continue; + } - iter = cb(&s->slot, user_data); - if (iter != BT_MESH_DFU_ITER_CONTINUE) { - break; - } - } + iter = cb(&s->slot, user_data); + if (iter != BLE_MESH_DFU_ITER_CONTINUE) { + break; + } + } - return cnt; + return cnt; } +#if 0 static int slot_data_load(const char *key, size_t len_rd, - settings_read_cb read_cb, void *cb_arg) + settings_read_cb read_cb, void *cb_arg) { - const char *prop; - size_t len; - uint16_t idx; + const char *prop; + size_t len; + uint16_t idx; - idx = strtol(key, NULL, 16); + idx = strtol(key, NULL, 16); - if (idx >= ARRAY_SIZE(slots)) { - return 0; - } + if (idx >= ARRAY_SIZE(slots)) { + return 0; + } - len = settings_name_next(key, &prop); + len = settings_name_next(key, &prop); - if (!strncmp(prop, PROP_HEADER, len)) { - if (read_cb(cb_arg, &slots[idx], HEADER_SIZE) > 0) { - struct slot *s, *prev = NULL; + if (!strncmp(prop, PROP_HEADER, len)) { + if (read_cb(cb_arg, &slots[idx], HEADER_SIZE) > 0) { + struct slot *s, *prev = NULL; - SYS_SLIST_FOR_EACH_CONTAINER(&list, s, n) { - if (s->idx > slots[idx].idx) { - break; - } + SYS_SLIST_FOR_EACH_CONTAINER(&list, s, n) { + if (s->idx > slots[idx].idx) { + break; + } - prev = s; - } + prev = s; + } - if (prev == NULL) { - sys_slist_prepend(&list, &slots[idx].n); - } else { - sys_slist_insert(&list, &prev->n, &slots[idx].n); - } + if (prev == NULL) { + sys_slist_prepend(&list, &slots[idx].n); + } else { + sys_slist_insert(&list, &prev->n, &slots[idx].n); + } - if (slots[idx].idx >= slot_index) { - slot_index = slots[idx].idx + 1; - } - } - return 0; - } + if (slots[idx].idx >= slot_index) { + slot_index = slots[idx].idx + 1; + } + } + return 0; + } - if (!strncmp(prop, PROP_FWID, len)) { - if (read_cb(cb_arg, &slots[idx].slot.fwid, - sizeof(slots[idx].slot.fwid)) < 0) { - slots[idx].idx = 0; - sys_slist_find_and_remove(&list, &slots[idx].n); - return 0; - } + if (!strncmp(prop, PROP_FWID, len)) { + if (read_cb(cb_arg, &slots[idx].slot.fwid, + sizeof(slots[idx].slot.fwid)) < 0) { + slots[idx].idx = 0; + sys_slist_find_and_remove(&list, &slots[idx].n); + return 0; + } - slots[idx].slot.fwid_len = len_rd; - return 0; - } + slots[idx].slot.fwid_len = len_rd; + return 0; + } - if (!strncmp(prop, PROP_METADATA, len)) { - if (read_cb(cb_arg, &slots[idx].slot.metadata, - sizeof(slots[idx].slot.metadata)) < 0) { - slots[idx].idx = 0; - sys_slist_find_and_remove(&list, &slots[idx].n); - return 0; - } + if (!strncmp(prop, PROP_METADATA, len)) { + if (read_cb(cb_arg, &slots[idx].slot.metadata, + sizeof(slots[idx].slot.metadata)) < 0) { + slots[idx].idx = 0; + sys_slist_find_and_remove(&list, &slots[idx].n); + return 0; + } - slots[idx].slot.metadata_len = len_rd; - return 0; - } + slots[idx].slot.metadata_len = len_rd; + return 0; + } - return 0; + return 0; } - -SETTINGS_STATIC_HANDLER_DEFINE(bt_mesh_dfu_slots, DFU_SLOT_SETTINGS_PATH, NULL, - slot_data_load, NULL, NULL); +#endif +#endif /* CONFIG_BLE_MESH_DFU_SLOTS */ diff --git a/components/bt/esp_ble_mesh/v1.1/dfu/dfu_slot.h b/components/bt/esp_ble_mesh/v1.1/dfu/dfu_slot.h index 969c6d3d25..7509ad24a6 100644 --- a/components/bt/esp_ble_mesh/v1.1/dfu/dfu_slot.h +++ b/components/bt/esp_ble_mesh/v1.1/dfu/dfu_slot.h @@ -1,11 +1,12 @@ /* - * Copyright (c) 2020 Nordic Semiconductor ASA + * SPDX-FileCopyrightText: 2020 Nordic Semiconductor ASA + * SPDX-FileContributor: 2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ -#include - +#include "mesh_v1.1/dfu/dfu.h" +#if CONFIG_BLE_MESH_DFU_SLOTS /** @brief Slot iteration callback. * * @param slot A valid DFU image slot. @@ -13,8 +14,8 @@ * * @return Iteration action determining next step. */ -typedef enum bt_mesh_dfu_iter (*bt_mesh_dfu_slot_cb_t)( - const struct bt_mesh_dfu_slot *slot, void *user_data); +typedef enum bt_mesh_dfu_iter(*bt_mesh_dfu_slot_cb_t) +(const struct bt_mesh_dfu_slot *slot, void *user_data); /** @brief Get the number of slots committed to the firmware list. * @@ -41,24 +42,24 @@ struct bt_mesh_dfu_slot *bt_mesh_dfu_slot_reserve(void); * @param size The size of the image. * @param metadata Metadata or NULL. * @param metadata_len Length of the metadata, at most @c - * CONFIG_BT_MESH_DFU_METADATA_MAXLEN. + * CONFIG_BLE_MESH_DFU_METADATA_MAXLEN. * * @return 0 on success, (negative) error code otherwise. */ int bt_mesh_dfu_slot_info_set(struct bt_mesh_dfu_slot *dfu_slot, size_t size, - const uint8_t *metadata, size_t metadata_len); + const uint8_t *metadata, size_t metadata_len); /** @brief Set the new fwid for the incoming image for a reserved slot. * * @param dfu_slot Pointer to the reserved slot for which to set the fwid. * @param fwid Fwid to set. * @param fwid_len Length of the fwid, at most @c - * CONFIG_BT_MESH_DFU_FWID_MAXLEN. + * CONFIG_BLE_MESH_DFU_FWID_MAXLEN. * * @return 0 on success, (negative) error code otherwise. */ int bt_mesh_dfu_slot_fwid_set(struct bt_mesh_dfu_slot *dfu_slot, - const uint8_t *fwid, size_t fwid_len); + const uint8_t *fwid, size_t fwid_len); /** @brief Commit the reserved slot to the list of slots, and store it * persistently. @@ -124,7 +125,7 @@ int bt_mesh_dfu_slot_img_idx_get(const struct bt_mesh_dfu_slot *slot); /** @brief Iterate through all DFU image slots. * * Calls the callback for every DFU image slot or until the callback returns - * something other than @ref BT_MESH_DFU_ITER_CONTINUE. + * something other than @ref BLE_MESH_DFU_ITER_CONTINUE. * * @param cb Callback to call for each slot, or NULL to just count the * number of slots. @@ -133,3 +134,4 @@ int bt_mesh_dfu_slot_img_idx_get(const struct bt_mesh_dfu_slot *slot); * @return The number of slots iterated over. */ size_t bt_mesh_dfu_slot_foreach(bt_mesh_dfu_slot_cb_t cb, void *user_data); +#endif /* CONFIG_BLE_MESH_DFU_SLOTS */ 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 0cb4b5e524..b36746a822 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 @@ -1,617 +1,631 @@ /* - * Copyright (c) 2020 Nordic Semiconductor ASA + * SPDX-FileCopyrightText: 2020 Nordic Semiconductor ASA + * SPDX-FileContributor: 2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ -#include +#include "mesh_v1.1/dfu/dfu.h" +#include "mesh_v1.1/dfu/dfu_srv.h" + #include "dfu.h" #include "blob.h" #include "access.h" -#define LOG_LEVEL CONFIG_BT_MESH_DFU_LOG_LEVEL -#include -LOG_MODULE_REGISTER(bt_mesh_dfu_srv); +#if CONFIG_BLE_MESH_SETTINGS +#include "settings.h" +#endif +#if CONFIG_BLE_MESH_DFU_SRV #define UPDATE_IDX_NONE 0xff -BUILD_ASSERT((DFU_UPDATE_START_MSG_MAXLEN + BT_MESH_MODEL_OP_LEN(BT_MESH_DFU_OP_UPDATE_START) + - BT_MESH_MIC_SHORT) <= BT_MESH_RX_SDU_MAX, - "The Firmware Update Start message does not fit into the maximum incoming SDU size."); +_Static_assert((DFU_UPDATE_START_MSG_MAXLEN + BLE_MESH_MODEL_OP_LEN(BLE_MESH_DFU_OP_UPDATE_START) + + BLE_MESH_MIC_SHORT) <= BLE_MESH_RX_SDU_MAX, + "The Firmware Update Start message does not fit into the maximum incoming SDU size."); -BUILD_ASSERT((DFU_UPDATE_INFO_STATUS_MSG_MINLEN + - BT_MESH_MODEL_OP_LEN(BT_MESH_DFU_OP_UPDATE_INFO_STATUS) + BT_MESH_MIC_SHORT) - <= BT_MESH_TX_SDU_MAX, - "The Firmware Update Info Status message does not fit into the maximum outgoing SDU " - "size."); +_Static_assert((DFU_UPDATE_INFO_STATUS_MSG_MINLEN + + BLE_MESH_MODEL_OP_LEN(BLE_MESH_DFU_OP_UPDATE_INFO_STATUS) + BLE_MESH_MIC_SHORT) + <= BLE_MESH_TX_SDU_MAX, + "The Firmware Update Info Status message does not fit into the maximum outgoing SDU " + "size."); static void store_state(struct bt_mesh_dfu_srv *srv) { - bt_mesh_model_data_store(srv->mod, false, NULL, &srv->update, - sizeof(srv->update)); +#if CONFIG_BLE_MESH_SETTINGS + bt_mesh_model_data_store(srv->mod, false, NULL, &srv->update, + sizeof(srv->update)); +#endif } static void erase_state(struct bt_mesh_dfu_srv *srv) { - bt_mesh_model_data_store(srv->mod, false, NULL, NULL, 0); +#if CONFIG_BLE_MESH_SETTINGS + bt_mesh_model_data_store(srv->mod, false, NULL, NULL, 0); +#endif } static void xfer_failed(struct bt_mesh_dfu_srv *srv) { - if (!bt_mesh_dfu_srv_is_busy(srv) || - srv->update.idx >= srv->img_count) { - return; - } + if (!bt_mesh_dfu_srv_is_busy(srv) || + srv->update.idx >= srv->img_count) { + return; + } - erase_state(srv); + erase_state(srv); - if (srv->cb->end) { - srv->cb->end(srv, &srv->imgs[srv->update.idx], false); - } + if (srv->cb->end) { + srv->cb->end(srv, &srv->imgs[srv->update.idx], false); + } } static enum bt_mesh_dfu_status metadata_check(struct bt_mesh_dfu_srv *srv, - uint8_t idx, - struct net_buf_simple *buf, - enum bt_mesh_dfu_effect *effect) + uint8_t idx, + struct net_buf_simple *buf, + enum bt_mesh_dfu_effect *effect) { - *effect = BT_MESH_DFU_EFFECT_NONE; + *effect = BLE_MESH_DFU_EFFECT_NONE; - if (idx >= srv->img_count) { - return BT_MESH_DFU_ERR_FW_IDX; - } + if (idx >= srv->img_count) { + return BLE_MESH_DFU_ERR_FW_IDX; + } - if (!srv->cb->check) { - return BT_MESH_DFU_SUCCESS; - } + if (!srv->cb->check) { + return BLE_MESH_DFU_SUCCESS; + } - if (srv->cb->check(srv, &srv->imgs[idx], buf, effect)) { - *effect = BT_MESH_DFU_EFFECT_NONE; - return BT_MESH_DFU_ERR_METADATA; - } + if (srv->cb->check(srv, &srv->imgs[idx], buf, effect)) { + *effect = BLE_MESH_DFU_EFFECT_NONE; + return BLE_MESH_DFU_ERR_METADATA; + } - return BT_MESH_DFU_SUCCESS; + return BLE_MESH_DFU_SUCCESS; } static void apply_rsp_sent(int err, void *cb_params) { - struct bt_mesh_dfu_srv *srv = cb_params; + struct bt_mesh_dfu_srv *srv = cb_params; - if (err) { - /* return phase back to give client one more chance. */ - srv->update.phase = BT_MESH_DFU_PHASE_VERIFY_OK; - LOG_WRN("Apply response failed, wait for retry (err %d)", err); - return; - } + if (err) { + /* return phase back to give client one more chance. */ + srv->update.phase = BLE_MESH_DFU_PHASE_VERIFY_OK; + BT_WARN("Apply response failed, wait for retry (err %d)", err); + return; + } - LOG_DBG(""); + BT_DBG(""); - if (!srv->cb->apply || srv->update.idx == UPDATE_IDX_NONE) { - srv->update.phase = BT_MESH_DFU_PHASE_IDLE; - store_state(srv); - LOG_DBG("Prerequisites for apply callback are wrong"); - return; - } + if (!srv->cb->apply || srv->update.idx == UPDATE_IDX_NONE) { + srv->update.phase = BLE_MESH_DFU_PHASE_IDLE; + store_state(srv); + BT_DBG("Prerequisites for apply callback are wrong"); + return; + } - store_state(srv); + store_state(srv); - err = srv->cb->apply(srv, &srv->imgs[srv->update.idx]); - if (err) { - srv->update.phase = BT_MESH_DFU_PHASE_IDLE; - store_state(srv); - LOG_DBG("Application apply callback failed (err %d)", err); - } + err = srv->cb->apply(srv, &srv->imgs[srv->update.idx]); + if (err) { + srv->update.phase = BLE_MESH_DFU_PHASE_IDLE; + store_state(srv); + BT_DBG("Application apply callback failed (err %d)", err); + } } static void apply_rsp_sending(uint16_t duration, int err, void *cb_params) { - if (err) { - apply_rsp_sent(err, cb_params); - } + if (err) { + apply_rsp_sent(err, cb_params); + } } static void verify(struct bt_mesh_dfu_srv *srv) { - srv->update.phase = BT_MESH_DFU_PHASE_VERIFY; + srv->update.phase = BLE_MESH_DFU_PHASE_VERIFY; - if (srv->update.idx >= srv->img_count) { - bt_mesh_dfu_srv_rejected(srv); - return; - } + if (srv->update.idx >= srv->img_count) { + bt_mesh_dfu_srv_rejected(srv); + return; + } - if (!srv->cb->end) { - bt_mesh_dfu_srv_verified(srv); - return; - } + if (!srv->cb->end) { + bt_mesh_dfu_srv_verified(srv); + return; + } - srv->cb->end(srv, &srv->imgs[srv->update.idx], true); - if (srv->update.phase == BT_MESH_DFU_PHASE_VERIFY) { - store_state(srv); - } + srv->cb->end(srv, &srv->imgs[srv->update.idx], true); + if (srv->update.phase == BLE_MESH_DFU_PHASE_VERIFY) { + store_state(srv); + } } static int handle_info_get(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx, - struct net_buf_simple *buf) + struct net_buf_simple *buf) { - struct bt_mesh_dfu_srv *srv = mod->rt->user_data; - uint8_t idx, limit; + struct bt_mesh_dfu_srv *srv = mod->user_data; + uint8_t idx, limit; - if (srv->update.phase == BT_MESH_DFU_PHASE_APPLYING) { - LOG_INF("Still applying, not responding"); - return -EBUSY; - } + if (srv->update.phase == BLE_MESH_DFU_PHASE_APPLYING) { + BT_INFO("Still applying, not responding"); + return -EBUSY; + } - idx = net_buf_simple_pull_u8(buf); - limit = net_buf_simple_pull_u8(buf); + idx = net_buf_simple_pull_u8(buf); + limit = net_buf_simple_pull_u8(buf); - LOG_DBG("from %u (limit: %u)", idx, limit); + BT_DBG("from %u (limit: %u)", idx, limit); - NET_BUF_SIMPLE_DEFINE(rsp, BT_MESH_TX_SDU_MAX); - bt_mesh_model_msg_init(&rsp, BT_MESH_DFU_OP_UPDATE_INFO_STATUS); - net_buf_simple_add_u8(&rsp, srv->img_count); - net_buf_simple_add_u8(&rsp, idx); + NET_BUF_SIMPLE_DEFINE(rsp, BLE_MESH_TX_SDU_MAX); + bt_mesh_model_msg_init(&rsp, BLE_MESH_DFU_OP_UPDATE_INFO_STATUS); + net_buf_simple_add_u8(&rsp, srv->img_count); + net_buf_simple_add_u8(&rsp, idx); - for (; idx < srv->img_count && limit > 0; ++idx) { - uint32_t entry_len; + for (; idx < srv->img_count && limit > 0; ++idx) { + uint32_t entry_len; - if (!srv->imgs[idx].fwid) { - continue; - } + if (!srv->imgs[idx].fwid) { + continue; + } - entry_len = 2 + srv->imgs[idx].fwid_len; - if (srv->imgs[idx].uri) { - entry_len += strlen(srv->imgs[idx].uri); - } + entry_len = 2 + srv->imgs[idx].fwid_len; + if (srv->imgs[idx].uri) { + entry_len += strlen(srv->imgs[idx].uri); + } - if (net_buf_simple_tailroom(&rsp) + BT_MESH_MIC_SHORT < entry_len) { - break; - } + if (net_buf_simple_tailroom(&rsp) + BLE_MESH_MIC_SHORT < entry_len) { + break; + } - net_buf_simple_add_u8(&rsp, srv->imgs[idx].fwid_len); - net_buf_simple_add_mem(&rsp, srv->imgs[idx].fwid, - srv->imgs[idx].fwid_len); + net_buf_simple_add_u8(&rsp, srv->imgs[idx].fwid_len); + net_buf_simple_add_mem(&rsp, srv->imgs[idx].fwid, + srv->imgs[idx].fwid_len); - if (srv->imgs[idx].uri) { - size_t len = strlen(srv->imgs[idx].uri); + if (srv->imgs[idx].uri) { + size_t len = strlen(srv->imgs[idx].uri); - net_buf_simple_add_u8(&rsp, len); - net_buf_simple_add_mem(&rsp, srv->imgs[idx].uri, len); - } else { - net_buf_simple_add_u8(&rsp, 0); - } + net_buf_simple_add_u8(&rsp, len); + net_buf_simple_add_mem(&rsp, srv->imgs[idx].uri, len); + } else { + net_buf_simple_add_u8(&rsp, 0); + } - limit--; - } + limit--; + } - if (srv->update.phase != BT_MESH_DFU_PHASE_IDLE) { - ctx->send_ttl = srv->update.ttl; - } + if (srv->update.phase != BLE_MESH_DFU_PHASE_IDLE) { + ctx->send_ttl = srv->update.ttl; + } - bt_mesh_model_send(mod, ctx, &rsp, NULL, NULL); + bt_mesh_model_send(mod, ctx, &rsp, NULL, NULL); - return 0; + return 0; } static int handle_metadata_check(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx, - struct net_buf_simple *buf) + struct net_buf_simple *buf) { - struct bt_mesh_dfu_srv *srv = mod->rt->user_data; - enum bt_mesh_dfu_status status; - enum bt_mesh_dfu_effect effect; - uint8_t idx; + struct bt_mesh_dfu_srv *srv = mod->user_data; + enum bt_mesh_dfu_status status; + enum bt_mesh_dfu_effect effect; + uint8_t idx; - BT_MESH_MODEL_BUF_DEFINE(rsp, BT_MESH_DFU_OP_UPDATE_METADATA_STATUS, 2); - bt_mesh_model_msg_init(&rsp, BT_MESH_DFU_OP_UPDATE_METADATA_STATUS); + BLE_MESH_MODEL_BUF_DEFINE(rsp, BLE_MESH_DFU_OP_UPDATE_METADATA_STATUS, 2); + bt_mesh_model_msg_init(&rsp, BLE_MESH_DFU_OP_UPDATE_METADATA_STATUS); - idx = net_buf_simple_pull_u8(buf); - status = metadata_check(srv, idx, buf, &effect); + idx = net_buf_simple_pull_u8(buf); + status = metadata_check(srv, idx, buf, &effect); - LOG_DBG("%u", idx); + BT_DBG("%u", idx); - net_buf_simple_add_u8(&rsp, (status & BIT_MASK(3)) | (effect << 3)); - net_buf_simple_add_u8(&rsp, idx); + net_buf_simple_add_u8(&rsp, (status & BIT_MASK(3)) | (effect << 3)); + net_buf_simple_add_u8(&rsp, idx); - if (srv->update.phase != BT_MESH_DFU_PHASE_IDLE) { - ctx->send_ttl = srv->update.ttl; - } + if (srv->update.phase != BLE_MESH_DFU_PHASE_IDLE) { + ctx->send_ttl = srv->update.ttl; + } - bt_mesh_model_send(mod, ctx, &rsp, NULL, NULL); + bt_mesh_model_send(mod, ctx, &rsp, NULL, NULL); - return 0; + return 0; } static void update_status_rsp(struct bt_mesh_dfu_srv *srv, - struct bt_mesh_msg_ctx *ctx, - enum bt_mesh_dfu_status status, - const struct bt_mesh_send_cb *send_cb) + struct bt_mesh_msg_ctx *ctx, + enum bt_mesh_dfu_status status, + const struct bt_mesh_send_cb *send_cb) { - BT_MESH_MODEL_BUF_DEFINE(buf, BT_MESH_DFU_OP_UPDATE_STATUS, 14); - bt_mesh_model_msg_init(&buf, BT_MESH_DFU_OP_UPDATE_STATUS); + BLE_MESH_MODEL_BUF_DEFINE(buf, BLE_MESH_DFU_OP_UPDATE_STATUS, 14); + bt_mesh_model_msg_init(&buf, BLE_MESH_DFU_OP_UPDATE_STATUS); - net_buf_simple_add_u8(&buf, ((status & BIT_MASK(3)) | - (srv->update.phase << 5))); + net_buf_simple_add_u8(&buf, ((status & BIT_MASK(3)) | + (srv->update.phase << 5))); - if (srv->update.phase != BT_MESH_DFU_PHASE_IDLE) { - net_buf_simple_add_u8(&buf, srv->update.ttl); - net_buf_simple_add_u8(&buf, srv->update.effect); - net_buf_simple_add_le16(&buf, srv->update.timeout_base); - net_buf_simple_add_le64(&buf, srv->blob.state.xfer.id); - net_buf_simple_add_u8(&buf, srv->update.idx); + if (srv->update.phase != BLE_MESH_DFU_PHASE_IDLE) { + net_buf_simple_add_u8(&buf, srv->update.ttl); + net_buf_simple_add_u8(&buf, srv->update.effect); + net_buf_simple_add_le16(&buf, srv->update.timeout_base); + net_buf_simple_add_le64(&buf, srv->blob.state.xfer.id); + net_buf_simple_add_u8(&buf, srv->update.idx); - ctx->send_ttl = srv->update.ttl; - } + ctx->send_ttl = srv->update.ttl; + } - bt_mesh_model_send(srv->mod, ctx, &buf, send_cb, srv); + bt_mesh_model_send(srv->mod, ctx, &buf, send_cb, srv); } static int handle_get(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx, - struct net_buf_simple *buf) + struct net_buf_simple *buf) { - struct bt_mesh_dfu_srv *srv = mod->rt->user_data; + struct bt_mesh_dfu_srv *srv = mod->user_data; - LOG_DBG(""); + BT_DBG(""); - update_status_rsp(srv, ctx, BT_MESH_DFU_SUCCESS, NULL); + update_status_rsp(srv, ctx, BLE_MESH_DFU_SUCCESS, NULL); - return 0; + return 0; } static inline bool is_active_update(struct bt_mesh_dfu_srv *srv, uint8_t idx, - uint16_t timeout_base, - const uint64_t *blob_id, uint8_t ttl, - uint16_t meta_checksum) + uint16_t timeout_base, + const uint64_t *blob_id, uint8_t ttl, + uint16_t meta_checksum) { - return (srv->update.idx != idx || srv->blob.state.xfer.id != *blob_id || - srv->update.ttl != ttl || - srv->update.timeout_base != timeout_base || - srv->update.meta != meta_checksum); + return (srv->update.idx != idx || srv->blob.state.xfer.id != *blob_id || + srv->update.ttl != ttl || + srv->update.timeout_base != timeout_base || + srv->update.meta != meta_checksum); } static int handle_start(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx, - struct net_buf_simple *buf) + struct net_buf_simple *buf) { - struct bt_mesh_dfu_srv *srv = mod->rt->user_data; - const struct bt_mesh_blob_io *io; - uint16_t timeout_base, meta_checksum; - enum bt_mesh_dfu_status status; - uint8_t ttl, idx; - uint64_t blob_id; - int err; - struct net_buf_simple_state buf_state; + struct bt_mesh_dfu_srv *srv = mod->user_data; + const struct bt_mesh_blob_io *io; + uint16_t timeout_base, meta_checksum; + enum bt_mesh_dfu_status status; + uint8_t ttl, idx; + uint64_t blob_id; + int err; + struct net_buf_simple_state buf_state; - ttl = net_buf_simple_pull_u8(buf); - timeout_base = net_buf_simple_pull_le16(buf); - blob_id = net_buf_simple_pull_le64(buf); - idx = net_buf_simple_pull_u8(buf); - meta_checksum = dfu_metadata_checksum(buf); + ttl = net_buf_simple_pull_u8(buf); + timeout_base = net_buf_simple_pull_le16(buf); + blob_id = net_buf_simple_pull_le64(buf); + idx = net_buf_simple_pull_u8(buf); + meta_checksum = dfu_metadata_checksum(buf); - LOG_DBG("%u ttl: %u extra time: %u", idx, ttl, timeout_base); + BT_DBG("%u ttl: %u extra time: %u", idx, ttl, timeout_base); - if ((!buf->len || meta_checksum == srv->update.meta) && - srv->update.phase == BT_MESH_DFU_PHASE_TRANSFER_ERR && - srv->update.ttl == ttl && - srv->update.timeout_base == timeout_base && - srv->update.idx == idx && - srv->blob.state.xfer.id == blob_id) { - srv->update.phase = BT_MESH_DFU_PHASE_TRANSFER_ACTIVE; - status = BT_MESH_DFU_SUCCESS; - store_state(srv); - /* blob srv will resume the transfer. */ - LOG_DBG("Resuming transfer"); - goto rsp; - } + if ((!buf->len || meta_checksum == srv->update.meta) && + srv->update.phase == BLE_MESH_DFU_PHASE_TRANSFER_ERR && + srv->update.ttl == ttl && + srv->update.timeout_base == timeout_base && + srv->update.idx == idx && + srv->blob.state.xfer.id == blob_id) { + srv->update.phase = BLE_MESH_DFU_PHASE_TRANSFER_ACTIVE; + status = BLE_MESH_DFU_SUCCESS; + store_state(srv); + /* blob srv will resume the transfer. */ + BT_DBG("Resuming transfer"); + goto rsp; + } - if (bt_mesh_dfu_srv_is_busy(srv)) { - if (is_active_update(srv, idx, timeout_base, &blob_id, ttl, - meta_checksum)) { - status = BT_MESH_DFU_ERR_WRONG_PHASE; - } else { - status = BT_MESH_DFU_SUCCESS; - srv->update.ttl = ttl; - srv->blob.state.xfer.id = blob_id; - } + if (bt_mesh_dfu_srv_is_busy(srv)) { + if (is_active_update(srv, idx, timeout_base, &blob_id, ttl, + meta_checksum)) { + status = BLE_MESH_DFU_ERR_WRONG_PHASE; + } else { + status = BLE_MESH_DFU_SUCCESS; + srv->update.ttl = ttl; + srv->blob.state.xfer.id = blob_id; + } - LOG_WRN("Busy. Phase: %u", srv->update.phase); - goto rsp; - } + BT_WARN("Busy. Phase: %u", srv->update.phase); + goto rsp; + } - net_buf_simple_save(buf, &buf_state); - status = metadata_check(srv, idx, buf, - (enum bt_mesh_dfu_effect *)&srv->update.effect); - net_buf_simple_restore(buf, &buf_state); - if (status != BT_MESH_DFU_SUCCESS) { - goto rsp; - } + net_buf_simple_save(buf, &buf_state); + status = metadata_check(srv, idx, buf, + (enum bt_mesh_dfu_effect *)&srv->update.effect); + net_buf_simple_restore(buf, &buf_state); + if (status != BLE_MESH_DFU_SUCCESS) { + goto rsp; + } - srv->update.ttl = ttl; - srv->update.timeout_base = timeout_base; - srv->update.meta = meta_checksum; + srv->update.ttl = ttl; + srv->update.timeout_base = timeout_base; + srv->update.meta = meta_checksum; - io = NULL; - err = srv->cb->start(srv, &srv->imgs[idx], buf, &io); - if (err == -EALREADY || (!err && bt_mesh_has_addr(ctx->addr))) { - /* This image has already been received or this is a - * self-update. Skip the transfer phase and proceed to - * verifying update. - */ - status = BT_MESH_DFU_SUCCESS; - srv->update.idx = idx; - srv->blob.state.xfer.id = blob_id; - srv->update.phase = BT_MESH_DFU_PHASE_VERIFY; - update_status_rsp(srv, ctx, status, NULL); - verify(srv); - return 0; - } + io = NULL; + err = srv->cb->start(srv, &srv->imgs[idx], buf, &io); + if (err == -EALREADY || (!err && bt_mesh_has_addr(ctx->addr))) { + /* This image has already been received or this is a + * self-update. Skip the transfer phase and proceed to + * verifying update. + */ + status = BLE_MESH_DFU_SUCCESS; + srv->update.idx = idx; + srv->blob.state.xfer.id = blob_id; + srv->update.phase = BLE_MESH_DFU_PHASE_VERIFY; + update_status_rsp(srv, ctx, status, NULL); + verify(srv); + return 0; + } - if (err == -ENOMEM) { - status = BT_MESH_DFU_ERR_RESOURCES; - goto rsp; - } + if (err == -ENOMEM) { + status = BLE_MESH_DFU_ERR_RESOURCES; + goto rsp; + } - if (err == -EBUSY) { - status = BT_MESH_DFU_ERR_TEMPORARILY_UNAVAILABLE; - goto rsp; - } + if (err == -EBUSY) { + status = BLE_MESH_DFU_ERR_TEMPORARILY_UNAVAILABLE; + goto rsp; + } - if (err || !io || !io->wr) { - status = BT_MESH_DFU_ERR_INTERNAL; - goto rsp; - } + if (err || !io || !io->wr) { + status = BLE_MESH_DFU_ERR_INTERNAL; + goto rsp; + } - err = bt_mesh_blob_srv_recv(&srv->blob, blob_id, io, - ttl, timeout_base); - if (err) { - status = BT_MESH_DFU_ERR_BLOB_XFER_BUSY; - goto rsp; - } + err = bt_mesh_blob_srv_recv(&srv->blob, blob_id, io, + ttl, timeout_base); + if (err) { + status = BLE_MESH_DFU_ERR_BLOB_XFER_BUSY; + goto rsp; + } - srv->update.idx = idx; - srv->update.phase = BT_MESH_DFU_PHASE_TRANSFER_ACTIVE; - status = BT_MESH_DFU_SUCCESS; - store_state(srv); + srv->update.idx = idx; + srv->update.phase = BLE_MESH_DFU_PHASE_TRANSFER_ACTIVE; + status = BLE_MESH_DFU_SUCCESS; + store_state(srv); rsp: - update_status_rsp(srv, ctx, status, NULL); + update_status_rsp(srv, ctx, status, NULL); - return 0; + return 0; } static int handle_cancel(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx, - struct net_buf_simple *buf) + struct net_buf_simple *buf) { - struct bt_mesh_dfu_srv *srv = mod->rt->user_data; + struct bt_mesh_dfu_srv *srv = mod->user_data; - if (srv->update.idx == UPDATE_IDX_NONE) { - goto rsp; - } + if (srv->update.idx == UPDATE_IDX_NONE) { + goto rsp; + } - LOG_DBG(""); + BT_DBG(""); - bt_mesh_blob_srv_cancel(&srv->blob); - srv->update.phase = BT_MESH_DFU_PHASE_IDLE; - xfer_failed(srv); + bt_mesh_blob_srv_cancel(&srv->blob); + srv->update.phase = BLE_MESH_DFU_PHASE_IDLE; + xfer_failed(srv); rsp: - update_status_rsp(srv, ctx, BT_MESH_DFU_SUCCESS, NULL); + update_status_rsp(srv, ctx, BLE_MESH_DFU_SUCCESS, NULL); - return 0; + return 0; } static int handle_apply(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx, - struct net_buf_simple *buf) + struct net_buf_simple *buf) { - struct bt_mesh_dfu_srv *srv = mod->rt->user_data; - static const struct bt_mesh_send_cb send_cb = { - .start = apply_rsp_sending, - .end = apply_rsp_sent, - }; + struct bt_mesh_dfu_srv *srv = mod->user_data; + static const struct bt_mesh_send_cb send_cb = { + .start = apply_rsp_sending, + .end = apply_rsp_sent, + }; - LOG_DBG(""); + BT_DBG(""); - if (srv->update.phase == BT_MESH_DFU_PHASE_APPLYING) { - update_status_rsp(srv, ctx, BT_MESH_DFU_SUCCESS, NULL); - return 0; - } + if (srv->update.phase == BLE_MESH_DFU_PHASE_APPLYING) { + update_status_rsp(srv, ctx, BLE_MESH_DFU_SUCCESS, NULL); + return 0; + } - if (srv->update.phase != BT_MESH_DFU_PHASE_VERIFY_OK) { - LOG_WRN("Apply: Invalid phase %u", srv->update.phase); - update_status_rsp(srv, ctx, BT_MESH_DFU_ERR_WRONG_PHASE, NULL); - return 0; - } + if (srv->update.phase != BLE_MESH_DFU_PHASE_VERIFY_OK) { + BT_WARN("Apply: Invalid phase %u", srv->update.phase); + update_status_rsp(srv, ctx, BLE_MESH_DFU_ERR_WRONG_PHASE, NULL); + return 0; + } - /* Postponing the apply callback until the response has been sent, in - * case it triggers a reboot: - */ - srv->update.phase = BT_MESH_DFU_PHASE_APPLYING; - update_status_rsp(srv, ctx, BT_MESH_DFU_SUCCESS, &send_cb); + /* Postponing the apply callback until the response has been sent, in + * case it triggers a reboot: + */ + srv->update.phase = BLE_MESH_DFU_PHASE_APPLYING; + update_status_rsp(srv, ctx, BLE_MESH_DFU_SUCCESS, &send_cb); - return 0; + return 0; } const struct bt_mesh_model_op _bt_mesh_dfu_srv_op[] = { - { BT_MESH_DFU_OP_UPDATE_INFO_GET, BT_MESH_LEN_EXACT(2), handle_info_get }, - { BT_MESH_DFU_OP_UPDATE_METADATA_CHECK, BT_MESH_LEN_MIN(1), handle_metadata_check }, - { BT_MESH_DFU_OP_UPDATE_GET, BT_MESH_LEN_EXACT(0), handle_get }, - { BT_MESH_DFU_OP_UPDATE_START, BT_MESH_LEN_MIN(12), handle_start }, - { BT_MESH_DFU_OP_UPDATE_CANCEL, BT_MESH_LEN_EXACT(0), handle_cancel }, - { BT_MESH_DFU_OP_UPDATE_APPLY, BT_MESH_LEN_EXACT(0), handle_apply }, - BT_MESH_MODEL_OP_END, + { BLE_MESH_DFU_OP_UPDATE_INFO_GET, 2, (void *)handle_info_get }, + { BLE_MESH_DFU_OP_UPDATE_METADATA_CHECK, 1, (void *)handle_metadata_check }, + { BLE_MESH_DFU_OP_UPDATE_GET, 0, (void *)handle_get }, + { BLE_MESH_DFU_OP_UPDATE_START, 12, (void *)handle_start }, + { BLE_MESH_DFU_OP_UPDATE_CANCEL, 0, (void *)handle_cancel }, + { BLE_MESH_DFU_OP_UPDATE_APPLY, 0, (void *)handle_apply }, + BLE_MESH_MODEL_OP_END, }; -static int dfu_srv_init(const struct bt_mesh_model *mod) +static int dfu_srv_init(struct bt_mesh_model *mod) { - struct bt_mesh_dfu_srv *srv = mod->rt->user_data; + struct bt_mesh_dfu_srv *srv = mod->user_data; - srv->mod = mod; - srv->update.idx = UPDATE_IDX_NONE; + srv->mod = mod; + srv->update.idx = UPDATE_IDX_NONE; + srv->blob.cb = &_bt_mesh_dfu_srv_blob_cb; - if (!srv->cb || !srv->cb->start || !srv->imgs || srv->img_count == 0 || - srv->img_count == UPDATE_IDX_NONE) { - LOG_ERR("Invalid DFU Server initialization"); - return -EINVAL; - } + if (!srv->cb || !srv->cb->start || !srv->imgs || srv->img_count == 0 || + srv->img_count == UPDATE_IDX_NONE) { + BT_ERR("Invalid DFU Server initialization"); + return -EINVAL; + } - if (IS_ENABLED(CONFIG_BT_MESH_MODEL_EXTENSIONS)) { - bt_mesh_model_extend(mod, srv->blob.mod); - } - - return 0; + return 0; } +__attribute__((unused)) static int dfu_srv_settings_set(const struct bt_mesh_model *mod, const char *name, - size_t len_rd, settings_read_cb read_cb, - void *cb_arg) + size_t len_rd, settings_read_cb read_cb, + void *cb_arg) { - struct bt_mesh_dfu_srv *srv = mod->rt->user_data; - ssize_t len; + struct bt_mesh_dfu_srv *srv = mod->user_data; + ssize_t len; - if (len_rd < sizeof(srv->update)) { - return -EINVAL; - } + if (len_rd < sizeof(srv->update)) { + return -EINVAL; + } - len = read_cb(cb_arg, &srv->update, sizeof(srv->update)); - if (len < 0) { - return len; - } + len = read_cb(cb_arg, &srv->update, sizeof(srv->update)); + if (len < 0) { + return len; + } - LOG_DBG("Recovered transfer (phase: %u, idx: %u)", srv->update.phase, - srv->update.idx); - if (srv->update.phase == BT_MESH_DFU_PHASE_TRANSFER_ACTIVE) { - LOG_DBG("Settings recovered mid-transfer, setting transfer error"); - srv->update.phase = BT_MESH_DFU_PHASE_TRANSFER_ERR; - } else if (srv->update.phase == BT_MESH_DFU_PHASE_VERIFY_OK) { - LOG_DBG("Settings recovered before application, setting verification fail"); - srv->update.phase = BT_MESH_DFU_PHASE_VERIFY_FAIL; - } + BT_DBG("Recovered transfer (phase: %u, idx: %u)", srv->update.phase, + srv->update.idx); + if (srv->update.phase == BLE_MESH_DFU_PHASE_TRANSFER_ACTIVE) { + BT_DBG("Settings recovered mid-transfer, setting transfer error"); + srv->update.phase = BLE_MESH_DFU_PHASE_TRANSFER_ERR; + } else if (srv->update.phase == BLE_MESH_DFU_PHASE_VERIFY_OK) { + BT_DBG("Settings recovered before application, setting verification fail"); + srv->update.phase = BLE_MESH_DFU_PHASE_VERIFY_FAIL; + } - return 0; + return 0; } -static void dfu_srv_reset(const struct bt_mesh_model *mod) +static void dfu_srv_reset(struct bt_mesh_model *mod) { - struct bt_mesh_dfu_srv *srv = mod->rt->user_data; + struct bt_mesh_dfu_srv *srv = mod->user_data; - srv->update.phase = BT_MESH_DFU_PHASE_IDLE; - erase_state(srv); + srv->update.phase = BLE_MESH_DFU_PHASE_IDLE; + erase_state(srv); +} + +static int dfu_srv_deinit(struct bt_mesh_model *mod) +{ + dfu_srv_reset(mod); + return 0; } const struct bt_mesh_model_cb _bt_mesh_dfu_srv_cb = { - .init = dfu_srv_init, - .settings_set = dfu_srv_settings_set, - .reset = dfu_srv_reset, + .init = dfu_srv_init, +#if CONFIG_BLE_MESH_DEINIT + .deinit = dfu_srv_deinit, +#endif }; static void blob_suspended(struct bt_mesh_blob_srv *b) { - struct bt_mesh_dfu_srv *srv = CONTAINER_OF(b, struct bt_mesh_dfu_srv, blob); + struct bt_mesh_dfu_srv *srv = CONTAINER_OF(b, struct bt_mesh_dfu_srv, blob); - srv->update.phase = BT_MESH_DFU_PHASE_TRANSFER_ERR; - store_state(srv); + srv->update.phase = BLE_MESH_DFU_PHASE_TRANSFER_ERR; + store_state(srv); } static void blob_end(struct bt_mesh_blob_srv *b, uint64_t id, bool success) { - struct bt_mesh_dfu_srv *srv = - CONTAINER_OF(b, struct bt_mesh_dfu_srv, blob); + struct bt_mesh_dfu_srv *srv = + CONTAINER_OF(b, struct bt_mesh_dfu_srv, blob); - LOG_DBG("success: %u", success); + BT_DBG("success: %u", success); - if (!success) { - srv->update.phase = BT_MESH_DFU_PHASE_TRANSFER_ERR; - xfer_failed(srv); - return; - } + if (!success) { + srv->update.phase = BLE_MESH_DFU_PHASE_TRANSFER_ERR; + xfer_failed(srv); + return; + } - verify(srv); + verify(srv); } static int blob_recover(struct bt_mesh_blob_srv *b, - struct bt_mesh_blob_xfer *xfer, - const struct bt_mesh_blob_io **io) + struct bt_mesh_blob_xfer *xfer, + const struct bt_mesh_blob_io **io) { - struct bt_mesh_dfu_srv *srv = - CONTAINER_OF(b, struct bt_mesh_dfu_srv, blob); + struct bt_mesh_dfu_srv *srv = + CONTAINER_OF(b, struct bt_mesh_dfu_srv, blob); - if (!srv->cb->recover || - srv->update.phase != BT_MESH_DFU_PHASE_TRANSFER_ERR || - srv->update.idx >= srv->img_count) { - return -ENOTSUP; - } + if (!srv->cb->recover || + srv->update.phase != BLE_MESH_DFU_PHASE_TRANSFER_ERR || + srv->update.idx >= srv->img_count) { + return -ENOTSUP; + } - return srv->cb->recover(srv, &srv->imgs[srv->update.idx], io); + return srv->cb->recover(srv, &srv->imgs[srv->update.idx], io); } const struct bt_mesh_blob_srv_cb _bt_mesh_dfu_srv_blob_cb = { - .suspended = blob_suspended, - .end = blob_end, - .recover = blob_recover, + .suspended = blob_suspended, + .end = blob_end, + .recover = blob_recover, }; void bt_mesh_dfu_srv_verified(struct bt_mesh_dfu_srv *srv) { - if (srv->update.phase != BT_MESH_DFU_PHASE_VERIFY) { - LOG_WRN("Wrong state"); - return; - } + if (srv->update.phase != BLE_MESH_DFU_PHASE_VERIFY) { + BT_WARN("Wrong state"); + return; + } - LOG_DBG(""); + BT_DBG(""); - srv->update.phase = BT_MESH_DFU_PHASE_VERIFY_OK; - store_state(srv); + srv->update.phase = BLE_MESH_DFU_PHASE_VERIFY_OK; + store_state(srv); } void bt_mesh_dfu_srv_rejected(struct bt_mesh_dfu_srv *srv) { - if (srv->update.phase != BT_MESH_DFU_PHASE_VERIFY) { - LOG_WRN("Wrong state"); - return; - } + if (srv->update.phase != BLE_MESH_DFU_PHASE_VERIFY) { + BT_WARN("Wrong state"); + return; + } - LOG_DBG(""); + BT_DBG(""); - srv->update.phase = BT_MESH_DFU_PHASE_VERIFY_FAIL; - store_state(srv); + srv->update.phase = BLE_MESH_DFU_PHASE_VERIFY_FAIL; + store_state(srv); } void bt_mesh_dfu_srv_cancel(struct bt_mesh_dfu_srv *srv) { - if (srv->update.phase == BT_MESH_DFU_PHASE_IDLE) { - LOG_WRN("Wrong state"); - return; - } + if (srv->update.phase == BLE_MESH_DFU_PHASE_IDLE) { + BT_WARN("Wrong state"); + return; + } - (void)bt_mesh_blob_srv_cancel(&srv->blob); + (void)bt_mesh_blob_srv_cancel(&srv->blob); } void bt_mesh_dfu_srv_applied(struct bt_mesh_dfu_srv *srv) { - if (srv->update.phase != BT_MESH_DFU_PHASE_APPLYING) { - LOG_WRN("Wrong state"); - return; - } + if (srv->update.phase != BLE_MESH_DFU_PHASE_APPLYING) { + BT_WARN("Wrong state"); + return; + } - LOG_DBG(""); + BT_DBG(""); - srv->update.phase = BT_MESH_DFU_PHASE_IDLE; - store_state(srv); + srv->update.phase = BLE_MESH_DFU_PHASE_IDLE; + store_state(srv); } bool bt_mesh_dfu_srv_is_busy(const struct bt_mesh_dfu_srv *srv) { - return srv->update.phase != BT_MESH_DFU_PHASE_IDLE && - srv->update.phase != BT_MESH_DFU_PHASE_TRANSFER_ERR && - srv->update.phase != BT_MESH_DFU_PHASE_VERIFY_FAIL; + return srv->update.phase != BLE_MESH_DFU_PHASE_IDLE && + srv->update.phase != BLE_MESH_DFU_PHASE_TRANSFER_ERR && + srv->update.phase != BLE_MESH_DFU_PHASE_VERIFY_FAIL; } uint8_t bt_mesh_dfu_srv_progress(const struct bt_mesh_dfu_srv *srv) { - if (!bt_mesh_dfu_srv_is_busy(srv)) { - return 0U; - } + if (!bt_mesh_dfu_srv_is_busy(srv)) { + return 0U; + } - if (srv->update.phase == BT_MESH_DFU_PHASE_TRANSFER_ACTIVE) { - return bt_mesh_blob_srv_progress(&srv->blob); - } + if (srv->update.phase == BLE_MESH_DFU_PHASE_TRANSFER_ACTIVE) { + return bt_mesh_blob_srv_progress(&srv->blob); + } - return 100U; + return 100U; } +#endif /* CONFIG_BLE_MESH_DFU_SRV */ diff --git a/components/bt/esp_ble_mesh/v1.1/include/mesh_v1.1/dfu/dfd.h b/components/bt/esp_ble_mesh/v1.1/include/mesh_v1.1/dfu/dfd.h index cddde76618..b91ee418e2 100644 --- a/components/bt/esp_ble_mesh/v1.1/include/mesh_v1.1/dfu/dfd.h +++ b/components/bt/esp_ble_mesh/v1.1/include/mesh_v1.1/dfu/dfd.h @@ -1,13 +1,12 @@ /* - * Copyright (c) 2020 Nordic Semiconductor ASA + * SPDX-FileCopyrightText: 2020 Nordic Semiconductor ASA + * SPDX-FileContributor: 2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ -#ifndef ZEPHYR_INCLUDE_BLUETOOTH_MESH_DFD_H__ -#define ZEPHYR_INCLUDE_BLUETOOTH_MESH_DFD_H__ - -#include +#ifndef _BLE_MESH_v11_DFD_H__ +#define _BLE_MESH_v11_DFD_H__ #ifdef __cplusplus extern "C" { @@ -21,97 +20,97 @@ extern "C" { /** Firmware distribution status. */ enum bt_mesh_dfd_status { - /** The message was processed successfully. */ - BT_MESH_DFD_SUCCESS, + /** The message was processed successfully. */ + BT_MESH_DFD_SUCCESS, - /** Insufficient resources on the node. */ - BT_MESH_DFD_ERR_INSUFFICIENT_RESOURCES, + /** Insufficient resources on the node. */ + BT_MESH_DFD_ERR_INSUFFICIENT_RESOURCES, - /** The operation cannot be performed while the Server is in the current - * phase. - */ - BT_MESH_DFD_ERR_WRONG_PHASE, + /** The operation cannot be performed while the Server is in the current + * phase. + */ + BT_MESH_DFD_ERR_WRONG_PHASE, - /** An internal error occurred on the node. */ - BT_MESH_DFD_ERR_INTERNAL, + /** An internal error occurred on the node. */ + BT_MESH_DFD_ERR_INTERNAL, - /** The requested firmware image is not stored on the Distributor. */ - BT_MESH_DFD_ERR_FW_NOT_FOUND, + /** The requested firmware image is not stored on the Distributor. */ + BT_MESH_DFD_ERR_FW_NOT_FOUND, - /** The AppKey identified by the AppKey Index is not known to the node. - */ - BT_MESH_DFD_ERR_INVALID_APPKEY_INDEX, + /** The AppKey identified by the AppKey Index is not known to the node. + */ + BT_MESH_DFD_ERR_INVALID_APPKEY_INDEX, - /** There are no Target nodes in the Distribution Receivers List - * state. - */ - BT_MESH_DFD_ERR_RECEIVERS_LIST_EMPTY, + /** There are no Target nodes in the Distribution Receivers List + * state. + */ + BT_MESH_DFD_ERR_RECEIVERS_LIST_EMPTY, - /** Another firmware image distribution is in progress. */ - BT_MESH_DFD_ERR_BUSY_WITH_DISTRIBUTION, + /** Another firmware image distribution is in progress. */ + BT_MESH_DFD_ERR_BUSY_WITH_DISTRIBUTION, - /** Another upload is in progress. */ - BT_MESH_DFD_ERR_BUSY_WITH_UPLOAD, + /** Another upload is in progress. */ + BT_MESH_DFD_ERR_BUSY_WITH_UPLOAD, - /** The URI scheme name indicated by the Update URI is not supported. */ - BT_MESH_DFD_ERR_URI_NOT_SUPPORTED, + /** The URI scheme name indicated by the Update URI is not supported. */ + BT_MESH_DFD_ERR_URI_NOT_SUPPORTED, - /** The format of the Update URI is invalid. */ - BT_MESH_DFD_ERR_URI_MALFORMED, + /** The format of the Update URI is invalid. */ + BT_MESH_DFD_ERR_URI_MALFORMED, - /** The URI is currently unreachable. */ - BT_MESH_DFD_ERR_URI_UNREACHABLE, + /** The URI is currently unreachable. */ + BT_MESH_DFD_ERR_URI_UNREACHABLE, - /** The Check Firmware OOB procedure did not find any new firmware. */ - BT_MESH_DFD_ERR_NEW_FW_NOT_AVAILABLE, + /** The Check Firmware OOB procedure did not find any new firmware. */ + BT_MESH_DFD_ERR_NEW_FW_NOT_AVAILABLE, - /** The suspension of the Distribute Firmware procedure failed. */ - BT_MESH_DFD_ERR_SUSPEND_FAILED, + /** The suspension of the Distribute Firmware procedure failed. */ + BT_MESH_DFD_ERR_SUSPEND_FAILED, }; /** Firmware distribution phases. */ enum bt_mesh_dfd_phase { - /** No firmware distribution is in progress. */ - BT_MESH_DFD_PHASE_IDLE, + /** No firmware distribution is in progress. */ + BT_MESH_DFD_PHASE_IDLE, - /** Firmware distribution is in progress. */ - BT_MESH_DFD_PHASE_TRANSFER_ACTIVE, + /** Firmware distribution is in progress. */ + BT_MESH_DFD_PHASE_TRANSFER_ACTIVE, - /** The Transfer BLOB procedure has completed successfully. */ - BT_MESH_DFD_PHASE_TRANSFER_SUCCESS, + /** The Transfer BLOB procedure has completed successfully. */ + BT_MESH_DFD_PHASE_TRANSFER_SUCCESS, - /** The Apply Firmware on Target Nodes procedure is being executed. */ - BT_MESH_DFD_PHASE_APPLYING_UPDATE, + /** The Apply Firmware on Target Nodes procedure is being executed. */ + BT_MESH_DFD_PHASE_APPLYING_UPDATE, - /** The Distribute Firmware procedure has completed successfully. */ - BT_MESH_DFD_PHASE_COMPLETED, + /** The Distribute Firmware procedure has completed successfully. */ + BT_MESH_DFD_PHASE_COMPLETED, - /** The Distribute Firmware procedure has failed. */ - BT_MESH_DFD_PHASE_FAILED, + /** The Distribute Firmware procedure has failed. */ + BT_MESH_DFD_PHASE_FAILED, - /** The Cancel Firmware Update procedure is being executed. */ - BT_MESH_DFD_PHASE_CANCELING_UPDATE, + /** The Cancel Firmware Update procedure is being executed. */ + BT_MESH_DFD_PHASE_CANCELING_UPDATE, - /** The Transfer BLOB procedure is suspended. */ - BT_MESH_DFD_PHASE_TRANSFER_SUSPENDED, + /** The Transfer BLOB procedure is suspended. */ + BT_MESH_DFD_PHASE_TRANSFER_SUSPENDED, }; /** Firmware upload phases. */ enum bt_mesh_dfd_upload_phase { - /** No firmware upload is in progress. */ - BT_MESH_DFD_UPLOAD_PHASE_IDLE, + /** No firmware upload is in progress. */ + BT_MESH_DFD_UPLOAD_PHASE_IDLE, - /** The Store Firmware procedure is being executed. */ - BT_MESH_DFD_UPLOAD_PHASE_TRANSFER_ACTIVE, + /** The Store Firmware procedure is being executed. */ + BT_MESH_DFD_UPLOAD_PHASE_TRANSFER_ACTIVE, - /** The Store Firmware procedure or Store Firmware OOB procedure failed. - */ - BT_MESH_DFD_UPLOAD_PHASE_TRANSFER_ERROR, + /** The Store Firmware procedure or Store Firmware OOB procedure failed. + */ + BT_MESH_DFD_UPLOAD_PHASE_TRANSFER_ERROR, - /** The Store Firmware procedure or the Store Firmware OOB procedure - * completed successfully. - */ - BT_MESH_DFD_UPLOAD_PHASE_TRANSFER_SUCCESS, + /** The Store Firmware procedure or the Store Firmware OOB procedure + * completed successfully. + */ + BT_MESH_DFD_UPLOAD_PHASE_TRANSFER_SUCCESS, }; /** @} */ @@ -120,4 +119,4 @@ enum bt_mesh_dfd_upload_phase { } #endif -#endif /* ZEPHYR_INCLUDE_BLUETOOTH_MESH_DFD_H__ */ +#endif /* _BLE_MESH_v11_DFD_H__ */ diff --git a/components/bt/esp_ble_mesh/v1.1/include/mesh_v1.1/dfu/dfd_srv.h b/components/bt/esp_ble_mesh/v1.1/include/mesh_v1.1/dfu/dfd_srv.h index 2abfbf0f90..fd0223af1c 100644 --- a/components/bt/esp_ble_mesh/v1.1/include/mesh_v1.1/dfu/dfd_srv.h +++ b/components/bt/esp_ble_mesh/v1.1/include/mesh_v1.1/dfu/dfd_srv.h @@ -1,5 +1,6 @@ /* - * Copyright (c) 2020 Nordic Semiconductor ASA + * SPDX-FileCopyrightText: 2020 Nordic Semiconductor ASA + * SPDX-FileContributor: 2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -12,34 +13,34 @@ * @brief API for the Firmware Distribution Server model */ -#ifndef ZEPHYR_INCLUDE_BLUETOOTH_MESH_DFD_SRV_H__ -#define ZEPHYR_INCLUDE_BLUETOOTH_MESH_DFD_SRV_H__ +#ifndef _BLE_MESH_v11_DFD_SRV_H__ +#define _BLE_MESH_v11_DFD_SRV_H__ -#include -#include -#include -#include -#include +#include "mesh/access.h" +#include "mesh_v1.1/dfu/dfd.h" +#include "mesh_v1.1/dfu/dfu_cli.h" +#include "mesh_v1.1/mbt/blob_cli.h" +#include "mesh_v1.1/mbt/blob_srv.h" #ifdef __cplusplus extern "C" { #endif -#ifndef CONFIG_BT_MESH_DFD_SRV_TARGETS_MAX -#define CONFIG_BT_MESH_DFD_SRV_TARGETS_MAX 0 +#ifndef CONFIG_BLE_MESH_DFD_SRV_TARGETS_MAX +#define CONFIG_BLE_MESH_DFD_SRV_TARGETS_MAX 0 #endif -#ifndef CONFIG_BT_MESH_DFD_SRV_SLOT_MAX_SIZE -#define CONFIG_BT_MESH_DFD_SRV_SLOT_MAX_SIZE 0 +#ifndef CONFIG_BLE_MESH_DFD_SRV_SLOT_MAX_SIZE +#define CONFIG_BLE_MESH_DFD_SRV_SLOT_MAX_SIZE 0 #endif -#ifndef CONFIG_BT_MESH_DFD_SRV_SLOT_SPACE -#define CONFIG_BT_MESH_DFD_SRV_SLOT_SPACE 0 +#ifndef CONFIG_BLE_MESH_DFD_SRV_SLOT_SPACE +#define CONFIG_BLE_MESH_DFD_SRV_SLOT_SPACE 0 #endif struct bt_mesh_dfd_srv; -#ifdef CONFIG_BT_MESH_DFD_SRV_OOB_UPLOAD +#ifdef CONFIG_BLE_MESH_DFD_SRV_OOB_UPLOAD /** * * @brief Initialization parameters for the @ref bt_mesh_dfd_srv with OOB @@ -51,19 +52,19 @@ struct bt_mesh_dfd_srv; * Bluetooth SIG Assigned Numbers document. * @param[in] _oob_schemes_count Number of schemes in @c _oob_schemes. */ -#define BT_MESH_DFD_SRV_OOB_INIT(_cb, _oob_schemes, _oob_schemes_count) \ - { \ - .cb = _cb, \ - .dfu = BT_MESH_DFU_CLI_INIT(&_bt_mesh_dfd_srv_dfu_cb), \ - .upload = { \ - .blob = { .cb = &_bt_mesh_dfd_srv_blob_cb }, \ - }, \ - .oob_schemes = { \ - .schemes = _oob_schemes, \ - .count = _oob_schemes_count, \ - }, \ - } -#endif /* CONFIG_BT_MESH_DFD_SRV_OOB_UPLOAD */ +#define BT_MESH_DFD_SRV_OOB_INIT(_cb, _oob_schemes, _oob_schemes_count) \ + { \ + .cb = _cb, \ + .dfu = BLE_MESH_DFU_CLI_INIT(&_bt_mesh_dfd_srv_dfu_cb), \ + .upload = { \ + .blob = { .cb = &_bt_mesh_dfd_srv_blob_cb }, \ + }, \ + .oob_schemes = { \ + .schemes = _oob_schemes, \ + .count = _oob_schemes_count, \ + }, \ + } +#endif /* CONFIG_BLE_MESH_DFD_SRV_OOB_UPLOAD */ /** * @@ -71,14 +72,14 @@ struct bt_mesh_dfd_srv; * * @param[in] _cb Pointer to a @ref bt_mesh_dfd_srv_cb instance. */ -#define BT_MESH_DFD_SRV_INIT(_cb) \ - { \ - .cb = _cb, \ - .dfu = BT_MESH_DFU_CLI_INIT(&_bt_mesh_dfd_srv_dfu_cb), \ - .upload = { \ - .blob = { .cb = &_bt_mesh_dfd_srv_blob_cb }, \ - }, \ - } +#define BT_MESH_DFD_SRV_INIT(_cb) \ + { \ + .cb = _cb, \ + .dfu = BLE_MESH_DFU_CLI_INIT(&_bt_mesh_dfd_srv_dfu_cb), \ + .upload = { \ + .blob = { .cb = &_bt_mesh_dfd_srv_blob_cb }, \ + }, \ + } /** * @@ -86,168 +87,168 @@ struct bt_mesh_dfd_srv; * * @param _srv Pointer to a @ref bt_mesh_dfd_srv instance. */ -#define BT_MESH_MODEL_DFD_SRV(_srv) \ - BT_MESH_MODEL_DFU_CLI(&(_srv)->dfu), \ - BT_MESH_MODEL_BLOB_SRV(&(_srv)->upload.blob), \ - BT_MESH_MODEL_CB(BT_MESH_MODEL_ID_DFD_SRV, _bt_mesh_dfd_srv_op, NULL, \ - _srv, &_bt_mesh_dfd_srv_cb) +#define BT_MESH_MODEL_DFD_SRV(_srv) \ + BT_MESH_MODEL_DFU_CLI(&(_srv)->dfu), \ + BT_MESH_MODEL_BLOB_SRV(&(_srv)->upload.blob), \ + BLE_MESH_MODEL_CB(BLE_MESH_MODEL_ID_DFD_SRV, _bt_mesh_dfd_srv_op, NULL, \ + _srv, &_bt_mesh_dfd_srv_cb) /** Firmware Distribution Server callbacks: */ struct bt_mesh_dfd_srv_cb { - /** @brief Slot receive callback. - * - * Called at the start of an upload procedure. The callback must fill - * @c io with a pointer to a writable BLOB stream for the Firmware Distribution - * Server to write the firmware image to. - * - * @param srv Firmware Distribution Server model instance. - * @param slot DFU image slot being received. - * @param io BLOB stream response pointer. - * - * @return 0 on success, or (negative) error code otherwise. - */ - int (*recv)(struct bt_mesh_dfd_srv *srv, - const struct bt_mesh_dfu_slot *slot, - const struct bt_mesh_blob_io **io); + /** @brief Slot receive callback. + * + * Called at the start of an upload procedure. The callback must fill + * @c io with a pointer to a writable BLOB stream for the Firmware Distribution + * Server to write the firmware image to. + * + * @param srv Firmware Distribution Server model instance. + * @param slot DFU image slot being received. + * @param io BLOB stream response pointer. + * + * @return 0 on success, or (negative) error code otherwise. + */ + int (*recv)(struct bt_mesh_dfd_srv *srv, + const struct bt_mesh_dfu_slot *slot, + const struct bt_mesh_blob_io **io); -#ifdef CONFIG_BT_MESH_DFD_SRV_OOB_UPLOAD - /** @brief Firmware upload OOB start callback. - * - * Called at the start of an OOB firmware upload. The application must - * start a firmware check using an OOB mechanism, and then call - * @ref bt_mesh_dfd_srv_oob_check_complete. Depending on the return - * value of this function, the application must then start storing the - * firmware image using an OOB mechanism, and call - * @ref bt_mesh_dfd_srv_oob_store_complete. This callback is mandatory - * to support OOB uploads. - * - * @param srv Firmware Distribution Server model instance. - * @param slot Slot to be used for the upload. - * @param uri Pointer to buffer containing the URI used to - * check for new firmware. - * @param uri_len Length of the URI buffer. - * @param fwid Pointer to buffer containing the current - * firmware ID to be used when checking for - * availability of new firmware. - * @param fwid_len Length of the current firmware ID. Must be set - * to the length of the new firmware ID if it is - * available, or to 0 if new firmware is not - * available. - * - * @return BT_MESH_DFD_SUCCESS on success, or error code otherwise. - */ - int (*start_oob_upload)(struct bt_mesh_dfd_srv *srv, - const struct bt_mesh_dfu_slot *slot, - const char *uri, uint8_t uri_len, - const uint8_t *fwid, uint16_t fwid_len); +#ifdef CONFIG_BLE_MESH_DFD_SRV_OOB_UPLOAD + /** @brief Firmware upload OOB start callback. + * + * Called at the start of an OOB firmware upload. The application must + * start a firmware check using an OOB mechanism, and then call + * @ref bt_mesh_dfd_srv_oob_check_complete. Depending on the return + * value of this function, the application must then start storing the + * firmware image using an OOB mechanism, and call + * @ref bt_mesh_dfd_srv_oob_store_complete. This callback is mandatory + * to support OOB uploads. + * + * @param srv Firmware Distribution Server model instance. + * @param slot Slot to be used for the upload. + * @param uri Pointer to buffer containing the URI used to + * check for new firmware. + * @param uri_len Length of the URI buffer. + * @param fwid Pointer to buffer containing the current + * firmware ID to be used when checking for + * availability of new firmware. + * @param fwid_len Length of the current firmware ID. Must be set + * to the length of the new firmware ID if it is + * available, or to 0 if new firmware is not + * available. + * + * @return BT_MESH_DFD_SUCCESS on success, or error code otherwise. + */ + int (*start_oob_upload)(struct bt_mesh_dfd_srv *srv, + const struct bt_mesh_dfu_slot *slot, + const char *uri, uint8_t uri_len, + const uint8_t *fwid, uint16_t fwid_len); - /** @brief Cancel store OOB callback - * - * Called when an OOB store is cancelled. The application must stop - * any ongoing OOB image transfer. This callback is mandatory to - * support OOB uploads. - * - * @param srv Firmware Distribution Server model instance. - * @param slot DFU image slot to cancel - */ - void (*cancel_oob_upload)(struct bt_mesh_dfd_srv *srv, - const struct bt_mesh_dfu_slot *slot); + /** @brief Cancel store OOB callback + * + * Called when an OOB store is cancelled. The application must stop + * any ongoing OOB image transfer. This callback is mandatory to + * support OOB uploads. + * + * @param srv Firmware Distribution Server model instance. + * @param slot DFU image slot to cancel + */ + void (*cancel_oob_upload)(struct bt_mesh_dfd_srv *srv, + const struct bt_mesh_dfu_slot *slot); - /** @brief Get the progress of an ongoing OOB store - * - * Called by the Firmware Distribution Server model when it needs to - * get the current progress of an ongoing OOB store from the - * application. This callback is mandatory to support OOB uploads. - * - * @param srv Firmware Distribution Server model instance. - * @param slot DFU image slot to get progress for. - * - * @return The current progress of the ongoing OOB store, in percent. - */ - uint8_t (*oob_progress_get)(struct bt_mesh_dfd_srv *srv, - const struct bt_mesh_dfu_slot *slot); -#endif /* CONFIG_BT_MESH_DFD_SRV_OOB_UPLOAD */ + /** @brief Get the progress of an ongoing OOB store + * + * Called by the Firmware Distribution Server model when it needs to + * get the current progress of an ongoing OOB store from the + * application. This callback is mandatory to support OOB uploads. + * + * @param srv Firmware Distribution Server model instance. + * @param slot DFU image slot to get progress for. + * + * @return The current progress of the ongoing OOB store, in percent. + */ + uint8_t (*oob_progress_get)(struct bt_mesh_dfd_srv *srv, + const struct bt_mesh_dfu_slot *slot); +#endif /* CONFIG_BLE_MESH_DFD_SRV_OOB_UPLOAD */ - /** @brief Slot delete callback. - * - * Called when the Firmware Distribution Server is about to delete a DFU image slot. - * All allocated data associated with the firmware slot should be - * deleted. - * - * @param srv Firmware Update Server instance. - * @param slot DFU image slot being deleted. - */ - void (*del)(struct bt_mesh_dfd_srv *srv, - const struct bt_mesh_dfu_slot *slot); + /** @brief Slot delete callback. + * + * Called when the Firmware Distribution Server is about to delete a DFU image slot. + * All allocated data associated with the firmware slot should be + * deleted. + * + * @param srv Firmware Update Server instance. + * @param slot DFU image slot being deleted. + */ + void (*del)(struct bt_mesh_dfd_srv *srv, + const struct bt_mesh_dfu_slot *slot); - /** @brief Slot send callback. - * - * Called at the start of a distribution procedure. The callback must - * fill @c io with a pointer to a readable BLOB stream for the Firmware - * Distribution Server to read the firmware image from. - * - * @param srv Firmware Distribution Server model instance. - * @param slot DFU image slot being sent. - * @param io BLOB stream response pointer. - * - * @return 0 on success, or (negative) error code otherwise. - */ - int (*send)(struct bt_mesh_dfd_srv *srv, - const struct bt_mesh_dfu_slot *slot, - const struct bt_mesh_blob_io **io); + /** @brief Slot send callback. + * + * Called at the start of a distribution procedure. The callback must + * fill @c io with a pointer to a readable BLOB stream for the Firmware + * Distribution Server to read the firmware image from. + * + * @param srv Firmware Distribution Server model instance. + * @param slot DFU image slot being sent. + * @param io BLOB stream response pointer. + * + * @return 0 on success, or (negative) error code otherwise. + */ + int (*send)(struct bt_mesh_dfd_srv *srv, + const struct bt_mesh_dfu_slot *slot, + const struct bt_mesh_blob_io **io); - /** @brief Phase change callback (Optional). - * - * Called whenever the phase of the Firmware Distribution Server changes. - * - * @param srv Firmware Distribution Server model instance. - * @param phase New Firmware Distribution phase. - */ - void (*phase)(struct bt_mesh_dfd_srv *srv, enum bt_mesh_dfd_phase phase); + /** @brief Phase change callback (Optional). + * + * Called whenever the phase of the Firmware Distribution Server changes. + * + * @param srv Firmware Distribution Server model instance. + * @param phase New Firmware Distribution phase. + */ + void (*phase)(struct bt_mesh_dfd_srv *srv, enum bt_mesh_dfd_phase phase); }; /** Firmware Distribution Server instance. */ struct bt_mesh_dfd_srv { - const struct bt_mesh_dfd_srv_cb *cb; - const struct bt_mesh_model *mod; - struct bt_mesh_dfu_cli dfu; - struct bt_mesh_dfu_target targets[CONFIG_BT_MESH_DFD_SRV_TARGETS_MAX]; - struct bt_mesh_blob_target_pull pull_ctxs[CONFIG_BT_MESH_DFD_SRV_TARGETS_MAX]; - const struct bt_mesh_blob_io *io; - uint16_t target_cnt; - uint16_t slot_idx; - bool apply; - enum bt_mesh_dfd_phase phase; - struct bt_mesh_blob_cli_inputs inputs; + const struct bt_mesh_dfd_srv_cb *cb; + const struct bt_mesh_model *mod; + struct bt_mesh_dfu_cli dfu; + struct bt_mesh_dfu_target targets[CONFIG_BLE_MESH_DFD_SRV_TARGETS_MAX]; + struct bt_mesh_blob_target_pull pull_ctxs[CONFIG_BLE_MESH_DFD_SRV_TARGETS_MAX]; + const struct bt_mesh_blob_io *io; + uint16_t target_cnt; + uint16_t slot_idx; + bool apply; + enum bt_mesh_dfd_phase phase; + struct bt_mesh_blob_cli_inputs inputs; - struct { - enum bt_mesh_dfd_upload_phase phase; - struct bt_mesh_dfu_slot *slot; - const struct flash_area *area; - struct bt_mesh_blob_srv blob; -#ifdef CONFIG_BT_MESH_DFD_SRV_OOB_UPLOAD - bool is_oob; - bool is_pending_oob_check; - struct { - uint8_t uri_len; - uint8_t uri[CONFIG_BT_MESH_DFU_URI_MAXLEN]; - uint16_t current_fwid_len; - uint8_t current_fwid[CONFIG_BT_MESH_DFU_FWID_MAXLEN]; - struct bt_mesh_msg_ctx ctx; - } oob; -#endif /* CONFIG_BT_MESH_DFD_SRV_OOB_UPLOAD */ - } upload; + struct { + enum bt_mesh_dfd_upload_phase phase; + struct bt_mesh_dfu_slot *slot; + const struct flash_area *area; + struct bt_mesh_blob_srv blob; +#ifdef CONFIG_BLE_MESH_DFD_SRV_OOB_UPLOAD + bool is_oob; + bool is_pending_oob_check; + struct { + uint8_t uri_len; + char uri[CONFIG_BLE_MESH_DFU_URI_MAXLEN]; + uint16_t current_fwid_len; + uint8_t current_fwid[CONFIG_BLE_MESH_DFU_FWID_MAXLEN]; + struct bt_mesh_msg_ctx ctx; + } oob; +#endif /* CONFIG_BLE_MESH_DFD_SRV_OOB_UPLOAD */ + } upload; -#ifdef CONFIG_BT_MESH_DFD_SRV_OOB_UPLOAD - struct { - const uint8_t *schemes; - const uint8_t count; - } oob_schemes; -#endif /* CONFIG_BT_MESH_DFD_SRV_OOB_UPLOAD */ +#ifdef CONFIG_BLE_MESH_DFD_SRV_OOB_UPLOAD + struct { + const uint8_t *schemes; + const uint8_t count; + } oob_schemes; +#endif /* CONFIG_BLE_MESH_DFD_SRV_OOB_UPLOAD */ }; -#ifdef CONFIG_BT_MESH_DFD_SRV_OOB_UPLOAD +#ifdef CONFIG_BLE_MESH_DFD_SRV_OOB_UPLOAD /** @brief Call when an OOB check has completed or failed * * This should be called by the application after an OOB check started by the @c start_oob_upload @@ -274,8 +275,8 @@ struct bt_mesh_dfd_srv { * @return 0 on success, (negative) error code otherwise. */ int bt_mesh_dfd_srv_oob_check_complete(struct bt_mesh_dfd_srv *srv, - const struct bt_mesh_dfu_slot *slot, int status, - uint8_t *fwid, size_t fwid_len); + const struct bt_mesh_dfu_slot *slot, int status, + uint8_t *fwid, size_t fwid_len); /** @brief Call when an OOB store has completed or failed * @@ -293,9 +294,9 @@ int bt_mesh_dfd_srv_oob_check_complete(struct bt_mesh_dfd_srv *srv, * @return 0 on success, (negative) error code otherwise. */ int bt_mesh_dfd_srv_oob_store_complete(struct bt_mesh_dfd_srv *srv, - const struct bt_mesh_dfu_slot *slot, bool success, - size_t size, const uint8_t *metadata, size_t metadata_len); -#endif /* CONFIG_BT_MESH_DFD_SRV_OOB_UPLOAD */ + const struct bt_mesh_dfu_slot *slot, bool success, + size_t size, const uint8_t *metadata, size_t metadata_len); +#endif /* CONFIG_BLE_MESH_DFD_SRV_OOB_UPLOAD */ /** @cond INTERNAL_HIDDEN */ extern const struct bt_mesh_model_op _bt_mesh_dfd_srv_op[]; @@ -308,6 +309,6 @@ extern const struct bt_mesh_blob_srv_cb _bt_mesh_dfd_srv_blob_cb; } #endif -#endif /* ZEPHYR_INCLUDE_BLUETOOTH_MESH_DFD_SRV_H__ */ +#endif /* _BLE_MESH_v11_DFD_SRV_H__ */ /** @} */ diff --git a/components/bt/esp_ble_mesh/v1.1/include/mesh_v1.1/dfu/dfu.h b/components/bt/esp_ble_mesh/v1.1/include/mesh_v1.1/dfu/dfu.h index 4745355f03..073aae901b 100644 --- a/components/bt/esp_ble_mesh/v1.1/include/mesh_v1.1/dfu/dfu.h +++ b/components/bt/esp_ble_mesh/v1.1/include/mesh_v1.1/dfu/dfu.h @@ -1,16 +1,19 @@ /* - * Copyright (c) 2020 Nordic Semiconductor ASA + * SPDX-FileCopyrightText: 2020 Nordic Semiconductor ASA + * SPDX-FileContributor: 2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ -#ifndef ZEPHYR_INCLUDE_BLUETOOTH_MESH_DFU_H__ -#define ZEPHYR_INCLUDE_BLUETOOTH_MESH_DFU_H__ +#ifndef _BLE_MESH_v11_DFU_H__ +#define _BLE_MESH_v11_DFU_H__ #include -#include -#include +#include "mesh/types.h" +#include "mesh/utils.h" + +#include "mesh_v1.1/mbt/blob.h" #ifdef __cplusplus extern "C" { @@ -22,115 +25,114 @@ extern "C" { * @{ */ -#ifndef CONFIG_BT_MESH_DFU_FWID_MAXLEN -#define CONFIG_BT_MESH_DFU_FWID_MAXLEN 0 +#ifndef CONFIG_BLE_MESH_DFU_FWID_MAXLEN +#define CONFIG_BLE_MESH_DFU_FWID_MAXLEN 0 #endif -#ifndef CONFIG_BT_MESH_DFU_METADATA_MAXLEN -#define CONFIG_BT_MESH_DFU_METADATA_MAXLEN 0 +#ifndef CONFIG_BLE_MESH_DFU_METADATA_MAXLEN +#define CONFIG_BLE_MESH_DFU_METADATA_MAXLEN 0 #endif -#ifndef CONFIG_BT_MESH_DFU_URI_MAXLEN -#define CONFIG_BT_MESH_DFU_URI_MAXLEN 0 +#ifndef CONFIG_BLE_MESH_DFU_URI_MAXLEN +#define CONFIG_BLE_MESH_DFU_URI_MAXLEN 0 #endif -#ifndef CONFIG_BT_MESH_DFU_SLOT_CNT -#define CONFIG_BT_MESH_DFU_SLOT_CNT 0 +#ifndef CONFIG_BLE_MESH_DFU_SLOT_CNT +#define CONFIG_BLE_MESH_DFU_SLOT_CNT 0 #endif /** DFU transfer phase. */ enum bt_mesh_dfu_phase { - /** Ready to start a Receive Firmware procedure. */ - BT_MESH_DFU_PHASE_IDLE, + /** Ready to start a Receive Firmware procedure. */ + BLE_MESH_DFU_PHASE_IDLE, - /** The Transfer BLOB procedure failed. */ - BT_MESH_DFU_PHASE_TRANSFER_ERR, + /** The Transfer BLOB procedure failed. */ + BLE_MESH_DFU_PHASE_TRANSFER_ERR, - /** The Receive Firmware procedure is being executed. */ - BT_MESH_DFU_PHASE_TRANSFER_ACTIVE, + /** The Receive Firmware procedure is being executed. */ + BLE_MESH_DFU_PHASE_TRANSFER_ACTIVE, - /** The Verify Firmware procedure is being executed. */ - BT_MESH_DFU_PHASE_VERIFY, + /** The Verify Firmware procedure is being executed. */ + BLE_MESH_DFU_PHASE_VERIFY, - /** The Verify Firmware procedure completed successfully. */ - BT_MESH_DFU_PHASE_VERIFY_OK, + /** The Verify Firmware procedure completed successfully. */ + BLE_MESH_DFU_PHASE_VERIFY_OK, - /** The Verify Firmware procedure failed. */ - BT_MESH_DFU_PHASE_VERIFY_FAIL, + /** The Verify Firmware procedure failed. */ + BLE_MESH_DFU_PHASE_VERIFY_FAIL, - /** The Apply New Firmware procedure is being executed. */ - BT_MESH_DFU_PHASE_APPLYING, + /** The Apply New Firmware procedure is being executed. */ + BLE_MESH_DFU_PHASE_APPLYING, - /** Firmware transfer has been canceled. */ - BT_MESH_DFU_PHASE_TRANSFER_CANCELED, + /** Firmware transfer has been canceled. */ + BLE_MESH_DFU_PHASE_TRANSFER_CANCELED, - /** Firmware applying succeeded. */ - BT_MESH_DFU_PHASE_APPLY_SUCCESS, + /** Firmware applying succeeded. */ + BLE_MESH_DFU_PHASE_APPLY_SUCCESS, - /** Firmware applying failed. */ - BT_MESH_DFU_PHASE_APPLY_FAIL, + /** Firmware applying failed. */ + BLE_MESH_DFU_PHASE_APPLY_FAIL, - /** Phase of a node was not yet retrieved. */ - BT_MESH_DFU_PHASE_UNKNOWN, + /** Phase of a node was not yet retrieved. */ + BLE_MESH_DFU_PHASE_UNKNOWN, }; - /** DFU status. */ enum bt_mesh_dfu_status { - /** The message was processed successfully. */ - BT_MESH_DFU_SUCCESS, + /** The message was processed successfully. */ + BLE_MESH_DFU_SUCCESS, - /** Insufficient resources on the node */ - BT_MESH_DFU_ERR_RESOURCES, + /** Insufficient resources on the node */ + BLE_MESH_DFU_ERR_RESOURCES, - /** The operation cannot be performed while the Server is in the current - * phase. - */ - BT_MESH_DFU_ERR_WRONG_PHASE, + /** The operation cannot be performed while the Server is in the current + * phase. + */ + BLE_MESH_DFU_ERR_WRONG_PHASE, - /** An internal error occurred on the node. */ - BT_MESH_DFU_ERR_INTERNAL, + /** An internal error occurred on the node. */ + BLE_MESH_DFU_ERR_INTERNAL, - /** The message contains a firmware index value that is not expected. */ - BT_MESH_DFU_ERR_FW_IDX, + /** The message contains a firmware index value that is not expected. */ + BLE_MESH_DFU_ERR_FW_IDX, - /** The metadata check failed. */ - BT_MESH_DFU_ERR_METADATA, + /** The metadata check failed. */ + BLE_MESH_DFU_ERR_METADATA, - /** The Server cannot start a firmware update. */ - BT_MESH_DFU_ERR_TEMPORARILY_UNAVAILABLE, + /** The Server cannot start a firmware update. */ + BLE_MESH_DFU_ERR_TEMPORARILY_UNAVAILABLE, - /** Another BLOB transfer is in progress. */ - BT_MESH_DFU_ERR_BLOB_XFER_BUSY, + /** Another BLOB transfer is in progress. */ + BLE_MESH_DFU_ERR_BLOB_XFER_BUSY, }; /** Expected effect of a DFU transfer. */ enum bt_mesh_dfu_effect { - /** No changes to node Composition Data. */ - BT_MESH_DFU_EFFECT_NONE, + /** No changes to node Composition Data. */ + BLE_MESH_DFU_EFFECT_NONE, - /** Node Composition Data changed and the node does not support remote - * provisioning. - */ - BT_MESH_DFU_EFFECT_COMP_CHANGE_NO_RPR, + /** Node Composition Data changed and the node does not support remote + * provisioning. + */ + BLE_MESH_DFU_EFFECT_COMP_CHANGE_NO_RPR, - /** Node Composition Data changed, and remote provisioning is supported. - * The node supports remote provisioning and Composition Data Page - * 0x80. Page 0x80 contains different Composition Data than Page 0x0. - */ - BT_MESH_DFU_EFFECT_COMP_CHANGE, + /** Node Composition Data changed, and remote provisioning is supported. + * The node supports remote provisioning and Composition Data Page + * 0x80. Page 0x80 contains different Composition Data than Page 0x0. + */ + BLE_MESH_DFU_EFFECT_COMP_CHANGE, - /** Node will be unprovisioned after the update. */ - BT_MESH_DFU_EFFECT_UNPROV, + /** Node will be unprovisioned after the update. */ + BLE_MESH_DFU_EFFECT_UNPROV, }; /** Action for DFU iteration callbacks. */ enum bt_mesh_dfu_iter { - /** Stop iterating. */ - BT_MESH_DFU_ITER_STOP, + /** Stop iterating. */ + BLE_MESH_DFU_ITER_STOP, - /** Continue iterating. */ - BT_MESH_DFU_ITER_CONTINUE, + /** Continue iterating. */ + BLE_MESH_DFU_ITER_CONTINUE, }; /** DFU image instance. @@ -138,28 +140,28 @@ enum bt_mesh_dfu_iter { * Each DFU image represents a single updatable firmware image. */ struct bt_mesh_dfu_img { - /** Firmware ID. */ - const void *fwid; + /** Firmware ID. */ + const void *fwid; - /** Length of the firmware ID. */ - size_t fwid_len; + /** Length of the firmware ID. */ + size_t fwid_len; - /** Update URI, or NULL. */ - const char *uri; + /** Update URI, or NULL. */ + const char *uri; }; /** DFU image slot for DFU distribution. */ struct bt_mesh_dfu_slot { - /** Size of the firmware in bytes. */ - size_t size; - /** Length of the firmware ID. */ - size_t fwid_len; - /** Length of the metadata. */ - size_t metadata_len; - /** Firmware ID. */ - uint8_t fwid[CONFIG_BT_MESH_DFU_FWID_MAXLEN]; - /** Metadata. */ - uint8_t metadata[CONFIG_BT_MESH_DFU_METADATA_MAXLEN]; + /** Size of the firmware in bytes. */ + size_t size; + /** Length of the firmware ID. */ + size_t fwid_len; + /** Length of the metadata. */ + size_t metadata_len; + /** Firmware ID. */ + uint8_t fwid[CONFIG_BLE_MESH_DFU_FWID_MAXLEN]; + /** Metadata. */ + uint8_t metadata[CONFIG_BLE_MESH_DFU_METADATA_MAXLEN]; }; /** @} */ @@ -168,4 +170,4 @@ struct bt_mesh_dfu_slot { } #endif -#endif /* ZEPHYR_INCLUDE_BLUETOOTH_MESH_DFU_H__ */ +#endif /* _BLE_MESH_v11_DFU_H__ */ 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 ad8881ebc2..531e359994 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 @@ -1,5 +1,6 @@ /* - * Copyright (c) 2020 Nordic Semiconductor ASA + * SPDX-FileCopyrightText: 2020 Nordic Semiconductor ASA + * SPDX-FileContributor: 2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -12,12 +13,14 @@ * @brief API for the Bluetooth Mesh Firmware Update Client model */ -#ifndef ZEPHYR_INCLUDE_BLUETOOTH_MESH_DFU_CLI_H__ -#define ZEPHYR_INCLUDE_BLUETOOTH_MESH_DFU_CLI_H__ +#ifndef _BLE_MESH_v11_DFU_CLI_H__ +#define _BLE_MESH_v11_DFU_CLI_H__ -#include -#include -#include +#include "freertos/semphr.h" + +#include "mesh/access.h" +#include "mesh_v1.1/mbt/blob_cli.h" +#include "mesh_v1.1/dfu/dfu.h" #ifdef __cplusplus extern "C" { @@ -33,11 +36,11 @@ struct bt_mesh_dfu_cli; * * @param _handlers Handler callback structure. */ -#define BT_MESH_DFU_CLI_INIT(_handlers) \ - { \ - .cb = _handlers, \ - .blob = { .cb = &_bt_mesh_dfu_cli_blob_handlers }, \ - } +#define BLE_MESH_DFU_CLI_INIT(_handlers) \ + { \ + .cb = _handlers, \ + .blob = { .cb = &_bt_mesh_dfu_cli_blob_handlers }, \ + } /** * @@ -45,64 +48,67 @@ struct bt_mesh_dfu_cli; * * @param _cli Pointer to a @ref bt_mesh_dfu_cli instance. */ -#define BT_MESH_MODEL_DFU_CLI(_cli) \ - BT_MESH_MODEL_BLOB_CLI(&(_cli)->blob), \ - BT_MESH_MODEL_CB(BT_MESH_MODEL_ID_DFU_CLI, _bt_mesh_dfu_cli_op, NULL, \ - _cli, &_bt_mesh_dfu_cli_cb) +#define BT_MESH_MODEL_DFU_CLI(_cli) \ + BT_MESH_MODEL_BLOB_CLI(&(_cli)->blob), \ + BT_MESH_MODEL_CB(BLE_MESH_MODEL_ID_DFU_CLI, _bt_mesh_dfu_cli_op, NULL, \ + _cli, &_bt_mesh_dfu_cli_cb) /** DFU Target node. */ struct bt_mesh_dfu_target { - /** BLOB Target node */ - struct bt_mesh_blob_target blob; - /** Image index on the Target node */ - uint8_t img_idx; - /** Expected DFU effect, see @ref bt_mesh_dfu_effect. */ - uint8_t effect; - /** Current DFU status, see @ref bt_mesh_dfu_status. */ - uint8_t status; - /** Current DFU phase, see @ref bt_mesh_dfu_phase. */ - uint8_t phase; + /** BLOB Target node */ + struct bt_mesh_blob_target blob; + /** Image index on the Target node */ + uint8_t img_idx; + /** Expected DFU effect, see @ref bt_mesh_dfu_effect. */ + uint8_t effect; + /** Current DFU status, see @ref bt_mesh_dfu_status. */ + uint8_t status; + /** Current DFU phase, see @ref bt_mesh_dfu_phase. */ + uint8_t phase; }; /** Metadata status response. */ struct bt_mesh_dfu_metadata_status { - /** Image index. */ - uint8_t idx; - /** Status code. */ - enum bt_mesh_dfu_status status; - /** Effect of transfer. */ - enum bt_mesh_dfu_effect effect; + /** Image index. */ + uint8_t idx; + /** Status code. */ + enum bt_mesh_dfu_status status; + /** Effect of transfer. */ + enum bt_mesh_dfu_effect effect; }; -/** DFU Target node status parameters. */ +/** DFU Target node status parameters. + * The structure should be same as + * @ref esp_ble_mesh_device_firmware_update_status_t +*/ struct bt_mesh_dfu_target_status { - /** Status of the previous operation. */ - enum bt_mesh_dfu_status status; - /** Phase of the current DFU transfer. */ - enum bt_mesh_dfu_phase phase; - /** The effect the update will have on the Target device's state. */ - enum bt_mesh_dfu_effect effect; - /** BLOB ID used in the transfer. */ - uint64_t blob_id; - /** Image index to transfer. */ - uint8_t img_idx; - /** TTL used in the transfer. */ - uint8_t ttl; + /** Status of the previous operation. */ + enum bt_mesh_dfu_status status; + /** Phase of the current DFU transfer. */ + enum bt_mesh_dfu_phase phase; + /** The effect the update will have on the Target device's state. */ + enum bt_mesh_dfu_effect effect; + /** BLOB ID used in the transfer. */ + uint64_t blob_id; + /** Image index to transfer. */ + uint8_t img_idx; + /** TTL used in the transfer. */ + uint8_t ttl; - /** Additional response time for the Target nodes, in 10-second increments. - * - * The extra time can be used to give the Target nodes more time to respond - * to messages from the Client. The actual timeout will be calculated - * according to the following formula: - * - * @verbatim - * timeout = 20 seconds + (10 seconds * timeout_base) + (100 ms * TTL) - * @endverbatim - * - * If a Target node fails to respond to a message from the Client within the - * configured transfer timeout, the Target node is dropped. - */ - uint16_t timeout_base; + /** Additional response time for the Target nodes, in 10-second increments. + * + * The extra time can be used to give the Target nodes more time to respond + * to messages from the Client. The actual timeout will be calculated + * according to the following formula: + * + * @verbatim + * timeout = 20 seconds + (10 seconds * timeout_base) + (100 ms * TTL) + * @endverbatim + * + * If a Target node fails to respond to a message from the Client within the + * configured transfer timeout, the Target node is dropped. + */ + uint16_t timeout_base; }; /** @brief DFU image callback. @@ -117,120 +123,140 @@ struct bt_mesh_dfu_target_status { * @param img Image information for the given image index. * @param cb_data Callback data. * - * @retval BT_MESH_DFU_ITER_STOP Stop iterating through the image list and + * @retval BLE_MESH_DFU_ITER_STOP Stop iterating through the image list and * return from @ref bt_mesh_dfu_cli_imgs_get. - * @retval BT_MESH_DFU_ITER_CONTINUE Continue iterating through the image list + * @retval BLE_MESH_DFU_ITER_CONTINUE Continue iterating through the image list * if any images remain. */ -typedef enum bt_mesh_dfu_iter (*bt_mesh_dfu_img_cb_t)( - struct bt_mesh_dfu_cli *cli, struct bt_mesh_msg_ctx *ctx, uint8_t idx, - uint8_t total, const struct bt_mesh_dfu_img *img, void *cb_data); +typedef enum bt_mesh_dfu_iter(*bt_mesh_dfu_img_cb_t)(struct bt_mesh_dfu_cli *cli, + struct bt_mesh_msg_ctx *ctx, + uint8_t idx, uint8_t total, + const struct bt_mesh_dfu_img *img, + void *cb_data); /** Firmware Update Client event callbacks. */ struct bt_mesh_dfu_cli_cb { - /** @brief BLOB transfer is suspended. - * - * Called when the BLOB transfer is suspended due to response timeout from all Target nodes. - * - * @param cli Firmware Update Client model instance. - */ - void (*suspended)(struct bt_mesh_dfu_cli *cli); + /** @brief BLOB transfer is suspended. + * + * Called when the BLOB transfer is suspended due to response timeout from all Target nodes. + * + * @param cli Firmware Update Client model instance. + */ + void (*suspended)(struct bt_mesh_dfu_cli *cli); - /** @brief DFU ended. - * - * Called when the DFU transfer ends, either because all Target nodes were - * lost or because the transfer was completed successfully. - * - * @param cli Firmware Update Client model instance. - * @param reason Reason for ending. - */ - void (*ended)(struct bt_mesh_dfu_cli *cli, - enum bt_mesh_dfu_status reason); + /** @brief DFU ended. + * + * Called when the DFU transfer ends, either because all Target nodes were + * lost or because the transfer was completed successfully. + * + * @param cli Firmware Update Client model instance. + * @param reason Reason for ending. + */ + void (*ended)(struct bt_mesh_dfu_cli *cli, + enum bt_mesh_dfu_status reason); - /** @brief DFU transfer applied on all active Target nodes. - * - * Called at the end of the apply procedure started by @ref - * bt_mesh_dfu_cli_apply. - * - * @param cli Firmware Update Client model instance. - */ - void (*applied)(struct bt_mesh_dfu_cli *cli); + /** @brief DFU transfer applied on all active Target nodes. + * + * Called at the end of the apply procedure started by @ref + * bt_mesh_dfu_cli_apply. + * + * @param cli Firmware Update Client model instance. + */ + void (*applied)(struct bt_mesh_dfu_cli *cli); - /** @brief DFU transfer confirmed on all active Target nodes. - * - * Called at the end of the apply procedure started by @ref - * bt_mesh_dfu_cli_confirm. - * - * @param cli Firmware Update Client model instance. - */ - void (*confirmed)(struct bt_mesh_dfu_cli *cli); + /** @brief DFU transfer confirmed on all active Target nodes. + * + * Called at the end of the apply procedure started by @ref + * bt_mesh_dfu_cli_confirm. + * + * @param cli Firmware Update Client model instance. + */ + void (*confirmed)(struct bt_mesh_dfu_cli *cli); - /** @brief DFU Target node was lost. - * - * A DFU Target node was dropped from the receivers list. The Target node's - * @c status is set to reflect the reason for the failure. - * - * @param cli Firmware Update Client model instance. - * @param target DFU Target node that was lost. - */ - void (*lost_target)(struct bt_mesh_dfu_cli *cli, - struct bt_mesh_dfu_target *target); + /** @brief DFU Target node was lost. + * + * A DFU Target node was dropped from the receivers list. The Target node's + * @c status is set to reflect the reason for the failure. + * + * @param cli Firmware Update Client model instance. + * @param target DFU Target node that was lost. + */ + void (*lost_target)(struct bt_mesh_dfu_cli *cli, + struct bt_mesh_dfu_target *target); }; +enum { + STATE_IDLE, + STATE_TRANSFER, + STATE_REFRESH, + STATE_VERIFIED, + STATE_APPLY, + STATE_APPLIED, + STATE_CONFIRM, + STATE_CANCEL, + STATE_SUSPENDED, +}; + +typedef struct { + sys_snode_t node; + uint8_t ttl; + uint8_t type; + uint8_t img_cnt; + uint32_t opcode; + void *params; + bt_mesh_dfu_img_cb_t img_cb; + struct bt_mesh_dfu_cli *dfu_cli; + struct bt_mesh_msg_ctx ctx; + int32_t timeout; + struct k_delayed_work timer; /* Time used to get response. Only for internal use. */ +} bt_mesh_dfu_cli_req_t; + /** Firmware Update Client model instance. * - * Should be initialized with @ref BT_MESH_DFU_CLI_INIT. + * Should be initialized with @ref BLE_MESH_DFU_CLI_INIT. */ struct bt_mesh_dfu_cli { - /** Callback structure. */ - const struct bt_mesh_dfu_cli_cb *cb; - /** Underlying BLOB Transfer Client. */ - struct bt_mesh_blob_cli blob; + /** Callback structure. */ + const struct bt_mesh_dfu_cli_cb *cb; + /** Underlying BLOB Transfer Client. */ + struct bt_mesh_blob_cli blob; - /* runtime state */ + /* runtime state */ - uint32_t op; - const struct bt_mesh_model *mod; + uint32_t op; + struct bt_mesh_model *mod; - struct { - const struct bt_mesh_dfu_slot *slot; - const struct bt_mesh_blob_io *io; - struct bt_mesh_blob_xfer blob; - uint8_t state; - uint8_t flags; - } xfer; + struct { + const struct bt_mesh_dfu_slot *slot; + const struct bt_mesh_blob_io *io; + struct bt_mesh_blob_xfer blob; + uint8_t state; + uint8_t flags; + } xfer; - struct { - uint8_t ttl; - uint8_t type; - uint8_t img_cnt; - uint16_t addr; - struct k_sem sem; - void *params; - bt_mesh_dfu_img_cb_t img_cb; - } req; + bt_mesh_dfu_cli_req_t *req; }; /** BLOB parameters for Firmware Update Client transfer: */ struct bt_mesh_dfu_cli_xfer_blob_params { - /* Logarithmic representation of the block size. */ - uint8_t block_size_log; - /** Base chunk size. May be smaller for the last chunk. */ - uint16_t chunk_size; + /* Logarithmic representation of the block size. */ + uint8_t block_size_log; + /** Base chunk size. May be smaller for the last chunk. */ + uint16_t chunk_size; }; /** Firmware Update Client transfer parameters: */ struct bt_mesh_dfu_cli_xfer { - /** BLOB ID to use for this transfer, or 0 to set it randomly. */ - uint64_t blob_id; - /** DFU image slot to transfer. */ - const struct bt_mesh_dfu_slot *slot; - /** Transfer mode (Push (Push BLOB Transfer Mode) or Pull (Pull BLOB Transfer Mode)) */ - enum bt_mesh_blob_xfer_mode mode; - /** BLOB parameters to be used for the transfer, or NULL to retrieve Target nodes' - * capabilities before sending a firmware. - */ - const struct bt_mesh_dfu_cli_xfer_blob_params *blob_params; + /** BLOB ID to use for this transfer, or 0 to set it randomly. */ + uint64_t blob_id; + /** DFU image slot to transfer. */ + const struct bt_mesh_dfu_slot *slot; + /** Transfer mode (Push (Push BLOB Transfer Mode) or Pull (Pull BLOB Transfer Mode)) */ + enum bt_mesh_blob_xfer_mode mode; + /** BLOB parameters to be used for the transfer, or NULL to retrieve Target nodes' + * capabilities before sending a firmware. + */ + const struct bt_mesh_dfu_cli_xfer_blob_params *blob_params; }; /** @brief Start distributing a DFU. @@ -250,9 +276,9 @@ struct bt_mesh_dfu_cli_xfer { * @return 0 on success, or (negative) error code otherwise. */ int bt_mesh_dfu_cli_send(struct bt_mesh_dfu_cli *cli, - const struct bt_mesh_blob_cli_inputs *inputs, - const struct bt_mesh_blob_io *io, - const struct bt_mesh_dfu_cli_xfer *xfer); + const struct bt_mesh_blob_cli_inputs *inputs, + const struct bt_mesh_blob_io *io, + const struct bt_mesh_dfu_cli_xfer *xfer); /** @brief Suspend a DFU transfer. * @@ -281,7 +307,7 @@ int bt_mesh_dfu_cli_resume(struct bt_mesh_dfu_cli *cli); * @return 0 on success, or (negative) error code otherwise. */ int bt_mesh_dfu_cli_cancel(struct bt_mesh_dfu_cli *cli, - struct bt_mesh_msg_ctx *ctx); + struct bt_mesh_msg_ctx *ctx); /** @brief Apply the completed DFU transfer. * @@ -300,7 +326,7 @@ int bt_mesh_dfu_cli_apply(struct bt_mesh_dfu_cli *cli); * Client's @c confirmed callback is called at the end of the confirm * procedure. * - * Target nodes that have reported the effect as @ref BT_MESH_DFU_EFFECT_UNPROV + * Target nodes that have reported the effect as @ref BLE_MESH_DFU_EFFECT_UNPROV * are expected to not respond to the query, and will fail if they do. * * @param cli Firmware Update Client model instance. @@ -341,14 +367,15 @@ bool bt_mesh_dfu_cli_is_busy(struct bt_mesh_dfu_cli *cli); * @param ctx Message context. * @param cb Callback to call for each image index. * @param cb_data Callback data to pass to @c cb. + * @param first_idx Index of the first requested imgs entry. * @param max_count Max number of images to return. * * @return 0 on success, or (negative) error code otherwise. */ int bt_mesh_dfu_cli_imgs_get(struct bt_mesh_dfu_cli *cli, - struct bt_mesh_msg_ctx *ctx, - bt_mesh_dfu_img_cb_t cb, void *cb_data, - uint8_t max_count); + struct bt_mesh_msg_ctx *ctx, + bt_mesh_dfu_img_cb_t cb, void *cb_data, + uint8_t first_idx, uint8_t max_count); /** @brief Perform a metadata check for the given DFU image slot. * @@ -357,30 +384,26 @@ int bt_mesh_dfu_cli_imgs_get(struct bt_mesh_dfu_cli *cli, * * Waits for a response until the procedure timeout expires. * - * @param cli Firmware Update Client model instance. - * @param ctx Message context. - * @param img_idx Target node's image index to check. - * @param slot DFU image slot to check for. - * @param rsp Metadata status response buffer. + * @param cli Firmware Update Client model instance. + * @param ctx Message context. + * @param img_idx Target node's image index to check. + * @param metadata DFU image metadata to check for. * * @return 0 on success, or (negative) error code otherwise. */ int bt_mesh_dfu_cli_metadata_check(struct bt_mesh_dfu_cli *cli, - struct bt_mesh_msg_ctx *ctx, uint8_t img_idx, - const struct bt_mesh_dfu_slot *slot, - struct bt_mesh_dfu_metadata_status *rsp); + struct bt_mesh_msg_ctx *ctx, uint8_t img_idx, + struct net_buf_simple *metadata); /** @brief Get the status of a Target node. * * @param cli Firmware Update Client model instance. * @param ctx Message context. - * @param rsp Response data buffer. * * @return 0 on success, or (negative) error code otherwise. */ int bt_mesh_dfu_cli_status_get(struct bt_mesh_dfu_cli *cli, - struct bt_mesh_msg_ctx *ctx, - struct bt_mesh_dfu_target_status *rsp); + struct bt_mesh_msg_ctx *ctx); /** @brief Get the current procedure timeout value. * @@ -404,6 +427,4 @@ extern const struct bt_mesh_model_op _bt_mesh_dfu_cli_op[]; } #endif -#endif /* ZEPHYR_INCLUDE_BLUETOOTH_MESH_DFU_CLI_H__ */ - -/** @} */ +#endif /* _BLE_MESH_v11_DFU_CLI_H__ */ diff --git a/components/bt/esp_ble_mesh/v1.1/include/mesh_v1.1/dfu/dfu_metadata.h b/components/bt/esp_ble_mesh/v1.1/include/mesh_v1.1/dfu/dfu_metadata.h index 8f6430c4b0..baa4049a2b 100644 --- a/components/bt/esp_ble_mesh/v1.1/include/mesh_v1.1/dfu/dfu_metadata.h +++ b/components/bt/esp_ble_mesh/v1.1/include/mesh_v1.1/dfu/dfu_metadata.h @@ -1,5 +1,6 @@ /* - * Copyright (c) 2021 Nordic Semiconductor ASA + * SPDX-FileCopyrightText: 2020 Nordic Semiconductor ASA + * SPDX-FileContributor: 2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -12,14 +13,15 @@ * @brief Common types and functions for the Bluetooth Mesh DFU metadata. */ -#ifndef ZEPHYR_INCLUDE_BLUETOOTH_MESH_DFU_METADATA_H__ -#define ZEPHYR_INCLUDE_BLUETOOTH_MESH_DFU_METADATA_H__ +#ifndef _BLE_MESH_v11_DFU_METADATA_H__ +#define _BLE_MESH_v11_DFU_METADATA_H__ #include #include -#include +#include "mesh/types.h" +#include "mesh/utils.h" #ifdef __cplusplus extern "C" { @@ -27,42 +29,40 @@ extern "C" { /** Firmware version. */ struct bt_mesh_dfu_metadata_fw_ver { - /** Firmware major version. */ - uint8_t major; - /** Firmware minor version. */ - uint8_t minor; - /** Firmware revision. */ - uint16_t revision; - /** Firmware build number. */ - uint32_t build_num; + /** Firmware major version. */ + uint8_t major; + /** Firmware minor version. */ + uint8_t minor; + /** Firmware revision. */ + uint16_t revision; + /** Firmware build number. */ + uint32_t build_num; }; /** Firmware core type. */ enum bt_mesh_dfu_metadata_fw_core_type { - /** Application core. */ - BT_MESH_DFU_FW_CORE_TYPE_APP = BIT(0), - /** Network core. */ - BT_MESH_DFU_FW_CORE_TYPE_NETWORK = BIT(1), - /** Application-specific BLOB. */ - BT_MESH_DFU_FW_CORE_TYPE_APP_SPECIFIC_BLOB = BIT(2), + /** Application core. */ + BLE_MESH_DFU_FW_CORE_TYPE_APP = BIT(0), + /** Application-specific BLOB. */ + BLE_MESH_DFU_FW_CORE_TYPE_APP_SPECIFIC_BLOB = BIT(2), }; /** Firmware metadata. */ struct bt_mesh_dfu_metadata { - /** New firmware version. */ - struct bt_mesh_dfu_metadata_fw_ver fw_ver; - /** New firmware size. */ - uint32_t fw_size; - /** New firmware core type. */ - enum bt_mesh_dfu_metadata_fw_core_type fw_core_type; - /** Hash of incoming Composition Data. */ - uint32_t comp_hash; - /** New number of node elements. */ - uint16_t elems; - /** Application-specific data for new firmware. This field is optional. */ - uint8_t *user_data; - /** Length of the application-specific field. */ - uint32_t user_data_len; + /** New firmware version. */ + struct bt_mesh_dfu_metadata_fw_ver fw_ver; + /** New firmware size. */ + uint32_t fw_size; + /** New firmware core type. */ + enum bt_mesh_dfu_metadata_fw_core_type fw_core_type; + /** Hash of incoming Composition Data. */ + uint32_t comp_hash; + /** New number of node elements. */ + uint16_t elems; + /** Application-specific data for new firmware. This field is optional. */ + uint8_t *user_data; + /** Length of the application-specific field. */ + uint32_t user_data_len; }; /** @brief Decode a firmware metadata from a network buffer. @@ -73,7 +73,7 @@ struct bt_mesh_dfu_metadata { * @return 0 on success, or (negative) error code otherwise. */ int bt_mesh_dfu_metadata_decode(struct net_buf_simple *buf, - struct bt_mesh_dfu_metadata *metadata); + struct bt_mesh_dfu_metadata *metadata); /** @brief Encode a firmware metadata into a network buffer. * @@ -83,7 +83,7 @@ int bt_mesh_dfu_metadata_decode(struct net_buf_simple *buf, * @return 0 on success, or (negative) error code otherwise. */ int bt_mesh_dfu_metadata_encode(const struct bt_mesh_dfu_metadata *metadata, - struct net_buf_simple *buf); + struct net_buf_simple *buf); /** @brief Compute hash of the Composition Data state. * @@ -110,6 +110,4 @@ int bt_mesh_dfu_metadata_comp_hash_local_get(uint8_t *key, uint32_t *hash); } #endif -#endif /* ZEPHYR_INCLUDE_BLUETOOTH_MESH_DFU_METADATA_H__ */ - -/** @} */ +#endif /* _BLE_MESH_v11_DFU_METADATA_H__ */ diff --git a/components/bt/esp_ble_mesh/v1.1/include/mesh_v1.1/dfu/dfu_srv.h b/components/bt/esp_ble_mesh/v1.1/include/mesh_v1.1/dfu/dfu_srv.h index 2beaf2d977..dc134fe07e 100644 --- a/components/bt/esp_ble_mesh/v1.1/include/mesh_v1.1/dfu/dfu_srv.h +++ b/components/bt/esp_ble_mesh/v1.1/include/mesh_v1.1/dfu/dfu_srv.h @@ -1,5 +1,6 @@ /* - * Copyright (c) 2020 Nordic Semiconductor ASA + * SPDX-FileCopyrightText: 2020 Nordic Semiconductor ASA + * SPDX-FileContributor: 2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -12,12 +13,11 @@ * @brief API for the Bluetooth Mesh Firmware Update Server model */ -#ifndef ZEPHYR_INCLUDE_BLUETOOTH_MESH_DFU_SRV_H__ -#define ZEPHYR_INCLUDE_BLUETOOTH_MESH_DFU_SRV_H__ +#ifndef _BLE_MESH_v11_DFU_SRV_H__ +#define _BLE_MESH_v11_DFU_SRV_H__ -#include -#include -#include +#include "mesh/access.h" +#include "mesh_v1.1/mbt/blob_srv.h" #ifdef __cplusplus extern "C" { @@ -33,11 +33,11 @@ struct bt_mesh_dfu_srv; * @param _imgs List of @ref bt_mesh_dfu_img managed by this Server. * @param _img_count Number of DFU images managed by this Server. */ -#define BT_MESH_DFU_SRV_INIT(_handlers, _imgs, _img_count) \ - { \ - .blob = { .cb = &_bt_mesh_dfu_srv_blob_cb }, .cb = _handlers, \ - .imgs = _imgs, .img_count = _img_count, \ - } +#define BLE_MESH_DFU_SRV_INIT(_handlers, _imgs, _img_count) \ + { \ + .blob = { .cb = &_bt_mesh_dfu_srv_blob_cb }, .cb = _handlers, \ + .imgs = _imgs, .img_count = _img_count, \ + } /** * @@ -45,156 +45,156 @@ struct bt_mesh_dfu_srv; * * @param _srv Pointer to a @ref bt_mesh_dfu_srv instance. */ -#define BT_MESH_MODEL_DFU_SRV(_srv) \ - BT_MESH_MODEL_BLOB_SRV(&(_srv)->blob), \ - BT_MESH_MODEL_CB(BT_MESH_MODEL_ID_DFU_SRV, _bt_mesh_dfu_srv_op, NULL, \ - _srv, &_bt_mesh_dfu_srv_cb) +#define BT_MESH_MODEL_DFU_SRV(_srv) \ + BT_MESH_MODEL_BLOB_SRV(&(_srv)->blob), \ + BLE_MESH_MODEL_CB(BLE_MESH_MODEL_ID_DFU_SRV, _bt_mesh_dfu_srv_op, NULL, \ + _srv, &_bt_mesh_dfu_srv_cb) /** @brief Firmware Update Server event callbacks. */ struct bt_mesh_dfu_srv_cb { - /** @brief Transfer check callback. - * - * The transfer check can be used to validate the incoming transfer - * before it starts. The contents of the metadata is implementation - * specific, and should contain all the information the application - * needs to determine whether this image should be accepted, and what - * the effect of the transfer would be. - * - * If applying the image will have an effect on the provisioning state - * of the mesh stack, this can be communicated through the @c effect - * return parameter. - * - * The metadata check can be performed both as part of starting a new - * transfer and as a separate procedure. - * - * This handler is optional. - * - * @param srv Firmware Update Server instance. - * @param img DFU image the metadata check is performed on. - * @param metadata Image metadata. - * @param effect Return parameter for the image effect on the - * provisioning state of the mesh stack. - * - * @return 0 on success, or (negative) error code otherwise. - */ - int (*check)(struct bt_mesh_dfu_srv *srv, - const struct bt_mesh_dfu_img *img, - struct net_buf_simple *metadata, - enum bt_mesh_dfu_effect *effect); + /** @brief Transfer check callback. + * + * The transfer check can be used to validate the incoming transfer + * before it starts. The contents of the metadata is implementation + * specific, and should contain all the information the application + * needs to determine whether this image should be accepted, and what + * the effect of the transfer would be. + * + * If applying the image will have an effect on the provisioning state + * of the mesh stack, this can be communicated through the @c effect + * return parameter. + * + * The metadata check can be performed both as part of starting a new + * transfer and as a separate procedure. + * + * This handler is optional. + * + * @param srv Firmware Update Server instance. + * @param img DFU image the metadata check is performed on. + * @param metadata Image metadata. + * @param effect Return parameter for the image effect on the + * provisioning state of the mesh stack. + * + * @return 0 on success, or (negative) error code otherwise. + */ + int (*check)(struct bt_mesh_dfu_srv *srv, + const struct bt_mesh_dfu_img *img, + struct net_buf_simple *metadata, + enum bt_mesh_dfu_effect *effect); - /** @brief Transfer start callback. - * - * Called when the Firmware Update Server is ready to start a new DFU transfer. - * The application must provide an initialized BLOB stream to be used - * during the DFU transfer. - * - * The following error codes are treated specially, and should be used - * to communicate these issues: - * - @c -ENOMEM: The device cannot fit this image. - * - @c -EBUSY: The application is temporarily unable to accept the - * transfer. - * - @c -EALREADY: The device has already received and verified this - * image, and there's no need to transfer it again. The Firmware Update model - * will skip the transfer phase, and mark the image as verified. - * - * This handler is mandatory. - * - * @param srv Firmware Update Server instance. - * @param img DFU image being updated. - * @param metadata Image metadata. - * @param io BLOB stream return parameter. Must be set to a - * valid BLOB stream by the callback. - * - * @return 0 on success, or (negative) error code otherwise. Return - * codes @c -ENOMEM, @c -EBUSY @c -EALREADY will be passed to - * the updater, other error codes are reported as internal - * errors. - */ - int (*start)(struct bt_mesh_dfu_srv *srv, - const struct bt_mesh_dfu_img *img, - struct net_buf_simple *metadata, - const struct bt_mesh_blob_io **io); + /** @brief Transfer start callback. + * + * Called when the Firmware Update Server is ready to start a new DFU transfer. + * The application must provide an initialized BLOB stream to be used + * during the DFU transfer. + * + * The following error codes are treated specially, and should be used + * to communicate these issues: + * - @c -ENOMEM: The device cannot fit this image. + * - @c -EBUSY: The application is temporarily unable to accept the + * transfer. + * - @c -EALREADY: The device has already received and verified this + * image, and there's no need to transfer it again. The Firmware Update model + * will skip the transfer phase, and mark the image as verified. + * + * This handler is mandatory. + * + * @param srv Firmware Update Server instance. + * @param img DFU image being updated. + * @param metadata Image metadata. + * @param io BLOB stream return parameter. Must be set to a + * valid BLOB stream by the callback. + * + * @return 0 on success, or (negative) error code otherwise. Return + * codes @c -ENOMEM, @c -EBUSY @c -EALREADY will be passed to + * the updater, other error codes are reported as internal + * errors. + */ + int (*start)(struct bt_mesh_dfu_srv *srv, + const struct bt_mesh_dfu_img *img, + struct net_buf_simple *metadata, + const struct bt_mesh_blob_io **io); - /** @brief Transfer end callback. - * - * This handler is optional. - * - * If the transfer is successful, the application should verify the - * firmware image, and call either @ref bt_mesh_dfu_srv_verified or - * @ref bt_mesh_dfu_srv_rejected depending on the outcome. - * - * If the transfer fails, the Firmware Update Server will be available for new - * transfers immediately after this function returns. - * - * @param srv Firmware Update Server instance. - * @param img DFU image that failed the update. - * @param success Whether the DFU transfer was successful. - */ - void (*end)(struct bt_mesh_dfu_srv *srv, - const struct bt_mesh_dfu_img *img, bool success); + /** @brief Transfer end callback. + * + * This handler is optional. + * + * If the transfer is successful, the application should verify the + * firmware image, and call either @ref bt_mesh_dfu_srv_verified or + * @ref bt_mesh_dfu_srv_rejected depending on the outcome. + * + * If the transfer fails, the Firmware Update Server will be available for new + * transfers immediately after this function returns. + * + * @param srv Firmware Update Server instance. + * @param img DFU image that failed the update. + * @param success Whether the DFU transfer was successful. + */ + void (*end)(struct bt_mesh_dfu_srv *srv, + const struct bt_mesh_dfu_img *img, bool success); - /** @brief Transfer recovery callback. - * - * If the device reboots in the middle of a transfer, the Firmware Update Server - * calls this function when the Bluetooth Mesh subsystem is started. - * - * This callback is optional, but transfers will not be recovered after - * a reboot without it. - * - * @param srv Firmware Update Server instance. - * @param img DFU image being updated. - * @param io BLOB stream return parameter. Must be set to a valid BLOB - * stream by the callback. - * - * @return 0 on success, or (negative) error code to abandon the - * transfer. - */ - int (*recover)(struct bt_mesh_dfu_srv *srv, - const struct bt_mesh_dfu_img *img, - const struct bt_mesh_blob_io **io); + /** @brief Transfer recovery callback. + * + * If the device reboots in the middle of a transfer, the Firmware Update Server + * calls this function when the Bluetooth Mesh subsystem is started. + * + * This callback is optional, but transfers will not be recovered after + * a reboot without it. + * + * @param srv Firmware Update Server instance. + * @param img DFU image being updated. + * @param io BLOB stream return parameter. Must be set to a valid BLOB + * stream by the callback. + * + * @return 0 on success, or (negative) error code to abandon the + * transfer. + */ + int (*recover)(struct bt_mesh_dfu_srv *srv, + const struct bt_mesh_dfu_img *img, + const struct bt_mesh_blob_io **io); - /** @brief Transfer apply callback. - * - * Called after a transfer has been validated, and the updater sends an - * apply message to the Target nodes. - * - * This handler is optional. - * - * @param srv Firmware Update Server instance. - * @param img DFU image that should be applied. - * - * @return 0 on success, or (negative) error code otherwise. - */ - int (*apply)(struct bt_mesh_dfu_srv *srv, - const struct bt_mesh_dfu_img *img); + /** @brief Transfer apply callback. + * + * Called after a transfer has been validated, and the updater sends an + * apply message to the Target nodes. + * + * This handler is optional. + * + * @param srv Firmware Update Server instance. + * @param img DFU image that should be applied. + * + * @return 0 on success, or (negative) error code otherwise. + */ + int (*apply)(struct bt_mesh_dfu_srv *srv, + const struct bt_mesh_dfu_img *img); }; /** @brief Firmware Update Server instance. * - * Should be initialized with @ref BT_MESH_DFU_SRV_INIT. + * Should be initialized with @ref BLE_MESH_DFU_SRV_INIT. */ struct bt_mesh_dfu_srv { - /** Underlying BLOB Transfer Server. */ - struct bt_mesh_blob_srv blob; - /** Callback structure. */ - const struct bt_mesh_dfu_srv_cb *cb; - /** List of updatable images. */ - const struct bt_mesh_dfu_img *imgs; - /** Number of updatable images. */ - size_t img_count; + /** Underlying BLOB Transfer Server. */ + struct bt_mesh_blob_srv blob; + /** Callback structure. */ + const struct bt_mesh_dfu_srv_cb *cb; + /** List of updatable images. */ + const struct bt_mesh_dfu_img *imgs; + /** Number of updatable images. */ + size_t img_count; - /* Runtime state */ - const struct bt_mesh_model *mod; - struct { - /* Effect of transfer, @see bt_mesh_dfu_effect. */ - uint8_t effect; - /* Current phase, @see bt_mesh_dfu_phase. */ - uint8_t phase; - uint8_t ttl; - uint8_t idx; - uint16_t timeout_base; - uint16_t meta; - } update; + /* Runtime state */ + const struct bt_mesh_model *mod; + struct { + /* Effect of transfer, @see bt_mesh_dfu_effect. */ + uint8_t effect; + /* Current phase, @see bt_mesh_dfu_phase. */ + uint8_t phase; + uint8_t ttl; + uint8_t idx; + uint16_t timeout_base; + uint16_t meta; + } update; }; /** @brief Accept the received DFU transfer. @@ -235,7 +235,7 @@ void bt_mesh_dfu_srv_cancel(struct bt_mesh_dfu_srv *srv); */ void bt_mesh_dfu_srv_applied(struct bt_mesh_dfu_srv *srv); -/** @brief Check if the Firmware Update Server is busy processing a transfer. +/** @brief Check if the Firmware Update Server is busy processing a transfer. * * @param srv Firmware Update Server instance. * @@ -261,6 +261,4 @@ extern const struct bt_mesh_blob_srv_cb _bt_mesh_dfu_srv_blob_cb; } #endif -#endif /* ZEPHYR_INCLUDE_BLUETOOTH_MESH_DFU_SRV_H__ */ - -/** @} */ +#endif /* _BLE_MESH_v11_DFU_SRV_H__ */ diff --git a/components/bt/esp_ble_mesh/v1.1/include/mesh_v1.1/mbt/blob.h b/components/bt/esp_ble_mesh/v1.1/include/mesh_v1.1/mbt/blob.h index 18304f39f6..29199360a4 100644 --- a/components/bt/esp_ble_mesh/v1.1/include/mesh_v1.1/mbt/blob.h +++ b/components/bt/esp_ble_mesh/v1.1/include/mesh_v1.1/mbt/blob.h @@ -1,15 +1,25 @@ /* - * Copyright (c) 2020 Nordic Semiconductor ASA + * SPDX-FileCopyrightText: 2020 Nordic Semiconductor ASA + * SPDX-FileContributor: 2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ -#ifndef ZEPHYR_INCLUDE_BLUETOOTH_MESH_BLOB_H__ -#define ZEPHYR_INCLUDE_BLUETOOTH_MESH_BLOB_H__ +#ifndef _BLE_MESH_v11_BLOB_H__ +#define _BLE_MESH_v11_BLOB_H__ #include - -#include +#include "mesh/types.h" +#include "mesh/utils.h" +#include "mesh.h" +#include "mesh/utils.h" +#include "mesh/access.h" +#include "mesh/atomic.h" +#include "mesh/timer.h" +#include "mesh/compiler.h" +#include "mesh/trace.h" +#include "transport.h" +#include "mesh_v1.1/mbt/blob.h" #ifdef __cplusplus extern "C" { @@ -21,236 +31,236 @@ extern "C" { * @{ */ -#ifndef CONFIG_BT_MESH_BLOB_CHUNK_COUNT_MAX -#define CONFIG_BT_MESH_BLOB_CHUNK_COUNT_MAX 0 +#ifndef CONFIG_BLE_MESH_BLOB_CHUNK_COUNT_MAX +#define CONFIG_BLE_MESH_BLOB_CHUNK_COUNT_MAX 0 #endif /** BLOB transfer mode. */ enum bt_mesh_blob_xfer_mode { - /** No valid transfer mode. */ - BT_MESH_BLOB_XFER_MODE_NONE, - /** Push mode (Push BLOB Transfer Mode). */ - BT_MESH_BLOB_XFER_MODE_PUSH, - /** Pull mode (Pull BLOB Transfer Mode). */ - BT_MESH_BLOB_XFER_MODE_PULL, - /** Both modes are valid. */ - BT_MESH_BLOB_XFER_MODE_ALL, + /** No valid transfer mode. */ + BT_MESH_BLOB_XFER_MODE_NONE, + /** Push mode (Push BLOB Transfer Mode). */ + BT_MESH_BLOB_XFER_MODE_PUSH, + /** Pull mode (Pull BLOB Transfer Mode). */ + BT_MESH_BLOB_XFER_MODE_PULL, + /** Both modes are valid. */ + BT_MESH_BLOB_XFER_MODE_ALL, }; /** Transfer phase. */ enum bt_mesh_blob_xfer_phase { - /** The BLOB Transfer Server is awaiting configuration. */ - BT_MESH_BLOB_XFER_PHASE_INACTIVE, - /** The BLOB Transfer Server is ready to receive a BLOB transfer. */ - BT_MESH_BLOB_XFER_PHASE_WAITING_FOR_START, - /** The BLOB Transfer Server is waiting for the next block of data. */ - BT_MESH_BLOB_XFER_PHASE_WAITING_FOR_BLOCK, - /** The BLOB Transfer Server is waiting for the next chunk of data. */ - BT_MESH_BLOB_XFER_PHASE_WAITING_FOR_CHUNK, - /** The BLOB was transferred successfully. */ - BT_MESH_BLOB_XFER_PHASE_COMPLETE, - /** The BLOB transfer is paused. */ - BT_MESH_BLOB_XFER_PHASE_SUSPENDED, + /** The BLOB Transfer Server is awaiting configuration. */ + BT_MESH_BLOB_XFER_PHASE_INACTIVE, + /** The BLOB Transfer Server is ready to receive a BLOB transfer. */ + BT_MESH_BLOB_XFER_PHASE_WAITING_FOR_START, + /** The BLOB Transfer Server is waiting for the next block of data. */ + BT_MESH_BLOB_XFER_PHASE_WAITING_FOR_BLOCK, + /** The BLOB Transfer Server is waiting for the next chunk of data. */ + BT_MESH_BLOB_XFER_PHASE_WAITING_FOR_CHUNK, + /** The BLOB was transferred successfully. */ + BT_MESH_BLOB_XFER_PHASE_COMPLETE, + /** The BLOB transfer is paused. */ + BT_MESH_BLOB_XFER_PHASE_SUSPENDED, }; /** BLOB model status codes. */ enum bt_mesh_blob_status { - /** The message was processed successfully. */ - BT_MESH_BLOB_SUCCESS, - /** The Block Number field value is not within the range of blocks being - * transferred. - */ - BT_MESH_BLOB_ERR_INVALID_BLOCK_NUM, - /** The block size is smaller than the size indicated by the Min Block - * Size Log state or is larger than the size indicated by the Max Block - * Size Log state. - */ - BT_MESH_BLOB_ERR_INVALID_BLOCK_SIZE, - /** The chunk size exceeds the size indicated by the Max Chunk Size - * state, or the number of chunks exceeds the number specified by the - * Max Total Chunks state. - */ - BT_MESH_BLOB_ERR_INVALID_CHUNK_SIZE, - /** The operation cannot be performed while the server is in the current - * phase. - */ - BT_MESH_BLOB_ERR_WRONG_PHASE, - /** A parameter value in the message cannot be accepted. */ - BT_MESH_BLOB_ERR_INVALID_PARAM, - /** The message contains a BLOB ID value that is not expected. */ - BT_MESH_BLOB_ERR_WRONG_BLOB_ID, - /** There is not enough space available in memory to receive the BLOB. - */ - BT_MESH_BLOB_ERR_BLOB_TOO_LARGE, - /** The transfer mode is not supported by the BLOB Transfer Server - * model. - */ - BT_MESH_BLOB_ERR_UNSUPPORTED_MODE, - /** An internal error occurred on the node. */ - BT_MESH_BLOB_ERR_INTERNAL, - /** The requested information cannot be provided while the server is in - * the current phase. - */ - BT_MESH_BLOB_ERR_INFO_UNAVAILABLE, + /** The message was processed successfully. */ + BT_MESH_BLOB_SUCCESS, + /** The Block Number field value is not within the range of blocks being + * transferred. + */ + BT_MESH_BLOB_ERR_INVALID_BLOCK_NUM, + /** The block size is smaller than the size indicated by the Min Block + * Size Log state or is larger than the size indicated by the Max Block + * Size Log state. + */ + BT_MESH_BLOB_ERR_INVALID_BLOCK_SIZE, + /** The chunk size exceeds the size indicated by the Max Chunk Size + * state, or the number of chunks exceeds the number specified by the + * Max Total Chunks state. + */ + BT_MESH_BLOB_ERR_INVALID_CHUNK_SIZE, + /** The operation cannot be performed while the server is in the current + * phase. + */ + BT_MESH_BLOB_ERR_WRONG_PHASE, + /** A parameter value in the message cannot be accepted. */ + BT_MESH_BLOB_ERR_INVALID_PARAM, + /** The message contains a BLOB ID value that is not expected. */ + BT_MESH_BLOB_ERR_WRONG_BLOB_ID, + /** There is not enough space available in memory to receive the BLOB. + */ + BT_MESH_BLOB_ERR_BLOB_TOO_LARGE, + /** The transfer mode is not supported by the BLOB Transfer Server + * model. + */ + BT_MESH_BLOB_ERR_UNSUPPORTED_MODE, + /** An internal error occurred on the node. */ + BT_MESH_BLOB_ERR_INTERNAL, + /** The requested information cannot be provided while the server is in + * the current phase. + */ + BT_MESH_BLOB_ERR_INFO_UNAVAILABLE, }; /** BLOB transfer data block. */ struct bt_mesh_blob_block { - /** Block size in bytes */ - size_t size; - /** Offset in bytes from the start of the BLOB. */ - off_t offset; - /** Block number */ - uint16_t number; - /** Number of chunks in block. */ - uint16_t chunk_count; - /** Bitmap of missing chunks. */ - uint8_t missing[DIV_ROUND_UP(CONFIG_BT_MESH_BLOB_CHUNK_COUNT_MAX, - 8)]; + /** Block size in bytes */ + size_t size; + /** Offset in bytes from the start of the BLOB. */ + off_t offset; + /** Block number */ + uint16_t number; + /** Number of chunks in block. */ + uint16_t chunk_count; + /** Bitmap of missing chunks. */ + uint8_t missing[DIV_ROUND_UP(CONFIG_BLE_MESH_BLOB_CHUNK_COUNT_MAX, + 8)]; }; /** BLOB data chunk. */ struct bt_mesh_blob_chunk { - /** Offset of the chunk data from the start of the block. */ - off_t offset; - /** Chunk data size. */ - size_t size; - /** Chunk data. */ - uint8_t *data; + /** Offset of the chunk data from the start of the block. */ + off_t offset; + /** Chunk data size. */ + size_t size; + /** Chunk data. */ + uint8_t *data; }; /** BLOB transfer. */ struct bt_mesh_blob_xfer { - /** BLOB ID. */ - uint64_t id; - /** Total BLOB size in bytes. */ - size_t size; - /** BLOB transfer mode. */ - enum bt_mesh_blob_xfer_mode mode; - /* Logarithmic representation of the block size. */ - uint8_t block_size_log; - /** Base chunk size. May be smaller for the last chunk. */ - uint16_t chunk_size; + /** BLOB ID. */ + uint64_t id; + /** Total BLOB size in bytes. */ + size_t size; + /** BLOB transfer mode. */ + enum bt_mesh_blob_xfer_mode mode; + /* Logarithmic representation of the block size. */ + uint8_t block_size_log; + /** Base chunk size. May be smaller for the last chunk. */ + uint16_t chunk_size; }; /** BLOB stream interaction mode. */ enum bt_mesh_blob_io_mode { - /** Read data from the stream. */ - BT_MESH_BLOB_READ, - /** Write data to the stream. */ - BT_MESH_BLOB_WRITE, + /** Read data from the stream. */ + BT_MESH_BLOB_READ, + /** Write data to the stream. */ + BT_MESH_BLOB_WRITE, }; /** BLOB stream. */ struct bt_mesh_blob_io { - /** @brief Open callback. - * - * Called when the reader is opened for reading. - * - * @param io BLOB stream. - * @param xfer BLOB transfer. - * @param mode Direction of the stream (read/write). - * - * @return 0 on success, or (negative) error code otherwise. - */ - int (*open)(const struct bt_mesh_blob_io *io, - const struct bt_mesh_blob_xfer *xfer, - enum bt_mesh_blob_io_mode mode); + /** @brief Open callback. + * + * Called when the reader is opened for reading. + * + * @param io BLOB stream. + * @param xfer BLOB transfer. + * @param mode Direction of the stream (read/write). + * + * @return 0 on success, or (negative) error code otherwise. + */ + int (*open)(const struct bt_mesh_blob_io *io, + const struct bt_mesh_blob_xfer *xfer, + enum bt_mesh_blob_io_mode mode); - /** @brief Close callback. - * - * Called when the reader is closed. - * - * @param io BLOB stream. - * @param xfer BLOB transfer. - */ - void (*close)(const struct bt_mesh_blob_io *io, - const struct bt_mesh_blob_xfer *xfer); + /** @brief Close callback. + * + * Called when the reader is closed. + * + * @param io BLOB stream. + * @param xfer BLOB transfer. + */ + void (*close)(const struct bt_mesh_blob_io *io, + const struct bt_mesh_blob_xfer *xfer); - /** @brief Block start callback. - * - * Called when a new block is opened for sending. Each block is only - * sent once, and are always sent in increasing order. The data chunks - * inside a single block may be requested out of order and multiple - * times. - * - * @param io BLOB stream. - * @param xfer BLOB transfer. - * @param block Block that was started. - */ - int (*block_start)(const struct bt_mesh_blob_io *io, - const struct bt_mesh_blob_xfer *xfer, - const struct bt_mesh_blob_block *block); + /** @brief Block start callback. + * + * Called when a new block is opened for sending. Each block is only + * sent once, and are always sent in increasing order. The data chunks + * inside a single block may be requested out of order and multiple + * times. + * + * @param io BLOB stream. + * @param xfer BLOB transfer. + * @param block Block that was started. + */ + int (*block_start)(const struct bt_mesh_blob_io *io, + const struct bt_mesh_blob_xfer *xfer, + const struct bt_mesh_blob_block *block); - /** @brief Block end callback. - * - * Called when the current block has been transmitted in full. - * No data from this block will be requested again, and the application - * data associated with this block may be discarded. - * - * @param io BLOB stream. - * @param xfer BLOB transfer. - * @param block Block that finished sending. - */ - void (*block_end)(const struct bt_mesh_blob_io *io, - const struct bt_mesh_blob_xfer *xfer, - const struct bt_mesh_blob_block *block); + /** @brief Block end callback. + * + * Called when the current block has been transmitted in full. + * No data from this block will be requested again, and the application + * data associated with this block may be discarded. + * + * @param io BLOB stream. + * @param xfer BLOB transfer. + * @param block Block that finished sending. + */ + void (*block_end)(const struct bt_mesh_blob_io *io, + const struct bt_mesh_blob_xfer *xfer, + const struct bt_mesh_blob_block *block); - /** @brief Chunk data write callback. - * - * Used by the BLOB Transfer Server on incoming data. - * - * Each block is divided into chunks of data. This callback is called - * when a new chunk of data is received. Chunks may be received in - * any order within their block. - * - * If the callback returns successfully, this chunk will be marked as - * received, and will not be received again unless the block is - * restarted due to a transfer suspension. If the callback returns a - * non-zero value, the chunk remains unreceived, and the BLOB Transfer - * Client will attempt to resend it later. - * - * Note that the Client will only perform a limited number of attempts - * at delivering a chunk before dropping a Target node from the transfer. - * The number of retries performed by the Client is implementation - * specific. - * - * @param io BLOB stream. - * @param xfer BLOB transfer. - * @param block Block the chunk is part of. - * @param chunk Received chunk. - * - * @return 0 on success, or (negative) error code otherwise. - */ - int (*wr)(const struct bt_mesh_blob_io *io, - const struct bt_mesh_blob_xfer *xfer, - const struct bt_mesh_blob_block *block, - const struct bt_mesh_blob_chunk *chunk); + /** @brief Chunk data write callback. + * + * Used by the BLOB Transfer Server on incoming data. + * + * Each block is divided into chunks of data. This callback is called + * when a new chunk of data is received. Chunks may be received in + * any order within their block. + * + * If the callback returns successfully, this chunk will be marked as + * received, and will not be received again unless the block is + * restarted due to a transfer suspension. If the callback returns a + * non-zero value, the chunk remains unreceived, and the BLOB Transfer + * Client will attempt to resend it later. + * + * Note that the Client will only perform a limited number of attempts + * at delivering a chunk before dropping a Target node from the transfer. + * The number of retries performed by the Client is implementation + * specific. + * + * @param io BLOB stream. + * @param xfer BLOB transfer. + * @param block Block the chunk is part of. + * @param chunk Received chunk. + * + * @return 0 on success, or (negative) error code otherwise. + */ + int (*wr)(const struct bt_mesh_blob_io *io, + const struct bt_mesh_blob_xfer *xfer, + const struct bt_mesh_blob_block *block, + const struct bt_mesh_blob_chunk *chunk); - /** @brief Chunk data read callback. - * - * Used by the BLOB Transfer Client to fetch outgoing data. - * - * The Client calls the chunk data request callback to populate a chunk - * message going out to the Target nodes. The data request callback - * may be called out of order and multiple times for each offset, and - * cannot be used as an indication of progress. - * - * Returning a non-zero status code on the chunk data request callback - * results in termination of the transfer. - * - * @param io BLOB stream. - * @param xfer BLOB transfer. - * @param block Block the chunk is part of. - * @param chunk Chunk to get the data of. The buffer pointer to by the - * @c data member should be filled by the callback. - * - * @return 0 on success, or (negative) error code otherwise. - */ - int (*rd)(const struct bt_mesh_blob_io *io, - const struct bt_mesh_blob_xfer *xfer, - const struct bt_mesh_blob_block *block, - const struct bt_mesh_blob_chunk *chunk); + /** @brief Chunk data read callback. + * + * Used by the BLOB Transfer Client to fetch outgoing data. + * + * The Client calls the chunk data request callback to populate a chunk + * message going out to the Target nodes. The data request callback + * may be called out of order and multiple times for each offset, and + * cannot be used as an indication of progress. + * + * Returning a non-zero status code on the chunk data request callback + * results in termination of the transfer. + * + * @param io BLOB stream. + * @param xfer BLOB transfer. + * @param block Block the chunk is part of. + * @param chunk Chunk to get the data of. The buffer pointer to by the + * @c data member should be filled by the callback. + * + * @return 0 on success, or (negative) error code otherwise. + */ + int (*rd)(const struct bt_mesh_blob_io *io, + const struct bt_mesh_blob_xfer *xfer, + const struct bt_mesh_blob_block *block, + const struct bt_mesh_blob_chunk *chunk); }; /** @} */ @@ -259,4 +269,4 @@ struct bt_mesh_blob_io { } #endif -#endif /* ZEPHYR_INCLUDE_BLUETOOTH_MESH_BLOB_H__ */ +#endif /* _BLE_MESH_v11_BLOB_H__ */ diff --git a/components/bt/esp_ble_mesh/v1.1/include/mesh_v1.1/mbt/blob_cli.h b/components/bt/esp_ble_mesh/v1.1/include/mesh_v1.1/mbt/blob_cli.h index 574ec82715..59b2169370 100644 --- a/components/bt/esp_ble_mesh/v1.1/include/mesh_v1.1/mbt/blob_cli.h +++ b/components/bt/esp_ble_mesh/v1.1/include/mesh_v1.1/mbt/blob_cli.h @@ -1,16 +1,26 @@ /* - * Copyright (c) 2020 Nordic Semiconductor ASA + * SPDX-FileCopyrightText: 2020 Nordic Semiconductor ASA + * SPDX-FileContributor: 2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ -#ifndef ZEPHYR_INCLUDE_BLUETOOTH_MESH_BLOB_CLI_H_ -#define ZEPHYR_INCLUDE_BLUETOOTH_MESH_BLOB_CLI_H_ +#ifndef _BLE_MESH_v11_BLOB_CLI_H_ +#define _BLE_MESH_v11_BLOB_CLI_H_ #include -#include -#include +#include "mesh/access.h" +#include "mesh/utils.h" +#include "mesh.h" +#include "mesh/utils.h" +#include "mesh/access.h" +#include "mesh/atomic.h" +#include "mesh/timer.h" +#include "mesh/compiler.h" +#include "mesh/trace.h" +#include "transport.h" +#include "mesh_v1.1/mbt/blob.h" #ifdef __cplusplus extern "C" { @@ -30,41 +40,41 @@ struct bt_mesh_blob_cli; * * @param _cli Pointer to a @ref bt_mesh_blob_cli instance. */ -#define BT_MESH_MODEL_BLOB_CLI(_cli) \ - BT_MESH_MODEL_CB(BT_MESH_MODEL_ID_BLOB_CLI, _bt_mesh_blob_cli_op, \ - NULL, _cli, &_bt_mesh_blob_cli_cb) +#define BT_MESH_MODEL_BLOB_CLI(_cli) \ + BLE_MESH_MODEL_CB(BLE_MESH_MODEL_ID_BLOB_CLI, _bt_mesh_blob_cli_op, \ + NULL, _cli, &_bt_mesh_blob_cli_cb) /** Target node's Pull mode (Pull BLOB Transfer Mode) context used * while sending chunks to the Target node. */ struct bt_mesh_blob_target_pull { - /** Timestamp when the Block Report Timeout Timer expires for this Target node. */ - int64_t block_report_timestamp; + /** Timestamp when the Block Report Timeout Timer expires for this Target node. */ + int64_t block_report_timestamp; - /** Missing chunks reported by this Target node. */ - uint8_t missing[DIV_ROUND_UP(CONFIG_BT_MESH_BLOB_CHUNK_COUNT_MAX, 8)]; + /** Missing chunks reported by this Target node. */ + uint8_t missing[DIV_ROUND_UP(CONFIG_BLE_MESH_BLOB_CHUNK_COUNT_MAX, 8)]; }; /** BLOB Transfer Client Target node. */ struct bt_mesh_blob_target { - /** Linked list node */ - sys_snode_t n; + /** Linked list node */ + sys_snode_t n; - /** Target node address. */ - uint16_t addr; + /** Target node address. */ + uint16_t addr; /** Target node's Pull mode context. * Needs to be initialized when sending a BLOB in Pull mode. */ - struct bt_mesh_blob_target_pull *pull; + struct bt_mesh_blob_target_pull *pull; - /** BLOB transfer status, see @ref bt_mesh_blob_status. */ - uint8_t status; + /** BLOB transfer status, see @ref bt_mesh_blob_status. */ + uint8_t status; - uint8_t procedure_complete:1, /* Procedure has been completed. */ - acked:1, /* Message has been acknowledged. Not used when sending. */ - timedout:1, /* Target node didn't respond after specified timeout. */ - skip:1; /* Skip Target node from broadcast. */ + uint8_t procedure_complete: 1, /* Procedure has been completed. */ + acked: 1, /* Message has been acknowledged. Not used when sending. */ + timedout: 1, /* Target node didn't respond after specified timeout. */ + skip: 1; /* Skip Target node from broadcast. */ }; /** BLOB transfer information. @@ -75,112 +85,112 @@ struct bt_mesh_blob_target { * the fields below @c id are not initialized. */ struct bt_mesh_blob_xfer_info { - /** BLOB transfer status. */ - enum bt_mesh_blob_status status; + /** BLOB transfer status. */ + enum bt_mesh_blob_status status; - /** BLOB transfer mode. */ - enum bt_mesh_blob_xfer_mode mode; + /** BLOB transfer mode. */ + enum bt_mesh_blob_xfer_mode mode; - /** BLOB transfer phase. */ - enum bt_mesh_blob_xfer_phase phase; + /** BLOB transfer phase. */ + enum bt_mesh_blob_xfer_phase phase; - /** BLOB ID. */ - uint64_t id; + /** BLOB ID. */ + uint64_t id; - /** BLOB size in octets. */ - uint32_t size; + /** BLOB size in octets. */ + uint32_t size; - /** Logarithmic representation of the block size. */ - uint8_t block_size_log; + /** Logarithmic representation of the block size. */ + uint8_t block_size_log; - /** MTU size in octets. */ - uint16_t mtu_size; + /** MTU size in octets. */ + uint16_t mtu_size; - /** Bit field indicating blocks that were not received. */ - const uint8_t *missing_blocks; + /** Bit field indicating blocks that were not received. */ + const uint8_t *missing_blocks; }; /** BLOB Transfer Client transfer inputs. */ struct bt_mesh_blob_cli_inputs { - /** Linked list of Target nodes. Each node should point to @ref - * bt_mesh_blob_target::n. - */ - sys_slist_t targets; + /** Linked list of Target nodes. Each node should point to @ref + * bt_mesh_blob_target::n. + */ + sys_slist_t targets; - /** AppKey index to send with. */ - uint16_t app_idx; + /** AppKey index to send with. */ + uint16_t app_idx; - /** Group address destination for the BLOB transfer, or @ref - * BT_MESH_ADDR_UNASSIGNED to send every message to each Target - * node individually. - */ - uint16_t group; + /** Group address destination for the BLOB transfer, or @ref + * BLE_MESH_ADDR_UNASSIGNED to send every message to each Target + * node individually. + */ + uint16_t group; - /** Time to live value of BLOB transfer messages. */ - uint8_t ttl; + /** Time to live value of BLOB transfer messages. */ + uint8_t ttl; - /** Additional response time for the Target nodes, in 10-second increments. - * - * The extra time can be used to give the Target nodes more time to respond - * to messages from the Client. The actual timeout will be calculated - * according to the following formula: - * - * @verbatim - * timeout = 20 seconds + (10 seconds * timeout_base) + (100 ms * TTL) - * @endverbatim - * - * If a Target node fails to respond to a message from the Client within the - * configured transfer timeout, the Target node is dropped. - */ - uint16_t timeout_base; + /** Additional response time for the Target nodes, in 10-second increments. + * + * The extra time can be used to give the Target nodes more time to respond + * to messages from the Client. The actual timeout will be calculated + * according to the following formula: + * + * @verbatim + * timeout = 20 seconds + (10 seconds * timeout_base) + (100 ms * TTL) + * @endverbatim + * + * If a Target node fails to respond to a message from the Client within the + * configured transfer timeout, the Target node is dropped. + */ + uint16_t timeout_base; }; /** Transfer capabilities of a Target node. */ struct bt_mesh_blob_cli_caps { - /** Max BLOB size. */ - size_t max_size; + /** Max BLOB size. */ + size_t max_size; - /** Logarithmic representation of the minimum block size. */ - uint8_t min_block_size_log; + /** Logarithmic representation of the minimum block size. */ + uint8_t min_block_size_log; - /** Logarithmic representation of the maximum block size. */ - uint8_t max_block_size_log; + /** Logarithmic representation of the maximum block size. */ + uint8_t max_block_size_log; - /** Max number of chunks per block. */ - uint16_t max_chunks; + /** Max number of chunks per block. */ + uint16_t max_chunks; - /** Max chunk size. */ - uint16_t max_chunk_size; + /** Max chunk size. */ + uint16_t max_chunk_size; - /** Max MTU size. */ - uint16_t mtu_size; + /** Max MTU size. */ + uint16_t mtu_size; - /** Supported transfer modes. */ - enum bt_mesh_blob_xfer_mode modes; + /** Supported transfer modes. */ + enum bt_mesh_blob_xfer_mode modes; }; /** BLOB Transfer Client state. */ enum bt_mesh_blob_cli_state { - /** No transfer is active. */ - BT_MESH_BLOB_CLI_STATE_NONE, - /** Retrieving transfer capabilities. */ - BT_MESH_BLOB_CLI_STATE_CAPS_GET, - /** Sending transfer start. */ - BT_MESH_BLOB_CLI_STATE_START, - /** Sending block start. */ - BT_MESH_BLOB_CLI_STATE_BLOCK_START, - /** Sending block chunks. */ - BT_MESH_BLOB_CLI_STATE_BLOCK_SEND, - /** Checking block status. */ - BT_MESH_BLOB_CLI_STATE_BLOCK_CHECK, - /** Checking transfer status. */ - BT_MESH_BLOB_CLI_STATE_XFER_CHECK, - /** Cancelling transfer. */ - BT_MESH_BLOB_CLI_STATE_CANCEL, - /** Transfer is suspended. */ - BT_MESH_BLOB_CLI_STATE_SUSPENDED, - /** Checking transfer progress. */ - BT_MESH_BLOB_CLI_STATE_XFER_PROGRESS_GET, + /** No transfer is active. */ + BT_MESH_BLOB_CLI_STATE_NONE, + /** Retrieving transfer capabilities. */ + BT_MESH_BLOB_CLI_STATE_CAPS_GET, + /** Sending transfer start. */ + BT_MESH_BLOB_CLI_STATE_START, + /** Sending block start. */ + BT_MESH_BLOB_CLI_STATE_BLOCK_START, + /** Sending block chunks. */ + BT_MESH_BLOB_CLI_STATE_BLOCK_SEND, + /** Checking block status. */ + BT_MESH_BLOB_CLI_STATE_BLOCK_CHECK, + /** Checking transfer status. */ + BT_MESH_BLOB_CLI_STATE_XFER_CHECK, + /** Cancelling transfer. */ + BT_MESH_BLOB_CLI_STATE_CANCEL, + /** Transfer is suspended. */ + BT_MESH_BLOB_CLI_STATE_SUSPENDED, + /** Checking transfer progress. */ + BT_MESH_BLOB_CLI_STATE_XFER_PROGRESS_GET, }; /** Event handler callbacks for the BLOB Transfer Client model. @@ -188,138 +198,138 @@ enum bt_mesh_blob_cli_state { * All handlers are optional. */ struct bt_mesh_blob_cli_cb { - /** @brief Capabilities retrieval completion callback. - * - * Called when the capabilities retrieval procedure completes, indicating that - * a common set of acceptable transfer parameters have been established - * for the given list of Target nodes. All compatible Target nodes have - * status code @ref BT_MESH_BLOB_SUCCESS. - * - * @param cli BLOB Transfer Client instance. - * @param caps Safe transfer capabilities if the transfer capabilities - * of at least one Target node has satisfied the Client, or NULL otherwise. - */ - void (*caps)(struct bt_mesh_blob_cli *cli, - const struct bt_mesh_blob_cli_caps *caps); + /** @brief Capabilities retrieval completion callback. + * + * Called when the capabilities retrieval procedure completes, indicating that + * a common set of acceptable transfer parameters have been established + * for the given list of Target nodes. All compatible Target nodes have + * status code @ref BT_MESH_BLOB_SUCCESS. + * + * @param cli BLOB Transfer Client instance. + * @param caps Safe transfer capabilities if the transfer capabilities + * of at least one Target node has satisfied the Client, or NULL otherwise. + */ + void (*caps)(struct bt_mesh_blob_cli *cli, + const struct bt_mesh_blob_cli_caps *caps); - /** @brief Target node loss callback. - * - * Called whenever a Target node has been lost due to some error in the - * transfer. Losing a Target node is not considered a fatal error for - * the Client until all Target nodes have been lost. - * - * @param cli BLOB Transfer Client instance. - * @param target Target node that was lost. - * @param reason Reason for the Target node loss. - */ - void (*lost_target)(struct bt_mesh_blob_cli *cli, - struct bt_mesh_blob_target *target, - enum bt_mesh_blob_status reason); + /** @brief Target node loss callback. + * + * Called whenever a Target node has been lost due to some error in the + * transfer. Losing a Target node is not considered a fatal error for + * the Client until all Target nodes have been lost. + * + * @param cli BLOB Transfer Client instance. + * @param target Target node that was lost. + * @param reason Reason for the Target node loss. + */ + void (*lost_target)(struct bt_mesh_blob_cli *cli, + struct bt_mesh_blob_target *target, + enum bt_mesh_blob_status reason); - /** @brief Transfer is suspended. - * - * Called when the transfer is suspended due to response timeout from all Target nodes. - * - * @param cli BLOB Transfer Client instance. - */ - void (*suspended)(struct bt_mesh_blob_cli *cli); + /** @brief Transfer is suspended. + * + * Called when the transfer is suspended due to response timeout from all Target nodes. + * + * @param cli BLOB Transfer Client instance. + */ + void (*suspended)(struct bt_mesh_blob_cli *cli); - /** @brief Transfer end callback. - * - * Called when the transfer ends. - * - * @param cli BLOB Transfer Client instance. - * @param xfer Completed transfer. - * @param success Status of the transfer. - * Is @c true if at least one Target - * node received the whole transfer. - */ - void (*end)(struct bt_mesh_blob_cli *cli, - const struct bt_mesh_blob_xfer *xfer, bool success); + /** @brief Transfer end callback. + * + * Called when the transfer ends. + * + * @param cli BLOB Transfer Client instance. + * @param xfer Completed transfer. + * @param success Status of the transfer. + * Is @c true if at least one Target + * node received the whole transfer. + */ + void (*end)(struct bt_mesh_blob_cli *cli, + const struct bt_mesh_blob_xfer *xfer, bool success); - /** @brief Transfer progress callback - * - * The content of @c info is invalidated upon exit from the callback. - * Therefore it needs to be copied if it is planned to be used later. - * - * @param cli BLOB Transfer Client instance. - * @param target Target node that responded to the request. - * @param info BLOB transfer information. - */ - void (*xfer_progress)(struct bt_mesh_blob_cli *cli, - struct bt_mesh_blob_target *target, - const struct bt_mesh_blob_xfer_info *info); + /** @brief Transfer progress callback + * + * The content of @c info is invalidated upon exit from the callback. + * Therefore it needs to be copied if it is planned to be used later. + * + * @param cli BLOB Transfer Client instance. + * @param target Target node that responded to the request. + * @param info BLOB transfer information. + */ + void (*xfer_progress)(struct bt_mesh_blob_cli *cli, + struct bt_mesh_blob_target *target, + const struct bt_mesh_blob_xfer_info *info); - /** @brief End of Get Transfer Progress procedure. - * - * Called when all Target nodes have responded or the procedure timed-out. - * - * @param cli BLOB Transfer Client instance. - */ - void (*xfer_progress_complete)(struct bt_mesh_blob_cli *cli); + /** @brief End of Get Transfer Progress procedure. + * + * Called when all Target nodes have responded or the procedure timed-out. + * + * @param cli BLOB Transfer Client instance. + */ + void (*xfer_progress_complete)(struct bt_mesh_blob_cli *cli); }; /** @cond INTERNAL_HIDDEN */ struct blob_cli_broadcast_ctx { - /** Called for every Target node in unicast mode, or once in case of multicast mode. */ - void (*send)(struct bt_mesh_blob_cli *cli, uint16_t dst); - /** Called after every @ref blob_cli_broadcast_ctx::send callback. */ - void (*send_complete)(struct bt_mesh_blob_cli *cli, uint16_t dst); - /** If @ref blob_cli_broadcast_ctx::acked is true, called after all Target nodes - * have confirmed reception by @ref blob_cli_broadcast_rsp. Otherwise, called - * after transmission has been completed. - */ - void (*next)(struct bt_mesh_blob_cli *cli); - /** If true, every transmission needs to be confirmed by @ref blob_cli_broadcast_rsp before - * @ref blob_cli_broadcast_ctx::next is called. - */ - bool acked; - /** If true, the message is always sent in a unicast way. */ - bool force_unicast; - /** If true, non-responsive Target nodes won't be dropped after transfer has timed out. */ - bool optional; - /** Set to true by the BLOB Transfer Client between blob_cli_broadcast - * and broadcast_complete calls. - */ - bool is_inited; - /* Defines a time in ms by which the broadcast API postpones sending the message to a next - * target or completing the broadcast. - */ - uint32_t post_send_delay_ms; + /** Called for every Target node in unicast mode, or once in case of multicast mode. */ + void (*send)(struct bt_mesh_blob_cli *cli, uint16_t dst); + /** Called after every @ref blob_cli_broadcast_ctx::send callback. */ + void (*send_complete)(struct bt_mesh_blob_cli *cli, uint16_t dst); + /** If @ref blob_cli_broadcast_ctx::acked is true, called after all Target nodes + * have confirmed reception by @ref blob_cli_broadcast_rsp. Otherwise, called + * after transmission has been completed. + */ + void (*next)(struct bt_mesh_blob_cli *cli); + /** If true, every transmission needs to be confirmed by @ref blob_cli_broadcast_rsp before + * @ref blob_cli_broadcast_ctx::next is called. + */ + bool acked; + /** If true, the message is always sent in a unicast way. */ + bool force_unicast; + /** If true, non-responsive Target nodes won't be dropped after transfer has timed out. */ + bool optional; + /** Set to true by the BLOB Transfer Client between blob_cli_broadcast + * and broadcast_complete calls. + */ + bool is_inited; + /* Defines a time in ms by which the broadcast API postpones sending the message to a next + * target or completing the broadcast. + */ + uint32_t post_send_delay_ms; }; /** INTERNAL_HIDDEN @endcond */ /** BLOB Transfer Client model instance. */ struct bt_mesh_blob_cli { - /** Event handler callbacks */ - const struct bt_mesh_blob_cli_cb *cb; + /** Event handler callbacks */ + const struct bt_mesh_blob_cli_cb *cb; - /* Runtime state */ - const struct bt_mesh_model *mod; + /* Runtime state */ + const struct bt_mesh_model *mod; - struct { - struct bt_mesh_blob_target *target; - struct blob_cli_broadcast_ctx ctx; - struct k_work_delayable retry; - /* Represents Client Timeout timer in a timestamp. Used in Pull mode only. */ - int64_t cli_timestamp; - struct k_work_delayable complete; - uint16_t pending; - uint8_t retries; - uint8_t sending : 1, - cancelled : 1; - } tx; + struct { + struct bt_mesh_blob_target *target; + struct blob_cli_broadcast_ctx ctx; + struct k_work_delayable retry; + /* Represents Client Timeout timer in a timestamp. Used in Pull mode only. */ + int64_t cli_timestamp; + struct k_work_delayable complete; + uint16_t pending; + uint8_t retries; + uint8_t sending : 1, + cancelled : 1; + } tx; - const struct bt_mesh_blob_io *io; - const struct bt_mesh_blob_cli_inputs *inputs; - const struct bt_mesh_blob_xfer *xfer; - uint32_t chunk_interval_ms; - uint16_t block_count; - uint16_t chunk_idx; - uint16_t mtu_size; - enum bt_mesh_blob_cli_state state; - struct bt_mesh_blob_block block; - struct bt_mesh_blob_cli_caps caps; + const struct bt_mesh_blob_io *io; + const struct bt_mesh_blob_cli_inputs *inputs; + const struct bt_mesh_blob_xfer *xfer; + uint32_t chunk_interval_ms; + uint16_t block_count; + uint16_t chunk_idx; + uint16_t mtu_size; + enum bt_mesh_blob_cli_state state; + struct bt_mesh_blob_block block; + struct bt_mesh_blob_cli_caps caps; }; /** @brief Retrieve transfer capabilities for a list of Target nodes. @@ -342,7 +352,7 @@ struct bt_mesh_blob_cli { * @return 0 on success, or (negative) error code otherwise. */ int bt_mesh_blob_cli_caps_get(struct bt_mesh_blob_cli *cli, - const struct bt_mesh_blob_cli_inputs *inputs); + const struct bt_mesh_blob_cli_inputs *inputs); /** @brief Perform a BLOB transfer. * @@ -366,9 +376,9 @@ int bt_mesh_blob_cli_caps_get(struct bt_mesh_blob_cli *cli, * @return 0 on success, or (negative) error code otherwise. */ int bt_mesh_blob_cli_send(struct bt_mesh_blob_cli *cli, - const struct bt_mesh_blob_cli_inputs *inputs, - const struct bt_mesh_blob_xfer *xfer, - const struct bt_mesh_blob_io *io); + const struct bt_mesh_blob_cli_inputs *inputs, + const struct bt_mesh_blob_xfer *xfer, + const struct bt_mesh_blob_io *io); /** @brief Suspend the active transfer. * @@ -392,6 +402,9 @@ int bt_mesh_blob_cli_resume(struct bt_mesh_blob_cli *cli); */ void bt_mesh_blob_cli_cancel(struct bt_mesh_blob_cli *cli); +void bt_mesh_blob_cli_send_cancel(struct bt_mesh_blob_cli *cli, + const struct bt_mesh_blob_xfer *xfer); + /** @brief Get the progress of BLOB transfer. * * This function can only be used if the BLOB Transfer Client is currently @@ -405,7 +418,7 @@ void bt_mesh_blob_cli_cancel(struct bt_mesh_blob_cli *cli); * @return 0 on success, or (negative) error code otherwise. */ int bt_mesh_blob_cli_xfer_progress_get(struct bt_mesh_blob_cli *cli, - const struct bt_mesh_blob_cli_inputs *inputs); + const struct bt_mesh_blob_cli_inputs *inputs); /** @brief Get the current progress of the active transfer in percent. * @@ -451,4 +464,4 @@ extern const struct bt_mesh_model_cb _bt_mesh_blob_cli_cb; } #endif -#endif /* ZEPHYR_INCLUDE_BLUETOOTH_MESH_BLOB_CLI_H_ */ +#endif /* _BLE_MESH_v11_BLOB_CLI_H_ */ diff --git a/components/bt/esp_ble_mesh/v1.1/include/mesh_v1.1/mbt/blob_srv.h b/components/bt/esp_ble_mesh/v1.1/include/mesh_v1.1/mbt/blob_srv.h index 8a0c6b67a1..c2a6e48765 100644 --- a/components/bt/esp_ble_mesh/v1.1/include/mesh_v1.1/mbt/blob_srv.h +++ b/components/bt/esp_ble_mesh/v1.1/include/mesh_v1.1/mbt/blob_srv.h @@ -1,14 +1,24 @@ /* - * Copyright (c) 2020 Nordic Semiconductor ASA + * SPDX-FileCopyrightText: 2020 Nordic Semiconductor ASA + * SPDX-FileContributor: 2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ -#ifndef ZEPHYR_INCLUDE_BLUETOOTH_MESH_BLOB_SRV_H_ -#define ZEPHYR_INCLUDE_BLUETOOTH_MESH_BLOB_SRV_H_ +#ifndef _BLE_MESH_v11_BLOB_SRV_H_ +#define _BLE_MESH_v11_BLOB_SRV_H_ -#include -#include +#include "mesh/access.h" +#include "mesh/utils.h" +#include "mesh_v1.1/mbt/blob.h" +#include "mesh.h" +#include "mesh/utils.h" +#include "mesh/access.h" +#include "mesh/atomic.h" +#include "mesh/timer.h" +#include "mesh/compiler.h" +#include "mesh/trace.h" +#include "transport.h" #ifdef __cplusplus extern "C" { @@ -26,10 +36,9 @@ struct bt_mesh_blob_srv; * * @brief Max number of blocks in a single transfer. */ -#if defined(CONFIG_BT_MESH_BLOB_SRV) -#define BT_MESH_BLOB_BLOCKS_MAX \ - (DIV_ROUND_UP(CONFIG_BT_MESH_BLOB_SIZE_MAX, \ - CONFIG_BT_MESH_BLOB_BLOCK_SIZE_MIN)) +#if defined(CONFIG_BLE_MESH_BLOB_SRV) +#define BT_MESH_BLOB_BLOCKS_MAX \ + (DIV_ROUND_UP(CONFIG_BLE_MESH_BLOB_SIZE_MAX, CONFIG_BLE_MESH_BLOB_BLOCK_SIZE_MIN)) #else #define BT_MESH_BLOB_BLOCKS_MAX 1 #endif @@ -41,121 +50,121 @@ struct bt_mesh_blob_srv; * @param _srv Pointer to a @ref bt_mesh_blob_srv instance. */ #define BT_MESH_MODEL_BLOB_SRV(_srv) \ - BT_MESH_MODEL_CB(BT_MESH_MODEL_ID_BLOB_SRV, _bt_mesh_blob_srv_op, \ - NULL, _srv, &_bt_mesh_blob_srv_cb) + BLE_MESH_MODEL_CB(BLE_MESH_MODEL_ID_BLOB_SRV, _bt_mesh_blob_srv_op, \ + NULL, _srv, &_bt_mesh_blob_srv_cb) /** @brief BLOB Transfer Server model event handlers. * * All callbacks are optional. */ struct bt_mesh_blob_srv_cb { - /** @brief Transfer start callback. - * - * Called when the transfer has started with the prepared BLOB ID. - * - * @param srv BLOB Transfer Server instance. - * @param ctx Message context for the incoming start message. The - * entire transfer will be sent from the same source - * address. - * @param xfer Transfer parameters. - * - * @return 0 on success, or (negative) error code to reject the - * transfer. - */ - int (*start)(struct bt_mesh_blob_srv *srv, struct bt_mesh_msg_ctx *ctx, - struct bt_mesh_blob_xfer *xfer); + /** @brief Transfer start callback. + * + * Called when the transfer has started with the prepared BLOB ID. + * + * @param srv BLOB Transfer Server instance. + * @param ctx Message context for the incoming start message. The + * entire transfer will be sent from the same source + * address. + * @param xfer Transfer parameters. + * + * @return 0 on success, or (negative) error code to reject the + * transfer. + */ + int (*start)(struct bt_mesh_blob_srv *srv, struct bt_mesh_msg_ctx *ctx, + struct bt_mesh_blob_xfer *xfer); - /** @brief Transfer end callback. - * - * Called when the transfer ends, either because it was cancelled, or - * because it finished successfully. A new transfer may be prepared. - * - * @note The transfer may end before it's started if the start - * parameters are invalid. - * - * @param srv BLOB Transfer Server instance. - * @param id BLOB ID of the cancelled transfer. - * @param success Whether the transfer was successful. - */ - void (*end)(struct bt_mesh_blob_srv *srv, uint64_t id, bool success); + /** @brief Transfer end callback. + * + * Called when the transfer ends, either because it was cancelled, or + * because it finished successfully. A new transfer may be prepared. + * + * @note The transfer may end before it's started if the start + * parameters are invalid. + * + * @param srv BLOB Transfer Server instance. + * @param id BLOB ID of the cancelled transfer. + * @param success Whether the transfer was successful. + */ + void (*end)(struct bt_mesh_blob_srv *srv, uint64_t id, bool success); - /** @brief Transfer suspended callback. - * - * Called if the Server timed out while waiting for a transfer packet. - * A suspended transfer may resume later from the start of the current - * block. Any received chunks in the current block should be discarded, - * they will be received again if the transfer resumes. - * - * The transfer will call @c resumed again when resuming. - * - * @note The BLOB Transfer Server does not run a timer in the suspended state, - * and it's up to the application to determine whether the - * transfer should be permanently cancelled. Without interaction, - * the transfer will be suspended indefinitely, and the BLOB Transfer - * Server will not accept any new transfers. - * - * @param srv BLOB Transfer Server instance. - */ - void (*suspended)(struct bt_mesh_blob_srv *srv); + /** @brief Transfer suspended callback. + * + * Called if the Server timed out while waiting for a transfer packet. + * A suspended transfer may resume later from the start of the current + * block. Any received chunks in the current block should be discarded, + * they will be received again if the transfer resumes. + * + * The transfer will call @c resumed again when resuming. + * + * @note The BLOB Transfer Server does not run a timer in the suspended state, + * and it's up to the application to determine whether the + * transfer should be permanently cancelled. Without interaction, + * the transfer will be suspended indefinitely, and the BLOB Transfer + * Server will not accept any new transfers. + * + * @param srv BLOB Transfer Server instance. + */ + void (*suspended)(struct bt_mesh_blob_srv *srv); - /** @brief Transfer resume callback. - * - * Called if the transfer is resumed after being suspended. - * - * @param srv BLOB Transfer Server instance. - */ - void (*resume)(struct bt_mesh_blob_srv *srv); + /** @brief Transfer resume callback. + * + * Called if the transfer is resumed after being suspended. + * + * @param srv BLOB Transfer Server instance. + */ + void (*resume)(struct bt_mesh_blob_srv *srv); - /** @brief Transfer recovery callback. - * - * Called when the Bluetooth Mesh subsystem is started if the device is rebooted - * in the middle of a transfer. - * - * Transfers will not be resumed after a reboot if this callback is not - * defined. - * - * @param srv BLOB Transfer Server instance. - * @param xfer Transfer to resume. - * @param io BLOB stream return parameter. Must be set to a valid - * BLOB stream by the callback. - * - * @return 0 on success, or (negative) error code to abandon the - * transfer. - */ - int (*recover)(struct bt_mesh_blob_srv *srv, - struct bt_mesh_blob_xfer *xfer, - const struct bt_mesh_blob_io **io); + /** @brief Transfer recovery callback. + * + * Called when the Bluetooth Mesh subsystem is started if the device is rebooted + * in the middle of a transfer. + * + * Transfers will not be resumed after a reboot if this callback is not + * defined. + * + * @param srv BLOB Transfer Server instance. + * @param xfer Transfer to resume. + * @param io BLOB stream return parameter. Must be set to a valid + * BLOB stream by the callback. + * + * @return 0 on success, or (negative) error code to abandon the + * transfer. + */ + int (*recover)(struct bt_mesh_blob_srv *srv, + struct bt_mesh_blob_xfer *xfer, + const struct bt_mesh_blob_io **io); }; /** @brief BLOB Transfer Server model instance. */ struct bt_mesh_blob_srv { - /** Event handler callbacks. */ - const struct bt_mesh_blob_srv_cb *cb; + /** Event handler callbacks. */ + const struct bt_mesh_blob_srv_cb *cb; - /* Runtime state: */ - const struct bt_mesh_blob_io *io; - struct k_work_delayable rx_timeout; - struct bt_mesh_blob_block block; - const struct bt_mesh_model *mod; - enum bt_mesh_blob_xfer_phase phase; + /* Runtime state: */ + const struct bt_mesh_blob_io *io; + struct k_work_delayable rx_timeout; + struct bt_mesh_blob_block block; + const struct bt_mesh_model *mod; + enum bt_mesh_blob_xfer_phase phase; - struct bt_mesh_blob_srv_state { - struct bt_mesh_blob_xfer xfer; - uint16_t cli; - uint16_t app_idx; - uint16_t timeout_base; - uint16_t mtu_size; - uint8_t ttl; + struct bt_mesh_blob_srv_state { + struct bt_mesh_blob_xfer xfer; + uint16_t cli; + uint16_t app_idx; + uint16_t timeout_base; + uint16_t mtu_size; + uint8_t ttl; - /* Bitfield of pending blocks. */ - ATOMIC_DEFINE(blocks, BT_MESH_BLOB_BLOCKS_MAX); - } state; + /* Bitfield of pending blocks. */ + BLE_MESH_ATOMIC_DEFINE(blocks, BT_MESH_BLOB_BLOCKS_MAX); + } state; - /* Pull mode (Pull BLOB Transfer Mode) behavior. */ - struct { - uint16_t chunk_idx; - struct k_work_delayable report; - } pull; + /* Pull mode (Pull BLOB Transfer Mode) behavior. */ + struct { + uint16_t chunk_idx; + struct k_work_delayable report; + } pull; }; /** @brief Prepare BLOB Transfer Server for an incoming transfer. @@ -174,8 +183,8 @@ struct bt_mesh_blob_srv { * @return 0 on success, or (negative) error code on failure. */ int bt_mesh_blob_srv_recv(struct bt_mesh_blob_srv *srv, uint64_t id, - const struct bt_mesh_blob_io *io, uint8_t ttl, - uint16_t timeout_base); + const struct bt_mesh_blob_io *io, uint8_t ttl, + uint16_t timeout_base); /** @brief Cancel the current BLOB transfer. * @@ -217,4 +226,4 @@ extern const struct bt_mesh_model_cb _bt_mesh_blob_srv_cb; } #endif -#endif /* ZEPHYR_INCLUDE_BLUETOOTH_MESH_BLOB_SRV_H_ */ +#endif /* _BLE_MESH_v11_BLOB_SRV_H_ */ diff --git a/components/bt/esp_ble_mesh/v1.1/mbt/blob.h b/components/bt/esp_ble_mesh/v1.1/mbt/blob.h index 162725480e..c539219d9e 100644 --- a/components/bt/esp_ble_mesh/v1.1/mbt/blob.h +++ b/components/bt/esp_ble_mesh/v1.1/mbt/blob.h @@ -1,44 +1,51 @@ /* - * Copyright (c) 2020 Nordic Semiconductor ASA + * SPDX-FileCopyrightText: 2020 Nordic Semiconductor ASA + * SPDX-FileContributor: 2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ #include -#include -#define BT_MESH_BLOB_OP_XFER_GET BT_MESH_MODEL_OP_2(0x83, 0x00) -#define BT_MESH_BLOB_OP_XFER_START BT_MESH_MODEL_OP_2(0x83, 0x01) -#define BT_MESH_BLOB_OP_XFER_CANCEL BT_MESH_MODEL_OP_2(0x83, 0x02) -#define BT_MESH_BLOB_OP_XFER_STATUS BT_MESH_MODEL_OP_2(0x83, 0x03) -#define BT_MESH_BLOB_OP_BLOCK_GET BT_MESH_MODEL_OP_2(0x83, 0x05) -#define BT_MESH_BLOB_OP_BLOCK_START BT_MESH_MODEL_OP_2(0x83, 0x04) -#define BT_MESH_BLOB_OP_CHUNK BT_MESH_MODEL_OP_1(0x66) -#define BT_MESH_BLOB_OP_BLOCK_STATUS BT_MESH_MODEL_OP_1(0x67) -#define BT_MESH_BLOB_OP_BLOCK_REPORT BT_MESH_MODEL_OP_1(0x68) -#define BT_MESH_BLOB_OP_INFO_GET BT_MESH_MODEL_OP_2(0x83, 0x06) -#define BT_MESH_BLOB_OP_INFO_STATUS BT_MESH_MODEL_OP_2(0x83, 0x07) +#if CONFIG_BLE_MESH_BLOB_CLI || CONFIG_BLE_MESH_BLOB_SRV + +#include "mesh/utils.h" +#include "mesh_v1.1/mbt/blob.h" +#include "mesh_v1.1/mbt/blob_cli.h" +#include "mesh_v1.1/mbt/blob_srv.h" + +#define BT_MESH_BLOB_OP_XFER_GET BLE_MESH_MODEL_OP_2(0x83, 0x00) +#define BT_MESH_BLOB_OP_XFER_START BLE_MESH_MODEL_OP_2(0x83, 0x01) +#define BT_MESH_BLOB_OP_XFER_CANCEL BLE_MESH_MODEL_OP_2(0x83, 0x02) +#define BT_MESH_BLOB_OP_XFER_STATUS BLE_MESH_MODEL_OP_2(0x83, 0x03) +#define BT_MESH_BLOB_OP_BLOCK_GET BLE_MESH_MODEL_OP_2(0x83, 0x05) +#define BT_MESH_BLOB_OP_BLOCK_START BLE_MESH_MODEL_OP_2(0x83, 0x04) +#define BT_MESH_BLOB_OP_CHUNK BLE_MESH_MODEL_OP_1(0x66) +#define BT_MESH_BLOB_OP_BLOCK_STATUS BLE_MESH_MODEL_OP_1(0x67) +#define BT_MESH_BLOB_OP_BLOCK_REPORT BLE_MESH_MODEL_OP_1(0x68) +#define BT_MESH_BLOB_OP_INFO_GET BLE_MESH_MODEL_OP_2(0x83, 0x06) +#define BT_MESH_BLOB_OP_INFO_STATUS BLE_MESH_MODEL_OP_2(0x83, 0x07) #define BLOB_BLOCK_NOT_SET 0xffff -#define BLOB_CHUNK_SDU_OVERHEAD \ - (BT_MESH_MODEL_OP_LEN(BT_MESH_BLOB_OP_CHUNK) + 2 + BT_MESH_MIC_SHORT) +#define BLOB_CHUNK_SDU_OVERHEAD \ + (BLE_MESH_MODEL_OP_LEN(BT_MESH_BLOB_OP_CHUNK) + 2 + BLE_MESH_MIC_SHORT) #define BLOB_CHUNK_SIZE_MAX(sdu_max) ((sdu_max) - BLOB_CHUNK_SDU_OVERHEAD) #define BLOB_CHUNK_SDU_LEN(chunk_size) (BLOB_CHUNK_SDU_OVERHEAD + (chunk_size)) -#if CONFIG_BT_MESH_ALIGN_CHUNK_SIZE_TO_MAX_SEGMENT || \ - CONFIG_BT_MESH_RX_BLOB_CHUNK_SIZE > BLOB_CHUNK_SIZE_MAX(BT_MESH_RX_SDU_MAX) -#define BLOB_RX_CHUNK_SIZE BLOB_CHUNK_SIZE_MAX(BT_MESH_RX_SDU_MAX) +#if CONFIG_BLE_MESH_ALIGN_CHUNK_SIZE_TO_MAX_SEGMENT || \ + CONFIG_BLE_MESH_RX_BLOB_CHUNK_SIZE > BLOB_CHUNK_SIZE_MAX(BLE_MESH_RX_SDU_MAX) +#define BLOB_RX_CHUNK_SIZE BLOB_CHUNK_SIZE_MAX(BLE_MESH_RX_SDU_MAX) #else -#define BLOB_RX_CHUNK_SIZE CONFIG_BT_MESH_RX_BLOB_CHUNK_SIZE +#define BLOB_RX_CHUNK_SIZE CONFIG_BLE_MESH_RX_BLOB_CHUNK_SIZE #endif -#if CONFIG_BT_MESH_ALIGN_CHUNK_SIZE_TO_MAX_SEGMENT || \ - CONFIG_BT_MESH_TX_BLOB_CHUNK_SIZE > BLOB_CHUNK_SIZE_MAX(BT_MESH_TX_SDU_MAX) -#define BLOB_TX_CHUNK_SIZE BLOB_CHUNK_SIZE_MAX(BT_MESH_TX_SDU_MAX) +#if CONFIG_BLE_MESH_ALIGN_CHUNK_SIZE_TO_MAX_SEGMENT || \ + CONFIG_BLE_MESH_TX_BLOB_CHUNK_SIZE > BLOB_CHUNK_SIZE_MAX(BLE_MESH_TX_SDU_MAX) +#define BLOB_TX_CHUNK_SIZE BLOB_CHUNK_SIZE_MAX(BLE_MESH_TX_SDU_MAX) #else -#define BLOB_TX_CHUNK_SIZE CONFIG_BT_MESH_TX_BLOB_CHUNK_SIZE +#define BLOB_TX_CHUNK_SIZE CONFIG_BLE_MESH_TX_BLOB_CHUNK_SIZE #endif /* Utility macros for calculating log2 of a number at compile time. @@ -57,17 +64,17 @@ #define BLOB_BLOCK_SIZE_LOG_FLOOR(x) (LISTIFY(20, _BLOB_LOG_2_FLOOR, (), x) 20) /* Log2 representation of the minimum block size */ -#define BLOB_BLOCK_SIZE_LOG_MIN BLOB_BLOCK_SIZE_LOG_CEIL(CONFIG_BT_MESH_BLOB_BLOCK_SIZE_MIN) +#define BLOB_BLOCK_SIZE_LOG_MIN BLOB_BLOCK_SIZE_LOG_CEIL(CONFIG_BLE_MESH_BLOB_BLOCK_SIZE_MIN) /* Log2 representation of the maximum block size */ -#define BLOB_BLOCK_SIZE_LOG_MAX BLOB_BLOCK_SIZE_LOG_FLOOR(CONFIG_BT_MESH_BLOB_BLOCK_SIZE_MAX) +#define BLOB_BLOCK_SIZE_LOG_MAX BLOB_BLOCK_SIZE_LOG_FLOOR(CONFIG_BLE_MESH_BLOB_BLOCK_SIZE_MAX) -#if defined(CONFIG_BT_MESH_BLOB_SRV) -#define BLOB_BLOCK_REPORT_STATUS_MSG_MAXLEN ( \ - MAX(sizeof(((struct bt_mesh_blob_block *)0)->missing), \ - CONFIG_BT_MESH_BLOB_SRV_PULL_REQ_COUNT * 3)) -#define BLOB_BLOCK_STATUS_MSG_MAXLEN (5 + \ - MAX(sizeof(((struct bt_mesh_blob_block *)0)->missing), \ - CONFIG_BT_MESH_BLOB_SRV_PULL_REQ_COUNT * 3)) +#if defined(CONFIG_BLE_MESH_BLOB_SRV) +#define BLOB_BLOCK_REPORT_STATUS_MSG_MAXLEN ( \ + MAX(sizeof(((struct bt_mesh_blob_block *)0)->missing), \ + CONFIG_BLE_MESH_BLOB_SRV_PULL_REQ_COUNT * 3)) +#define BLOB_BLOCK_STATUS_MSG_MAXLEN (5 + \ + MAX(sizeof(((struct bt_mesh_blob_block *)0)->missing), \ + CONFIG_BLE_MESH_BLOB_SRV_PULL_REQ_COUNT * 3)) #else #define BLOB_BLOCK_REPORT_STATUS_MSG_MAXLEN sizeof(((struct bt_mesh_blob_srv *)0)->block.missing) #define BLOB_BLOCK_STATUS_MSG_MAXLEN (5 + sizeof(((struct bt_mesh_blob_srv *)0)->block.missing)) @@ -76,49 +83,50 @@ #define BLOB_XFER_STATUS_MSG_MAXLEN (17 + sizeof(((struct bt_mesh_blob_srv *)0)->state.blocks)) enum bt_mesh_blob_chunks_missing { - BT_MESH_BLOB_CHUNKS_MISSING_ALL, - BT_MESH_BLOB_CHUNKS_MISSING_NONE, - BT_MESH_BLOB_CHUNKS_MISSING_SOME, - BT_MESH_BLOB_CHUNKS_MISSING_ENCODED, + BT_MESH_BLOB_CHUNKS_MISSING_ALL, + BT_MESH_BLOB_CHUNKS_MISSING_NONE, + BT_MESH_BLOB_CHUNKS_MISSING_SOME, + BT_MESH_BLOB_CHUNKS_MISSING_ENCODED, }; static inline size_t blob_block_size(size_t xfer_size, uint8_t block_size_log, - uint32_t idx) + uint32_t idx) { - if (((idx + 1U) << block_size_log) <= xfer_size) { - return (1U << block_size_log); - } + if (((idx + 1U) << block_size_log) <= xfer_size) { + return (1U << block_size_log); + } - return xfer_size & BIT_MASK(block_size_log); + return xfer_size & BIT_MASK(block_size_log); } static inline void blob_chunk_missing_set(uint8_t *missing_chunks, - int idx, bool missing) + int idx, bool missing) { - WRITE_BIT(missing_chunks[idx / 8], idx % 8, missing); + WRITE_BIT(missing_chunks[idx / 8], idx % 8, missing); } static inline bool blob_chunk_missing_get(const uint8_t *missing_chunks, int idx) { - return !!(missing_chunks[idx / 8] & BIT(idx % 8)); + return !!(missing_chunks[idx / 8] & BIT(idx % 8)); } static inline void blob_chunk_missing_set_all(struct bt_mesh_blob_block *block) { - size_t bytes = block->chunk_count / 8; + size_t bytes = block->chunk_count / 8; - memset(block->missing, 0xff, bytes); - if (block->chunk_count % 8) { - block->missing[bytes] = BIT_MASK(block->chunk_count % 8); - } + memset(block->missing, 0xff, bytes); + if (block->chunk_count % 8) { + block->missing[bytes] = BIT_MASK(block->chunk_count % 8); + } } static inline void blob_chunk_missing_set_none(struct bt_mesh_blob_block *block) { - memset(block->missing, 0, sizeof(block->missing)); + memset(block->missing, 0, sizeof(block->missing)); } +#if CONFIG_BLE_MESH_BLOB_CLI /** @brief Perform a message broadcast to all BLOB Transfer Client Target nodes. * * Will send to a group or each Target node individually, repeating until @@ -128,7 +136,7 @@ static inline void blob_chunk_missing_set_none(struct bt_mesh_blob_block *block) * @param ctx Broadcast context */ void blob_cli_broadcast(struct bt_mesh_blob_cli *cli, - const struct blob_cli_broadcast_ctx *ctx); + const struct blob_cli_broadcast_ctx *ctx); /** @brief Register that a Target node responded to a broadcast. * @@ -136,7 +144,7 @@ void blob_cli_broadcast(struct bt_mesh_blob_cli *cli, * @param target Target node that responded. */ void blob_cli_broadcast_rsp(struct bt_mesh_blob_cli *cli, - struct bt_mesh_blob_target *target); + struct bt_mesh_blob_target *target); /** @brief Notify the BLOB Transfer Client that the requested transmission is complete. * @@ -152,3 +160,5 @@ void blob_cli_broadcast_tx_complete(struct bt_mesh_blob_cli *cli); * @param cli BLOB Transfer Client instance. */ void blob_cli_broadcast_abort(struct bt_mesh_blob_cli *cli); +#endif /* CONFIG_BLE_MESH_BLOB_CLI */ +#endif /* CONFIG_BLE_MESH_BLOB_CLI || CONFIG_BLE_MESH_BLOB_SRV */ 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 40d3d37a6c..9132829a23 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,337 +1,341 @@ /* - * Copyright (c) 2020 Nordic Semiconductor ASA + * SPDX-FileCopyrightText: 2020 Nordic Semiconductor ASA + * SPDX-FileContributor: 2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ #include -#include -#include -#include "mesh.h" + #include "blob.h" #include "net.h" #include "transport.h" +#include "mesh/common.h" +#include "mesh/slist.h" +#include "mesh_v1.1/mbt/blob_cli.h" +#include "mesh_v1.1/mbt/blob_srv.h" +#include "mesh_v1.1/mbt/blob.h" +#include "mesh/buf.h" -#define LOG_LEVEL CONFIG_BT_MESH_MODEL_LOG_LEVEL -#include -LOG_MODULE_REGISTER(bt_mesh_blob_cli); +#if CONFIG_BLE_MESH_BLOB_CLI #define TARGETS_FOR_EACH(cli, target) \ - SYS_SLIST_FOR_EACH_CONTAINER((sys_slist_t *)&(cli)->inputs->targets, \ - target, n) + SYS_SLIST_FOR_EACH_CONTAINER((sys_slist_t *)&(cli)->inputs->targets, \ + target, n) /* The Maximum BLOB Poll Interval - T_MBPI */ #define BLOB_POLL_TIME_MAX_SECS 30 #define CLIENT_TIMEOUT_MSEC(cli) (10 * MSEC_PER_SEC * (cli->inputs->timeout_base + 2) + \ - 100 * cli->inputs->ttl) + 100 * cli->inputs->ttl) #define BLOCK_REPORT_TIME_MSEC ((BLOB_POLL_TIME_MAX_SECS * 2 + 7) * 1000) /* BLOB Client is running Send Data State Machine from section 6.2.4.2. */ #define SENDING_CHUNKS_IN_PULL_MODE(cli) ((cli)->state == BT_MESH_BLOB_CLI_STATE_BLOCK_SEND && \ - (cli)->xfer->mode == BT_MESH_BLOB_XFER_MODE_PULL) -#define UNICAST_MODE(cli) ((cli)->inputs->group == BT_MESH_ADDR_UNASSIGNED || \ - (cli)->tx.ctx.force_unicast) + (cli)->xfer->mode == BT_MESH_BLOB_XFER_MODE_PULL) +#define UNICAST_MODE(cli) ((cli)->inputs->group == BLE_MESH_ADDR_UNASSIGNED || \ + (cli)->tx.ctx.force_unicast) -BUILD_ASSERT((BLOB_XFER_STATUS_MSG_MAXLEN + BT_MESH_MODEL_OP_LEN(BT_MESH_BLOB_OP_XFER_STATUS) + - BT_MESH_MIC_SHORT) <= BT_MESH_RX_SDU_MAX, - "The BLOB Transfer Status message does not fit into the maximum incoming SDU size."); +_Static_assert((BLOB_XFER_STATUS_MSG_MAXLEN + BLE_MESH_MODEL_OP_LEN(BT_MESH_BLOB_OP_XFER_STATUS) + + BLE_MESH_MIC_SHORT) <= BLE_MESH_RX_SDU_MAX, + "The BLOB Transfer Status message does not fit into the maximum incoming SDU size."); -BUILD_ASSERT((BLOB_BLOCK_REPORT_STATUS_MSG_MAXLEN + - BT_MESH_MODEL_OP_LEN(BT_MESH_BLOB_OP_BLOCK_REPORT) + BT_MESH_MIC_SHORT) - <= BT_MESH_RX_SDU_MAX, - "The BLOB Partial Block Report message does not fit into the maximum incoming SDU " - "size."); +_Static_assert((BLOB_BLOCK_REPORT_STATUS_MSG_MAXLEN + + BLE_MESH_MODEL_OP_LEN(BT_MESH_BLOB_OP_BLOCK_REPORT) + BLE_MESH_MIC_SHORT) + <= BLE_MESH_RX_SDU_MAX, + "The BLOB Partial Block Report message does not fit into the maximum incoming SDU " + "size."); -BUILD_ASSERT((BLOB_BLOCK_STATUS_MSG_MAXLEN + BT_MESH_MODEL_OP_LEN(BT_MESH_BLOB_OP_BLOCK_STATUS) + - BT_MESH_MIC_SHORT) <= BT_MESH_RX_SDU_MAX, - "The BLOB Block Status message does not fit into the maximum incoming SDU size."); +_Static_assert((BLOB_BLOCK_STATUS_MSG_MAXLEN + BLE_MESH_MODEL_OP_LEN(BT_MESH_BLOB_OP_BLOCK_STATUS) + + BLE_MESH_MIC_SHORT) <= BLE_MESH_RX_SDU_MAX, + "The BLOB Block Status message does not fit into the maximum incoming SDU size."); +NET_BUF_SIMPLE_DEFINE_STATIC(chunk_buf, BLE_MESH_TX_SDU_MAX); struct block_status { - enum bt_mesh_blob_status status; - enum bt_mesh_blob_chunks_missing missing; - struct bt_mesh_blob_block block; + enum bt_mesh_blob_status status; + enum bt_mesh_blob_chunks_missing missing; + struct bt_mesh_blob_block block; }; static struct bt_mesh_blob_target *next_target(struct bt_mesh_blob_cli *cli, - struct bt_mesh_blob_target **current); + struct bt_mesh_blob_target **current); static void transfer_cancel(struct bt_mesh_blob_cli *cli); static void start_retry_timer(struct bt_mesh_blob_cli *cli) { - k_timeout_t next_timeout; + k_timeout_t next_timeout; - if (SENDING_CHUNKS_IN_PULL_MODE(cli)) { - int64_t next_timeout_ms = cli->tx.cli_timestamp; - struct bt_mesh_blob_target *target = NULL; + if (SENDING_CHUNKS_IN_PULL_MODE(cli)) { + int64_t next_timeout_ms = cli->tx.cli_timestamp; + struct bt_mesh_blob_target *target = NULL; - TARGETS_FOR_EACH(cli, target) { - if (!target->procedure_complete && - target->status == BT_MESH_BLOB_SUCCESS && - target->pull->block_report_timestamp < next_timeout_ms) { - next_timeout_ms = target->pull->block_report_timestamp; - } - } + TARGETS_FOR_EACH(cli, target) { + if (!target->procedure_complete && + target->status == BT_MESH_BLOB_SUCCESS && + target->pull->block_report_timestamp < next_timeout_ms) { + next_timeout_ms = target->pull->block_report_timestamp; + } + } - /* cli_timestamp and block_report_timestamp represent absolute time, while - * k_work_* functions use relative time. - */ - next_timeout_ms -= k_uptime_get(); - next_timeout = next_timeout_ms <= 0 ? K_NO_WAIT : K_MSEC(next_timeout_ms); - } else { - next_timeout = K_MSEC(CLIENT_TIMEOUT_MSEC(cli) / - CONFIG_BT_MESH_BLOB_CLI_BLOCK_RETRIES); - } + /* cli_timestamp and block_report_timestamp represent absolute time, while + * k_work_* functions use relative time. + */ + next_timeout_ms -= k_uptime_get(); + next_timeout = next_timeout_ms <= 0 ? K_NO_WAIT : K_MSEC(next_timeout_ms); + } else { + next_timeout = K_MSEC(CLIENT_TIMEOUT_MSEC(cli) / + CONFIG_BLE_MESH_BLOB_CLI_BLOCK_RETRIES); + } - (void)k_work_reschedule(&cli->tx.retry, next_timeout); + (void)k_work_reschedule(&cli->tx.retry, next_timeout); } static void cli_state_reset(struct bt_mesh_blob_cli *cli) { - k_work_cancel_delayable(&cli->tx.retry); - cli->xfer = NULL; - cli->state = BT_MESH_BLOB_CLI_STATE_NONE; - cli->tx.ctx.is_inited = 0; - cli->tx.cli_timestamp = 0ll; - cli->tx.sending = 0; + k_work_cancel_delayable(&cli->tx.retry); + cli->xfer = NULL; + cli->state = BT_MESH_BLOB_CLI_STATE_NONE; + cli->tx.ctx.is_inited = 0; + cli->tx.cli_timestamp = 0ll; + cli->tx.sending = 0; } static struct bt_mesh_blob_target *target_get(struct bt_mesh_blob_cli *cli, - uint16_t addr) + uint16_t addr) { - struct bt_mesh_blob_target *target; + struct bt_mesh_blob_target *target; - TARGETS_FOR_EACH(cli, target) { - if (target->addr == addr) { - return target; - } - } + TARGETS_FOR_EACH(cli, target) { + if (target->addr == addr) { + return target; + } + } - LOG_ERR("Unknown target 0x%04x", addr); - return NULL; + BT_ERR("Unknown target 0x%04x", addr); + return NULL; } static void target_drop(struct bt_mesh_blob_cli *cli, - struct bt_mesh_blob_target *target, - enum bt_mesh_blob_status reason) + struct bt_mesh_blob_target *target, + enum bt_mesh_blob_status reason) { - LOG_WRN("Dropping 0x%04x: %u", target->addr, reason); + BT_WARN("Dropping 0x%04x: %u", target->addr, reason); - target->status = reason; - if (cli->cb && cli->cb->lost_target) { - cli->cb->lost_target(cli, target, reason); - } + target->status = reason; + if (cli->cb && cli->cb->lost_target) { + cli->cb->lost_target(cli, target, reason); + } } static uint32_t targets_reset(struct bt_mesh_blob_cli *cli) { - struct bt_mesh_blob_target *target; - uint32_t count = 0; + struct bt_mesh_blob_target *target; + uint32_t count = 0; - TARGETS_FOR_EACH(cli, target) { - if (target->status == BT_MESH_BLOB_SUCCESS) { - target->acked = 0U; - count++; - } - } + TARGETS_FOR_EACH(cli, target) { + if (target->status == BT_MESH_BLOB_SUCCESS) { + target->acked = 0U; + count++; + } + } - return count; + return count; } static bool targets_active(struct bt_mesh_blob_cli *cli) { - struct bt_mesh_blob_target *target; + struct bt_mesh_blob_target *target; - TARGETS_FOR_EACH(cli, target) { - if (target->status == BT_MESH_BLOB_SUCCESS) { - return true; - } - } + TARGETS_FOR_EACH(cli, target) { + if (target->status == BT_MESH_BLOB_SUCCESS) { + return true; + } + } - return false; + return false; } static bool targets_timedout(struct bt_mesh_blob_cli *cli) { - struct bt_mesh_blob_target *target; + struct bt_mesh_blob_target *target; - TARGETS_FOR_EACH(cli, target) { - if (!!target->timedout) { - return true; - } - } + TARGETS_FOR_EACH(cli, target) { + if (!!target->timedout) { + return true; + } + } - return false; + return false; } static int io_open(struct bt_mesh_blob_cli *cli) { - if (!cli->io->open) { - return 0; - } + if (!cli->io->open) { + return 0; + } - return cli->io->open(cli->io, cli->xfer, BT_MESH_BLOB_READ); + return cli->io->open(cli->io, cli->xfer, BT_MESH_BLOB_READ); } static void io_close(struct bt_mesh_blob_cli *cli) { - if (!cli->io->close) { - return; - } + if (!cli->io->close) { + return; + } - cli->io->close(cli->io, cli->xfer); + cli->io->close(cli->io, cli->xfer); } static uint16_t next_missing_chunk(struct bt_mesh_blob_cli *cli, - const uint8_t *missing_chunks, - uint16_t idx) + const uint8_t *missing_chunks, + uint16_t idx) { - do { - if (blob_chunk_missing_get(missing_chunks, idx)) { - break; - } - } while (++idx < cli->block.chunk_count); + do { + if (blob_chunk_missing_get(missing_chunks, idx)) { + break; + } + } while (++idx < cli->block.chunk_count); - return idx; + return idx; } /* Used in Pull mode to collect all missing chunks from each target in cli->block.missing. */ static void update_missing_chunks(struct bt_mesh_blob_cli *cli) { - struct bt_mesh_blob_target *target; + struct bt_mesh_blob_target *target; - memset(cli->block.missing, 0, sizeof(cli->block.missing)); + memset(cli->block.missing, 0, sizeof(cli->block.missing)); - TARGETS_FOR_EACH(cli, target) { - if (target->procedure_complete || target->timedout) { - continue; - } + TARGETS_FOR_EACH(cli, target) { + if (target->procedure_complete || target->timedout) { + continue; + } - for (size_t idx = 0; idx < cli->block.chunk_count; idx++) { - bool missing = blob_chunk_missing_get(cli->block.missing, idx) || - blob_chunk_missing_get(target->pull->missing, idx); - blob_chunk_missing_set(cli->block.missing, idx, missing); - } - } + for (size_t idx = 0; idx < cli->block.chunk_count; idx++) { + bool missing = blob_chunk_missing_get(cli->block.missing, idx) || + blob_chunk_missing_get(target->pull->missing, idx); + blob_chunk_missing_set(cli->block.missing, idx, missing); + } + } } static inline size_t chunk_size(const struct bt_mesh_blob_xfer *xfer, - const struct bt_mesh_blob_block *block, - uint16_t chunk_idx) + const struct bt_mesh_blob_block *block, + uint16_t chunk_idx) { - if ((chunk_idx == block->chunk_count - 1) && - (block->size % xfer->chunk_size)) { - return block->size % xfer->chunk_size; - } + if ((chunk_idx == block->chunk_count - 1) && + (block->size % xfer->chunk_size)) { + return block->size % xfer->chunk_size; + } - return xfer->chunk_size; + return xfer->chunk_size; } static int chunk_idx_decode(struct net_buf_simple *buf) { - uint16_t data; - uint8_t byte; + uint16_t data; + uint8_t byte; - if (buf->len == 0) { - return -EINVAL; - } + if (buf->len == 0) { + return -EINVAL; + } - byte = net_buf_simple_pull_u8(buf); + byte = net_buf_simple_pull_u8(buf); - /* utf-8 decoding */ - if ((byte & 0xf0) == 0xe0) { /* 0x800 - 0xffff */ - if (buf->len < 2) { - return -EINVAL; - } + /* utf-8 decoding */ + if ((byte & 0xf0) == 0xe0) { /* 0x800 - 0xffff */ + if (buf->len < 2) { + return -EINVAL; + } - data = (byte & 0x0f) << 12; - data |= (net_buf_simple_pull_u8(buf) & 0x3f) << 6; - data |= (net_buf_simple_pull_u8(buf) & 0x3f); - } else if ((byte & 0xe0) == 0xc0) { /* 0x80 - 0x7ff */ - if (buf->len < 1) { - return -EINVAL; - } + data = (byte & 0x0f) << 12; + data |= (net_buf_simple_pull_u8(buf) & 0x3f) << 6; + data |= (net_buf_simple_pull_u8(buf) & 0x3f); + } else if ((byte & 0xe0) == 0xc0) { /* 0x80 - 0x7ff */ + if (buf->len < 1) { + return -EINVAL; + } - data = (byte & 0x1f) << 6; - data |= (net_buf_simple_pull_u8(buf) & 0x3f); - } else { /* 0x00 - 0x7f */ - data = byte & 0x7f; - } + data = (byte & 0x1f) << 6; + data |= (net_buf_simple_pull_u8(buf) & 0x3f); + } else { /* 0x00 - 0x7f */ + data = byte & 0x7f; + } - return data; + return data; } static void block_set(struct bt_mesh_blob_cli *cli, uint16_t block_idx) { - cli->block.number = block_idx; - cli->block.offset = block_idx * (1UL << cli->xfer->block_size_log); - cli->block.size = blob_block_size(cli->xfer->size, cli->xfer->block_size_log, - block_idx); - cli->block.chunk_count = - DIV_ROUND_UP(cli->block.size, cli->xfer->chunk_size); + cli->block.number = block_idx; + cli->block.offset = block_idx * (1UL << cli->xfer->block_size_log); + cli->block.size = blob_block_size(cli->xfer->size, cli->xfer->block_size_log, + block_idx); + cli->block.chunk_count = + DIV_ROUND_UP(cli->block.size, cli->xfer->chunk_size); - if (cli->xfer->mode == BT_MESH_BLOB_XFER_MODE_PUSH) { - blob_chunk_missing_set_all(&cli->block); - } else { - struct bt_mesh_blob_target *target; + if (cli->xfer->mode == BT_MESH_BLOB_XFER_MODE_PUSH) { + blob_chunk_missing_set_all(&cli->block); + } else { + struct bt_mesh_blob_target *target; - /* In pull mode, the server will tell us which blocks are missing. */ - memset(cli->block.missing, 0, sizeof(cli->block.missing)); + /* In pull mode, the server will tell us which blocks are missing. */ + memset(cli->block.missing, 0, sizeof(cli->block.missing)); - TARGETS_FOR_EACH(cli, target) { - memset(target->pull->missing, 0, sizeof(target->pull->missing)); - } - } + TARGETS_FOR_EACH(cli, target) { + memset(target->pull->missing, 0, sizeof(target->pull->missing)); + } + } - LOG_DBG("%u size: %u chunks: %u", block_idx, cli->block.size, - cli->block.chunk_count); + BT_DBG("%u size: %u chunks: %u", block_idx, cli->block.size, + cli->block.chunk_count); } static void suspend(struct bt_mesh_blob_cli *cli) { - cli->state = BT_MESH_BLOB_CLI_STATE_SUSPENDED; + cli->state = BT_MESH_BLOB_CLI_STATE_SUSPENDED; - if (cli->cb && cli->cb->suspended) { - cli->cb->suspended(cli); - } + if (cli->cb && cli->cb->suspended) { + cli->cb->suspended(cli); + } } static void end(struct bt_mesh_blob_cli *cli, bool success) { - const struct bt_mesh_blob_xfer *xfer = cli->xfer; + const struct bt_mesh_blob_xfer *xfer = cli->xfer; - LOG_DBG("%u", success); + BT_DBG("%u", success); - io_close(cli); - cli_state_reset(cli); - if (cli->cb && cli->cb->end) { - cli->cb->end(cli, xfer, success); - } + io_close(cli); + cli_state_reset(cli); + if (cli->cb && cli->cb->end) { + cli->cb->end(cli, xfer, success); + } } static enum bt_mesh_blob_status caps_adjust(struct bt_mesh_blob_cli *cli, - const struct bt_mesh_blob_cli_caps *in) + const struct bt_mesh_blob_cli_caps *in) { - if (!(in->modes & cli->caps.modes)) { - return BT_MESH_BLOB_ERR_UNSUPPORTED_MODE; - } + if (!(in->modes & cli->caps.modes)) { + return BT_MESH_BLOB_ERR_UNSUPPORTED_MODE; + } - if ((in->min_block_size_log > cli->caps.max_block_size_log) || - (in->max_block_size_log < cli->caps.min_block_size_log)) { - return BT_MESH_BLOB_ERR_INVALID_BLOCK_SIZE; - } + if ((in->min_block_size_log > cli->caps.max_block_size_log) || + (in->max_block_size_log < cli->caps.min_block_size_log)) { + return BT_MESH_BLOB_ERR_INVALID_BLOCK_SIZE; + } - cli->caps.min_block_size_log = - MAX(cli->caps.min_block_size_log, in->min_block_size_log); - cli->caps.max_block_size_log = - MIN(cli->caps.max_block_size_log, in->max_block_size_log); - cli->caps.max_chunks = MIN(cli->caps.max_chunks, in->max_chunks); - cli->caps.mtu_size = MIN(cli->caps.mtu_size, in->mtu_size); - cli->caps.max_chunk_size = MIN(cli->caps.max_chunk_size, in->max_chunk_size); - cli->caps.modes &= in->modes; - cli->caps.max_size = MIN(cli->caps.max_size, in->max_size); + cli->caps.min_block_size_log = + MAX(cli->caps.min_block_size_log, in->min_block_size_log); + cli->caps.max_block_size_log = + MIN(cli->caps.max_block_size_log, in->max_block_size_log); + cli->caps.max_chunks = MIN(cli->caps.max_chunks, in->max_chunks); + cli->caps.mtu_size = MIN(cli->caps.mtu_size, in->mtu_size); + cli->caps.max_chunk_size = MIN(cli->caps.max_chunk_size, in->max_chunk_size); + cli->caps.modes &= in->modes; + cli->caps.max_size = MIN(cli->caps.max_size, in->max_size); - return BT_MESH_BLOB_SUCCESS; + return BT_MESH_BLOB_SUCCESS; } /******************************************************************************* @@ -346,276 +350,276 @@ static enum bt_mesh_blob_status caps_adjust(struct bt_mesh_blob_cli *cli, ******************************************************************************/ static struct bt_mesh_blob_target *next_target(struct bt_mesh_blob_cli *cli, - struct bt_mesh_blob_target **current) + struct bt_mesh_blob_target **current) { - if (*current) { - *current = SYS_SLIST_PEEK_NEXT_CONTAINER(*current, n); - } else { - *current = SYS_SLIST_PEEK_HEAD_CONTAINER( - (sys_slist_t *)&cli->inputs->targets, *current, n); - } + if (*current) { + *current = SYS_SLIST_PEEK_NEXT_CONTAINER(*current, n); + } else { + *current = SYS_SLIST_PEEK_HEAD_CONTAINER( + (sys_slist_t *)&cli->inputs->targets, *current, n); + } - while (*current) { - if ((*current)->acked || (*current)->procedure_complete || - (*current)->status != BT_MESH_BLOB_SUCCESS || (*current)->timedout || - (*current)->skip) { - goto next; - } + while (*current) { + if ((*current)->acked || (*current)->procedure_complete || + (*current)->status != BT_MESH_BLOB_SUCCESS || (*current)->timedout || + (*current)->skip) { + goto next; + } - if (SENDING_CHUNKS_IN_PULL_MODE(cli) && - (k_uptime_get() < (*current)->pull->block_report_timestamp || - !blob_chunk_missing_get((*current)->pull->missing, cli->chunk_idx))) { - /* Skip targets that didn't time out or timed out, but confirmed - * the currently transmitted chunk (cli->chunk_idx). - */ - goto next; - } + if (SENDING_CHUNKS_IN_PULL_MODE(cli) && + (k_uptime_get() < (*current)->pull->block_report_timestamp || + !blob_chunk_missing_get((*current)->pull->missing, cli->chunk_idx))) { + /* Skip targets that didn't time out or timed out, but confirmed + * the currently transmitted chunk (cli->chunk_idx). + */ + goto next; + } - break; + break; next: - *current = SYS_SLIST_PEEK_NEXT_CONTAINER(*current, n); - } + *current = SYS_SLIST_PEEK_NEXT_CONTAINER(*current, n); + } - return *current; + return *current; } static void send(struct bt_mesh_blob_cli *cli) { - cli->tx.sending = 1U; - if (UNICAST_MODE(cli)) { - cli->tx.ctx.send(cli, cli->tx.target->addr); - } else { - cli->tx.ctx.send(cli, cli->inputs->group); - } + cli->tx.sending = 1U; + if (UNICAST_MODE(cli)) { + cli->tx.ctx.send(cli, cli->tx.target->addr); + } else { + cli->tx.ctx.send(cli, cli->inputs->group); + } } static void broadcast_complete(struct bt_mesh_blob_cli *cli) { - LOG_DBG("%s", cli->tx.cancelled ? "cancelling" : "continuing"); + BT_DBG("%s", cli->tx.cancelled ? "cancelling" : "continuing"); - cli->tx.ctx.is_inited = 0; - k_work_cancel_delayable(&cli->tx.retry); + cli->tx.ctx.is_inited = 0; + k_work_cancel_delayable(&cli->tx.retry); - if (cli->tx.cancelled) { - transfer_cancel(cli); - } else { - __ASSERT(cli->tx.ctx.next, "No next callback"); - cli->tx.ctx.next(cli); - } + if (cli->tx.cancelled) { + transfer_cancel(cli); + } else { + __ASSERT(cli->tx.ctx.next, "No next callback"); + cli->tx.ctx.next(cli); + } } static void tx_complete(struct k_work *work) { - struct k_work_delayable *dwork = k_work_delayable_from_work(work); - struct bt_mesh_blob_cli *cli = CONTAINER_OF(dwork, struct bt_mesh_blob_cli, tx.complete); + struct k_work_delayable *dwork = k_work_delayable_from_work(work); + struct bt_mesh_blob_cli *cli = CONTAINER_OF(dwork, struct bt_mesh_blob_cli, tx.complete); - if (!cli->tx.ctx.is_inited || !cli->tx.sending) { - return; - } + if (!cli->tx.ctx.is_inited || !cli->tx.sending) { + return; + } - cli->tx.sending = 0U; + cli->tx.sending = 0U; - if (cli->tx.cancelled) { - broadcast_complete(cli); - return; - } + if (cli->tx.cancelled) { + broadcast_complete(cli); + return; + } - if (cli->tx.ctx.send_complete) { - cli->tx.ctx.send_complete(cli, cli->tx.target->addr); - } + if (cli->tx.ctx.send_complete) { + cli->tx.ctx.send_complete(cli, cli->tx.target->addr); + } - if (UNICAST_MODE(cli) && next_target(cli, &cli->tx.target)) { - send(cli); - return; - } + if (UNICAST_MODE(cli) && next_target(cli, &cli->tx.target)) { + send(cli); + return; + } - if (cli->tx.ctx.acked && cli->tx.pending) { - start_retry_timer(cli); - return; - } + if (cli->tx.ctx.acked && cli->tx.pending) { + start_retry_timer(cli); + return; + } - broadcast_complete(cli); + broadcast_complete(cli); } static void drop_remaining_targets(struct bt_mesh_blob_cli *cli) { - struct bt_mesh_blob_target *target; + struct bt_mesh_blob_target *target; - LOG_DBG(""); + BT_DBG(""); - cli->tx.pending = 0; + cli->tx.pending = 0; - TARGETS_FOR_EACH(cli, target) { - if (!target->acked && !target->timedout && !target->procedure_complete && - !target->skip) { - target->timedout = 1U; - target_drop(cli, target, BT_MESH_BLOB_ERR_INTERNAL); - } - } + TARGETS_FOR_EACH(cli, target) { + if (!target->acked && !target->timedout && !target->procedure_complete && + !target->skip) { + target->timedout = 1U; + target_drop(cli, target, BT_MESH_BLOB_ERR_INTERNAL); + } + } - /* Update missing chunks to exclude chunks from dropped targets. */ - if (SENDING_CHUNKS_IN_PULL_MODE(cli)) { - update_missing_chunks(cli); - } + /* Update missing chunks to exclude chunks from dropped targets. */ + if (SENDING_CHUNKS_IN_PULL_MODE(cli)) { + update_missing_chunks(cli); + } } static void retry_timeout(struct k_work *work) { - struct bt_mesh_blob_cli *cli = - CONTAINER_OF(work, struct bt_mesh_blob_cli, tx.retry.work); + struct bt_mesh_blob_cli *cli = + CONTAINER_OF(work, struct bt_mesh_blob_cli, tx.retry.work); - /* When sending chunks in Pull mode, timeout is handled differently. Client will drop all - * non-responsive servers by cli_timestamp. By calling broadcast_complete(), client will - * either retransmit the missing chunks (if any), or proceed to the next block, or suspend - * the transfer if all targets timed out. All this is handled in block_check_end(). - * Retry logic for all other procedures in Pull mode is handled as in Push mode. - */ - if (SENDING_CHUNKS_IN_PULL_MODE(cli)) { - if (k_uptime_get() >= cli->tx.cli_timestamp) { - LOG_DBG("Transfer timed out."); + /* When sending chunks in Pull mode, timeout is handled differently. Client will drop all + * non-responsive servers by cli_timestamp. By calling broadcast_complete(), client will + * either retransmit the missing chunks (if any), or proceed to the next block, or suspend + * the transfer if all targets timed out. All this is handled in block_check_end(). + * Retry logic for all other procedures in Pull mode is handled as in Push mode. + */ + if (SENDING_CHUNKS_IN_PULL_MODE(cli)) { + if (k_uptime_get() >= cli->tx.cli_timestamp) { + BT_DBG("Transfer timed out."); - if (!cli->tx.ctx.optional) { - drop_remaining_targets(cli); - } - } + if (!cli->tx.ctx.optional) { + drop_remaining_targets(cli); + } + } - broadcast_complete(cli); - return; - } + broadcast_complete(cli); + return; + } - LOG_DBG("%u", cli->tx.retries); + BT_DBG("%u", cli->tx.retries); - cli->tx.retries--; - cli->tx.target = NULL; + cli->tx.retries--; + cli->tx.target = NULL; - __ASSERT(!cli->tx.sending, "still sending"); - __ASSERT(cli->tx.ctx.is_inited, "ctx is not initialized"); + __ASSERT(!cli->tx.sending, "still sending"); + __ASSERT(cli->tx.ctx.is_inited, "ctx is not initialized"); - if (!cli->tx.retries) { - LOG_DBG("Transfer timed out."); + if (!cli->tx.retries) { + BT_DBG("Transfer timed out."); - if (!cli->tx.ctx.optional) { - drop_remaining_targets(cli); - } + if (!cli->tx.ctx.optional) { + drop_remaining_targets(cli); + } - broadcast_complete(cli); - return; - } + broadcast_complete(cli); + return; + } - if (!cli->tx.ctx.acked || !next_target(cli, &cli->tx.target) || cli->tx.cancelled) { - broadcast_complete(cli); - return; - } + if (!cli->tx.ctx.acked || !next_target(cli, &cli->tx.target) || cli->tx.cancelled) { + broadcast_complete(cli); + return; + } - send(cli); + send(cli); } void blob_cli_broadcast(struct bt_mesh_blob_cli *cli, - const struct blob_cli_broadcast_ctx *ctx) + const struct blob_cli_broadcast_ctx *ctx) { - if (cli->tx.ctx.is_inited || cli->tx.sending) { - LOG_ERR("BLOB cli busy"); - return; - } + if (cli->tx.ctx.is_inited || cli->tx.sending) { + BT_ERR("BLOB cli busy"); + return; + } - cli->tx.cancelled = 0U; - cli->tx.retries = CONFIG_BT_MESH_BLOB_CLI_BLOCK_RETRIES; - cli->tx.ctx = *ctx; - cli->tx.ctx.is_inited = 1U; + cli->tx.cancelled = 0U; + cli->tx.retries = CONFIG_BLE_MESH_BLOB_CLI_BLOCK_RETRIES; + cli->tx.ctx = *ctx; + cli->tx.ctx.is_inited = 1U; - cli->tx.pending = targets_reset(cli); + cli->tx.pending = targets_reset(cli); - LOG_DBG("%u targets", cli->tx.pending); + BT_DBG("%u targets", cli->tx.pending); - cli->tx.target = NULL; - if (!next_target(cli, &cli->tx.target)) { - LOG_DBG("No active targets"); - broadcast_complete(cli); - return; - } + cli->tx.target = NULL; + if (!next_target(cli, &cli->tx.target)) { + BT_DBG("No active targets"); + broadcast_complete(cli); + return; + } - send(cli); + send(cli); } void blob_cli_broadcast_tx_complete(struct bt_mesh_blob_cli *cli) { - k_work_schedule(&cli->tx.complete, K_MSEC(cli->tx.ctx.post_send_delay_ms)); + k_work_schedule(&cli->tx.complete, K_MSEC(cli->tx.ctx.post_send_delay_ms)); } void blob_cli_broadcast_rsp(struct bt_mesh_blob_cli *cli, - struct bt_mesh_blob_target *target) + struct bt_mesh_blob_target *target) { - if (target->acked) { - return; - } + if (target->acked) { + return; + } - LOG_DBG("0x%04x, pending: %d", target->addr, cli->tx.pending); + BT_DBG("0x%04x, pending: %d", target->addr, cli->tx.pending); - target->acked = 1U; + target->acked = 1U; - if (!--cli->tx.pending && !cli->tx.sending) { - broadcast_complete(cli); - } + if (!--cli->tx.pending && !cli->tx.sending) { + broadcast_complete(cli); + } } void blob_cli_broadcast_abort(struct bt_mesh_blob_cli *cli) { - if (!cli->tx.ctx.is_inited) { - return; - } + if (!cli->tx.ctx.is_inited) { + return; + } - if ((cli)->state >= BT_MESH_BLOB_CLI_STATE_START) { - io_close(cli); - } + if ((cli)->state >= BT_MESH_BLOB_CLI_STATE_START) { + io_close(cli); + } - cli_state_reset(cli); + cli_state_reset(cli); } static void send_start(uint16_t duration, int err, void *cb_data); static void send_end(int err, void *user_data); static int tx(struct bt_mesh_blob_cli *cli, uint16_t addr, - struct net_buf_simple *buf) + struct net_buf_simple *buf) { - static const struct bt_mesh_send_cb end_cb = { - .start = send_start, - .end = send_end, - }; - struct bt_mesh_msg_ctx ctx = { - .app_idx = cli->inputs->app_idx, - .addr = addr, - .send_ttl = cli->inputs->ttl, - }; - int err; + static const struct bt_mesh_send_cb end_cb = { + .start = send_start, + .end = send_end, + }; + struct bt_mesh_msg_ctx ctx = { + .app_idx = cli->inputs->app_idx, + .addr = addr, + .send_ttl = cli->inputs->ttl, + }; + int err; - err = bt_mesh_model_send(cli->mod, &ctx, buf, &end_cb, cli); - if (err) { - LOG_ERR("Send err: %d", err); - send_end(err, cli); - return err; - } + err = bt_mesh_model_send((struct bt_mesh_model *)cli->mod, &ctx, buf, &end_cb, cli); + if (err) { + BT_ERR("Send err: %d", err); + send_end(err, cli); + return err; + } - return 0; + return 0; } static void send_start(uint16_t duration, int err, void *cb_data) { - if (err) { - LOG_ERR("TX Start failed: %d", err); - send_end(err, cb_data); - } + if (err) { + BT_ERR("TX Start failed: %d", err); + send_end(err, cb_data); + } } static void send_end(int err, void *user_data) { - struct bt_mesh_blob_cli *cli = user_data; + struct bt_mesh_blob_cli *cli = user_data; - if (!cli->tx.ctx.is_inited) { - return; - } + if (!cli->tx.ctx.is_inited) { + return; + } - blob_cli_broadcast_tx_complete(cli); + blob_cli_broadcast_tx_complete(cli); } /******************************************************************************* @@ -624,80 +628,83 @@ static void send_end(int err, void *user_data) static void info_get_tx(struct bt_mesh_blob_cli *cli, uint16_t dst) { - BT_MESH_MODEL_BUF_DEFINE(buf, BT_MESH_BLOB_OP_INFO_GET, 0); - bt_mesh_model_msg_init(&buf, BT_MESH_BLOB_OP_INFO_GET); + BLE_MESH_MODEL_BUF_DEFINE(buf, BT_MESH_BLOB_OP_INFO_GET, 0); + bt_mesh_model_msg_init(&buf, BT_MESH_BLOB_OP_INFO_GET); - tx(cli, dst, &buf); + tx(cli, dst, &buf); } static void xfer_start_tx(struct bt_mesh_blob_cli *cli, uint16_t dst) { - BT_MESH_MODEL_BUF_DEFINE(buf, BT_MESH_BLOB_OP_XFER_START, 16); - bt_mesh_model_msg_init(&buf, BT_MESH_BLOB_OP_XFER_START); - net_buf_simple_add_u8(&buf, cli->xfer->mode << 6); - net_buf_simple_add_le64(&buf, cli->xfer->id); - net_buf_simple_add_le32(&buf, cli->xfer->size); - net_buf_simple_add_u8(&buf, cli->xfer->block_size_log); - net_buf_simple_add_le16(&buf, BT_MESH_TX_SDU_MAX); + BLE_MESH_MODEL_BUF_DEFINE(buf, BT_MESH_BLOB_OP_XFER_START, 16); + bt_mesh_model_msg_init(&buf, BT_MESH_BLOB_OP_XFER_START); + net_buf_simple_add_u8(&buf, cli->xfer->mode << 6); + net_buf_simple_add_le64(&buf, cli->xfer->id); + net_buf_simple_add_le32(&buf, cli->xfer->size); + net_buf_simple_add_u8(&buf, cli->xfer->block_size_log); + net_buf_simple_add_le16(&buf, BLE_MESH_TX_SDU_MAX); - tx(cli, dst, &buf); + tx(cli, dst, &buf); } static void xfer_get_tx(struct bt_mesh_blob_cli *cli, uint16_t dst) { - BT_MESH_MODEL_BUF_DEFINE(buf, BT_MESH_BLOB_OP_XFER_GET, 0); - bt_mesh_model_msg_init(&buf, BT_MESH_BLOB_OP_XFER_GET); + BLE_MESH_MODEL_BUF_DEFINE(buf, BT_MESH_BLOB_OP_XFER_GET, 0); + bt_mesh_model_msg_init(&buf, BT_MESH_BLOB_OP_XFER_GET); - tx(cli, dst, &buf); + tx(cli, dst, &buf); } static void xfer_cancel_tx(struct bt_mesh_blob_cli *cli, uint16_t dst) { - BT_MESH_MODEL_BUF_DEFINE(buf, BT_MESH_BLOB_OP_XFER_CANCEL, 8); - bt_mesh_model_msg_init(&buf, BT_MESH_BLOB_OP_XFER_CANCEL); - net_buf_simple_add_le64(&buf, cli->xfer->id); + BLE_MESH_MODEL_BUF_DEFINE(buf, BT_MESH_BLOB_OP_XFER_CANCEL, 8); + bt_mesh_model_msg_init(&buf, BT_MESH_BLOB_OP_XFER_CANCEL); + net_buf_simple_add_le64(&buf, cli->xfer->id); - tx(cli, dst, &buf); + tx(cli, dst, &buf); } static void block_start_tx(struct bt_mesh_blob_cli *cli, uint16_t dst) { - BT_MESH_MODEL_BUF_DEFINE(buf, BT_MESH_BLOB_OP_BLOCK_START, 4); - bt_mesh_model_msg_init(&buf, BT_MESH_BLOB_OP_BLOCK_START); - net_buf_simple_add_le16(&buf, cli->block.number); - net_buf_simple_add_le16(&buf, cli->xfer->chunk_size); + BLE_MESH_MODEL_BUF_DEFINE(buf, BT_MESH_BLOB_OP_BLOCK_START, 4); + bt_mesh_model_msg_init(&buf, BT_MESH_BLOB_OP_BLOCK_START); + net_buf_simple_add_le16(&buf, cli->block.number); + net_buf_simple_add_le16(&buf, cli->xfer->chunk_size); - tx(cli, dst, &buf); + tx(cli, dst, &buf); } static void chunk_tx(struct bt_mesh_blob_cli *cli, uint16_t dst) { - NET_BUF_SIMPLE_DEFINE(buf, BT_MESH_TX_SDU_MAX); - struct bt_mesh_blob_chunk chunk; - int err; + struct bt_mesh_blob_chunk chunk; + int err; - bt_mesh_model_msg_init(&buf, BT_MESH_BLOB_OP_CHUNK); - net_buf_simple_add_le16(&buf, cli->chunk_idx); + BT_DBG("Chunk will send to 0x%04x", dst); - chunk.size = chunk_size(cli->xfer, &cli->block, cli->chunk_idx); - chunk.offset = cli->xfer->chunk_size * cli->chunk_idx; - chunk.data = net_buf_simple_add(&buf, chunk.size); + /* Changed by Espressif: + * Use static chunk buf to avoid taking up large stack space */ + bt_mesh_model_msg_init(&chunk_buf, BT_MESH_BLOB_OP_CHUNK); + net_buf_simple_add_le16(&chunk_buf, cli->chunk_idx); - 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); - return; - } + chunk.size = chunk_size(cli->xfer, &cli->block, cli->chunk_idx); + chunk.offset = cli->xfer->chunk_size * cli->chunk_idx; + chunk.data = net_buf_simple_add(&chunk_buf, chunk.size); - tx(cli, dst, &buf); + 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); + return; + } + + tx(cli, dst, &chunk_buf); } static void block_get_tx(struct bt_mesh_blob_cli *cli, uint16_t dst) { - BT_MESH_MODEL_BUF_DEFINE(buf, BT_MESH_BLOB_OP_BLOCK_GET, 0); - bt_mesh_model_msg_init(&buf, BT_MESH_BLOB_OP_BLOCK_GET); + BLE_MESH_MODEL_BUF_DEFINE(buf, BT_MESH_BLOB_OP_BLOCK_GET, 0); + bt_mesh_model_msg_init(&buf, BT_MESH_BLOB_OP_BLOCK_GET); - tx(cli, dst, &buf); + tx(cli, dst, &buf); } /************************************************************************************************** @@ -786,197 +793,199 @@ static void transfer_complete(struct bt_mesh_blob_cli *cli); static void caps_get(struct bt_mesh_blob_cli *cli) { - const struct blob_cli_broadcast_ctx ctx = { - .send = info_get_tx, - .next = caps_collected, - .acked = true, - }; + const struct blob_cli_broadcast_ctx ctx = { + .send = info_get_tx, + .next = caps_collected, + .acked = true, + }; - cli->state = BT_MESH_BLOB_CLI_STATE_CAPS_GET; - blob_cli_broadcast(cli, &ctx); + cli->state = BT_MESH_BLOB_CLI_STATE_CAPS_GET; + blob_cli_broadcast(cli, &ctx); } static void caps_collected(struct bt_mesh_blob_cli *cli) { - struct bt_mesh_blob_target *target; - bool success = false; + struct bt_mesh_blob_target *target; + bool success = false; - cli->state = BT_MESH_BLOB_CLI_STATE_NONE; + cli->state = BT_MESH_BLOB_CLI_STATE_NONE; - cli_state_reset(cli); + cli_state_reset(cli); - TARGETS_FOR_EACH(cli, target) { - if (target->status == BT_MESH_BLOB_SUCCESS) { - success = true; - break; - } - } + TARGETS_FOR_EACH(cli, target) { + if (target->status == BT_MESH_BLOB_SUCCESS) { + success = true; + break; + } + } - while (success && - (1UL << cli->caps.max_block_size_log) > - (cli->caps.max_chunk_size * cli->caps.max_chunks)) { - cli->caps.max_block_size_log--; - } + while (success && + (1UL << cli->caps.max_block_size_log) > + (cli->caps.max_chunk_size * cli->caps.max_chunks)) { + cli->caps.max_block_size_log--; + } - if (cli->cb && cli->cb->caps) { - cli->cb->caps(cli, success ? &cli->caps : NULL); - } + if (cli->cb && cli->cb->caps) { + cli->cb->caps(cli, success ? &cli->caps : NULL); + } } static int xfer_start(struct bt_mesh_blob_cli *cli) { - const struct blob_cli_broadcast_ctx ctx = { - .send = xfer_start_tx, - .next = block_start, - .acked = true, - }; - int err; + const struct blob_cli_broadcast_ctx ctx = { + .send = xfer_start_tx, + .next = block_start, + .acked = true, + }; + int err; - err = io_open(cli); - if (err) { - return -EIO; - } + err = io_open(cli); + if (err) { + return -EIO; + } - cli->state = BT_MESH_BLOB_CLI_STATE_START; + cli->state = BT_MESH_BLOB_CLI_STATE_START; - blob_cli_broadcast(cli, &ctx); - return 0; + blob_cli_broadcast(cli, &ctx); + return 0; } static void block_start(struct bt_mesh_blob_cli *cli) { - const struct blob_cli_broadcast_ctx ctx = { - .send = block_start_tx, - .next = chunk_send, - .acked = true, - }; - struct bt_mesh_blob_target *target; + const struct blob_cli_broadcast_ctx ctx = { + .send = block_start_tx, + .next = chunk_send, + .acked = true, + }; + struct bt_mesh_blob_target *target; + if (!targets_active(cli)) { + if (targets_timedout(cli)) { + suspend(cli); + return; + } - if (!targets_active(cli)) { - if (targets_timedout(cli)) { - suspend(cli); - return; - } + end(cli, false); + return; + } - end(cli, false); - return; - } + BT_DBG("%u (%u chunks, %u/%u)", cli->block.number, + cli->block.chunk_count, cli->block.number + 1, cli->block_count); - LOG_DBG("%u (%u chunks, %u/%u)", cli->block.number, - cli->block.chunk_count, cli->block.number + 1, cli->block_count); + cli->chunk_idx = 0; + cli->state = BT_MESH_BLOB_CLI_STATE_BLOCK_START; + /* Client Timeout Timer in Send Data State Machine is initialized initially after + * transmitting the first bunch of chunks (see block_report_wait()). Next time it will be + * updated after every Partial Block Report message. + */ + cli->tx.cli_timestamp = 0ll; - cli->chunk_idx = 0; - cli->state = BT_MESH_BLOB_CLI_STATE_BLOCK_START; - /* Client Timeout Timer in Send Data State Machine is initialized initially after - * transmitting the first bunch of chunks (see block_report_wait()). Next time it will be - * updated after every Partial Block Report message. - */ - cli->tx.cli_timestamp = 0ll; + TARGETS_FOR_EACH(cli, target) { + target->procedure_complete = 0U; - TARGETS_FOR_EACH(cli, target) { - target->procedure_complete = 0U; + if (cli->xfer->mode == BT_MESH_BLOB_XFER_MODE_PULL) { + target->pull->block_report_timestamp = 0ll; + } + } - if (cli->xfer->mode == BT_MESH_BLOB_XFER_MODE_PULL) { - target->pull->block_report_timestamp = 0ll; - } - } + if (cli->io->block_start) { + cli->io->block_start(cli->io, cli->xfer, &cli->block); + if (cli->state == BT_MESH_BLOB_CLI_STATE_NONE) { + return; + } + } - if (cli->io->block_start) { - cli->io->block_start(cli->io, cli->xfer, &cli->block); - if (cli->state == BT_MESH_BLOB_CLI_STATE_NONE) { - return; - } - } - - blob_cli_broadcast(cli, &ctx); + blob_cli_broadcast(cli, &ctx); } static void chunk_tx_complete(struct bt_mesh_blob_cli *cli, uint16_t dst) { - if (cli->xfer->mode != BT_MESH_BLOB_XFER_MODE_PULL) { - return; - } + if (cli->xfer->mode != BT_MESH_BLOB_XFER_MODE_PULL) { + return; + } - /* Update Block Report Timer individually for each target after sending out the last chunk - * in current iteration. - */ - uint16_t chunk_idx = next_missing_chunk(cli, cli->tx.target->pull->missing, - cli->chunk_idx + 1); - if (chunk_idx < cli->block.chunk_count) { - /* Will send more chunks to this target in this iteration. */ - return; - } + /* Update Block Report Timer individually for each target after sending out the last chunk + * in current iteration. + */ + uint16_t chunk_idx = next_missing_chunk(cli, cli->tx.target->pull->missing, + cli->chunk_idx + 1); + if (chunk_idx < cli->block.chunk_count) { + /* Will send more chunks to this target in this iteration. */ + return; + } - /* This was the last chunk sent for this target. Now start the Block Report Timeout Timer. - */ - struct bt_mesh_blob_target *target; - int64_t timestamp = k_uptime_get() + BLOCK_REPORT_TIME_MSEC; + /* This was the last chunk sent for this target. Now start the Block Report Timeout Timer. + */ + struct bt_mesh_blob_target *target; + int64_t timestamp = k_uptime_get() + BLOCK_REPORT_TIME_MSEC; - if (!UNICAST_MODE(cli)) { - /* If using group addressing, reset timestamp for all targets after all chunks are - * sent to the group address - */ - TARGETS_FOR_EACH(cli, target) { - target->pull->block_report_timestamp = timestamp; - } - return; - } + if (!UNICAST_MODE(cli)) { + /* If using group addressing, reset timestamp for all targets after all chunks are + * sent to the group address + */ + TARGETS_FOR_EACH(cli, target) { + target->pull->block_report_timestamp = timestamp; + } + return; + } - cli->tx.target->pull->block_report_timestamp = timestamp; + cli->tx.target->pull->block_report_timestamp = timestamp; } static void chunk_send(struct bt_mesh_blob_cli *cli) { - struct blob_cli_broadcast_ctx ctx = { - .send = chunk_tx, - .next = chunk_send_end, - .acked = false, - .post_send_delay_ms = cli->chunk_interval_ms, - }; + struct blob_cli_broadcast_ctx ctx = { + .send = chunk_tx, + .next = chunk_send_end, + .acked = false, + .post_send_delay_ms = cli->chunk_interval_ms, + }; - if (cli->xfer->mode == BT_MESH_BLOB_XFER_MODE_PULL) { - ctx.send_complete = chunk_tx_complete; - } + if (cli->xfer->mode == BT_MESH_BLOB_XFER_MODE_PULL) { + ctx.send_complete = chunk_tx_complete; + } - if (!targets_active(cli)) { - if (targets_timedout(cli)) { - suspend(cli); - return; - } + if (!targets_active(cli)) { + if (targets_timedout(cli)) { + suspend(cli); + return; + } - end(cli, false); - return; - } + end(cli, false); + return; + } - LOG_DBG("%u / %u size: %u", cli->chunk_idx + 1, cli->block.chunk_count, - chunk_size(cli->xfer, &cli->block, cli->chunk_idx)); + BT_DBG("%u / %u size: %u", cli->chunk_idx + 1, cli->block.chunk_count, + chunk_size(cli->xfer, &cli->block, cli->chunk_idx)); - cli->state = BT_MESH_BLOB_CLI_STATE_BLOCK_SEND; - blob_cli_broadcast(cli, &ctx); + cli->state = BT_MESH_BLOB_CLI_STATE_BLOCK_SEND; + blob_cli_broadcast(cli, &ctx); } static void chunk_send_end(struct bt_mesh_blob_cli *cli) { - /* In pull mode, the partial block reports are used to confirm which - * chunks have been received, while in push mode, we just assume that a - * sent chunk has been received. - */ - if (cli->xfer->mode == BT_MESH_BLOB_XFER_MODE_PUSH) { - blob_chunk_missing_set(cli->block.missing, cli->chunk_idx, false); - } + /* In pull mode, the partial block reports are used to confirm which + * chunks have been received, while in push mode, we just assume that a + * sent chunk has been received. + */ - cli->chunk_idx = next_missing_chunk(cli, cli->block.missing, cli->chunk_idx + 1); - if (cli->chunk_idx < cli->block.chunk_count) { - chunk_send(cli); - return; - } + BT_DBG("A Chunk sent finished"); - if (cli->xfer->mode == BT_MESH_BLOB_XFER_MODE_PUSH) { - block_check(cli); - } else { - block_report_wait(cli); - } + if (cli->xfer->mode == BT_MESH_BLOB_XFER_MODE_PUSH) { + blob_chunk_missing_set(cli->block.missing, cli->chunk_idx, false); + } + + cli->chunk_idx = next_missing_chunk(cli, cli->block.missing, cli->chunk_idx + 1); + if (cli->chunk_idx < cli->block.chunk_count) { + chunk_send(cli); + return; + } + + if (cli->xfer->mode == BT_MESH_BLOB_XFER_MODE_PUSH) { + block_check(cli); + } else { + block_report_wait(cli); + } } /* The block checking pair(block_check - block_check_end) @@ -984,149 +993,149 @@ static void chunk_send_end(struct bt_mesh_blob_cli *cli) */ static void block_check(struct bt_mesh_blob_cli *cli) { - const struct blob_cli_broadcast_ctx ctx = { - .send = block_get_tx, - .next = block_check_end, - .acked = true, - }; + const struct blob_cli_broadcast_ctx ctx = { + .send = block_get_tx, + .next = block_check_end, + .acked = true, + }; - cli->state = BT_MESH_BLOB_CLI_STATE_BLOCK_CHECK; + cli->state = BT_MESH_BLOB_CLI_STATE_BLOCK_CHECK; - LOG_DBG(""); + BT_DBG(""); - blob_cli_broadcast(cli, &ctx); + blob_cli_broadcast(cli, &ctx); } static void block_report_wait(struct bt_mesh_blob_cli *cli) { - const struct blob_cli_broadcast_ctx ctx = { - .next = block_check_end, - .acked = false, - }; + const struct blob_cli_broadcast_ctx ctx = { + .next = block_check_end, + .acked = false, + }; - /* Check if all servers already confirmed all chunks during the transmission. */ - if (next_missing_chunk(cli, cli->block.missing, 0) >= cli->block.chunk_count) { - block_check_end(cli); - return; - } + /* Check if all servers already confirmed all chunks during the transmission. */ + if (next_missing_chunk(cli, cli->block.missing, 0) >= cli->block.chunk_count) { + block_check_end(cli); + return; + } - LOG_DBG("Waiting for partial block report..."); - cli->tx.ctx = ctx; + BT_DBG("Waiting for partial block report..."); + cli->tx.ctx = ctx; - /* Start Client Timeout Timer in Send Data sub-procedure for the first time. */ - if (!cli->tx.cli_timestamp) { - cli->tx.cli_timestamp = k_uptime_get() + CLIENT_TIMEOUT_MSEC(cli); - } + /* Start Client Timeout Timer in Send Data sub-procedure for the first time. */ + if (!cli->tx.cli_timestamp) { + cli->tx.cli_timestamp = k_uptime_get() + CLIENT_TIMEOUT_MSEC(cli); + } - start_retry_timer(cli); + start_retry_timer(cli); } static void block_check_end(struct bt_mesh_blob_cli *cli) { - LOG_DBG(""); + BT_DBG(""); - if (!targets_active(cli)) { - if (targets_timedout(cli)) { - suspend(cli); - return; - } + if (!targets_active(cli)) { + if (targets_timedout(cli)) { + suspend(cli); + return; + } - end(cli, false); - return; - } + end(cli, false); + return; + } - cli->chunk_idx = next_missing_chunk(cli, cli->block.missing, 0); - if (cli->chunk_idx < cli->block.chunk_count) { - chunk_send(cli); - return; - } + cli->chunk_idx = next_missing_chunk(cli, cli->block.missing, 0); + if (cli->chunk_idx < cli->block.chunk_count) { + chunk_send(cli); + return; + } - LOG_DBG("No more missing chunks for block %u", cli->block.number); + BT_DBG("No more missing chunks for block %u", cli->block.number); - if (cli->io->block_end) { - cli->io->block_end(cli->io, cli->xfer, &cli->block); - if (cli->state == BT_MESH_BLOB_CLI_STATE_NONE) { - return; - } - } + if (cli->io->block_end) { + cli->io->block_end(cli->io, cli->xfer, &cli->block); + if (cli->state == BT_MESH_BLOB_CLI_STATE_NONE) { + return; + } + } - if (cli->block.number == cli->block_count - 1) { - struct bt_mesh_blob_target *target; + if (cli->block.number == cli->block_count - 1) { + struct bt_mesh_blob_target *target; - TARGETS_FOR_EACH(cli, target) { - target->procedure_complete = 0U; - } + TARGETS_FOR_EACH(cli, target) { + target->procedure_complete = 0U; + } - confirm_transfer(cli); - return; - } + confirm_transfer(cli); + return; + } - block_set(cli, cli->block.number + 1); - block_start(cli); + block_set(cli, cli->block.number + 1); + block_start(cli); } static void confirm_transfer(struct bt_mesh_blob_cli *cli) { - const struct blob_cli_broadcast_ctx ctx = { - .send = xfer_get_tx, - .next = transfer_complete, - .acked = true, - }; + const struct blob_cli_broadcast_ctx ctx = { + .send = xfer_get_tx, + .next = transfer_complete, + .acked = true, + }; - LOG_DBG(""); + BT_DBG(""); - cli->state = BT_MESH_BLOB_CLI_STATE_XFER_CHECK; + cli->state = BT_MESH_BLOB_CLI_STATE_XFER_CHECK; - blob_cli_broadcast(cli, &ctx); + blob_cli_broadcast(cli, &ctx); } static void progress_checked(struct bt_mesh_blob_cli *cli) { - LOG_DBG(""); + BT_DBG(""); - cli->state = BT_MESH_BLOB_CLI_STATE_NONE; + cli->state = BT_MESH_BLOB_CLI_STATE_NONE; - if (cli->cb && cli->cb->end) { - cli->cb->xfer_progress_complete(cli); - } + if (cli->cb && cli->cb->end) { + cli->cb->xfer_progress_complete(cli); + } } static void check_transfer(struct bt_mesh_blob_cli *cli) { - const struct blob_cli_broadcast_ctx ctx = { - .send = xfer_get_tx, - .next = progress_checked, - .acked = true, - }; + const struct blob_cli_broadcast_ctx ctx = { + .send = xfer_get_tx, + .next = progress_checked, + .acked = true, + }; - LOG_DBG(""); + BT_DBG(""); - cli->state = BT_MESH_BLOB_CLI_STATE_XFER_PROGRESS_GET; + cli->state = BT_MESH_BLOB_CLI_STATE_XFER_PROGRESS_GET; - blob_cli_broadcast(cli, &ctx); + blob_cli_broadcast(cli, &ctx); } static void transfer_cancel(struct bt_mesh_blob_cli *cli) { - const struct blob_cli_broadcast_ctx ctx = { - .send = xfer_cancel_tx, - .next = transfer_complete, - .acked = true, - }; + const struct blob_cli_broadcast_ctx ctx = { + .send = xfer_cancel_tx, + .next = transfer_complete, + .acked = true, + }; - LOG_DBG(""); + BT_DBG(""); - cli->state = BT_MESH_BLOB_CLI_STATE_CANCEL; + cli->state = BT_MESH_BLOB_CLI_STATE_CANCEL; - blob_cli_broadcast(cli, &ctx); + blob_cli_broadcast(cli, &ctx); } static void transfer_complete(struct bt_mesh_blob_cli *cli) { - bool success = targets_active(cli) && - cli->state == BT_MESH_BLOB_CLI_STATE_XFER_CHECK; + bool success = targets_active(cli) && + cli->state == BT_MESH_BLOB_CLI_STATE_XFER_CHECK; - end(cli, success); + end(cli, success); } /******************************************************************************* @@ -1134,525 +1143,551 @@ static void transfer_complete(struct bt_mesh_blob_cli *cli) ******************************************************************************/ static void rx_block_status(struct bt_mesh_blob_cli *cli, - struct bt_mesh_blob_target *target, - struct block_status *block) + struct bt_mesh_blob_target *target, + struct block_status *block) { - if (cli->state != BT_MESH_BLOB_CLI_STATE_BLOCK_START && - cli->state != BT_MESH_BLOB_CLI_STATE_BLOCK_SEND && - cli->state != BT_MESH_BLOB_CLI_STATE_BLOCK_CHECK) { - LOG_WRN("Invalid state %u", cli->state); - return; - } + if (cli->state != BT_MESH_BLOB_CLI_STATE_BLOCK_START && + cli->state != BT_MESH_BLOB_CLI_STATE_BLOCK_SEND && + cli->state != BT_MESH_BLOB_CLI_STATE_BLOCK_CHECK) { + BT_WARN("Invalid state %u", cli->state); + return; + } - LOG_DBG("0x%04x: block: %u status: %u", target->addr, block->block.number, block->status); + BT_WARN("0x%04x: block: %u status: %u", target->addr, block->block.number, block->status); - if (block->status != BT_MESH_BLOB_SUCCESS) { - target_drop(cli, target, block->status); - blob_cli_broadcast_rsp(cli, target); - return; - } + if (block->status != BT_MESH_BLOB_SUCCESS && + /** + * Changed by Espressif. + * This behavior is not allowed by spec! + * + * BT_MESH_BLOB_ERR_INFO_UNAVAILABLE will caused + * by blob server suspend, and the suspend state + * will be resume in most scenes. + * + * so let blob client try to resume blob server. + */ + block->status != BT_MESH_BLOB_ERR_INFO_UNAVAILABLE) { + target_drop(cli, target, block->status); + blob_cli_broadcast_rsp(cli, target); + return; + } - if (block->block.number != cli->block.number) { - LOG_DBG("Invalid block num (expected %u)", cli->block.number); - return; - } + if (block->block.number != cli->block.number) { + BT_DBG("Invalid block num (expected %u)", cli->block.number); + return; + } - if (block->missing == BT_MESH_BLOB_CHUNKS_MISSING_NONE) { - target->procedure_complete = 1U; + if (block->missing == BT_MESH_BLOB_CHUNKS_MISSING_NONE) { + target->procedure_complete = 1U; - if (cli->xfer->mode == BT_MESH_BLOB_XFER_MODE_PULL) { - memset(target->pull->missing, 0, sizeof(target->pull->missing)); - update_missing_chunks(cli); - } + if (cli->xfer->mode == BT_MESH_BLOB_XFER_MODE_PULL) { + memset(target->pull->missing, 0, sizeof(target->pull->missing)); + update_missing_chunks(cli); + } - LOG_DBG("Target 0x%04x received all chunks", target->addr); - } else if (block->missing == BT_MESH_BLOB_CHUNKS_MISSING_ALL) { - blob_chunk_missing_set_all(&cli->block); - } else if (cli->xfer->mode == BT_MESH_BLOB_XFER_MODE_PULL) { - memcpy(target->pull->missing, block->block.missing, sizeof(block->block.missing)); + BT_DBG("Target 0x%04x received all chunks", target->addr); + } else if (block->missing == BT_MESH_BLOB_CHUNKS_MISSING_ALL) { + blob_chunk_missing_set_all(&cli->block); + } else if (cli->xfer->mode == BT_MESH_BLOB_XFER_MODE_PULL) { + memcpy(target->pull->missing, block->block.missing, sizeof(block->block.missing)); - LOG_DBG("Missing: %s", bt_hex(target->pull->missing, cli->block.chunk_count)); + BT_DBG("Missing: %s", bt_hex(target->pull->missing, cli->block.chunk_count)); - update_missing_chunks(cli); + update_missing_chunks(cli); - /* Target has responded. Reset the timestamp so that client can start transmitting - * missing chunks to it. - */ - target->pull->block_report_timestamp = 0ll; - } else { - for (int i = 0; i < ARRAY_SIZE(block->block.missing); ++i) { - cli->block.missing[i] |= block->block.missing[i]; - } - } + /* Target has responded. Reset the timestamp so that client can start transmitting + * missing chunks to it. + */ + target->pull->block_report_timestamp = 0ll; + } else { + for (int i = 0; i < ARRAY_SIZE(block->block.missing); ++i) { + cli->block.missing[i] |= block->block.missing[i]; + } + } - if (SENDING_CHUNKS_IN_PULL_MODE(cli)) { - if (!cli->tx.sending) { - /* If not sending, then the retry timer is running. Call - * broadcast_complete() to proceed to block_check_end() and start - * transmitting missing chunks. - */ - broadcast_complete(cli); - } + if (SENDING_CHUNKS_IN_PULL_MODE(cli)) { + if (!cli->tx.sending) { + /* If not sending, then the retry timer is running. Call + * broadcast_complete() to proceed to block_check_end() and start + * transmitting missing chunks. + */ + broadcast_complete(cli); + } - /* When sending chunks in Pull mode, we don't confirm transaction when receiving - * Partial Block Report message. - */ - return; - } + /* When sending chunks in Pull mode, we don't confirm transaction when receiving + * Partial Block Report message. + */ + return; + } - blob_cli_broadcast_rsp(cli, target); + blob_cli_broadcast_rsp(cli, target); } static int handle_xfer_status(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx, - struct net_buf_simple *buf) + struct net_buf_simple *buf) { - struct bt_mesh_blob_cli *cli = mod->rt->user_data; - enum bt_mesh_blob_xfer_phase expected_phase; - struct bt_mesh_blob_target *target; - struct bt_mesh_blob_xfer_info info = { 0 }; - uint8_t status_and_mode; + struct bt_mesh_blob_cli *cli = mod->user_data; + enum bt_mesh_blob_xfer_phase expected_phase; + struct bt_mesh_blob_target *target; + struct bt_mesh_blob_xfer_info info = { 0 }; + uint8_t status_and_mode; - status_and_mode = net_buf_simple_pull_u8(buf); - info.status = status_and_mode & BIT_MASK(4); - info.mode = status_and_mode >> 6; - info.phase = net_buf_simple_pull_u8(buf); + status_and_mode = net_buf_simple_pull_u8(buf); + info.status = status_and_mode & BIT_MASK(4); + info.mode = status_and_mode >> 6; + info.phase = net_buf_simple_pull_u8(buf); - if (buf->len) { - info.id = net_buf_simple_pull_le64(buf); - } + if (buf->len) { + info.id = net_buf_simple_pull_le64(buf); + } - if (buf->len >= 7) { - info.size = net_buf_simple_pull_le32(buf); - info.block_size_log = net_buf_simple_pull_u8(buf); - info.mtu_size = net_buf_simple_pull_le16(buf); - info.missing_blocks = net_buf_simple_pull(buf, buf->len); - } + if (buf->len >= 7) { + info.size = net_buf_simple_pull_le32(buf); + info.block_size_log = net_buf_simple_pull_u8(buf); + info.mtu_size = net_buf_simple_pull_le16(buf); + info.missing_blocks = net_buf_simple_pull(buf, buf->len); + } - LOG_DBG("status: %u %s phase: %u %s", info.status, - info.mode == BT_MESH_BLOB_XFER_MODE_PUSH ? "push" : "pull", - info.phase, bt_hex(&info.id, 8)); + BT_DBG("status: %u %s phase: %u %s", info.status, + info.mode == BT_MESH_BLOB_XFER_MODE_PUSH ? "push" : "pull", + info.phase, bt_hex(&info.id, 8)); + if (cli->state != BT_MESH_BLOB_CLI_STATE_START && + cli->state != BT_MESH_BLOB_CLI_STATE_XFER_CHECK && + cli->state != BT_MESH_BLOB_CLI_STATE_CANCEL && + cli->state != BT_MESH_BLOB_CLI_STATE_XFER_PROGRESS_GET) { + BT_WARN("Wrong state: %d", cli->state); + return -EBUSY; + } - if (cli->state != BT_MESH_BLOB_CLI_STATE_START && - cli->state != BT_MESH_BLOB_CLI_STATE_XFER_CHECK && - cli->state != BT_MESH_BLOB_CLI_STATE_CANCEL && - cli->state != BT_MESH_BLOB_CLI_STATE_XFER_PROGRESS_GET) { - LOG_WRN("Wrong state: %d", cli->state); - return -EBUSY; - } + target = target_get(cli, ctx->addr); + if (!target) { + return -ENOENT; + } - target = target_get(cli, ctx->addr); - if (!target) { - return -ENOENT; - } + if (cli->state == BT_MESH_BLOB_CLI_STATE_START) { + expected_phase = BT_MESH_BLOB_XFER_PHASE_WAITING_FOR_BLOCK; + } else if (cli->state == BT_MESH_BLOB_CLI_STATE_XFER_CHECK) { + expected_phase = BT_MESH_BLOB_XFER_PHASE_COMPLETE; + } else if (cli->state != BT_MESH_BLOB_CLI_STATE_XFER_PROGRESS_GET) { + expected_phase = BT_MESH_BLOB_XFER_PHASE_INACTIVE; + } else { /* cli->state == BT_MESH_BLOB_CLI_STATE_XFER_PROGRESS_GET */ + blob_cli_broadcast_rsp(cli, target); + if (cli->cb && cli->cb->xfer_progress) { + cli->cb->xfer_progress(cli, target, &info); + } + return 0; + } - if (cli->state == BT_MESH_BLOB_CLI_STATE_START) { - expected_phase = BT_MESH_BLOB_XFER_PHASE_WAITING_FOR_BLOCK; - } else if (cli->state == BT_MESH_BLOB_CLI_STATE_XFER_CHECK) { - expected_phase = BT_MESH_BLOB_XFER_PHASE_COMPLETE; - } else if (cli->state != BT_MESH_BLOB_CLI_STATE_XFER_PROGRESS_GET) { - expected_phase = BT_MESH_BLOB_XFER_PHASE_INACTIVE; - } else { /* cli->state == BT_MESH_BLOB_CLI_STATE_XFER_PROGRESS_GET */ - blob_cli_broadcast_rsp(cli, target); - if (cli->cb && cli->cb->xfer_progress) { - cli->cb->xfer_progress(cli, target, &info); - } - return 0; - } + if (info.status != BT_MESH_BLOB_SUCCESS) { + target_drop(cli, target, info.status); + } else if (info.phase != expected_phase) { + BT_WARN("Wrong phase: %u != %u", expected_phase, info.phase); + return -EINVAL; + } else if (info.phase != BT_MESH_BLOB_XFER_PHASE_INACTIVE && + info.id != cli->xfer->id) { + target_drop(cli, target, BT_MESH_BLOB_ERR_WRONG_BLOB_ID); + } - if (info.status != BT_MESH_BLOB_SUCCESS) { - target_drop(cli, target, info.status); - } else if (info.phase != expected_phase) { - LOG_WRN("Wrong phase: %u != %u", expected_phase, info.phase); - return -EINVAL; - } else if (info.phase != BT_MESH_BLOB_XFER_PHASE_INACTIVE && - info.id != cli->xfer->id) { - target_drop(cli, target, BT_MESH_BLOB_ERR_WRONG_BLOB_ID); - } + blob_cli_broadcast_rsp(cli, target); - blob_cli_broadcast_rsp(cli, target); - - return 0; + return 0; } static int handle_block_report(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx, - struct net_buf_simple *buf) + struct net_buf_simple *buf) { - struct bt_mesh_blob_cli *cli = mod->rt->user_data; - struct block_status status = { - .status = BT_MESH_BLOB_SUCCESS, - .block.number = cli->block.number, - .missing = (buf->len ? BT_MESH_BLOB_CHUNKS_MISSING_ENCODED : - BT_MESH_BLOB_CHUNKS_MISSING_NONE), - }; - struct bt_mesh_blob_target *target; + struct bt_mesh_blob_cli *cli = mod->user_data; + struct block_status status = { + .status = BT_MESH_BLOB_SUCCESS, + .block.number = cli->block.number, + .missing = (buf->len ? BT_MESH_BLOB_CHUNKS_MISSING_ENCODED : + BT_MESH_BLOB_CHUNKS_MISSING_NONE), + }; + struct bt_mesh_blob_target *target; - if (!cli->xfer) { - return -EINVAL; - } + if (!cli->xfer) { + return -EINVAL; + } - if (cli->xfer->mode == BT_MESH_BLOB_XFER_MODE_PUSH) { - LOG_WRN("Unexpected encoded block report in push mode"); - return -EINVAL; - } + if (cli->xfer->mode == BT_MESH_BLOB_XFER_MODE_PUSH) { + BT_WARN("Unexpected encoded block report in push mode"); + return -EINVAL; + } - LOG_DBG(""); + BT_DBG(""); - target = target_get(cli, ctx->addr); - if (!target) { - return -ENOENT; - } + target = target_get(cli, ctx->addr); + if (!target) { + return -ENOENT; + } - while (buf->len) { - int idx; + while (buf->len) { + int idx; - idx = chunk_idx_decode(buf); - if (idx < 0) { - return idx; - } + idx = chunk_idx_decode(buf); + if (idx < 0) { + return idx; + } - blob_chunk_missing_set(status.block.missing, idx, true); - } + blob_chunk_missing_set(status.block.missing, idx, true); + } - /* If all chunks were already confirmed by this target, Send Data State Machine is in Final - * state for this target. Therefore, the message should be ignored. - */ - if (next_missing_chunk(cli, target->pull->missing, 0) >= cli->block.chunk_count) { - LOG_DBG("All chunks already confirmed"); - return 0; - } + /* If all chunks were already confirmed by this target, Send Data State Machine is in Final + * state for this target. Therefore, the message should be ignored. + */ + if (next_missing_chunk(cli, target->pull->missing, 0) >= cli->block.chunk_count) { + BT_DBG("All chunks already confirmed"); + return 0; + } - cli->tx.cli_timestamp = k_uptime_get() + CLIENT_TIMEOUT_MSEC(cli); + cli->tx.cli_timestamp = k_uptime_get() + CLIENT_TIMEOUT_MSEC(cli); - rx_block_status(cli, target, &status); + rx_block_status(cli, target, &status); - return 0; + return 0; } static int handle_block_status(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx, - struct net_buf_simple *buf) + struct net_buf_simple *buf) { - struct bt_mesh_blob_cli *cli = mod->rt->user_data; - struct bt_mesh_blob_target *target; - struct block_status status = { 0 }; - uint8_t status_and_format; - uint16_t chunk_size; - size_t len; - int idx; + struct bt_mesh_blob_cli *cli = mod->user_data; + struct bt_mesh_blob_target *target; + struct block_status status = { 0 }; + uint8_t status_and_format; + uint16_t chunk_size; + size_t len; + int idx; - target = target_get(cli, ctx->addr); - if (!target) { - return -ENOENT; - } + target = target_get(cli, ctx->addr); + if (!target) { + return -ENOENT; + } - status_and_format = net_buf_simple_pull_u8(buf); - status.status = status_and_format & BIT_MASK(4); - status.missing = status_and_format >> 6; - status.block.number = net_buf_simple_pull_le16(buf); - chunk_size = net_buf_simple_pull_le16(buf); - status.block.chunk_count = - DIV_ROUND_UP(cli->block.size, chunk_size); + status_and_format = net_buf_simple_pull_u8(buf); + status.status = status_and_format & BIT_MASK(4); + status.missing = status_and_format >> 6; + status.block.number = net_buf_simple_pull_le16(buf); + chunk_size = net_buf_simple_pull_le16(buf); + status.block.chunk_count = + DIV_ROUND_UP(cli->block.size, chunk_size); - LOG_DBG("status: %u block: %u encoding: %u", status.status, - status.block.number, status.missing); + BT_DBG("status: %u block: %u encoding: %u", status.status, + status.block.number, status.missing); - switch (status.missing) { - case BT_MESH_BLOB_CHUNKS_MISSING_ALL: - blob_chunk_missing_set_all(&status.block); - break; - case BT_MESH_BLOB_CHUNKS_MISSING_NONE: - break; - case BT_MESH_BLOB_CHUNKS_MISSING_SOME: - if (buf->len > sizeof(status.block.missing)) { - return -EINVAL; - } + switch (status.missing) { + case BT_MESH_BLOB_CHUNKS_MISSING_ALL: + blob_chunk_missing_set_all(&status.block); + break; + case BT_MESH_BLOB_CHUNKS_MISSING_NONE: + break; + case BT_MESH_BLOB_CHUNKS_MISSING_SOME: + if (buf->len > sizeof(status.block.missing)) { + return -EINVAL; + } - len = buf->len; - memcpy(status.block.missing, net_buf_simple_pull_mem(buf, len), - len); + len = buf->len; + memcpy(status.block.missing, net_buf_simple_pull_mem(buf, len), + len); - LOG_DBG("Missing: %s", bt_hex(status.block.missing, len)); - break; - case BT_MESH_BLOB_CHUNKS_MISSING_ENCODED: - /** MshMBTv1.0: 5.3.8: An empty Missing Chunks field entails that there are no - * missing chunks for this block. - */ - if (!buf->len) { - status.missing = BT_MESH_BLOB_CHUNKS_MISSING_NONE; - } + BT_DBG("Missing: %s", bt_hex(status.block.missing, len)); + break; + case BT_MESH_BLOB_CHUNKS_MISSING_ENCODED: + /** MshMBTv1.0: 5.3.8: An empty Missing Chunks field entails that there are no + * missing chunks for this block. + */ + if (!buf->len) { + status.missing = BT_MESH_BLOB_CHUNKS_MISSING_NONE; + } - while (buf->len) { - idx = chunk_idx_decode(buf); - if (idx < 0 || idx >= status.block.chunk_count) { - LOG_ERR("Invalid encoding"); - return -EINVAL; - } + while (buf->len) { + idx = chunk_idx_decode(buf); + if (idx < 0 || idx >= status.block.chunk_count) { + BT_ERR("Invalid encoding"); + return -EINVAL; + } - LOG_DBG("Missing %d", idx); + BT_DBG("Missing %d", idx); - blob_chunk_missing_set(status.block.missing, idx, true); - } - break; - } + blob_chunk_missing_set(status.block.missing, idx, true); + } + break; + } - rx_block_status(cli, target, &status); + rx_block_status(cli, target, &status); - return 0; + return 0; } static int handle_info_status(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx, - struct net_buf_simple *buf) + struct net_buf_simple *buf) { - struct bt_mesh_blob_cli *cli = mod->rt->user_data; - struct bt_mesh_blob_cli_caps caps; - enum bt_mesh_blob_status status; - struct bt_mesh_blob_target *target; + struct bt_mesh_blob_cli *cli = mod->user_data; + struct bt_mesh_blob_cli_caps caps; + enum bt_mesh_blob_status status; + struct bt_mesh_blob_target *target; - if (cli->state != BT_MESH_BLOB_CLI_STATE_CAPS_GET) { - return -EBUSY; - } + if (cli->state != BT_MESH_BLOB_CLI_STATE_CAPS_GET) { + return -EBUSY; + } - caps.min_block_size_log = net_buf_simple_pull_u8(buf); - caps.max_block_size_log = net_buf_simple_pull_u8(buf); - caps.max_chunks = net_buf_simple_pull_le16(buf); - caps.max_chunk_size = net_buf_simple_pull_le16(buf); - caps.max_size = net_buf_simple_pull_le32(buf); - caps.mtu_size = net_buf_simple_pull_le16(buf); - caps.modes = net_buf_simple_pull_u8(buf); + caps.min_block_size_log = net_buf_simple_pull_u8(buf); + caps.max_block_size_log = net_buf_simple_pull_u8(buf); + caps.max_chunks = net_buf_simple_pull_le16(buf); + caps.max_chunk_size = net_buf_simple_pull_le16(buf); + caps.max_size = net_buf_simple_pull_le32(buf); + caps.mtu_size = net_buf_simple_pull_le16(buf); + caps.modes = net_buf_simple_pull_u8(buf); - if (caps.min_block_size_log < 0x06 || - caps.max_block_size_log > 0x20 || - caps.max_block_size_log < caps.min_block_size_log || - caps.max_chunks == 0 || caps.max_chunk_size < 8 || - caps.max_size == 0 || caps.mtu_size < 0x14) { - return -EINVAL; - } + if (caps.min_block_size_log < 0x06 || + caps.max_block_size_log > 0x20 || + caps.max_block_size_log < caps.min_block_size_log || + caps.max_chunks == 0 || caps.max_chunk_size < 8 || + caps.max_size == 0 || caps.mtu_size < 0x14) { + return -EINVAL; + } - LOG_DBG("0x%04x\n\tblock size: %u - %u\n\tchunks: %u\n\tchunk size: %u\n" - "\tblob size: %u\n\tmtu size: %u\n\tmodes: %x", - ctx->addr, caps.min_block_size_log, caps.max_block_size_log, - caps.max_chunks, caps.max_chunk_size, caps.max_size, - caps.mtu_size, caps.modes); + BT_DBG("0x%04x\n\tblock size: %u - %u\n\tchunks: %u\n\tchunk size: %u\n" + "\tblob size: %u\n\tmtu size: %u\n\tmodes: %x", + ctx->addr, caps.min_block_size_log, caps.max_block_size_log, + caps.max_chunks, caps.max_chunk_size, caps.max_size, + caps.mtu_size, caps.modes); - target = target_get(cli, ctx->addr); - if (!target) { - return -ENOENT; - } + target = target_get(cli, ctx->addr); + if (!target) { + return -ENOENT; + } - status = caps_adjust(cli, &caps); - if (status != BT_MESH_BLOB_SUCCESS) { - target_drop(cli, target, status); - } + status = caps_adjust(cli, &caps); + if (status != BT_MESH_BLOB_SUCCESS) { + target_drop(cli, target, status); + } - blob_cli_broadcast_rsp(cli, target); + blob_cli_broadcast_rsp(cli, target); - return 0; + return 0; } const struct bt_mesh_model_op _bt_mesh_blob_cli_op[] = { - { BT_MESH_BLOB_OP_XFER_STATUS, BT_MESH_LEN_MIN(2), handle_xfer_status }, - { BT_MESH_BLOB_OP_BLOCK_REPORT, BT_MESH_LEN_MIN(0), handle_block_report }, - { BT_MESH_BLOB_OP_BLOCK_STATUS, BT_MESH_LEN_MIN(5), handle_block_status }, - { BT_MESH_BLOB_OP_INFO_STATUS, BT_MESH_LEN_EXACT(13), handle_info_status }, - BT_MESH_MODEL_OP_END, + { BT_MESH_BLOB_OP_XFER_STATUS, 2, (void *)handle_xfer_status }, + { BT_MESH_BLOB_OP_BLOCK_REPORT, 0, (void *)handle_block_report }, + { BT_MESH_BLOB_OP_BLOCK_STATUS, 5, (void *)handle_block_status }, + { BT_MESH_BLOB_OP_INFO_STATUS, 13, (void *)handle_info_status }, + BLE_MESH_MODEL_OP_END, }; -static int blob_cli_init(const struct bt_mesh_model *mod) +static int blob_cli_init(struct bt_mesh_model *mod) { - struct bt_mesh_blob_cli *cli = mod->rt->user_data; + struct bt_mesh_blob_cli *cli = mod->user_data; - cli->mod = mod; + cli->mod = mod; - bt_mesh_blob_cli_set_chunk_interval_ms(cli, CONFIG_BT_MESH_TX_BLOB_CHUNK_SEND_INTERVAL); - cli->tx.cli_timestamp = 0ll; - k_work_init_delayable(&cli->tx.retry, retry_timeout); - k_work_init_delayable(&cli->tx.complete, tx_complete); + bt_mesh_blob_cli_set_chunk_interval_ms(cli, CONFIG_BLE_MESH_TX_BLOB_CHUNK_SEND_INTERVAL); + cli->tx.cli_timestamp = 0ll; + k_work_init_delayable(&cli->tx.retry, retry_timeout); + k_work_init_delayable(&cli->tx.complete, tx_complete); - return 0; + return 0; } -static void blob_cli_reset(const struct bt_mesh_model *mod) +static void blob_cli_reset(struct bt_mesh_model *mod) { - struct bt_mesh_blob_cli *cli = mod->rt->user_data; + struct bt_mesh_blob_cli *cli = mod->user_data; - cli_state_reset(cli); + cli_state_reset(cli); +} + +static int blob_cli_deinit(struct bt_mesh_model *mod) +{ + blob_cli_reset(mod); + return 0; } const struct bt_mesh_model_cb _bt_mesh_blob_cli_cb = { - .init = blob_cli_init, - .reset = blob_cli_reset, + .init = blob_cli_init, +#if CONFIG_BLE_MESH_DEINIT + .deinit = blob_cli_deinit, +#endif }; - int bt_mesh_blob_cli_caps_get(struct bt_mesh_blob_cli *cli, - const struct bt_mesh_blob_cli_inputs *inputs) + const struct bt_mesh_blob_cli_inputs *inputs) { - if (bt_mesh_blob_cli_is_busy(cli)) { - return -EBUSY; - } + if (bt_mesh_blob_cli_is_busy(cli)) { + return -EBUSY; + } - cli->inputs = inputs; + cli->inputs = inputs; - cli->caps.min_block_size_log = 0x06; - cli->caps.max_block_size_log = 0x20; - cli->caps.max_chunks = CONFIG_BT_MESH_BLOB_CHUNK_COUNT_MAX; - cli->caps.max_chunk_size = BLOB_TX_CHUNK_SIZE; - cli->caps.max_size = 0xffffffff; - cli->caps.mtu_size = 0xffff; - cli->caps.modes = BT_MESH_BLOB_XFER_MODE_ALL; + cli->caps.min_block_size_log = 0x06; + cli->caps.max_block_size_log = 0x20; + cli->caps.max_chunks = CONFIG_BLE_MESH_BLOB_CHUNK_COUNT_MAX; + cli->caps.max_chunk_size = BLOB_TX_CHUNK_SIZE; + cli->caps.max_size = 0xffffffff; + cli->caps.mtu_size = 0xffff; + cli->caps.modes = BT_MESH_BLOB_XFER_MODE_ALL; - if (!targets_reset(cli)) { - LOG_ERR("No valid targets"); - return -ENODEV; - } + if (!targets_reset(cli)) { + BT_ERR("No valid targets"); + return -ENODEV; + } - caps_get(cli); + caps_get(cli); - return 0; + return 0; } int bt_mesh_blob_cli_send(struct bt_mesh_blob_cli *cli, - const struct bt_mesh_blob_cli_inputs *inputs, - const struct bt_mesh_blob_xfer *xfer, - const struct bt_mesh_blob_io *io) + const struct bt_mesh_blob_cli_inputs *inputs, + const struct bt_mesh_blob_xfer *xfer, + const struct bt_mesh_blob_io *io) { - if (bt_mesh_blob_cli_is_busy(cli)) { - LOG_ERR("BLOB Client is busy"); - return -EBUSY; - } + if (bt_mesh_blob_cli_is_busy(cli)) { + BT_ERR("BLOB Client is busy"); + return -EBUSY; + } - if (!(xfer->mode & BT_MESH_BLOB_XFER_MODE_ALL) || xfer->block_size_log < 0x06 || - xfer->block_size_log > 0x20 || xfer->chunk_size < 8 || - xfer->chunk_size > BLOB_TX_CHUNK_SIZE) { - LOG_ERR("Incompatible transfer parameters"); - return -EINVAL; - } + if (!(xfer->mode & BT_MESH_BLOB_XFER_MODE_ALL) || xfer->block_size_log < 0x06 || + xfer->block_size_log > 0x20 || xfer->chunk_size < 8 || + xfer->chunk_size > BLOB_TX_CHUNK_SIZE) { + BT_ERR("Incompatible transfer parameters"); + return -EINVAL; + } - cli->xfer = xfer; - cli->inputs = inputs; - cli->io = io; + cli->xfer = xfer; + cli->inputs = inputs; + cli->io = io; - if (cli->xfer->block_size_log == 0x20) { - cli->block_count = 1; - } else { - cli->block_count = DIV_ROUND_UP(cli->xfer->size, (1U << cli->xfer->block_size_log)); - } + if (cli->xfer->block_size_log == 0x20) { + cli->block_count = 1; + } else { + cli->block_count = DIV_ROUND_UP(cli->xfer->size, (1U << cli->xfer->block_size_log)); + } - block_set(cli, 0); + block_set(cli, 0); - if (cli->block.chunk_count > CONFIG_BT_MESH_BLOB_CHUNK_COUNT_MAX) { - LOG_ERR("Too many chunks"); - return -EINVAL; - } + if (cli->block.chunk_count > CONFIG_BLE_MESH_BLOB_CHUNK_COUNT_MAX) { + BT_ERR("Too many chunks"); + return -EINVAL; + } - if (!targets_reset(cli)) { - LOG_ERR("No valid targets"); - return -ENODEV; - } + if (!targets_reset(cli)) { + BT_ERR("No valid targets"); + return -ENODEV; + } - LOG_DBG("\n\tblock size log: %u\n\tchunk size: %u\n" - "\tblob size: %u\n\tmode: %x", - cli->xfer->block_size_log, cli->xfer->chunk_size, - cli->xfer->size, cli->xfer->mode); + BT_INFO("block size log: %u chunk size: %u blob size: %u tmode: %x", + cli->xfer->block_size_log, cli->xfer->chunk_size, + cli->xfer->size, cli->xfer->mode); - return xfer_start(cli); + return xfer_start(cli); } int bt_mesh_blob_cli_suspend(struct bt_mesh_blob_cli *cli) { - if (cli->state == BT_MESH_BLOB_CLI_STATE_SUSPENDED) { - return 0; - } + if (cli->state == BT_MESH_BLOB_CLI_STATE_SUSPENDED) { + return 0; + } - if (cli->state != BT_MESH_BLOB_CLI_STATE_BLOCK_START && - cli->state != BT_MESH_BLOB_CLI_STATE_BLOCK_SEND && - cli->state != BT_MESH_BLOB_CLI_STATE_BLOCK_CHECK) { - LOG_WRN("BLOB xfer not started: %d", cli->state); - return -EINVAL; - } + if (cli->state != BT_MESH_BLOB_CLI_STATE_BLOCK_START && + cli->state != BT_MESH_BLOB_CLI_STATE_BLOCK_SEND && + cli->state != BT_MESH_BLOB_CLI_STATE_BLOCK_CHECK) { + BT_WARN("BLOB xfer not started: %d", cli->state); + return -EINVAL; + } - cli->state = BT_MESH_BLOB_CLI_STATE_SUSPENDED; - (void)k_work_cancel_delayable(&cli->tx.retry); - cli->tx.ctx.is_inited = 0; - cli->tx.sending = 0; - cli->tx.cli_timestamp = 0ll; - return 0; + cli->state = BT_MESH_BLOB_CLI_STATE_SUSPENDED; + (void)k_work_cancel_delayable(&cli->tx.retry); + cli->tx.ctx.is_inited = 0; + cli->tx.sending = 0; + cli->tx.cli_timestamp = 0ll; + return 0; } int bt_mesh_blob_cli_resume(struct bt_mesh_blob_cli *cli) { - struct bt_mesh_blob_target *target; + struct bt_mesh_blob_target *target; - if (cli->state != BT_MESH_BLOB_CLI_STATE_SUSPENDED) { - LOG_WRN("Not suspended"); - return -EINVAL; - } + if (cli->state != BT_MESH_BLOB_CLI_STATE_SUSPENDED) { + BT_WARN("Not suspended"); + return -EINVAL; + } - /* Restore timed out targets. */ - TARGETS_FOR_EACH(cli, target) { - if (!!target->timedout) { - target->status = BT_MESH_BLOB_SUCCESS; - target->timedout = 0U; - } - } + /* Restore timed out targets. */ + TARGETS_FOR_EACH(cli, target) { + if (!!target->timedout) { + target->status = BT_MESH_BLOB_SUCCESS; + target->timedout = 0U; + } + } - if (!targets_reset(cli)) { - LOG_ERR("No valid targets"); - return -ENODEV; - } + if (!targets_reset(cli)) { + BT_ERR("No valid targets"); + return -ENODEV; + } - block_set(cli, 0); - return xfer_start(cli); + block_set(cli, 0); + return xfer_start(cli); } void bt_mesh_blob_cli_cancel(struct bt_mesh_blob_cli *cli) { - if (!bt_mesh_blob_cli_is_busy(cli)) { - LOG_WRN("BLOB xfer already cancelled"); - return; - } + if (!bt_mesh_blob_cli_is_busy(cli)) { + BT_WARN("BLOB xfer already cancelled"); + return; + } - LOG_DBG(""); + BT_DBG(""); - if (cli->state == BT_MESH_BLOB_CLI_STATE_CAPS_GET || - cli->state == BT_MESH_BLOB_CLI_STATE_SUSPENDED) { - cli_state_reset(cli); - return; - } + if (cli->state == BT_MESH_BLOB_CLI_STATE_CAPS_GET || + cli->state == BT_MESH_BLOB_CLI_STATE_SUSPENDED) { + cli_state_reset(cli); + return; + } - cli->tx.cancelled = 1U; - cli->state = BT_MESH_BLOB_CLI_STATE_CANCEL; + cli->tx.cancelled = 1U; + cli->state = BT_MESH_BLOB_CLI_STATE_CANCEL; +} + +void bt_mesh_blob_cli_send_cancel(struct bt_mesh_blob_cli *cli, + const struct bt_mesh_blob_xfer *xfer) +{ + cli->xfer = xfer; + + transfer_cancel(cli); } int bt_mesh_blob_cli_xfer_progress_get(struct bt_mesh_blob_cli *cli, - const struct bt_mesh_blob_cli_inputs *inputs) + const struct bt_mesh_blob_cli_inputs *inputs) { - if (bt_mesh_blob_cli_is_busy(cli)) { - return -EBUSY; - } + if (bt_mesh_blob_cli_is_busy(cli)) { + return -EBUSY; + } - cli->inputs = inputs; + cli->inputs = inputs; - check_transfer(cli); + check_transfer(cli); - return 0; + return 0; } uint8_t bt_mesh_blob_cli_xfer_progress_active_get(struct bt_mesh_blob_cli *cli) { - if (cli->state < BT_MESH_BLOB_CLI_STATE_START) { - return 0; - } + if (cli->state < BT_MESH_BLOB_CLI_STATE_START) { + return 0; + } - return (100U * cli->block.number) / cli->block_count; + return (100U * cli->block.number) / cli->block_count; } bool bt_mesh_blob_cli_is_busy(struct bt_mesh_blob_cli *cli) { - return cli->state != BT_MESH_BLOB_CLI_STATE_NONE; + return cli->state != BT_MESH_BLOB_CLI_STATE_NONE; } void bt_mesh_blob_cli_set_chunk_interval_ms(struct bt_mesh_blob_cli *cli, uint32_t interval_ms) { - cli->chunk_interval_ms = interval_ms; + cli->chunk_interval_ms = interval_ms; } + +#endif /* CONFIG_BLE_MESH_BLOB_CLI */ 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 2d4ec561f8..3540a6abea 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 @@ -1,315 +1,321 @@ /* - * Copyright (c) 2020 Nordic Semiconductor ASA + * SPDX-FileCopyrightText: 2020 Nordic Semiconductor ASA + * SPDX-FileContributor: 2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ #include -#include -#include + #include "net.h" #include "access.h" #include "transport.h" #include "lpn.h" +#include "mesh/main.h" +#include "mesh/utils.h" +#include "mesh/common.h" +#include "mesh/slist.h" #include "blob.h" +#include "mesh_v1.1/mbt/blob.h" +#include "mesh_v1.1/mbt/blob_cli.h" +#include "mesh_v1.1/mbt/blob_srv.h" -#define LOG_LEVEL CONFIG_BT_MESH_MODEL_LOG_LEVEL -#include -LOG_MODULE_REGISTER(bt_mesh_blob_srv); +#if CONFIG_BLE_MESH_SETTINGS +#include "settings.h" +#endif -#define MTU_SIZE_MAX (BT_MESH_RX_SDU_MAX - BT_MESH_MIC_SHORT) +#if CONFIG_BLE_MESH_BLOB_SRV + +#define MTU_SIZE_MAX (BLE_MESH_RX_SDU_MAX - BLE_MESH_MIC_SHORT) /* The Receive BLOB Timeout Timer */ #define SERVER_TIMEOUT_SECS(srv) (10 * (1 + (srv)->state.timeout_base)) /* The initial timer value used by an instance of the Pull BLOB State machine - T_BPI */ -#define REPORT_TIMER_TIMEOUT K_SECONDS(CONFIG_BT_MESH_BLOB_REPORT_TIMEOUT) +#define REPORT_TIMER_TIMEOUT K_SECONDS(CONFIG_BLE_MESH_BLOB_REPORT_TIMEOUT) -BUILD_ASSERT(BLOB_BLOCK_SIZE_LOG_MIN <= BLOB_BLOCK_SIZE_LOG_MAX, - "The must be at least one number between the min and " - "max block size that is the power of two."); +_Static_assert(BLOB_BLOCK_SIZE_LOG_MIN <= BLOB_BLOCK_SIZE_LOG_MAX, + "The must be at least one number between the min and " + "max block size that is the power of two."); -BUILD_ASSERT((BLOB_XFER_STATUS_MSG_MAXLEN + BT_MESH_MODEL_OP_LEN(BT_MESH_BLOB_OP_XFER_STATUS) + - BT_MESH_MIC_SHORT) <= BT_MESH_TX_SDU_MAX, - "The BLOB Transfer Status message does not fit into the maximum outgoing SDU size."); +_Static_assert((BLOB_XFER_STATUS_MSG_MAXLEN + BLE_MESH_MODEL_OP_LEN(BT_MESH_BLOB_OP_XFER_STATUS) + + BLE_MESH_MIC_SHORT) <= BLE_MESH_TX_SDU_MAX, + "The BLOB Transfer Status message does not fit into the maximum outgoing SDU size."); -BUILD_ASSERT((BLOB_BLOCK_REPORT_STATUS_MSG_MAXLEN + - BT_MESH_MODEL_OP_LEN(BT_MESH_BLOB_OP_BLOCK_REPORT) + BT_MESH_MIC_SHORT) - <= BT_MESH_TX_SDU_MAX, - "The BLOB Partial Block Report message does not fit into the maximum outgoing SDU " - "size."); +_Static_assert((BLOB_BLOCK_REPORT_STATUS_MSG_MAXLEN + + BLE_MESH_MODEL_OP_LEN(BT_MESH_BLOB_OP_BLOCK_REPORT) + BLE_MESH_MIC_SHORT) + <= BLE_MESH_TX_SDU_MAX, + "The BLOB Partial Block Report message does not fit into the maximum outgoing SDU " + "size."); -BUILD_ASSERT((BLOB_BLOCK_STATUS_MSG_MAXLEN + BT_MESH_MODEL_OP_LEN(BT_MESH_BLOB_OP_BLOCK_STATUS) + - BT_MESH_MIC_SHORT) <= BT_MESH_TX_SDU_MAX, - "The BLOB Block Status message does not fit into the maximum outgoing SDU size."); +_Static_assert((BLOB_BLOCK_STATUS_MSG_MAXLEN + BLE_MESH_MODEL_OP_LEN(BT_MESH_BLOB_OP_BLOCK_STATUS) + + BLE_MESH_MIC_SHORT) <= BLE_MESH_TX_SDU_MAX, + "The BLOB Block Status message does not fit into the maximum outgoing SDU size."); static void cancel(struct bt_mesh_blob_srv *srv); static void suspend(struct bt_mesh_blob_srv *srv); static inline uint32_t block_count_get(const struct bt_mesh_blob_srv *srv) { - return DIV_ROUND_UP(srv->state.xfer.size, - (1U << srv->state.xfer.block_size_log)); + return DIV_ROUND_UP(srv->state.xfer.size, + (1U << srv->state.xfer.block_size_log)); } static inline uint32_t max_chunk_size(const struct bt_mesh_blob_srv *srv) { - return MIN((srv->state.mtu_size - 2 - BT_MESH_MODEL_OP_LEN(BT_MESH_BLOB_OP_CHUNK)), - BLOB_RX_CHUNK_SIZE); + return MIN((srv->state.mtu_size - 2 - BLE_MESH_MODEL_OP_LEN(BT_MESH_BLOB_OP_CHUNK)), + BLOB_RX_CHUNK_SIZE); } static inline uint32_t max_chunk_count(const struct bt_mesh_blob_srv *srv) { - return MIN(8 * (srv->state.mtu_size - 6), - CONFIG_BT_MESH_BLOB_CHUNK_COUNT_MAX); + return MIN(8 * (srv->state.mtu_size - 6), + CONFIG_BLE_MESH_BLOB_CHUNK_COUNT_MAX); } static inline uint32_t missing_chunks(const struct bt_mesh_blob_block *block) { - int i; - uint32_t count = 0; + int i; + uint32_t count = 0; - for (i = 0; i < ARRAY_SIZE(block->missing); ++i) { - count += POPCOUNT(block->missing[i]); - } + for (i = 0; i < ARRAY_SIZE(block->missing); ++i) { + count += popcount(block->missing[i]); + } - return count; + return count; } static void store_state(const struct bt_mesh_blob_srv *srv) { - if (!IS_ENABLED(CONFIG_BT_SETTINGS)) { - return; - } +#if CONFIG_BLE_MESH_SETTINGS + /* Convert bit count to byte count: */ + uint32_t block_len = DIV_ROUND_UP(block_count_get(srv), 8); - /* Convert bit count to byte count: */ - uint32_t block_len = DIV_ROUND_UP(block_count_get(srv), 8); - - bt_mesh_model_data_store( - srv->mod, false, NULL, &srv->state, - offsetof(struct bt_mesh_blob_srv_state, blocks) + block_len); + bt_mesh_model_data_store( + srv->mod, false, NULL, &srv->state, + offsetof(struct bt_mesh_blob_srv_state, blocks) + block_len); +#endif } static void erase_state(struct bt_mesh_blob_srv *srv) { - if (!IS_ENABLED(CONFIG_BT_SETTINGS)) { - return; - } - - bt_mesh_model_data_store(srv->mod, false, NULL, NULL, 0); +#if CONFIG_BLE_MESH_SETTINGS + bt_mesh_model_data_store(srv->mod, false, NULL, NULL, 0); +#endif } static int io_open(struct bt_mesh_blob_srv *srv) { - if (!srv->io->open) { - return 0; - } + if (!srv->io->open) { + return 0; + } - return srv->io->open(srv->io, &srv->state.xfer, BT_MESH_BLOB_WRITE); + return srv->io->open(srv->io, &srv->state.xfer, BT_MESH_BLOB_WRITE); } static void io_close(struct bt_mesh_blob_srv *srv) { - if (!srv->io->close) { - return; - } + if (!srv->io->close) { + return; + } - srv->io->close(srv->io, &srv->state.xfer); + srv->io->close(srv->io, &srv->state.xfer); } static void reset_timer(struct bt_mesh_blob_srv *srv) { - uint32_t timeout_secs = - srv->state.xfer.mode == BT_MESH_BLOB_XFER_MODE_PULL ? - MAX(SERVER_TIMEOUT_SECS(srv), - CONFIG_BT_MESH_BLOB_REPORT_TIMEOUT + 1) : - SERVER_TIMEOUT_SECS(srv); - k_work_reschedule(&srv->rx_timeout, K_SECONDS(timeout_secs)); + uint32_t timeout_secs = + srv->state.xfer.mode == BT_MESH_BLOB_XFER_MODE_PULL ? + MAX(SERVER_TIMEOUT_SECS(srv), + CONFIG_BLE_MESH_BLOB_REPORT_TIMEOUT + 1) : + SERVER_TIMEOUT_SECS(srv); + BT_DBG("Next Msg should be arrive in %ds", timeout_secs); + k_work_reschedule(&srv->rx_timeout, K_SECONDS(timeout_secs)); } static void buf_chunk_index_add(struct net_buf_simple *buf, uint16_t chunk) { - /* utf-8 encoded: */ - if (chunk < 0x80) { - net_buf_simple_add_u8(buf, chunk); - } else if (chunk < 0x8000) { - net_buf_simple_add_u8(buf, 0xc0 | chunk >> 6); - net_buf_simple_add_u8(buf, 0x80 | (chunk & BIT_MASK(6))); - } else { - net_buf_simple_add_u8(buf, 0xe0 | chunk >> 12); - net_buf_simple_add_u8(buf, 0x80 | ((chunk >> 6) & BIT_MASK(6))); - net_buf_simple_add_u8(buf, 0x80 | (chunk & BIT_MASK(6))); - } + /* utf-8 encoded: */ + if (chunk < 0x80) { + net_buf_simple_add_u8(buf, chunk); + } else if (chunk < 0x8000) { + net_buf_simple_add_u8(buf, 0xc0 | chunk >> 6); + net_buf_simple_add_u8(buf, 0x80 | (chunk & BIT_MASK(6))); + } else { + net_buf_simple_add_u8(buf, 0xe0 | chunk >> 12); + net_buf_simple_add_u8(buf, 0x80 | ((chunk >> 6) & BIT_MASK(6))); + net_buf_simple_add_u8(buf, 0x80 | (chunk & BIT_MASK(6))); + } } static int pull_req_max(const struct bt_mesh_blob_srv *srv) { - int count = CONFIG_BT_MESH_BLOB_SRV_PULL_REQ_COUNT; + int count = CONFIG_BLE_MESH_BLOB_SRV_PULL_REQ_COUNT; -#if defined(CONFIG_BT_MESH_LOW_POWER) - /* No point in requesting more than the friend node can hold: */ - if (bt_mesh_lpn_established()) { - uint32_t segments_per_chunk = DIV_ROUND_UP( - BLOB_CHUNK_SDU_LEN(srv->state.xfer.chunk_size), - BT_MESH_APP_SEG_SDU_MAX); +#if defined(CONFIG_BLE_MESH_LOW_POWER) + /* No point in requesting more than the friend node can hold: */ + if (bt_mesh_lpn_established()) { + uint32_t segments_per_chunk = DIV_ROUND_UP( + BLOB_CHUNK_SDU_LEN(srv->state.xfer.chunk_size), + BLE_MESH_APP_SEG_SDU_MAX); - count = MIN(CONFIG_BT_MESH_BLOB_SRV_PULL_REQ_COUNT, - bt_mesh.lpn.queue_size / segments_per_chunk); - } + count = MIN(CONFIG_BLE_MESH_BLOB_SRV_PULL_REQ_COUNT, + bt_mesh.lpn.queue_size / segments_per_chunk); + } #endif - return MIN(count, missing_chunks(&srv->block)); + return MIN(count, missing_chunks(&srv->block)); } static void report_sent(int err, void *cb_data) { - struct bt_mesh_blob_srv *srv = cb_data; + struct bt_mesh_blob_srv *srv = cb_data; - LOG_DBG(""); + BT_DBG(""); - if (IS_ENABLED(CONFIG_BT_MESH_LOW_POWER) && bt_mesh_lpn_established()) { - bt_mesh_lpn_poll(); - } + if (IS_ENABLED(CONFIG_BLE_MESH_LOW_POWER) && bt_mesh_lpn_established()) { + bt_mesh_lpn_poll(); + } - if (k_work_delayable_is_pending(&srv->rx_timeout)) { - k_work_reschedule(&srv->pull.report, REPORT_TIMER_TIMEOUT); - } + if (k_work_delayable_is_pending(&srv->rx_timeout)) { + k_work_reschedule(&srv->pull.report, REPORT_TIMER_TIMEOUT); + } } static void block_report(struct bt_mesh_blob_srv *srv) { - static const struct bt_mesh_send_cb report_cb = { .end = report_sent }; - struct bt_mesh_msg_ctx ctx = { - .app_idx = srv->state.app_idx, - .send_ttl = srv->state.ttl, - .addr = srv->state.cli, - }; - int count; - int i; + static const struct bt_mesh_send_cb report_cb = { .end = report_sent }; + struct bt_mesh_msg_ctx ctx = { + .app_idx = srv->state.app_idx, + .send_ttl = srv->state.ttl, + .addr = srv->state.cli, + }; + int count; + int i; - LOG_DBG("rx BLOB Timeout Timer: %i", k_work_delayable_is_pending(&srv->rx_timeout)); + BT_DBG("rx BLOB Timeout Timer: %i", k_work_delayable_is_pending(&srv->rx_timeout)); - BT_MESH_MODEL_BUF_DEFINE(buf, BT_MESH_BLOB_OP_BLOCK_REPORT, - BLOB_BLOCK_REPORT_STATUS_MSG_MAXLEN); - bt_mesh_model_msg_init(&buf, BT_MESH_BLOB_OP_BLOCK_REPORT); + BLE_MESH_MODEL_BUF_DEFINE(buf, BT_MESH_BLOB_OP_BLOCK_REPORT, + BLOB_BLOCK_REPORT_STATUS_MSG_MAXLEN); + bt_mesh_model_msg_init(&buf, BT_MESH_BLOB_OP_BLOCK_REPORT); - count = pull_req_max(srv); + count = pull_req_max(srv); - for (i = 0; i < srv->block.chunk_count && count; ++i) { - if (blob_chunk_missing_get(srv->block.missing, i)) { - buf_chunk_index_add(&buf, i); - count--; - } - } + for (i = 0; i < srv->block.chunk_count && count; ++i) { + if (blob_chunk_missing_get(srv->block.missing, i)) { + buf_chunk_index_add(&buf, i); + count--; + } + } - (void)bt_mesh_model_send(srv->mod, &ctx, &buf, &report_cb, srv); + (void)bt_mesh_model_send(srv->mod, &ctx, &buf, &report_cb, srv); } static void phase_set(struct bt_mesh_blob_srv *srv, - enum bt_mesh_blob_xfer_phase phase) + enum bt_mesh_blob_xfer_phase phase) { - srv->phase = phase; - LOG_DBG("Phase: %u", phase); + srv->phase = phase; + BT_INFO("Phase: %u", phase); } static void cancel(struct bt_mesh_blob_srv *srv) { - /* TODO: Could this state be preserved instead somehow? Wiping the - * entire transfer state is a bit overkill - */ - phase_set(srv, BT_MESH_BLOB_XFER_PHASE_INACTIVE); - srv->state.xfer.mode = BT_MESH_BLOB_XFER_MODE_NONE; - srv->state.ttl = BT_MESH_TTL_DEFAULT; - srv->block.number = 0xffff; - memset(srv->block.missing, 0, sizeof(srv->block.missing)); - srv->state.xfer.chunk_size = 0xffff; - k_work_cancel_delayable(&srv->rx_timeout); - k_work_cancel_delayable(&srv->pull.report); - io_close(srv); - erase_state(srv); + /* TODO: Could this state be preserved instead somehow? Wiping the + * entire transfer state is a bit overkill + */ + phase_set(srv, BT_MESH_BLOB_XFER_PHASE_INACTIVE); + srv->state.xfer.mode = BT_MESH_BLOB_XFER_MODE_NONE; + srv->state.ttl = BLE_MESH_TTL_DEFAULT; + srv->block.number = 0xffff; + memset(srv->block.missing, 0, sizeof(srv->block.missing)); + srv->state.xfer.chunk_size = 0xffff; + k_work_cancel_delayable(&srv->rx_timeout); + k_work_cancel_delayable(&srv->pull.report); + io_close(srv); + erase_state(srv); - if (srv->cb && srv->cb->end) { - srv->cb->end(srv, srv->state.xfer.id, false); - } + if (srv->cb && srv->cb->end) { + srv->cb->end(srv, srv->state.xfer.id, false); + } } static void suspend(struct bt_mesh_blob_srv *srv) { - LOG_DBG(""); - k_work_cancel_delayable(&srv->rx_timeout); - k_work_cancel_delayable(&srv->pull.report); - phase_set(srv, BT_MESH_BLOB_XFER_PHASE_SUSPENDED); - if (srv->cb && srv->cb->suspended) { - srv->cb->suspended(srv); - } + BT_DBG("%s", __func__); + k_work_cancel_delayable(&srv->rx_timeout); + k_work_cancel_delayable(&srv->pull.report); + phase_set(srv, BT_MESH_BLOB_XFER_PHASE_SUSPENDED); + if (srv->cb && srv->cb->suspended) { + srv->cb->suspended(srv); + } } static void resume(struct bt_mesh_blob_srv *srv) { - LOG_DBG("Resuming"); + BT_DBG("%s", __func__); - phase_set(srv, BT_MESH_BLOB_XFER_PHASE_WAITING_FOR_BLOCK); - reset_timer(srv); + phase_set(srv, BT_MESH_BLOB_XFER_PHASE_WAITING_FOR_BLOCK); + reset_timer(srv); } static void end(struct bt_mesh_blob_srv *srv) { - phase_set(srv, BT_MESH_BLOB_XFER_PHASE_COMPLETE); - k_work_cancel_delayable(&srv->rx_timeout); - k_work_cancel_delayable(&srv->pull.report); - io_close(srv); - erase_state(srv); + phase_set(srv, BT_MESH_BLOB_XFER_PHASE_COMPLETE); + k_work_cancel_delayable(&srv->rx_timeout); + k_work_cancel_delayable(&srv->pull.report); + io_close(srv); + erase_state(srv); - if (srv->cb && srv->cb->end) { - srv->cb->end(srv, srv->state.xfer.id, true); - } + if (srv->cb && srv->cb->end) { + srv->cb->end(srv, srv->state.xfer.id, true); + } } static bool all_blocks_received(struct bt_mesh_blob_srv *srv) { - for (int i = 0; i < ARRAY_SIZE(srv->state.blocks); ++i) { - if (srv->state.blocks[i]) { - return false; - } - } + for (int i = 0; i < ARRAY_SIZE(srv->state.blocks); ++i) { + if (srv->state.blocks[i]) { + return false; + } + } - return true; + return true; } static bool pull_mode_xfer_complete(struct bt_mesh_blob_srv *srv) { - return srv->state.xfer.mode == BT_MESH_BLOB_XFER_MODE_PULL && - srv->phase == BT_MESH_BLOB_XFER_PHASE_WAITING_FOR_CHUNK && - all_blocks_received(srv); + return srv->state.xfer.mode == BT_MESH_BLOB_XFER_MODE_PULL && + srv->phase == BT_MESH_BLOB_XFER_PHASE_WAITING_FOR_CHUNK && + all_blocks_received(srv); } static void timeout(struct k_work *work) { - struct bt_mesh_blob_srv *srv = - CONTAINER_OF(work, struct bt_mesh_blob_srv, rx_timeout.work); + struct bt_mesh_blob_srv *srv = + CONTAINER_OF(work, struct bt_mesh_blob_srv, rx_timeout.work); - LOG_DBG(""); + BT_DBG("phase %d", srv->phase); - if (srv->phase == BT_MESH_BLOB_XFER_PHASE_WAITING_FOR_START) { - cancel(srv); - } else if (pull_mode_xfer_complete(srv)) { - end(srv); - } else { - suspend(srv); - } + if (srv->phase == BT_MESH_BLOB_XFER_PHASE_WAITING_FOR_START) { + cancel(srv); + } else if (pull_mode_xfer_complete(srv)) { + end(srv); + } else { + suspend(srv); + } } static void report_timeout(struct k_work *work) { - struct bt_mesh_blob_srv *srv = - CONTAINER_OF(work, struct bt_mesh_blob_srv, pull.report.work); + struct bt_mesh_blob_srv *srv = + CONTAINER_OF(work, struct bt_mesh_blob_srv, pull.report.work); - LOG_DBG(""); + BT_DBG(""); - if (srv->phase != BT_MESH_BLOB_XFER_PHASE_WAITING_FOR_BLOCK && - srv->phase != BT_MESH_BLOB_XFER_PHASE_WAITING_FOR_CHUNK) { - return; - } + if (srv->phase != BT_MESH_BLOB_XFER_PHASE_WAITING_FOR_BLOCK && + srv->phase != BT_MESH_BLOB_XFER_PHASE_WAITING_FOR_CHUNK) { + return; + } - block_report(srv); + block_report(srv); } /******************************************************************************* @@ -317,697 +323,751 @@ static void report_timeout(struct k_work *work) ******************************************************************************/ static void xfer_status_rsp(struct bt_mesh_blob_srv *srv, - struct bt_mesh_msg_ctx *ctx, - enum bt_mesh_blob_status status) + struct bt_mesh_msg_ctx *ctx, + enum bt_mesh_blob_status status) { - BT_MESH_MODEL_BUF_DEFINE(buf, BT_MESH_BLOB_OP_XFER_STATUS, - BLOB_XFER_STATUS_MSG_MAXLEN); - bt_mesh_model_msg_init(&buf, BT_MESH_BLOB_OP_XFER_STATUS); + BLE_MESH_MODEL_BUF_DEFINE(buf, BT_MESH_BLOB_OP_XFER_STATUS, + BLOB_XFER_STATUS_MSG_MAXLEN); + bt_mesh_model_msg_init(&buf, BT_MESH_BLOB_OP_XFER_STATUS); - net_buf_simple_add_u8(&buf, ((status & BIT_MASK(4)) | - (srv->state.xfer.mode << 6))); - net_buf_simple_add_u8(&buf, srv->phase); + net_buf_simple_add_u8(&buf, ((status & BIT_MASK(4)) | + (srv->state.xfer.mode << 6))); + net_buf_simple_add_u8(&buf, srv->phase); - if (srv->phase == BT_MESH_BLOB_XFER_PHASE_INACTIVE) { - goto send; - } + if (srv->phase == BT_MESH_BLOB_XFER_PHASE_INACTIVE) { + goto send; + } - net_buf_simple_add_le64(&buf, srv->state.xfer.id); + net_buf_simple_add_le64(&buf, srv->state.xfer.id); - if (srv->phase == BT_MESH_BLOB_XFER_PHASE_WAITING_FOR_START) { - goto send; - } + if (srv->phase == BT_MESH_BLOB_XFER_PHASE_WAITING_FOR_START) { + goto send; + } - net_buf_simple_add_le32(&buf, srv->state.xfer.size); - net_buf_simple_add_u8(&buf, srv->state.xfer.block_size_log); - net_buf_simple_add_le16(&buf, srv->state.mtu_size); - net_buf_simple_add_mem(&buf, srv->state.blocks, - DIV_ROUND_UP(block_count_get(srv), 8)); + net_buf_simple_add_le32(&buf, srv->state.xfer.size); + net_buf_simple_add_u8(&buf, srv->state.xfer.block_size_log); + net_buf_simple_add_le16(&buf, srv->state.mtu_size); + net_buf_simple_add_mem(&buf, srv->state.blocks, + DIV_ROUND_UP(block_count_get(srv), 8)); send: - ctx->send_ttl = srv->state.ttl; - (void)bt_mesh_model_send(srv->mod, ctx, &buf, NULL, NULL); + ctx->send_ttl = srv->state.ttl; + (void)bt_mesh_model_send(srv->mod, ctx, &buf, NULL, NULL); } static void block_status_rsp(struct bt_mesh_blob_srv *srv, - struct bt_mesh_msg_ctx *ctx, - enum bt_mesh_blob_status status) + struct bt_mesh_msg_ctx *ctx, + enum bt_mesh_blob_status status) { - enum bt_mesh_blob_chunks_missing format; - uint32_t missing; - int i; + enum bt_mesh_blob_chunks_missing format; + uint32_t missing; + int i; - BT_MESH_MODEL_BUF_DEFINE(buf, BT_MESH_BLOB_OP_BLOCK_STATUS, - BLOB_BLOCK_STATUS_MSG_MAXLEN); - bt_mesh_model_msg_init(&buf, BT_MESH_BLOB_OP_BLOCK_STATUS); + BLE_MESH_MODEL_BUF_DEFINE(buf, BT_MESH_BLOB_OP_BLOCK_STATUS, + BLOB_BLOCK_STATUS_MSG_MAXLEN); + bt_mesh_model_msg_init(&buf, BT_MESH_BLOB_OP_BLOCK_STATUS); - if (srv->phase == BT_MESH_BLOB_XFER_PHASE_INACTIVE || - srv->phase == BT_MESH_BLOB_XFER_PHASE_WAITING_FOR_START) { - missing = srv->block.chunk_count; - } else if (srv->phase == BT_MESH_BLOB_XFER_PHASE_COMPLETE) { - missing = 0U; - } else { - missing = missing_chunks(&srv->block); - } + if (srv->phase == BT_MESH_BLOB_XFER_PHASE_INACTIVE || + srv->phase == BT_MESH_BLOB_XFER_PHASE_WAITING_FOR_START) { + missing = srv->block.chunk_count; + } else if (srv->phase == BT_MESH_BLOB_XFER_PHASE_COMPLETE) { + missing = 0U; + } else { + missing = missing_chunks(&srv->block); + } - if (srv->state.xfer.mode == BT_MESH_BLOB_XFER_MODE_PULL) { - format = BT_MESH_BLOB_CHUNKS_MISSING_ENCODED; - } else if (missing == srv->block.chunk_count) { - format = BT_MESH_BLOB_CHUNKS_MISSING_ALL; - } else if (missing == 0) { - format = BT_MESH_BLOB_CHUNKS_MISSING_NONE; - } else { - format = BT_MESH_BLOB_CHUNKS_MISSING_SOME; - } + if (srv->state.xfer.mode == BT_MESH_BLOB_XFER_MODE_PULL) { + format = BT_MESH_BLOB_CHUNKS_MISSING_ENCODED; + } else if (missing == srv->block.chunk_count) { + format = BT_MESH_BLOB_CHUNKS_MISSING_ALL; + } else if (missing == 0) { + format = BT_MESH_BLOB_CHUNKS_MISSING_NONE; + } else { + format = BT_MESH_BLOB_CHUNKS_MISSING_SOME; + } - LOG_DBG("Status: %u, missing: %u/%u", status, missing, srv->block.chunk_count); + BT_DBG("Status: %u, missing: %u/%u", status, missing, srv->block.chunk_count); - net_buf_simple_add_u8(&buf, (status & BIT_MASK(4)) | (format << 6)); - net_buf_simple_add_le16(&buf, srv->block.number); - net_buf_simple_add_le16(&buf, srv->state.xfer.chunk_size); + net_buf_simple_add_u8(&buf, (status & BIT_MASK(4)) | (format << 6)); + net_buf_simple_add_le16(&buf, srv->block.number); + net_buf_simple_add_le16(&buf, srv->state.xfer.chunk_size); - if (format == BT_MESH_BLOB_CHUNKS_MISSING_SOME) { - net_buf_simple_add_mem(&buf, srv->block.missing, - DIV_ROUND_UP(srv->block.chunk_count, - 8)); + if (format == BT_MESH_BLOB_CHUNKS_MISSING_SOME) { + net_buf_simple_add_mem(&buf, srv->block.missing, + DIV_ROUND_UP(srv->block.chunk_count, + 8)); - LOG_DBG("Bits: %s", - bt_hex(srv->block.missing, - DIV_ROUND_UP(srv->block.chunk_count, 8))); + BT_DBG("Bits: %s", + bt_hex(srv->block.missing, + DIV_ROUND_UP(srv->block.chunk_count, 8))); - } else if (format == BT_MESH_BLOB_CHUNKS_MISSING_ENCODED) { - int count = pull_req_max(srv); + } else if (format == BT_MESH_BLOB_CHUNKS_MISSING_ENCODED) { + int count = pull_req_max(srv); - for (i = 0; (i < srv->block.chunk_count) && count; ++i) { - if (blob_chunk_missing_get(srv->block.missing, i)) { - LOG_DBG("Missing %u", i); - buf_chunk_index_add(&buf, i); - count--; - } - } - } + for (i = 0; (i < srv->block.chunk_count) && count; ++i) { + if (blob_chunk_missing_get(srv->block.missing, i)) { + BT_DBG("Missing %u", i); + buf_chunk_index_add(&buf, i); + count--; + } + } + } - if (srv->phase != BT_MESH_BLOB_XFER_PHASE_INACTIVE) { - ctx->send_ttl = srv->state.ttl; - } + if (srv->phase != BT_MESH_BLOB_XFER_PHASE_INACTIVE) { + ctx->send_ttl = srv->state.ttl; + } - (void)bt_mesh_model_send(srv->mod, ctx, &buf, NULL, NULL); + (void)bt_mesh_model_send(srv->mod, ctx, &buf, NULL, NULL); } -static int handle_xfer_get(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx, - struct net_buf_simple *buf) +static int handle_xfer_get(struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) { - struct bt_mesh_blob_srv *srv = mod->rt->user_data; + struct bt_mesh_blob_srv *srv = mod->user_data; - LOG_DBG(""); + BT_DBG(""); - if (pull_mode_xfer_complete(srv)) { - /* The client requested transfer. If we are in Pull mode and all blocks were - * received, we should change the Transfer state here to Complete so that the client - * receives the correct state. - */ - end(srv); - } + if (pull_mode_xfer_complete(srv)) { + /* The client requested transfer. If we are in Pull mode and all blocks were + * received, we should change the Transfer state here to Complete so that the client + * receives the correct state. + */ + end(srv); + } - xfer_status_rsp(srv, ctx, BT_MESH_BLOB_SUCCESS); + xfer_status_rsp(srv, ctx, BT_MESH_BLOB_SUCCESS); - return 0; + return 0; } static int handle_xfer_start(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx, - struct net_buf_simple *buf) + struct net_buf_simple *buf) { - struct bt_mesh_blob_srv *srv = mod->rt->user_data; - enum bt_mesh_blob_status status; - enum bt_mesh_blob_xfer_mode mode; - uint64_t id; - size_t size; - uint8_t block_size_log; - uint32_t block_count; - uint16_t mtu_size; - int err; + struct bt_mesh_blob_srv *srv = mod->user_data; + enum bt_mesh_blob_status status; + enum bt_mesh_blob_xfer_mode mode; + uint64_t id; + size_t size; + uint8_t block_size_log; + uint32_t block_count; + uint16_t mtu_size; + int err; - mode = (net_buf_simple_pull_u8(buf) >> 6); - id = net_buf_simple_pull_le64(buf); - size = net_buf_simple_pull_le32(buf); - block_size_log = net_buf_simple_pull_u8(buf); - mtu_size = net_buf_simple_pull_le16(buf); + mode = (net_buf_simple_pull_u8(buf) >> 6); + id = net_buf_simple_pull_le64(buf); + size = net_buf_simple_pull_le32(buf); + block_size_log = net_buf_simple_pull_u8(buf); + mtu_size = net_buf_simple_pull_le16(buf); - LOG_DBG("\n\tsize: %u block size: %u\n\tmtu_size: %u\n\tmode: %s", - size, (1U << block_size_log), mtu_size, - mode == BT_MESH_BLOB_XFER_MODE_PUSH ? "push" : "pull"); + BT_INFO("size: %u block size: %u mtu_size: %u mode: %s", + size, (1U << block_size_log), mtu_size, + mode == BT_MESH_BLOB_XFER_MODE_PUSH ? "push" : "pull"); - if (mode != BT_MESH_BLOB_XFER_MODE_PULL && - mode != BT_MESH_BLOB_XFER_MODE_PUSH) { - LOG_WRN("Invalid mode 0x%x", mode); - return -EINVAL; - } + if (mode != BT_MESH_BLOB_XFER_MODE_PULL && + mode != BT_MESH_BLOB_XFER_MODE_PUSH) { + BT_WARN("Invalid mode 0x%x", mode); + return -EINVAL; + } - if (srv->phase == BT_MESH_BLOB_XFER_PHASE_INACTIVE) { - status = BT_MESH_BLOB_ERR_WRONG_PHASE; - LOG_WRN("Uninitialized"); - goto rsp; - } + if (srv->phase == BT_MESH_BLOB_XFER_PHASE_INACTIVE) { + status = BT_MESH_BLOB_ERR_WRONG_PHASE; + BT_WARN("Uninitialized"); + goto rsp; + } - if (srv->state.xfer.id != id) { - status = BT_MESH_BLOB_ERR_WRONG_BLOB_ID; - /* bt_hex uses static array for the resulting hex string. - * Not possible to use bt_hex in the same logging function twice. - */ - LOG_WRN("Invalid ID: %s", bt_hex(&id, sizeof(uint64_t))); - LOG_WRN("Expected ID: %s", bt_hex(&srv->state.xfer.id, sizeof(uint64_t))); - goto rsp; - } +#if 0 /* For case DFU/SR/FD/BV-59-C, workaround */ + srv->state.xfer.id = id; +#endif - if (srv->phase != BT_MESH_BLOB_XFER_PHASE_WAITING_FOR_START) { - if (srv->state.xfer.mode != mode || - srv->state.xfer.size != size || - srv->state.xfer.block_size_log != block_size_log || - srv->state.mtu_size > mtu_size) { - status = BT_MESH_BLOB_ERR_WRONG_PHASE; - LOG_WRN("Busy"); - goto rsp; - } + if (srv->state.xfer.id != id) { + status = BT_MESH_BLOB_ERR_WRONG_BLOB_ID; + /* bt_hex uses static array for the resulting hex string. + * Not possible to use bt_hex in the same logging function twice. + */ + BT_WARN("Invalid ID: %s", bt_hex(&id, sizeof(uint64_t))); + BT_WARN("Expected ID: %s", bt_hex(&srv->state.xfer.id, sizeof(uint64_t))); + goto rsp; + } - if (srv->phase == BT_MESH_BLOB_XFER_PHASE_SUSPENDED) { - resume(srv); - store_state(srv); - } else { - LOG_DBG("Duplicate"); - } + if (srv->phase != BT_MESH_BLOB_XFER_PHASE_WAITING_FOR_START) { + if (srv->state.xfer.mode != mode || + srv->state.xfer.size != size || + srv->state.xfer.block_size_log != block_size_log || + srv->state.mtu_size > mtu_size) { + status = BT_MESH_BLOB_ERR_WRONG_PHASE; + BT_WARN("Busy"); + goto rsp; + } - status = BT_MESH_BLOB_SUCCESS; - goto rsp; - } + if (srv->phase == BT_MESH_BLOB_XFER_PHASE_SUSPENDED) { + resume(srv); + store_state(srv); + } else { + BT_DBG("Duplicate"); + } - if (size > CONFIG_BT_MESH_BLOB_SIZE_MAX) { - LOG_WRN("Too large"); - status = BT_MESH_BLOB_ERR_BLOB_TOO_LARGE; - goto rsp; - } + status = BT_MESH_BLOB_SUCCESS; + goto rsp; + } - if (((1U << block_size_log) < CONFIG_BT_MESH_BLOB_BLOCK_SIZE_MIN) || - ((1U << block_size_log) > CONFIG_BT_MESH_BLOB_BLOCK_SIZE_MAX)) { - LOG_WRN("Invalid block size: %u", block_size_log); - status = BT_MESH_BLOB_ERR_INVALID_BLOCK_SIZE; - goto rsp; - } + if (size > CONFIG_BLE_MESH_BLOB_SIZE_MAX) { + BT_WARN("Too large"); + status = BT_MESH_BLOB_ERR_BLOB_TOO_LARGE; + goto rsp; + } - srv->state.cli = ctx->addr; - srv->state.app_idx = ctx->app_idx; - srv->state.mtu_size = MIN(mtu_size, MTU_SIZE_MAX); - srv->state.xfer.id = id; - srv->state.xfer.size = size; - srv->state.xfer.mode = mode; - srv->state.xfer.block_size_log = block_size_log; - srv->state.xfer.chunk_size = 0xffff; - srv->block.number = 0xffff; + if (((1U << block_size_log) < CONFIG_BLE_MESH_BLOB_BLOCK_SIZE_MIN) || + ((1U << block_size_log) > CONFIG_BLE_MESH_BLOB_BLOCK_SIZE_MAX)) { + BT_WARN("Invalid block size: %u", block_size_log); + status = BT_MESH_BLOB_ERR_INVALID_BLOCK_SIZE; + goto rsp; + } - block_count = block_count_get(srv); - if (block_count > BT_MESH_BLOB_BLOCKS_MAX) { - LOG_WRN("Invalid block count (%u)", block_count); - status = BT_MESH_BLOB_ERR_INVALID_PARAM; - cancel(srv); - goto rsp; - } + srv->state.cli = ctx->addr; + srv->state.app_idx = ctx->app_idx; + srv->state.mtu_size = MIN(mtu_size, MTU_SIZE_MAX); + srv->state.xfer.id = id; + srv->state.xfer.size = size; + srv->state.xfer.mode = mode; + srv->state.xfer.block_size_log = block_size_log; + srv->state.xfer.chunk_size = 0xffff; + srv->block.number = 0xffff; - memset(srv->state.blocks, 0, sizeof(srv->state.blocks)); - for (int i = 0; i < block_count; i++) { - atomic_set_bit(srv->state.blocks, i); - } + block_count = block_count_get(srv); + if (block_count > BT_MESH_BLOB_BLOCKS_MAX) { + BT_WARN("Invalid block count (%u)", block_count); + status = BT_MESH_BLOB_ERR_INVALID_PARAM; + cancel(srv); + goto rsp; + } - err = io_open(srv); - if (err) { - LOG_ERR("Couldn't open stream (err: %d)", err); - status = BT_MESH_BLOB_ERR_INTERNAL; - cancel(srv); - goto rsp; - } + memset(srv->state.blocks, 0, sizeof(srv->state.blocks)); + for (int i = 0; i < block_count; i++) { + bt_mesh_atomic_set_bit(srv->state.blocks, i); + } - if (srv->cb && srv->cb->start) { - err = srv->cb->start(srv, ctx, &srv->state.xfer); - if (err) { - LOG_ERR("Couldn't start transfer (err: %d)", err); - status = BT_MESH_BLOB_ERR_INTERNAL; - cancel(srv); - goto rsp; - } - } + err = io_open(srv); + if (err) { + BT_ERR("Couldn't open stream (err: %d)", err); + status = BT_MESH_BLOB_ERR_INTERNAL; + cancel(srv); + goto rsp; + } - reset_timer(srv); - phase_set(srv, BT_MESH_BLOB_XFER_PHASE_WAITING_FOR_BLOCK); - store_state(srv); - status = BT_MESH_BLOB_SUCCESS; + if (srv->cb && srv->cb->start) { + err = srv->cb->start(srv, ctx, &srv->state.xfer); + if (err) { + BT_ERR("Couldn't start transfer (err: %d)", err); + status = BT_MESH_BLOB_ERR_INTERNAL; + cancel(srv); + goto rsp; + } + } + + reset_timer(srv); + phase_set(srv, BT_MESH_BLOB_XFER_PHASE_WAITING_FOR_BLOCK); + store_state(srv); + status = BT_MESH_BLOB_SUCCESS; rsp: - xfer_status_rsp(srv, ctx, status); + xfer_status_rsp(srv, ctx, status); - return 0; + return 0; } static int handle_xfer_cancel(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx, - struct net_buf_simple *buf) + struct net_buf_simple *buf) { - enum bt_mesh_blob_status status = BT_MESH_BLOB_SUCCESS; - struct bt_mesh_blob_srv *srv = mod->rt->user_data; - uint64_t id; + enum bt_mesh_blob_status status = BT_MESH_BLOB_SUCCESS; + struct bt_mesh_blob_srv *srv = mod->user_data; + uint64_t id; - id = net_buf_simple_pull_le64(buf); + id = net_buf_simple_pull_le64(buf); - LOG_DBG("%u", (uint32_t)id); + BT_DBG("%u", (uint32_t)id); - if (srv->phase == BT_MESH_BLOB_XFER_PHASE_INACTIVE) { - goto rsp; - } + if (srv->phase == BT_MESH_BLOB_XFER_PHASE_INACTIVE) { + goto rsp; + } - if (srv->state.xfer.id != id) { - status = BT_MESH_BLOB_ERR_WRONG_BLOB_ID; - goto rsp; - } + if (srv->state.xfer.id != id) { + status = BT_MESH_BLOB_ERR_WRONG_BLOB_ID; + goto rsp; + } - cancel(srv); + cancel(srv); rsp: - xfer_status_rsp(srv, ctx, status); + xfer_status_rsp(srv, ctx, status); - return 0; + return 0; } static int handle_block_get(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx, - struct net_buf_simple *buf) + struct net_buf_simple *buf) { - enum bt_mesh_blob_status status; - struct bt_mesh_blob_srv *srv = mod->rt->user_data; + enum bt_mesh_blob_status status; + struct bt_mesh_blob_srv *srv = mod->user_data; - switch (srv->phase) { - case BT_MESH_BLOB_XFER_PHASE_WAITING_FOR_BLOCK: - case BT_MESH_BLOB_XFER_PHASE_WAITING_FOR_CHUNK: - case BT_MESH_BLOB_XFER_PHASE_COMPLETE: - status = BT_MESH_BLOB_SUCCESS; - break; - case BT_MESH_BLOB_XFER_PHASE_SUSPENDED: - status = BT_MESH_BLOB_ERR_INFO_UNAVAILABLE; - break; - case BT_MESH_BLOB_XFER_PHASE_WAITING_FOR_START: - case BT_MESH_BLOB_XFER_PHASE_INACTIVE: - status = BT_MESH_BLOB_ERR_WRONG_PHASE; - break; - default: - status = BT_MESH_BLOB_ERR_INTERNAL; - break; - } + switch (srv->phase) { + case BT_MESH_BLOB_XFER_PHASE_WAITING_FOR_BLOCK: + case BT_MESH_BLOB_XFER_PHASE_WAITING_FOR_CHUNK: + case BT_MESH_BLOB_XFER_PHASE_COMPLETE: + status = BT_MESH_BLOB_SUCCESS; + break; + case BT_MESH_BLOB_XFER_PHASE_SUSPENDED: + status = BT_MESH_BLOB_ERR_INFO_UNAVAILABLE; + break; + case BT_MESH_BLOB_XFER_PHASE_WAITING_FOR_START: + case BT_MESH_BLOB_XFER_PHASE_INACTIVE: + status = BT_MESH_BLOB_ERR_WRONG_PHASE; + break; + default: + status = BT_MESH_BLOB_ERR_INTERNAL; + break; + } - LOG_DBG(""); + BT_DBG(""); - block_status_rsp(srv, ctx, status); + block_status_rsp(srv, ctx, status); - return 0; + return 0; } static int handle_block_start(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx, - struct net_buf_simple *buf) + struct net_buf_simple *buf) { - struct bt_mesh_blob_srv *srv = mod->rt->user_data; - enum bt_mesh_blob_status status; - uint16_t block_number, chunk_size; - int err; + struct bt_mesh_blob_srv *srv = mod->user_data; + enum bt_mesh_blob_status status; + uint16_t block_number, chunk_size; + int err; - block_number = net_buf_simple_pull_le16(buf); - chunk_size = net_buf_simple_pull_le16(buf); + block_number = net_buf_simple_pull_le16(buf); + chunk_size = net_buf_simple_pull_le16(buf); - if (srv->phase == BT_MESH_BLOB_XFER_PHASE_WAITING_FOR_START || - srv->phase == BT_MESH_BLOB_XFER_PHASE_INACTIVE) { - status = BT_MESH_BLOB_ERR_WRONG_PHASE; - goto rsp; - } + if (srv->phase == BT_MESH_BLOB_XFER_PHASE_WAITING_FOR_START || + srv->phase == BT_MESH_BLOB_XFER_PHASE_INACTIVE) { + status = BT_MESH_BLOB_ERR_WRONG_PHASE; + goto rsp; + } - reset_timer(srv); + reset_timer(srv); - if (srv->phase == BT_MESH_BLOB_XFER_PHASE_WAITING_FOR_CHUNK) { - if (block_number != srv->block.number || - chunk_size != srv->state.xfer.chunk_size) { - status = BT_MESH_BLOB_ERR_WRONG_PHASE; - } else { - status = BT_MESH_BLOB_SUCCESS; - } + if (srv->phase == BT_MESH_BLOB_XFER_PHASE_WAITING_FOR_CHUNK) { + if (block_number != srv->block.number || + chunk_size != srv->state.xfer.chunk_size) { + status = BT_MESH_BLOB_ERR_WRONG_PHASE; + } else { + status = BT_MESH_BLOB_SUCCESS; + } - goto rsp; - } + goto rsp; + } - if (block_number >= block_count_get(srv)) { - status = BT_MESH_BLOB_ERR_INVALID_BLOCK_NUM; - goto rsp; - } + if (block_number >= block_count_get(srv)) { + status = BT_MESH_BLOB_ERR_INVALID_BLOCK_NUM; + goto rsp; + } - if (!chunk_size || chunk_size > max_chunk_size(srv) || - (DIV_ROUND_UP((1 << srv->state.xfer.block_size_log), chunk_size) > - max_chunk_count(srv))) { - LOG_WRN("Invalid chunk size: (chunk size: %u, max: %u, block log: %u, count: %u)", - chunk_size, max_chunk_size(srv), - srv->state.xfer.block_size_log, - max_chunk_count(srv)); - status = BT_MESH_BLOB_ERR_INVALID_CHUNK_SIZE; - goto rsp; - } + if (!chunk_size || chunk_size > max_chunk_size(srv) || + (DIV_ROUND_UP((1 << srv->state.xfer.block_size_log), chunk_size) > + max_chunk_count(srv))) { + BT_WARN("Invalid chunk size: (chunk size: %u, max: %u, block log: %u, count: %u)", + chunk_size, max_chunk_size(srv), + srv->state.xfer.block_size_log, + max_chunk_count(srv)); + status = BT_MESH_BLOB_ERR_INVALID_CHUNK_SIZE; + goto rsp; + } - srv->block.size = blob_block_size( - srv->state.xfer.size, srv->state.xfer.block_size_log, block_number); - srv->block.number = block_number; - srv->block.chunk_count = DIV_ROUND_UP(srv->block.size, chunk_size); - srv->state.xfer.chunk_size = chunk_size; - srv->block.offset = block_number * (1UL << srv->state.xfer.block_size_log); + srv->block.size = blob_block_size( + srv->state.xfer.size, srv->state.xfer.block_size_log, block_number); + srv->block.number = block_number; + srv->block.chunk_count = DIV_ROUND_UP(srv->block.size, chunk_size); + srv->state.xfer.chunk_size = chunk_size; + srv->block.offset = block_number * (1UL << srv->state.xfer.block_size_log); - if (srv->phase == BT_MESH_BLOB_XFER_PHASE_COMPLETE || - !atomic_test_bit(srv->state.blocks, block_number)) { - memset(srv->block.missing, 0, sizeof(srv->block.missing)); - status = BT_MESH_BLOB_SUCCESS; - goto rsp; - } + if (srv->phase == BT_MESH_BLOB_XFER_PHASE_COMPLETE || + !bt_mesh_atomic_test_bit(srv->state.blocks, block_number)) { + memset(srv->block.missing, 0, sizeof(srv->block.missing)); + status = BT_MESH_BLOB_SUCCESS; + goto rsp; + } - if (srv->phase == BT_MESH_BLOB_XFER_PHASE_SUSPENDED && srv->cb && - srv->cb->resume) { - srv->cb->resume(srv); - } + if (srv->phase == BT_MESH_BLOB_XFER_PHASE_SUSPENDED && srv->cb && + srv->cb->resume) { + srv->cb->resume(srv); + } - phase_set(srv, BT_MESH_BLOB_XFER_PHASE_WAITING_FOR_CHUNK); - blob_chunk_missing_set_all(&srv->block); + if (srv->phase == BT_MESH_BLOB_XFER_PHASE_SUSPENDED) { + BT_INFO("Waiting nxt chunk to resume blob srv"); + } - LOG_DBG("%u: (%u/%u)\n\tsize: %u\n\tchunk size: %u\n\tchunk count: %u", - srv->block.number, srv->block.number + 1, block_count_get(srv), - srv->block.size, chunk_size, srv->block.chunk_count); + phase_set(srv, BT_MESH_BLOB_XFER_PHASE_WAITING_FOR_CHUNK); + blob_chunk_missing_set_all(&srv->block); - if (srv->io->block_start) { - err = srv->io->block_start(srv->io, &srv->state.xfer, - &srv->block); - if (err) { - cancel(srv); - status = BT_MESH_BLOB_ERR_INTERNAL; - goto rsp; - } - } + BT_WARN("%u: (%u/%u)\n\tsize: %u\n\tchunk size: %u\n\tchunk count: %u", + srv->block.number, srv->block.number + 1, block_count_get(srv), + srv->block.size, chunk_size, srv->block.chunk_count); - if (srv->state.xfer.mode == BT_MESH_BLOB_XFER_MODE_PULL) { - /* Wait for the client to send the first chunk */ - k_work_reschedule(&srv->pull.report, REPORT_TIMER_TIMEOUT); - } + if (srv->io->block_start) { + err = srv->io->block_start(srv->io, &srv->state.xfer, + &srv->block); + if (err) { + cancel(srv); + status = BT_MESH_BLOB_ERR_INTERNAL; + goto rsp; + } + } - status = BT_MESH_BLOB_SUCCESS; + if (srv->state.xfer.mode == BT_MESH_BLOB_XFER_MODE_PULL) { + /* Wait for the client to send the first chunk */ + k_work_reschedule(&srv->pull.report, REPORT_TIMER_TIMEOUT); + } + + status = BT_MESH_BLOB_SUCCESS; rsp: - block_status_rsp(srv, ctx, status); + block_status_rsp(srv, ctx, status); - return 0; + return 0; } -static int handle_chunk(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx, - struct net_buf_simple *buf) +static int handle_chunk(struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) { - struct bt_mesh_blob_srv *srv = mod->rt->user_data; - struct bt_mesh_blob_chunk chunk; - size_t expected_size = 0; - uint16_t idx; - int err; + struct bt_mesh_blob_srv *srv = mod->user_data; + struct bt_mesh_blob_chunk chunk; + size_t expected_size = 0; + uint16_t idx; + int err; - idx = net_buf_simple_pull_le16(buf); - chunk.size = buf->len; - chunk.data = net_buf_simple_pull_mem(buf, chunk.size); - chunk.offset = idx * srv->state.xfer.chunk_size; + idx = net_buf_simple_pull_le16(buf); + chunk.size = buf->len; + chunk.data = net_buf_simple_pull_mem(buf, chunk.size); + chunk.offset = idx * srv->state.xfer.chunk_size; - if (srv->phase != BT_MESH_BLOB_XFER_PHASE_WAITING_FOR_CHUNK || - idx >= srv->block.chunk_count) { - LOG_ERR("Invalid phase or index (%u %u)", srv->phase, - idx); - 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. + BT_INFO("Discord chunk(%d), because the block is already received", idx); + return -EINVAL; + } + /** + * Changed by Espressif. + * This behavior is not allowed by spec! + * + * Change resason: + * If the former chunk lost and the following received chunks + * all will be drop, so this change is therefore used to + * avoid such waste. + * + */ + if ((srv->phase != BT_MESH_BLOB_XFER_PHASE_WAITING_FOR_CHUNK && + srv->phase != BT_MESH_BLOB_XFER_PHASE_SUSPENDED) || + idx >= srv->block.chunk_count) { + BT_ERR("Invalid phase or index (%u %u)", srv->phase, + idx); + return -EINVAL; + } - if (idx == srv->block.chunk_count - 1) { - expected_size = srv->block.size % srv->state.xfer.chunk_size; - } + if (idx == srv->block.chunk_count - 1) { + expected_size = srv->block.size % srv->state.xfer.chunk_size; + } - if (expected_size == 0) { - expected_size = srv->state.xfer.chunk_size; - } + if (expected_size == 0) { + expected_size = srv->state.xfer.chunk_size; + } - if (chunk.size != expected_size) { - LOG_ERR("Unexpected size: %u != %u", expected_size, chunk.size); - return -EINVAL; - } + if (chunk.size != expected_size) { + BT_ERR("Unexpected size: %u != %u", expected_size, chunk.size); + return -EINVAL; + } - LOG_DBG("%u/%u (%u bytes)", idx + 1, srv->block.chunk_count, - chunk.size); + /** + * Changed By Espressif. + * + * The blob server should resume if received a new chunk + * after suspend (that's behavior is not required by spec). + * + * Change resason: + * The blob server will be suspend after a chunk missed + * and resume at next block start received. + * but a block get will be received before a block start + * sent, the blob server which in suspend state will response + * a err state to blob client,and the blob client will drop + * that blob server, causing the server couldn't receive + * complete. + */ + if (srv->phase == BT_MESH_BLOB_XFER_PHASE_SUSPENDED) { + phase_set(srv, BT_MESH_BLOB_XFER_PHASE_WAITING_FOR_CHUNK); + } - reset_timer(srv); - if (srv->state.xfer.mode == BT_MESH_BLOB_XFER_MODE_PULL) { - k_work_reschedule(&srv->pull.report, REPORT_TIMER_TIMEOUT); - } + BT_INFO("%u/%u (%u bytes)", idx + 1, srv->block.chunk_count, + chunk.size); - if (!blob_chunk_missing_get(srv->block.missing, idx)) { - LOG_DBG("Duplicate chunk %u", idx); - return -EALREADY; - } + reset_timer(srv); + if (srv->state.xfer.mode == BT_MESH_BLOB_XFER_MODE_PULL) { + k_work_reschedule(&srv->pull.report, REPORT_TIMER_TIMEOUT); + } - err = srv->io->wr(srv->io, &srv->state.xfer, &srv->block, &chunk); - if (err) { - return err; - } + if (!blob_chunk_missing_get(srv->block.missing, idx)) { + BT_DBG("Duplicate chunk %u", idx); + return -EALREADY; + } - blob_chunk_missing_set(srv->block.missing, idx, false); - if (missing_chunks(&srv->block)) { - return 0; - } + err = srv->io->wr(srv->io, &srv->state.xfer, &srv->block, &chunk); + if (err) { + return err; + } - if (srv->state.xfer.mode == BT_MESH_BLOB_XFER_MODE_PULL) { - block_report(srv); - } + blob_chunk_missing_set(srv->block.missing, idx, false); + if (missing_chunks(&srv->block)) { + return 0; + } - if (srv->io->block_end) { - srv->io->block_end(srv->io, &srv->state.xfer, &srv->block); - } + if (srv->state.xfer.mode == BT_MESH_BLOB_XFER_MODE_PULL) { + block_report(srv); + } - atomic_clear_bit(srv->state.blocks, srv->block.number); + if (srv->io->block_end) { + srv->io->block_end(srv->io, &srv->state.xfer, &srv->block); + } - if (!all_blocks_received(srv)) { - phase_set(srv, BT_MESH_BLOB_XFER_PHASE_WAITING_FOR_BLOCK); - store_state(srv); - return 0; - } + bt_mesh_atomic_clear_bit(srv->state.blocks, srv->block.number); - if (srv->state.xfer.mode == BT_MESH_BLOB_XFER_MODE_PULL) { - /* By spec (section 5.2.4), the BLOB Server stops sending BLOB Partial Block Report - * messages "If the current block is the last block, then the server determines that - * the client knows the transfer is complete. For example, a higher-layer model may - * indicate that the client considers the transfer complete." - * - * We don't have any way for higher-layer model to indicate that the transfer is - * complete. Therefore we need to keep sending Partial Block Report messages until - * the client sends BLOB Transfer Get message or the Block Timer expires. - */ - return 0; - } + if (!all_blocks_received(srv)) { + phase_set(srv, BT_MESH_BLOB_XFER_PHASE_WAITING_FOR_BLOCK); + store_state(srv); + return 0; + } - end(srv); - return 0; + if (srv->state.xfer.mode == BT_MESH_BLOB_XFER_MODE_PULL) { + /* By spec (section 5.2.4), the BLOB Server stops sending BLOB Partial Block Report + * messages "If the current block is the last block, then the server determines that + * the client knows the transfer is complete. For example, a higher-layer model may + * indicate that the client considers the transfer complete." + * + * We don't have any way for higher-layer model to indicate that the transfer is + * complete. Therefore we need to keep sending Partial Block Report messages until + * the client sends BLOB Transfer Get message or the Block Timer expires. + */ + return 0; + } + + end(srv); + return 0; } static int handle_info_get(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx, - struct net_buf_simple *buf) + struct net_buf_simple *buf) { - struct bt_mesh_blob_srv *srv = mod->rt->user_data; + struct bt_mesh_blob_srv *srv = mod->user_data; - LOG_DBG(""); + BT_DBG(""); - BT_MESH_MODEL_BUF_DEFINE(rsp, BT_MESH_BLOB_OP_INFO_STATUS, 15); - bt_mesh_model_msg_init(&rsp, BT_MESH_BLOB_OP_INFO_STATUS); - net_buf_simple_add_u8(&rsp, BLOB_BLOCK_SIZE_LOG_MIN); - net_buf_simple_add_u8(&rsp, BLOB_BLOCK_SIZE_LOG_MAX); - net_buf_simple_add_le16(&rsp, CONFIG_BT_MESH_BLOB_CHUNK_COUNT_MAX); - net_buf_simple_add_le16(&rsp, BLOB_RX_CHUNK_SIZE); - net_buf_simple_add_le32(&rsp, CONFIG_BT_MESH_BLOB_SIZE_MAX); - net_buf_simple_add_le16(&rsp, MTU_SIZE_MAX); - net_buf_simple_add_u8(&rsp, BT_MESH_BLOB_XFER_MODE_ALL); + BLE_MESH_MODEL_BUF_DEFINE(rsp, BT_MESH_BLOB_OP_INFO_STATUS, 15); + bt_mesh_model_msg_init(&rsp, BT_MESH_BLOB_OP_INFO_STATUS); + net_buf_simple_add_u8(&rsp, BLOB_BLOCK_SIZE_LOG_MIN); + net_buf_simple_add_u8(&rsp, BLOB_BLOCK_SIZE_LOG_MAX); + net_buf_simple_add_le16(&rsp, CONFIG_BLE_MESH_BLOB_CHUNK_COUNT_MAX); + net_buf_simple_add_le16(&rsp, BLOB_RX_CHUNK_SIZE); + net_buf_simple_add_le32(&rsp, CONFIG_BLE_MESH_BLOB_SIZE_MAX); + net_buf_simple_add_le16(&rsp, MTU_SIZE_MAX); + net_buf_simple_add_u8(&rsp, BT_MESH_BLOB_XFER_MODE_ALL); - if (srv->phase != BT_MESH_BLOB_XFER_PHASE_INACTIVE) { - ctx->send_ttl = srv->state.ttl; - } + if (srv->phase != BT_MESH_BLOB_XFER_PHASE_INACTIVE) { + ctx->send_ttl = srv->state.ttl; + } - (void)bt_mesh_model_send(srv->mod, ctx, &rsp, NULL, NULL); + (void)bt_mesh_model_send(srv->mod, ctx, &rsp, NULL, NULL); - return 0; + return 0; } const struct bt_mesh_model_op _bt_mesh_blob_srv_op[] = { - { BT_MESH_BLOB_OP_XFER_GET, BT_MESH_LEN_EXACT(0), handle_xfer_get }, - { BT_MESH_BLOB_OP_XFER_START, BT_MESH_LEN_EXACT(16), handle_xfer_start }, - { BT_MESH_BLOB_OP_XFER_CANCEL, BT_MESH_LEN_EXACT(8), handle_xfer_cancel }, - { BT_MESH_BLOB_OP_BLOCK_GET, BT_MESH_LEN_EXACT(0), handle_block_get }, - { BT_MESH_BLOB_OP_BLOCK_START, BT_MESH_LEN_EXACT(4), handle_block_start }, - { BT_MESH_BLOB_OP_CHUNK, BT_MESH_LEN_MIN(2), handle_chunk }, - { BT_MESH_BLOB_OP_INFO_GET, BT_MESH_LEN_EXACT(0), handle_info_get }, - BT_MESH_MODEL_OP_END, + { BT_MESH_BLOB_OP_XFER_GET, 0, (void *)handle_xfer_get }, + { BT_MESH_BLOB_OP_XFER_START, 16, (void *)handle_xfer_start }, + { BT_MESH_BLOB_OP_XFER_CANCEL, 8, (void *)handle_xfer_cancel }, + { BT_MESH_BLOB_OP_BLOCK_GET, 0, (void *)handle_block_get }, + { BT_MESH_BLOB_OP_BLOCK_START, 4, (void *)handle_block_start }, + { BT_MESH_BLOB_OP_CHUNK, 2, (void *)handle_chunk }, + { BT_MESH_BLOB_OP_INFO_GET, 0, (void *)handle_info_get }, + BLE_MESH_MODEL_OP_END, }; -static int blob_srv_init(const struct bt_mesh_model *mod) +static int blob_srv_init(struct bt_mesh_model *mod) { - struct bt_mesh_blob_srv *srv = mod->rt->user_data; + struct bt_mesh_blob_srv *srv = mod->user_data; - srv->mod = mod; - srv->state.ttl = BT_MESH_TTL_DEFAULT; - srv->block.number = 0xffff; - srv->state.xfer.chunk_size = 0xffff; - k_work_init_delayable(&srv->rx_timeout, timeout); - k_work_init_delayable(&srv->pull.report, report_timeout); + srv->mod = mod; + srv->state.ttl = BLE_MESH_TTL_DEFAULT; + srv->block.number = 0xffff; + srv->state.xfer.chunk_size = 0xffff; + k_work_init_delayable(&srv->rx_timeout, timeout); + k_work_init_delayable(&srv->pull.report, report_timeout); - return 0; + return 0; } +__attribute__((unused)) static int blob_srv_settings_set(const struct bt_mesh_model *mod, const char *name, - size_t len_rd, settings_read_cb read_cb, - void *cb_arg) + size_t len_rd, settings_read_cb read_cb, + void *cb_arg) { - struct bt_mesh_blob_srv *srv = mod->rt->user_data; - ssize_t len; + struct bt_mesh_blob_srv *srv = mod->user_data; + ssize_t len; - if (len_rd < offsetof(struct bt_mesh_blob_srv_state, blocks)) { - return -EINVAL; - } + if (len_rd < offsetof(struct bt_mesh_blob_srv_state, blocks)) { + return -EINVAL; + } - len = read_cb(cb_arg, &srv->state, sizeof(srv->state)); - if (len < 0) { - return len; - } + len = read_cb(cb_arg, &srv->state, sizeof(srv->state)); + if (len < 0) { + return len; + } - srv->block.number = 0xffff; - srv->state.xfer.chunk_size = 0xffff; + srv->block.number = 0xffff; + srv->state.xfer.chunk_size = 0xffff; - if (block_count_get(srv) > BT_MESH_BLOB_BLOCKS_MAX) { - LOG_WRN("Loaded block count too high (%u, max: %u)", - block_count_get(srv), BT_MESH_BLOB_BLOCKS_MAX); - return 0; - } + if (block_count_get(srv) > BT_MESH_BLOB_BLOCKS_MAX) { + BT_WARN("Loaded block count too high (%u, max: %u)", + block_count_get(srv), BT_MESH_BLOB_BLOCKS_MAX); + return 0; + } - /* If device restarted before it handled `XFER_START` server we restore state into - * BT_MESH_BLOB_XFER_PHASE_WAITING_FOR_START phase, so `XFER_START` can be accepted - * as it would before reboot - */ - if (srv->state.cli == BT_MESH_ADDR_UNASSIGNED) { - LOG_DBG("Transfer (id=%llu) waiting for start", srv->state.xfer.id); - phase_set(srv, BT_MESH_BLOB_XFER_PHASE_WAITING_FOR_START); - } else { - phase_set(srv, BT_MESH_BLOB_XFER_PHASE_SUSPENDED); + /* If device restarted before it handled `XFER_START` server we restore state into + * BT_MESH_BLOB_XFER_PHASE_WAITING_FOR_START phase, so `XFER_START` can be accepted + * as it would before reboot + */ + if (srv->state.cli == BLE_MESH_ADDR_UNASSIGNED) { + BT_DBG("Transfer (id=%llu) waiting for start", srv->state.xfer.id); + phase_set(srv, BT_MESH_BLOB_XFER_PHASE_WAITING_FOR_START); + } else { + phase_set(srv, BT_MESH_BLOB_XFER_PHASE_SUSPENDED); - LOG_DBG("Recovered transfer from 0x%04x (%llu)", srv->state.cli, - srv->state.xfer.id); - } + BT_DBG("Recovered transfer from 0x%04x (%llu)", srv->state.cli, + srv->state.xfer.id); + } - return 0; + return 0; } +__attribute__((unused)) static int blob_srv_start(const struct bt_mesh_model *mod) { - struct bt_mesh_blob_srv *srv = mod->rt->user_data; - int err = -ENOTSUP; + struct bt_mesh_blob_srv *srv = mod->user_data; + int err = -ENOTSUP; - if (srv->phase == BT_MESH_BLOB_XFER_PHASE_INACTIVE) { - return 0; - } + if (srv->phase == BT_MESH_BLOB_XFER_PHASE_INACTIVE) { + return 0; + } - if (srv->cb && srv->cb->recover) { - srv->io = NULL; - err = srv->cb->recover(srv, &srv->state.xfer, &srv->io); - if (!err && srv->io) { - err = io_open(srv); - } - } + if (srv->cb && srv->cb->recover) { + srv->io = NULL; + err = srv->cb->recover(srv, &srv->state.xfer, &srv->io); + if (!err && srv->io) { + err = io_open(srv); + } + } - if (err || !srv->io) { - LOG_WRN("Abandoning transfer."); - phase_set(srv, BT_MESH_BLOB_XFER_PHASE_INACTIVE); - srv->state.xfer.mode = BT_MESH_BLOB_XFER_MODE_NONE; - srv->state.ttl = BT_MESH_TTL_DEFAULT; - erase_state(srv); - } + if (err || !srv->io) { + BT_WARN("Abandoning transfer."); + phase_set(srv, BT_MESH_BLOB_XFER_PHASE_INACTIVE); + srv->state.xfer.mode = BT_MESH_BLOB_XFER_MODE_NONE; + srv->state.ttl = BLE_MESH_TTL_DEFAULT; + erase_state(srv); + } - return 0; + return 0; } -static void blob_srv_reset(const struct bt_mesh_model *mod) +static void blob_srv_reset(struct bt_mesh_model *mod) { - struct bt_mesh_blob_srv *srv = mod->rt->user_data; + struct bt_mesh_blob_srv *srv = mod->user_data; - phase_set(srv, BT_MESH_BLOB_XFER_PHASE_INACTIVE); - srv->state.xfer.mode = BT_MESH_BLOB_XFER_MODE_NONE; - k_work_cancel_delayable(&srv->rx_timeout); - k_work_cancel_delayable(&srv->pull.report); - erase_state(srv); + phase_set(srv, BT_MESH_BLOB_XFER_PHASE_INACTIVE); + srv->state.xfer.mode = BT_MESH_BLOB_XFER_MODE_NONE; + k_work_cancel_delayable(&srv->rx_timeout); + k_work_cancel_delayable(&srv->pull.report); + erase_state(srv); +} + +static int blob_srv_deinit(struct bt_mesh_model *mod) +{ + blob_srv_reset(mod); + return 0; } const struct bt_mesh_model_cb _bt_mesh_blob_srv_cb = { - .init = blob_srv_init, - .settings_set = blob_srv_settings_set, - .start = blob_srv_start, - .reset = blob_srv_reset, + .init = blob_srv_init, +#if CONFIG_BLE_MESH_DEINIT + .deinit = blob_srv_deinit, +#endif }; int bt_mesh_blob_srv_recv(struct bt_mesh_blob_srv *srv, uint64_t id, - const struct bt_mesh_blob_io *io, uint8_t ttl, - uint16_t timeout_base) + const struct bt_mesh_blob_io *io, uint8_t ttl, + uint16_t timeout_base) { - if (bt_mesh_blob_srv_is_busy(srv)) { - return -EBUSY; - } + if (bt_mesh_blob_srv_is_busy(srv)) { + return -EBUSY; + } - if (!io || !io->wr) { - return -EINVAL; - } + if (!io || !io->wr) { + return -EINVAL; + } - srv->state.xfer.id = id; - srv->state.ttl = ttl; - srv->state.timeout_base = timeout_base; - srv->io = io; - srv->block.number = 0xffff; - srv->state.xfer.chunk_size = 0xffff; - phase_set(srv, BT_MESH_BLOB_XFER_PHASE_WAITING_FOR_START); - store_state(srv); + srv->state.xfer.id = id; + srv->state.ttl = ttl; + srv->state.timeout_base = timeout_base; + srv->io = io; + srv->block.number = 0xffff; + srv->state.xfer.chunk_size = 0xffff; + phase_set(srv, BT_MESH_BLOB_XFER_PHASE_WAITING_FOR_START); + store_state(srv); - return 0; + return 0; } int bt_mesh_blob_srv_cancel(struct bt_mesh_blob_srv *srv) { - if (!bt_mesh_blob_srv_is_busy(srv)) { - return -EALREADY; - } + if (!bt_mesh_blob_srv_is_busy(srv)) { + return -EALREADY; + } - cancel(srv); + cancel(srv); - return 0; + return 0; } bool bt_mesh_blob_srv_is_busy(const struct bt_mesh_blob_srv *srv) { - return srv->phase != BT_MESH_BLOB_XFER_PHASE_INACTIVE && - srv->phase != BT_MESH_BLOB_XFER_PHASE_SUSPENDED && - srv->phase != BT_MESH_BLOB_XFER_PHASE_COMPLETE; + return srv->phase != BT_MESH_BLOB_XFER_PHASE_INACTIVE && + srv->phase != BT_MESH_BLOB_XFER_PHASE_SUSPENDED && + srv->phase != BT_MESH_BLOB_XFER_PHASE_COMPLETE; } uint8_t bt_mesh_blob_srv_progress(const struct bt_mesh_blob_srv *srv) { - uint32_t total; - uint32_t received; + uint32_t total; + uint32_t received; - if (srv->phase == BT_MESH_BLOB_XFER_PHASE_INACTIVE || - srv->phase == BT_MESH_BLOB_XFER_PHASE_WAITING_FOR_START) { - return 0; - } + if (srv->phase == BT_MESH_BLOB_XFER_PHASE_INACTIVE || + srv->phase == BT_MESH_BLOB_XFER_PHASE_WAITING_FOR_START) { + return 0; + } - total = block_count_get(srv); + total = block_count_get(srv); - received = 0; - for (int i = 0; i < total; ++i) { - if (!atomic_test_bit(srv->state.blocks, i)) { - received++; - } - } + received = 0; + for (int i = 0; i < total; ++i) { + if (!bt_mesh_atomic_test_bit(srv->state.blocks, i)) { + received++; + } + } - return (100U * received) / total; + return (100U * received) / total; } + +#endif /* CONFIG_BLE_MESH_BLOB_SRV */ diff --git a/docs/doxygen/Doxyfile b/docs/doxygen/Doxyfile index a81b73bb74..49f700f026 100644 --- a/docs/doxygen/Doxyfile +++ b/docs/doxygen/Doxyfile @@ -46,6 +46,8 @@ INPUT = \ $(PROJECT_PATH)/components/bt/esp_ble_mesh/v1.1/api/core/include/esp_ble_mesh_rpr_model_api.h \ $(PROJECT_PATH)/components/bt/esp_ble_mesh/v1.1/api/core/include/esp_ble_mesh_sar_model_api.h \ $(PROJECT_PATH)/components/bt/esp_ble_mesh/v1.1/api/core/include/esp_ble_mesh_srpl_model_api.h \ + $(PROJECT_PATH)/components/bt/esp_ble_mesh/v1.1/api/models/include/esp_ble_mesh_dfu_model_api.h \ + $(PROJECT_PATH)/components/bt/esp_ble_mesh/v1.1/api/models/include/esp_ble_mesh_dfu_slot_api.h \ $(PROJECT_PATH)/components/bt/esp_ble_mesh/api/esp_ble_mesh_defs.h \ $(PROJECT_PATH)/components/bt/esp_ble_mesh/api/models/include/esp_ble_mesh_config_model_api.h \ $(PROJECT_PATH)/components/bt/esp_ble_mesh/api/models/include/esp_ble_mesh_generic_model_api.h \ diff --git a/docs/en/api-guides/esp-ble-mesh/ble-mesh-feature-list.rst b/docs/en/api-guides/esp-ble-mesh/ble-mesh-feature-list.rst index 5955646296..b83b6059ff 100644 --- a/docs/en/api-guides/esp-ble-mesh/ble-mesh-feature-list.rst +++ b/docs/en/api-guides/esp-ble-mesh/ble-mesh-feature-list.rst @@ -29,6 +29,7 @@ Mesh Core * Private Beacon * Subnet Bridge * Minor Enhancements + * Device Firmware Update (Preview) * Multiple Client Models Run Simultaneously * Support multiple client models send packets to different nodes simultaneously @@ -132,6 +133,14 @@ Mesh Models * Light LC Server * Light LC Setup Server +* Device Firmware Update Client models + * Firmware Update Client model (Preview) + * Firmware Distribution Client model (coming soon) +* Device Firmware Update Server models + * Firmware Update Server model (Preview) + * Firmware Distribution Server model (coming soon) + + Mesh Examples """"""""""""" diff --git a/docs/en/api-reference/bluetooth/esp-ble-mesh.rst b/docs/en/api-reference/bluetooth/esp-ble-mesh.rst index 35de1143e5..03e522c3bf 100644 --- a/docs/en/api-reference/bluetooth/esp-ble-mesh.rst +++ b/docs/en/api-reference/bluetooth/esp-ble-mesh.rst @@ -228,3 +228,12 @@ Composition and Metadata ^^^^^^^^^^^^^^^^^^^^^^^^ .. include-build-file:: inc/esp_ble_mesh_cm_data_api.inc + +Device Firmware Update + +.. include-build-file:: inc/esp_ble_mesh_dfu_model_api.inc + +Device Firmware Slots + +.. include-build-file:: inc/esp_ble_mesh_dfu_slot_api.inc + From e71a79f6798426d5dfba7ac0bf7382de296d895c Mon Sep 17 00:00:00 2001 From: luoxu Date: Mon, 11 Aug 2025 15:17:55 +0800 Subject: [PATCH 3/4] fix(bt): Fixed the issue of duplicate MAX macro definitions --- components/bt/common/include/bt_common.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/components/bt/common/include/bt_common.h b/components/bt/common/include/bt_common.h index d8fb107878..f687e3afe7 100644 --- a/components/bt/common/include/bt_common.h +++ b/components/bt/common/include/bt_common.h @@ -141,7 +141,9 @@ #define LOG_LOCAL_LEVEL_MAPPING LOG_LOCAL_LEVEL #endif +#ifndef MAX #define MAX(a, b) ((a) > (b) ? (a) : (b)) +#endif #define BT_LOG_LEVEL_CHECK(LAYER, LEVEL) (MAX(LAYER##_INITIAL_TRACE_LEVEL, LOG_LOCAL_LEVEL_MAPPING) >= BT_TRACE_LEVEL_##LEVEL) From 16d9fcf4605cf77a6e3b48322fc1298aa895ca8c Mon Sep 17 00:00:00 2001 From: luoxu Date: Sun, 30 Nov 2025 17:15:01 +0800 Subject: [PATCH 4/4] feat(ble_mesh): dfd client/server supported --- components/bt/CMakeLists.txt | 2 + components/bt/common/btc/core/btc_task.c | 3 + .../bt/common/btc/include/btc/btc_task.h | 2 + components/bt/esp_ble_mesh/Kconfig.in | 47 + .../bt/esp_ble_mesh/api/esp_ble_mesh_defs.h | 3 +- .../bt/esp_ble_mesh/btc/btc_ble_mesh_prov.c | 26 + .../btc/include/btc_ble_mesh_model_common.h | 68 +- .../api/models/esp_ble_mesh_dfu_model_api.c | 101 ++ .../include/esp_ble_mesh_dfu_model_api.h | 1055 ++++++++++++++++- .../v1.1/btc/btc_ble_mesh_dfu_model.c | 810 ++++++++++++- .../v1.1/btc/include/btc_ble_mesh_dfu_model.h | 49 + components/bt/esp_ble_mesh/v1.1/dfu/dfd.h | 35 - components/bt/esp_ble_mesh/v1.1/dfu/dfd_cli.c | 699 +++++++++++ components/bt/esp_ble_mesh/v1.1/dfu/dfd_srv.c | 333 +++--- components/bt/esp_ble_mesh/v1.1/dfu/dfu_cli.c | 6 +- .../v1.1/include/mesh_v1.1/dfu/dfd.h | 76 +- .../v1.1/include/mesh_v1.1/dfu/dfd_cli.h | 472 ++++++++ .../v1.1/include/mesh_v1.1/dfu/dfd_srv.h | 51 +- .../esp-ble-mesh/ble-mesh-feature-list.rst | 6 +- .../esp-ble-mesh/ble-mesh-feature-list.rst | 8 + 20 files changed, 3526 insertions(+), 326 deletions(-) delete mode 100644 components/bt/esp_ble_mesh/v1.1/dfu/dfd.h create mode 100644 components/bt/esp_ble_mesh/v1.1/dfu/dfd_cli.c create mode 100644 components/bt/esp_ble_mesh/v1.1/include/mesh_v1.1/dfu/dfd_cli.h diff --git a/components/bt/CMakeLists.txt b/components/bt/CMakeLists.txt index 7d4cc40fd3..cfd8eef05d 100644 --- a/components/bt/CMakeLists.txt +++ b/components/bt/CMakeLists.txt @@ -646,6 +646,8 @@ if(CONFIG_BT_ENABLED) "esp_ble_mesh/v1.1/dfu/dfu_srv.c" "esp_ble_mesh/v1.1/dfu/dfu_slot.c" "esp_ble_mesh/v1.1/dfu/dfu_metadata.c" + "esp_ble_mesh/v1.1/dfu/dfd_srv.c" + "esp_ble_mesh/v1.1/dfu/dfd_cli.c" "esp_ble_mesh/lib/ext.c") if(CONFIG_BLE_MESH_SAR_ENHANCEMENT) list(APPEND srcs "esp_ble_mesh/core/transport.enh.c") diff --git a/components/bt/common/btc/core/btc_task.c b/components/bt/common/btc/core/btc_task.c index 3bc91c9659..9299015591 100644 --- a/components/bt/common/btc/core/btc_task.c +++ b/components/bt/common/btc/core/btc_task.c @@ -266,6 +266,9 @@ static const btc_func_t profile_tab[BTC_PID_NUM] = { #if CONFIG_BLE_MESH_DFU_CLI [BTC_PID_DFU_CLIENT] = {btc_ble_mesh_dfu_client_call_handler, btc_ble_mesh_dfu_client_cb_handler}, #endif /* CONFIG_BLE_MESH_DFU_CLI */ +#if CONFIG_BLE_MESH_DFD_CLI + [BTC_PID_DFD_CLIENT] = {btc_ble_mesh_dfd_client_call_handler, btc_ble_mesh_dfd_client_cb_handler}, +#endif /* CONFIG_BLE_MESH_DFD_CLI */ #if CONFIG_BLE_MESH_BLE_COEX_SUPPORT || CONFIG_BLE_MESH_USE_BLE_50 [BTC_PID_BLE_MESH_BLE_COEX] = {btc_ble_mesh_ble_call_handler, btc_ble_mesh_ble_cb_handler }, #endif /* CONFIG_BLE_MESH_BLE_COEX_SUPPORT || CONFIG_BLE_MESH_USE_BLE_50 */ diff --git a/components/bt/common/btc/include/btc/btc_task.h b/components/bt/common/btc/include/btc/btc_task.h index e87ce59311..5d17120180 100644 --- a/components/bt/common/btc/include/btc/btc_task.h +++ b/components/bt/common/btc/include/btc/btc_task.h @@ -110,6 +110,8 @@ typedef enum { BTC_PID_BLOB_SERVER, BTC_PID_DFU_CLIENT, BTC_PID_DFU_SERVER, + BTC_PID_DFD_CLIENT, + BTC_PID_DFD_SERVER, BTC_PID_BLE_MESH_BLE_COEX, #endif /* CONFIG_BLE_MESH */ #if (BLE_FEAT_ISO_EN == TRUE) diff --git a/components/bt/esp_ble_mesh/Kconfig.in b/components/bt/esp_ble_mesh/Kconfig.in index c1beb54857..e6c19f0e45 100644 --- a/components/bt/esp_ble_mesh/Kconfig.in +++ b/components/bt/esp_ble_mesh/Kconfig.in @@ -1862,6 +1862,53 @@ if BLE_MESH endif #BLE_MESH_DFU_SLOTS + config BLE_MESH_DFD_CLI + bool "Support for Device Distribution Client model" + help + Enable the Device Distribution Client model + + config BLE_MESH_DFD_SRV + bool "Support for Firmware Distribution Server model" + depends on BLE_MESH_BLOB_SRV + depends on BLE_MESH_DFU_CLI + help + Enable the Firmware Distribution Server model. + + if BLE_MESH_DFD_SRV + + config BLE_MESH_DFD_SRV_SLOT_MAX_SIZE + int "Largest DFU image that can be stored" + default BLE_MESH_BLOB_SIZE_MAX + range 0 BLE_MESH_BLOB_SIZE_MAX + help + This value defines the largest DFU image a single slot can store. + + config BLE_MESH_DFD_SRV_SLOT_SPACE + int "Total DFU image storage space" + default BLE_MESH_DFD_SRV_SLOT_MAX_SIZE + range 0 4294967295 + help + This value defines the total storage space dedicated to storing DFU + images on the Firmware Distribution Server. + + config BLE_MESH_DFD_SRV_TARGETS_MAX + int "Maximum Target node count" + default 8 + range 1 65535 + help + This value defines the maximum number of Target nodes the Firmware + Distribution Server can target simultaneously. + + config BLE_MESH_DFD_SRV_OOB_UPLOAD + bool "Support for DFU image OOB upload" + help + This enables support for OOB upload of firmware images for + distribution. This makes several callbacks and use of the init + macro BLE_MESH_DFD_SRV_INIT_OOB mandatory. See the API documentation + for bt_mesh_dfd_srv_cb for details about the mandatory callbacks. + + endif #BLE_MESH_DFD_SRV + endmenu # Device Firmware Update model endmenu #Support for BLE Mesh Client/Server models diff --git a/components/bt/esp_ble_mesh/api/esp_ble_mesh_defs.h b/components/bt/esp_ble_mesh/api/esp_ble_mesh_defs.h index 8b7ff38ed7..01ade02668 100644 --- a/components/bt/esp_ble_mesh/api/esp_ble_mesh_defs.h +++ b/components/bt/esp_ble_mesh/api/esp_ble_mesh_defs.h @@ -1988,7 +1988,8 @@ typedef union { #define ESP_BLE_MESH_MODEL_ID_BLOB_CLI 0x1401 #define ESP_BLE_MESH_MODEL_ID_DFU_SRV 0x1402 #define ESP_BLE_MESH_MODEL_ID_DFU_CLI 0x1403 - +#define ESP_BLE_MESH_MODEL_ID_DFD_SRV 0x1404 +#define ESP_BLE_MESH_MODEL_ID_DFD_CLI 0x1405 /** * esp_ble_mesh_opcode_config_client_get_t belongs to esp_ble_mesh_opcode_t, this typedef is only * used to locate the opcodes used by esp_ble_mesh_config_client_get_state. 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 babbdf5408..49ae1d5dda 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 @@ -1427,6 +1427,14 @@ extern const struct bt_mesh_model_cb _bt_mesh_dfu_srv_cb; extern const struct bt_mesh_model_op _bt_mesh_dfu_cli_op[]; extern const struct bt_mesh_model_cb _bt_mesh_dfu_cli_cb; #endif /* CONFIG_BLE_MESH_DFU_CLI */ +#if CONFIG_BLE_MESH_DFD_SRV +extern const struct bt_mesh_model_op _bt_mesh_dfd_srv_op[]; +extern const struct bt_mesh_model_cb _bt_mesh_dfd_srv_cb; +#endif /* CONFIG_BLE_MESH_DFD_SRV */ +#if CONFIG_BLE_MESH_DFD_CLI +extern const struct bt_mesh_model_op _bt_mesh_dfd_cli_op[]; +extern const struct bt_mesh_model_cb _bt_mesh_dfd_cli_cb; +#endif /* CONFIG_BLE_MESH_DFD_CLI */ static void btc_ble_mesh_model_op_set(esp_ble_mesh_model_t *model) { @@ -2274,6 +2282,24 @@ static void btc_ble_mesh_model_op_set(esp_ble_mesh_model_t *model) } break; #endif /* CONFIG_BLE_MESH_DFU_SRV */ +#if CONFIG_BLE_MESH_DFD_SRV + case BLE_MESH_MODEL_ID_DFD_SRV: + model->op = (esp_ble_mesh_model_op_t *)_bt_mesh_dfd_srv_op; + model->cb = (esp_ble_mesh_model_cbs_t *)&_bt_mesh_dfd_srv_cb; + if (model->pub) { + model->pub->update = (esp_ble_mesh_cb_t)btc_ble_mesh_model_publish_update; + } + break; +#endif /* CONFIG_BLE_MESH_DFD_SRV */ +#if CONFIG_BLE_MESH_DFD_CLI + case BLE_MESH_MODEL_ID_DFD_CLI: + model->op = (esp_ble_mesh_model_op_t *)_bt_mesh_dfd_cli_op; + model->cb = (esp_ble_mesh_model_cbs_t *)&_bt_mesh_dfd_cli_cb; + if (model->pub) { + model->pub->update = (esp_ble_mesh_cb_t)btc_ble_mesh_model_publish_update; + } + break; +#endif /* CONFIG_BLE_MESH_DFD_SRV */ default: goto set_vnd_op; } diff --git a/components/bt/esp_ble_mesh/btc/include/btc_ble_mesh_model_common.h b/components/bt/esp_ble_mesh/btc/include/btc_ble_mesh_model_common.h index 73b00db41f..66507a1bb2 100644 --- a/components/bt/esp_ble_mesh/btc/include/btc_ble_mesh_model_common.h +++ b/components/bt/esp_ble_mesh/btc/include/btc_ble_mesh_model_common.h @@ -17,6 +17,45 @@ extern "C" { #endif +static inline void btc_ble_mesh_msg_ctx_copy(struct bt_mesh_msg_ctx *dst, + const struct bt_mesh_msg_ctx *src, + bool use_dev_key) +{ + if (dst == NULL || + src == NULL) { + return; + } + + dst->net_idx = src->net_idx; + dst->app_idx = use_dev_key ? BLE_MESH_KEY_DEV : src->app_idx; + dst->addr = src->addr; + dst->send_szmic = src->send_szmic; + dst->send_ttl = src->send_ttl; + dst->send_cred = src->send_cred; + dst->send_tag = src->send_tag; + if (src->enh.adv_cfg_used) { + dst->enh.adv_cfg_used = src->enh.adv_cfg_used; + dst->enh.adv_cfg.adv_cnt = src->enh.adv_cfg.adv_cnt; + dst->enh.adv_cfg.adv_itvl = src->enh.adv_cfg.adv_itvl; + dst->enh.adv_cfg.channel_map = src->enh.adv_cfg.channel_map; + } +#if CONFIG_BLE_MESH_EXT_ADV + if (src->enh.ext_adv_cfg_used) { + dst->enh.ext_adv_cfg_used = src->enh.ext_adv_cfg_used; + dst->enh.ext_adv_cfg.primary_phy = src->enh.ext_adv_cfg.primary_phy; + dst->enh.ext_adv_cfg.secondary_phy = src->enh.ext_adv_cfg.secondary_phy; + dst->enh.ext_adv_cfg.include_tx_power = src->enh.ext_adv_cfg.include_tx_power; + dst->enh.ext_adv_cfg.tx_power = src->enh.ext_adv_cfg.tx_power; + } +#if CONFIG_BLE_MESH_LONG_PACKET + if (src->enh.long_pkt_cfg_used) { + dst->enh.long_pkt_cfg_used = src->enh.long_pkt_cfg_used; + dst->enh.long_pkt_cfg = src->enh.long_pkt_cfg; + } +#endif /* CONFIG_BLE_MESH_LONG_PACKET */ +#endif /* CONFIG_BLE_MESH_EXT_ADV */ +} + static inline void btc_ble_mesh_set_client_common_param(esp_ble_mesh_client_common_param_t *input, bt_mesh_client_common_param_t *output, bool use_dev_key) @@ -24,35 +63,8 @@ static inline void btc_ble_mesh_set_client_common_param(esp_ble_mesh_client_comm if (input && output) { output->opcode = input->opcode; output->model = (struct bt_mesh_model *)input->model; - output->ctx.net_idx = input->ctx.net_idx; - output->ctx.app_idx = use_dev_key ? BLE_MESH_KEY_DEV : input->ctx.app_idx; - output->ctx.addr = input->ctx.addr; - output->ctx.send_szmic = input->ctx.send_szmic; - output->ctx.send_ttl = input->ctx.send_ttl; - output->ctx.send_cred = input->ctx.send_cred; - output->ctx.send_tag = input->ctx.send_tag; output->msg_timeout = input->msg_timeout; - if (input->ctx.enh.adv_cfg_used) { - output->ctx.enh.adv_cfg_used = input->ctx.enh.adv_cfg_used; - output->ctx.enh.adv_cfg.adv_cnt = input->ctx.enh.adv_cfg.adv_cnt; - output->ctx.enh.adv_cfg.adv_itvl = input->ctx.enh.adv_cfg.adv_itvl; - output->ctx.enh.adv_cfg.channel_map = input->ctx.enh.adv_cfg.channel_map; - } -#if CONFIG_BLE_MESH_EXT_ADV - if (input->ctx.enh.ext_adv_cfg_used) { - output->ctx.enh.ext_adv_cfg_used = input->ctx.enh.ext_adv_cfg_used; - output->ctx.enh.ext_adv_cfg.primary_phy = input->ctx.enh.ext_adv_cfg.primary_phy; - output->ctx.enh.ext_adv_cfg.secondary_phy = input->ctx.enh.ext_adv_cfg.secondary_phy; - output->ctx.enh.ext_adv_cfg.include_tx_power = input->ctx.enh.ext_adv_cfg.include_tx_power; - output->ctx.enh.ext_adv_cfg.tx_power = input->ctx.enh.ext_adv_cfg.tx_power; - } -#if CONFIG_BLE_MESH_LONG_PACKET - if (input->ctx.enh.long_pkt_cfg_used) { - output->ctx.enh.long_pkt_cfg_used = input->ctx.enh.long_pkt_cfg_used; - output->ctx.enh.long_pkt_cfg = input->ctx.enh.long_pkt_cfg; - } -#endif -#endif + btc_ble_mesh_msg_ctx_copy(&output->ctx, (const struct bt_mesh_msg_ctx *)&input->ctx, use_dev_key); } } 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 feb082cd66..84110f8ade 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 @@ -146,3 +146,104 @@ uint8_t esp_ble_mesh_dfu_srv_progress(const esp_ble_mesh_dfu_srv_t *srv) return btc_ble_mesh_dfu_srv_progress(srv); } #endif /* CONFIG_BLE_MESH_DFU_SRV */ + +#if CONFIG_BLE_MESH_DFD_CLI + +esp_err_t esp_ble_mesh_register_dfd_cli_callback(esp_ble_mesh_dfd_client_cb_t callback) +{ + ESP_BLE_HOST_STATUS_CHECK(ESP_BLE_HOST_STATUS_ENABLED); + + return (btc_profile_cb_set(BTC_PID_DFD_CLIENT, callback) == 0 ? ESP_OK : ESP_FAIL); +} + +static bool dfd_client_opcode_need_param(esp_ble_mesh_opcode_t opcode) +{ + switch (opcode) + { + /* Get opcode */ + case ESP_BLE_MESH_DFD_OP_RECEIVERS_GET: + case ESP_BLE_MESH_DFD_OP_FW_GET: + case ESP_BLE_MESH_DFD_OP_FW_GET_BY_INDEX: + /* Set opcode */ + case ESP_BLE_MESH_DFD_OP_RECEIVERS_ADD: + case ESP_BLE_MESH_DFD_OP_START: + case ESP_BLE_MESH_DFD_OP_UPLOAD_START: + case ESP_BLE_MESH_DFD_OP_UPLOAD_START_OOB: + case ESP_BLE_MESH_DFD_OP_FW_DELETE: + return true; + default: + return false; + } +} + +esp_err_t esp_ble_mesh_dfd_cli_get(esp_ble_mesh_client_common_param_t *params, + esp_ble_mesh_dfd_client_get_param_t *get_param) +{ + btc_ble_mesh_dfd_client_args_t arg = {0}; + btc_msg_t msg = {0}; + + if (params == NULL || params->model == NULL || + params->ctx.net_idx == ESP_BLE_MESH_KEY_UNUSED || + !ESP_BLE_MESH_ADDR_IS_UNICAST(params->ctx.addr) || + (dfd_client_opcode_need_param(params->opcode) && get_param == NULL)) { + return ESP_ERR_INVALID_ARG; + } + + ESP_BLE_HOST_STATUS_CHECK(ESP_BLE_HOST_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_DFD_CLIENT; + msg.act = ESP_BLE_MESH_ACT_DFD_CLIENT_GET; + arg.dfd_client_get.params = params; + arg.dfd_client_get.get = get_param; + + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_mesh_dfd_client_args_t), btc_ble_mesh_dfd_client_arg_deep_copy, + btc_ble_mesh_dfd_client_arg_deep_free) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_ble_mesh_dfd_cli_set(esp_ble_mesh_client_common_param_t *params, + esp_ble_mesh_dfd_client_set_param_t *set_param) +{ + btc_ble_mesh_dfd_client_args_t arg = {0}; + btc_msg_t msg = {0}; + + if (params == NULL || params->model == NULL || + params->ctx.net_idx == ESP_BLE_MESH_KEY_UNUSED || + !ESP_BLE_MESH_ADDR_IS_UNICAST(params->ctx.addr) || + (dfd_client_opcode_need_param(params->opcode) && set_param == NULL)) { + return ESP_ERR_INVALID_ARG; + } + + switch (params->opcode) { + case ESP_BLE_MESH_DFD_OP_RECEIVERS_ADD: + if (set_param->receivers_add.receivers_cnt == 0) { + return ESP_ERR_INVALID_ARG; + } + break; + case ESP_BLE_MESH_DFD_OP_UPLOAD_START: + if (set_param->dist_upload_start.fw_size == 0) { + return ESP_ERR_INVALID_ARG; + } + if (set_param->dist_upload_start.fwid == NULL) { + return ESP_ERR_INVALID_ARG; + } + break; + case ESP_BLE_MESH_DFD_OP_UPLOAD_START_OOB: + if (set_param->dist_upload_oob_start.url == NULL) { + return ESP_ERR_INVALID_ARG; + } + break; + } + + ESP_BLE_HOST_STATUS_CHECK(ESP_BLE_HOST_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_DFD_CLIENT; + msg.act = ESP_BLE_MESH_ACT_DFD_CLIENT_SET; + arg.dfd_client_set.params = params; + arg.dfd_client_set.set = set_param; + + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_mesh_dfd_client_args_t), btc_ble_mesh_dfd_client_arg_deep_copy, + btc_ble_mesh_dfd_client_arg_deep_free) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} +#endif /* CONFIG_BLE_MESH_DFD_CLI */ diff --git a/components/bt/esp_ble_mesh/v1.1/api/models/include/esp_ble_mesh_dfu_model_api.h b/components/bt/esp_ble_mesh/v1.1/api/models/include/esp_ble_mesh_dfu_model_api.h index bf91d9e06f..6ef42d3dd8 100644 --- a/components/bt/esp_ble_mesh/v1.1/api/models/include/esp_ble_mesh_dfu_model_api.h +++ b/components/bt/esp_ble_mesh/v1.1/api/models/include/esp_ble_mesh_dfu_model_api.h @@ -34,18 +34,55 @@ extern "C" { #define ESP_BLE_MESH_DFU_OP_UPDATE_APPLY ESP_BLE_MESH_MODEL_OP_2(0x83, 0x0f) #define ESP_BLE_MESH_DFU_OP_UPDATE_STATUS ESP_BLE_MESH_MODEL_OP_2(0x83, 0x10) +/* Distribution Receivers Management Operations (Section 4.3.1) */ +#define ESP_BLE_MESH_DFD_OP_RECEIVERS_ADD ESP_BLE_MESH_MODEL_OP_2(0x83, 0x11) /*!< Add receivers to the distribution list. */ +#define ESP_BLE_MESH_DFD_OP_RECEIVERS_DELETE_ALL ESP_BLE_MESH_MODEL_OP_2(0x83, 0x12) /*!< Delete all receivers from the distribution list. */ +#define ESP_BLE_MESH_DFD_OP_RECEIVERS_STATUS ESP_BLE_MESH_MODEL_OP_2(0x83, 0x13) /*!< Status of receivers add/delete operations. */ +#define ESP_BLE_MESH_DFD_OP_RECEIVERS_GET ESP_BLE_MESH_MODEL_OP_2(0x83, 0x14) /*!< Get the distribution receivers list. */ +#define ESP_BLE_MESH_DFD_OP_RECEIVERS_LIST ESP_BLE_MESH_MODEL_OP_2(0x83, 0x15) /*!< List of distribution receivers. */ + +/* Distribution Capabilities Operations (Section 4.3.2) */ +#define ESP_BLE_MESH_DFD_OP_CAPABILITIES_GET ESP_BLE_MESH_MODEL_OP_2(0x83, 0x16) /*!< Get distribution capabilities of the node. */ +#define ESP_BLE_MESH_DFD_OP_CAPABILITIES_STATUS ESP_BLE_MESH_MODEL_OP_2(0x83, 0x17) /*!< Distribution capabilities status. */ + +/* Firmware Distribution Control Operations (Section 4.3.3) */ +#define ESP_BLE_MESH_DFD_OP_GET ESP_BLE_MESH_MODEL_OP_2(0x83, 0x18) /*!< Get current firmware distribution status. */ +#define ESP_BLE_MESH_DFD_OP_START ESP_BLE_MESH_MODEL_OP_2(0x83, 0x19) /*!< Start firmware distribution to receivers. */ +#define ESP_BLE_MESH_DFD_OP_SUSPEND ESP_BLE_MESH_MODEL_OP_2(0x83, 0x1a) /*!< Suspend ongoing firmware distribution. */ +#define ESP_BLE_MESH_DFD_OP_CANCEL ESP_BLE_MESH_MODEL_OP_2(0x83, 0x1b) /*!< Cancel firmware distribution. */ +#define ESP_BLE_MESH_DFD_OP_APPLY ESP_BLE_MESH_MODEL_OP_2(0x83, 0x1c) /*!< Apply distributed firmware on target nodes. */ +#define ESP_BLE_MESH_DFD_OP_STATUS ESP_BLE_MESH_MODEL_OP_2(0x83, 0x1d) /*!< Firmware distribution status. */ + +/* Firmware Upload Management Operations (Section 4.3.4) */ +#define ESP_BLE_MESH_DFD_OP_UPLOAD_GET ESP_BLE_MESH_MODEL_OP_2(0x83, 0x1e) /*!< Get firmware upload status and progress. */ +#define ESP_BLE_MESH_DFD_OP_UPLOAD_START ESP_BLE_MESH_MODEL_OP_2(0x83, 0x1f) /*!< Start firmware upload (in-band transfer). */ +#define ESP_BLE_MESH_DFD_OP_UPLOAD_START_OOB ESP_BLE_MESH_MODEL_OP_2(0x83, 0x20) /*!< Start firmware upload (out-of-band transfer). */ +#define ESP_BLE_MESH_DFD_OP_UPLOAD_CANCEL ESP_BLE_MESH_MODEL_OP_2(0x83, 0x21) /*!< Cancel firmware upload. */ +#define ESP_BLE_MESH_DFD_OP_UPLOAD_STATUS ESP_BLE_MESH_MODEL_OP_2(0x83, 0x22) /*!< Firmware upload status and progress. */ + +/* Firmware Image Management Operations (Section 4.3.5) */ +#define ESP_BLE_MESH_DFD_OP_FW_GET ESP_BLE_MESH_MODEL_OP_2(0x83, 0x23) /*!< Get firmware image information by FW ID. */ +#define ESP_BLE_MESH_DFD_OP_FW_GET_BY_INDEX ESP_BLE_MESH_MODEL_OP_2(0x83, 0x24) /*!< Get firmware image information by index. */ +#define ESP_BLE_MESH_DFD_OP_FW_DELETE ESP_BLE_MESH_MODEL_OP_2(0x83, 0x25) /*!< Delete specific firmware image. */ +#define ESP_BLE_MESH_DFD_OP_FW_DELETE_ALL ESP_BLE_MESH_MODEL_OP_2(0x83, 0x26) /*!< Delete all firmware images. */ +#define ESP_BLE_MESH_DFD_OP_FW_STATUS ESP_BLE_MESH_MODEL_OP_2(0x83, 0x27) /*!< Firmware image management status. */ + +/* Device Firmware Distribution Constants and Status Codes */ +#define ESP_BLE_MESH_DFD_UPLOAD_PROGRESS_UNSET 101 /*!< Upload progress is not set/available. */ +#define ESP_BLE_MESH_DFD_UPLOAD_TYPE_INBAND 0 /*!< In-band firmware upload type. */ +#define ESP_BLE_MESH_DFD_UPLOAD_TYPE_OOB BIT0 /*!< Out-of-band firmware upload type. */ + #if CONFIG_BLE_MESH_DFU_CLI -/** @def ESP_BLE_MESH_MODEL_DFU_CLI +/** + * @brief Define a new DFU Client model. * - * @brief Define a new DFU Client model. + * @note This API needs to be called for each element on which + * the application needs to have a DFU Client model. * - * @note This API needs to be called for each element on which - * the application needs to have a DFU Client model. + * @param cli_pub Pointer to the unique `esp_ble_mesh_model_pub_t`. + * @param cli_data Pointer to the unique `esp_ble_mesh_client_t`. * - * @param cli_pub Pointer to the unique struct esp_ble_mesh_model_pub_t. - * @param cli_data Pointer to the unique struct esp_ble_mesh_client_t. - * - * @return New DFU Client model instance. + * @return New DFU Client model instance. */ #define ESP_BLE_MESH_MODEL_DFU_CLI(cli_pub, cli_data) \ ESP_BLE_MESH_MODEL_BLOB_CLI(NULL, &(cli_data)->blob), \ @@ -53,16 +90,14 @@ extern "C" { NULL, cli_pub, cli_data) -/** @def ESP_BLE_MESH_DFU_CLI_INIT +/** + * @brief Initialize a DFU Client model instance. * - * @brief Initialize a DFU Client model instance. + * @note This macro initializes the DFU Client model and sets callback handlers. * - * @note This macro is used to initialize the DFU Client model. - * It sets the callback handlers for the DFU Client. + * @param _handlers Pointer to the callback handler structure `esp_ble_mesh_dfu_cli_cb_t`. * - * @param _handlers Pointer to the callback handler structure (esp_ble_mesh_dfu_cli_cb_t). - * - * @return An initialized DFU Client model instance. + * @return An initialized DFU Client model instance. */ #define ESP_BLE_MESH_DFU_CLI_INIT(_handlers) \ { \ @@ -71,17 +106,16 @@ extern "C" { #endif /* CONFIG_BLE_MESH_DFU_CLI */ #if CONFIG_BLE_MESH_DFU_SRV -/** @def ESP_BLE_MESH_MODEL_DFU_SRV +/** + * @brief Define a new DFU Server model. * - * @brief Define a new DFU Server model. + * @note This API needs to be called for each element on which + * the application needs to have a DFU Server model. * - * @note This API needs to be called for each element on which - * the application needs to have a DFU Server model. + * @param srv_pub Pointer to the unique `esp_ble_mesh_model_pub_t`. + * @param srv_data Pointer to the unique `esp_ble_mesh_dfu_trans_srv_t`. * - * @param srv_pub Pointer to the unique struct esp_ble_mesh_model_pub_t. - * @param srv_data Pointer to the unique struct esp_ble_mesh_dfu_trans_srv_t. - * - * @return New DFU Server model instance. + * @return New DFU Server model instance. */ #define ESP_BLE_MESH_MODEL_DFU_SRV(srv_pub, srv_data) \ ESP_BLE_MESH_MODEL_BLOB_SRV(NULL, &(srv_data)->blob), \ @@ -89,18 +123,17 @@ extern "C" { NULL, srv_pub, srv_data) -/** @def ESP_BLE_MESH_DFU_SRV_INIT +/** + * @brief Initialize a DFU Server model instance. * - * @brief Initialize a DFU Server model instance. + * @note This macro initializes the DFU Server model and sets callback handlers + * and image information. * - * @note This macro is used to initialize the DFU Server model. - * It sets the callback handlers and image information for the DFU Server. + * @param _handlers Pointer to the callback handler structure `esp_ble_mesh_dfu_srv_cb_t`. + * @param _imgs Pointer to the array of DFU images `esp_ble_mesh_dfu_img_t`. + * @param _img_count Number of DFU images in the array. * - * @param _handlers Pointer to the callback handler structure (esp_ble_mesh_dfu_srv_cb_t). - * @param _imgs Pointer to the array of DFU images (esp_ble_mesh_dfu_img_t). - * @param _img_count Number of DFU images in the array. - * - * @return An initialized DFU Server model instance. + * @return An initialized DFU Server model instance. */ #define ESP_BLE_MESH_DFU_SRV_INIT(_handlers, _imgs, _img_count) \ { \ @@ -109,6 +142,75 @@ extern "C" { } #endif /* CONFIG_BLE_MESH_DFU_SRV */ +#if CONFIG_BLE_MESH_DFD_CLI +/** + * @def ESP_BLE_MESH_MODEL_DFD_CLI + * + * @brief Define a new Device Firmware Distribution Client model. + * + * Define a DFD Client model for managing firmware distribution operations, + * including receiver lists, upload control, progress monitoring, and + * firmware transfers. + * + * @param cli_pub Pointer to the unique `esp_ble_mesh_model_pub_t` for publish messages. + * @param cli_data Pointer to the unique `esp_ble_mesh_client_t` containing client data. + * + * @return New DFD Client model instance. + */ +#define ESP_BLE_MESH_MODEL_DFD_CLI(cli_pub, cli_data) \ + ESP_BLE_MESH_SIG_MODEL(ESP_BLE_MESH_MODEL_ID_DFD_CLI, \ + NULL, cli_pub, cli_data) +#endif /* CONFIG_BLE_MESH_DFD_CLI */ + +#if CONFIG_BLE_MESH_DFD_SRV +/** + * @def ESP_BLE_MESH_MODEL_DFD_SRV + * + * @brief Define a new Device Firmware Distribution Server model. + * + * Define a DFD Server model that stores firmware, distributes to targets, + * handles uploads, manages receiver lists, and controls distribution phases. + * Contains internal DFU Client for updates and BLOB Server for transfers. + * + * @param srv_pub Pointer to the unique `esp_ble_mesh_model_pub_t` for publish messages. + * @param srv_data Pointer to the unique `esp_ble_dfd_trans_srv_t` containing server data. + * + * @return New DFD Server model instance. + */ +#define ESP_BLE_MESH_MODEL_DFD_SRV(srv_pub, srv_data) \ + ESP_BLE_MESH_MODEL_DFU_CLI(NULL, &(srv_data)->dfu), \ + ESP_BLE_MESH_MODEL_BLOB_SRV(NULL, &((srv_data)->upload.blob)),\ + ESP_BLE_MESH_SIG_MODEL(ESP_BLE_MESH_MODEL_ID_DFD_SRV, \ + NULL, srv_pub, srv_data) + +/* Internal callback structures for DFD Server implementation */ +extern const struct bt_mesh_dfu_cli_cb _bt_mesh_dfd_srv_dfu_cb; /*!< Internal DFU Client callbacks for DFD Server. */ +extern const struct bt_mesh_blob_srv_cb _bt_mesh_dfd_srv_blob_cb; /*!< Internal BLOB Server callbacks for DFD Server. */ + +/** + * @def ESP_BLE_MESH_DFD_SRV_INIT + * + * @brief Initialize a Device Firmware Distribution Server. + * + * Initialize a DFD Server instance with the provided callback structure + * and set up the internal DFU Client and BLOB Server components. + * + * @param _cb Pointer to a `bt_mesh_dfd_srv_cb` instance containing the server callbacks. + * + * @return Initialized DFD Server structure with internal components configured. + */ +#define ESP_BLE_MESH_DFD_SRV_INIT(_cb) \ + { \ + .cb = _cb, \ + .dfu = ESP_BLE_MESH_DFU_CLI_INIT((struct esp_ble_mesh_dfu_cli_cb *) \ + &_bt_mesh_dfd_srv_dfu_cb), \ + .upload = { \ + .blob = { .cb = (esp_ble_mesh_blob_srv_cb_t *) \ + &_bt_mesh_dfd_srv_blob_cb }, \ + }, \ + } +#endif /* CONFIG_BLE_MESH_DFD_SRV */ + /** @brief DFU image structure. **/ typedef struct esp_ble_mesh_dfu_img esp_ble_mesh_dfu_img_t; @@ -298,6 +400,95 @@ typedef struct esp_ble_mesh_dfu_srv esp_ble_mesh_dfu_srv_t; typedef struct esp_ble_mesh_dfu_srv_cb esp_ble_mesh_dfu_srv_cb_t; #endif /* CONFIG_BLE_MESH_DFU_SRV */ +#if CONFIG_BLE_MESH_DFD_CLI +/* DFD Client Forward Declarations */ +typedef enum esp_ble_mesh_dfd_client_act esp_ble_mesh_dfd_client_act_t; /*!< DFD Client action enumeration. */ +typedef enum esp_ble_mesh_dfd_client_cb_evt esp_ble_mesh_dfd_client_cb_evt_t; /*!< DFD Client callback event enumeration. */ +typedef struct esp_ble_mesh_dfd_cli_receiver_entry esp_ble_mesh_dfd_cli_receiver_entry_t; /*!< DFD Client receiver entry structure. */ + +/* DFD Client Receiver Management Structures */ +typedef struct esp_ble_mesh_dfd_cli_receivers_add esp_ble_mesh_dfd_cli_receivers_add_t; /*!< Parameters for adding receivers to distribution list. */ +typedef struct esp_ble_mesh_dfd_receiver_status esp_ble_mesh_dfd_receiver_status_t; /*!< Status response for receiver operations. */ + +typedef struct esp_ble_mesh_dfd_receivers_get esp_ble_mesh_dfd_receivers_get_t; /*!< Parameters for getting distribution receivers list. */ +typedef struct esp_ble_mesh_dfd_target_node_entry esp_ble_mesh_dfd_target_node_entry_t; /*!< Target node entry information. */ +typedef struct esp_ble_mesh_dfd_receiver_list esp_ble_mesh_dfd_receiver_list_t; /*!< Distribution receivers list response. */ + +/* DFD Client Distribution Control Structures */ +typedef struct esp_ble_mesh_dfd_dist_caps esp_ble_mesh_dfd_dist_caps_t; /*!< Distribution capabilities of the DFD Server. */ + +typedef struct esp_ble_mesh_dfd_cli_dist_start esp_ble_mesh_dfd_cli_dist_start_t; /*!< Parameters for starting firmware distribution. */ +typedef struct esp_ble_mesh_dfd_dist_status esp_ble_mesh_dfd_dist_status_t; /*!< Firmware distribution status response. */ + +/* DFD Client Upload Management Structures */ +typedef struct esp_ble_mesh_dfd_cli_dist_upload_start esp_ble_mesh_dfd_cli_dist_upload_start_t; /*!< Parameters for starting in-band firmware upload. */ +typedef struct esp_ble_mesh_dfd_cli_dist_upload_oob_start esp_ble_mesh_dfd_cli_dist_upload_oob_start_t; /*!< Parameters for starting out-of-band firmware upload. */ +typedef struct esp_ble_mesh_dfd_upload_status esp_ble_mesh_dfd_upload_status_t; /*!< Firmware upload status and progress. */ + +/* DFD Client Firmware Management Structures */ +typedef struct esp_ble_mesh_dfd_cli_dist_fw_get esp_ble_mesh_dfd_cli_dist_fw_get_t; /*!< Parameters for getting firmware by FW ID. */ +typedef struct esp_ble_mesh_dfd_cli_dist_fw_get_by_idx esp_ble_mesh_dfd_cli_dist_fw_get_by_idx_t; /*!< Parameters for getting firmware by index. */ +typedef struct esp_ble_mesh_dfd_cli_dist_fw_del esp_ble_mesh_dfd_cli_dist_fw_del_t; /*!< Parameters for deleting firmware. */ +typedef struct esp_ble_mesh_dfd_firmware_status esp_ble_mesh_dfd_firmware_status_t; /*!< Firmware management status response. */ + +/* DFD Client Callback Structures */ +typedef struct esp_ble_mesh_dfd_client_cb_param esp_ble_mesh_dfd_client_cb_param_t; /*!< DFD Client callback parameters. */ +typedef union esp_ble_mesh_dfd_client_common_cb_param esp_ble_mesh_dfd_client_common_cb_param_t; /*!< Common callback parameter union. */ + +/* DFD Client Callback Function Type */ +typedef void (*esp_ble_mesh_dfd_client_cb_t)(esp_ble_mesh_dfd_client_cb_evt_t event, /*!< DFD Client callback function type. */ + esp_ble_mesh_dfd_client_cb_param_t *param); +#endif /* CONFIG_BLE_MESH_DFD_CLI */ + +#if CONFIG_BLE_MESH_DFD_SRV +/** + * @brief Firmware Distribution status enumeration. + * + * This enumeration defines the status codes used in DFD operations + * according to the Bluetooth Mesh Model Specification v1.0. These + * status codes indicate success, failure, and error conditions + * for various firmware distribution operations. + */ +typedef enum esp_ble_mesh_dfd_status esp_ble_mesh_dfd_status_t; + +/** + * @brief Firmware distribution phases enumeration. + * + * This enumeration defines the different phases of a firmware distribution + * operation as specified in the Bluetooth Mesh Model Specification v1.0. + * The phases track the state of firmware distribution from idle through + * transfer, verification, application, and completion states. + */ +typedef enum esp_ble_mesh_dfd_phase esp_ble_mesh_dfd_phase_t; + +/** + * @brief Firmware upload phases enumeration. + * + * This enumeration defines the phases of firmware upload operations + * on the DFD Server. It tracks the state of firmware storage procedures + * including in-band and out-of-band upload methods. + */ +typedef enum esp_ble_mesh_dfd_upload_phase esp_ble_mesh_dfd_upload_phase_t; + +/** + * @brief Firmware Distribution Server callback structure. + * + * This structure contains callback function pointers that allow the + * application to handle various DFD Server events including firmware + * reception, distribution control, and phase changes. + */ +typedef struct esp_ble_mesh_dfd_srv_cb esp_ble_mesh_dfd_srv_cb_t; + +/** + * @brief Firmware Distribution Server instance structure. + * + * This structure represents a DFD Server model instance that manages + * firmware distribution operations. It contains state information for + * ongoing distributions, firmware storage, and target node management. + */ +typedef struct esp_ble_mesh_dfd_srv esp_ble_mesh_dfd_srv_t; +#endif /* CONFIG_BLE_MESH_DFD_SRV */ + /** DFU transfer phase. */ typedef enum esp_ble_mesh_dfu_phase { /** Ready to start a Receive Firmware procedure. */ @@ -613,7 +804,7 @@ struct esp_ble_mesh_dfu_cli_xfer { struct esp_ble_mesh_device_firmware_update_info_get { uint8_t first_index; /*!< Index of the first requested entry from the Firmware Information List state */ uint8_t entries_limit; /*!< Maximum number of entries that the server includes in a Firmware Update Information Status message */ - esp_ble_mesh_dfu_img_cb_t img_cb; /*!< Added by Espressif, callback function used to walkthrough recceived images */ + esp_ble_mesh_dfu_img_cb_t img_cb; /*!< Added by Espressif, callback function used to walkthrough received images */ }; /** @@ -992,6 +1183,804 @@ bool esp_ble_mesh_dfu_srv_is_busy(const esp_ble_mesh_dfu_srv_t *srv); uint8_t esp_ble_mesh_dfu_srv_progress(const esp_ble_mesh_dfu_srv_t *srv); #endif /* CONFIG_BLE_MESH_DFU_SRV */ +#if CONFIG_BLE_MESH_DFD_CLI +/** + * @brief DFD Client receiver entry structure. + * + * This structure represents a single receiver node that will be added + * to the distribution list for firmware updates. Each entry contains + * the target node's address and the specific firmware image index + * that should be distributed to that node. + * + * @note This is used with the DFD Receivers Add message to build + * the distribution receiver list. + */ +struct esp_ble_mesh_dfd_cli_receiver_entry { + /** The unicast address of the target node to receive firmware updates */ + uint16_t addr; + /** Index of the firmware image that should be distributed to this target node */ + uint8_t fw_idx; +}; + +/** + * @brief DFD Client receivers add parameters. + * + * This structure contains the parameters for the DFD Receivers Add + * message. It is used to add multiple target nodes to the distribution + * receiver list. + * + * @note The receivers array should contain valid unicast addresses + * and corresponding firmware image indices. + */ +struct esp_ble_mesh_dfd_cli_receivers_add { + /** Pointer to array of receiver entries to add to the distribution list */ + esp_ble_mesh_dfd_cli_receiver_entry_t *receivers; + /** Number of receiver entries in the receivers array */ + uint16_t receivers_cnt; +}; + +/** + * @brief DFD receiver status response structure. + * + * This structure contains the response parameters for the DFD + * Receivers Add/Delete operations. + * + * The status indicates whether the receiver list modification + * was successful, and the receiver_list_cnt provides the current + * size of the distribution receiver list. + */ +struct esp_ble_mesh_dfd_receiver_status { + /** Status code for the receiver add/delete operation (see esp_ble_mesh_dfd_status) */ + uint8_t status; + /** Current number of entries in the Distribution Receivers List state */ + uint16_t receiver_list_cnt; +}; + +/** + * @brief DFD receivers get parameters. + * + * This structure contains the parameters for the DFD Receivers Get + * message. It is used to retrieve a portion of the distribution + * receiver list. + * + * @note This supports pagination of large receiver lists by allowing + * the client to request specific ranges of entries. + */ +struct esp_ble_mesh_dfd_receivers_get { + /** Index of the first entry in the Distribution Receivers List to return */ + uint16_t first_index; + /** Maximum number of entries to include in the response */ + uint16_t entries_limit; +}; + +/** + * @brief DFD target node entry structure. + * + * This structure represents the status of a single target node + * in the distribution receiver list. It contains detailed information + * about the target's current update phase, transfer status, and progress. + * + * This is returned in the DFD Receivers List message. + */ +struct esp_ble_mesh_dfd_target_node_entry { + /** Unicast address of the target node (15 bits) */ + uint32_t addr:15, + /** Retrieved update phase of the target node (4 bits, see esp_ble_mesh_dfu_phase) */ + retrieved_update_phase:4, + /** Update status of the target node (3 bits, see esp_ble_mesh_dfu_status) */ + update_status:3, + /** Transfer status of the target node (4 bits) */ + transfer_status:4, + /** Transfer progress percentage of the target node (6 bits, in 2 percent increments) */ + transfer_progress:6; + /** Index of the firmware image being updated on the target node */ + uint8_t update_fw_idx; +}; + +/** + * @brief DFD receiver list response structure. + * + * This structure contains the response parameters for the DFD Receivers Get + * message. It provides a list of target nodes in the distribution receiver + * list along with their current status. + */ +struct esp_ble_mesh_dfd_receiver_list { + /** Number of entries included in this response */ + uint16_t entries_cnt; + /** Index of the first entry in the Distribution Receivers List returned */ + uint16_t first_index; + /** Pointer to array of target node entries containing receiver status information */ + esp_ble_mesh_dfd_target_node_entry_t *entries; +}; + +/** + * @brief DFD distribution capabilities structure. + * + * This structure contains the capabilities of a DFD Server as + * returned by the DFD Capabilities Get message. + * + * It provides information about the server's capacity for handling + * firmware distribution operations, storage limits, and supported + * out-of-band retrieval methods. + */ +struct esp_ble_mesh_dfd_dist_caps { + /** Maximum number of receivers that can be stored in the Distribution Receivers List */ + uint16_t max_receiver_list_sz; + /** Maximum number of firmware images that can be stored in the Firmware Information List */ + uint16_t max_fw_list_sz; + /** Maximum size of a single firmware image that can be stored (in bytes) */ + uint32_t max_fw_sz; + /** Maximum upload space available for storing firmware images (in bytes) */ + uint32_t max_upload_space; + /** Remaining upload space available after current stored firmware (in bytes) */ + uint32_t remaining_upload_space; + /** Bit field indicating supported out-of-band retrieval methods (0 = none supported) */ + uint8_t oob_retrieval_supported; + /** Pointer to buffer containing supported URL scheme names (UTF-8, null-terminated) */ + struct net_buf_simple *supported_url_scheme_names; +}; + +/** + * @brief DFD Client distribution start parameters. + * + * This structure contains the parameters for the DFD Start message. + * It initiates the firmware distribution process to all target nodes + * in the Distribution Receivers List. + */ +struct esp_ble_mesh_dfd_cli_dist_start { + /** TTL value to be used for all messages sent during firmware distribution */ + uint8_t ttl; + /** Transfer mode: 0x00 = Push BLOB Transfer Mode, 0x01 = Pull BLOB Transfer Mode (2 bits) */ + uint8_t trans_mode : 2, + /** Update policy: 0x00 = Single Target Node, 0x01 = All Target Nodes (1 bit) */ + update_policy : 1, + /** Reserved for Future Use (5 bits) */ + RFU : 5; + /** Application Key Index to be used for securing firmware distribution messages */ + uint16_t app_idx; + /** Base time value used to calculate transfer timeout (in 100ms units) */ + uint16_t timeout_base; + /** Index of the firmware image in the Firmware Information List to distribute */ + uint16_t fw_idx; + /** Flag indicating whether the target address is a virtual address (true) or group address (false) */ + bool is_va; + /** Target address for firmware distribution (group address or virtual label UUID) */ + union { + /** Group address for firmware distribution (used when is_va is false) */ + uint16_t group_addr; + /** Virtual Label UUID for firmware distribution (used when is_va is true) */ + uint8_t label_uuid[16]; + }; +}; + +/** + * @brief DFD distribution status structure. + * + * This structure contains the response parameters for the DFD Get and + * DFD Start messages. It provides the current status and configuration + * of the firmware distribution operation. + */ +struct esp_ble_mesh_dfd_dist_status { + /** Status code for the distribution operation (see esp_ble_mesh_dfd_status) */ + uint8_t status; + /** Current firmware distribution phase (see esp_ble_mesh_dfd_phase) */ + uint8_t dist_phase; + /** Multicast address used for the firmware distribution (group address or virtual label) */ + uint16_t multicast_address; + /** Application Key Index being used for securing firmware distribution messages */ + uint16_t appkey_idx; + /** TTL value being used for firmware distribution messages */ + uint8_t ttl; + /** Base time value used to calculate transfer timeout (in 100ms units) */ + uint16_t timeout_base; + /** Transfer mode: 0x00 = Push BLOB Transfer Mode, 0x01 = Pull BLOB Transfer Mode (2 bits) */ + uint8_t trans_mode:2, + /** Update policy: 0x00 = Single Target Node, 0x01 = All Target Nodes (1 bit) */ + update_policy:1, + /** Reserved for Future Use (5 bits) */ + RFU:5; + /** Index of the firmware image currently being distributed */ + uint16_t firmware_image_index; +}; + +/** + * @brief DFD Client upload start parameters. + * + * This structure contains the parameters for the DFD Upload Start + * message. It initiates an in-band firmware + * upload to the DFD Server using BLOB Transfer. + * + * @note This is used for in-band firmware transfers where the firmware + * data is transferred through the mesh network. + */ +struct esp_ble_mesh_dfd_cli_dist_upload_start { + /** TTL value to be used for all messages during the upload */ + uint8_t ttl; + /** Base time value used to calculate upload timeout (in 100ms units) */ + uint16_t timeout_base; + /** Total size of the firmware image to be uploaded (in bytes) */ + uint32_t fw_size; + /** BLOB identifier that will be used for the firmware transfer */ + uint64_t blob_id; + /** Pointer to buffer containing the firmware ID of the image to upload */ + struct net_buf_simple *fwid; + /** Pointer to buffer containing the firmware metadata for the upload */ + struct net_buf_simple *fw_metadata; +}; + +/** + * @brief DFD Client out-of-band upload start parameters. + * + * This structure contains the parameters for the DFD Upload Start OOB + * message. It initiates an out-of-band firmware + * upload where the firmware is retrieved from an external source. + * + * @note This is used for out-of-band firmware transfers where the firmware + * is obtained from a URI or external source. + */ +struct esp_ble_mesh_dfd_cli_dist_upload_oob_start { + /** Pointer to buffer containing the current firmware ID to check against */ + struct net_buf_simple *fwid; + /** Pointer to buffer containing the URI for firmware retrieval (UTF-8 string) */ + struct net_buf_simple *url; +}; + +/** + * @brief DFD upload status structure. + * + * This structure contains the response parameters for the DFD Upload Get, + * DFD Upload Start, and DFD Upload Start OOB messages. + * + * It provides the current status and progress of an ongoing or completed + * firmware upload operation on the DFD Server. + */ +struct esp_ble_mesh_dfd_upload_status { + /** Status code for the upload operation (see esp_ble_mesh_dfd_status) */ + uint8_t status; + /** Current upload phase (see esp_ble_mesh_dfd_upload_phase) */ + uint8_t upload_phase; + /** Upload progress percentage (0-100%, only valid when progress != 101) */ + uint8_t upload_progress:7, + /** Upload type: 0x00 = In-band upload, 0x01 = Out-of-band upload */ + upload_type:1; + /** Union containing firmware ID information based on upload type */ + union { + /** Firmware ID for in-band upload operations */ + struct net_buf_simple *fwid; + /** Firmware ID for out-of-band upload operations */ + struct net_buf_simple *oob_fwid; + }; +}; + +/** + * @brief DFD Client firmware get parameters. + * + * This structure contains the parameters for the DFD Firmware Get + * message. It requests information about a specific + * firmware image identified by its firmware ID. + */ +struct esp_ble_mesh_dfd_cli_dist_fw_get { + /** Pointer to buffer containing the firmware ID to query */ + struct net_buf_simple *fwid; +}; + +/** + * @brief DFD Client firmware get by index parameters. + * + * This structure contains the parameters for the DFD Firmware Get By Index + * message. It requests information about a specific firmware image identified + * by its index in the Firmware Information List. + */ +struct esp_ble_mesh_dfd_cli_dist_fw_get_by_idx { + /** Index of the firmware image in the Firmware Information List */ + uint16_t dist_fw_idx; +}; + +/** + * @brief DFD Client firmware delete parameters. + * + * This structure contains the parameters for the DFD Firmware Delete + * message. It requests deletion of a specific firmware image from the + * DFD Server. + * + * @note Deleting a firmware image that is currently being distributed will + * cause the distribution to fail. + */ +struct esp_ble_mesh_dfd_cli_dist_fw_del { + /** Pointer to buffer containing the firmware ID of the image to delete */ + struct net_buf_simple *fwid; +}; + +/** + * @brief DFD firmware status structure. + * + * This structure contains the response parameters for the DFD Firmware Get + * and DFD Firmware Get By Index messages. + * + * It provides detailed information about a specific firmware image stored + * on the DFD Server. + */ +struct esp_ble_mesh_dfd_firmware_status { + /** Status code for the firmware query operation (see esp_ble_mesh_dfd_status) */ + uint8_t status; + /** Total number of entries in the Firmware Information List */ + uint16_t entry_cnt; + /** Index of the firmware image in the Firmware Information List */ + uint16_t fw_idx; + /** Pointer to buffer containing the firmware ID of the queried image */ + struct net_buf_simple *fwid; +}; + +/** + * @brief DFD Client action enumeration. + * + * This enumeration defines the different types of actions that can be performed + * by the DFD Client model. These actions are used internally to + * distinguish between different DFD message types when processing + * client requests. + */ +enum esp_ble_mesh_dfd_client_act { + /** DFD Client Get action - retrieving information from DFD Server */ + ESP_BLE_MESH_ACT_DFD_CLIENT_GET, + /** DFD Client Set action - sending commands/configuration to DFD Server */ + ESP_BLE_MESH_ACT_DFD_CLIENT_SET, + /** Maximum value for DFD Client action enumeration (bounds checking) */ + ESP_BLE_MESH_ACT_DFD_CLIENT_MAX, +}; + +/** + * @brief DFD Client callback event enumeration. + * + * This enumeration defines the different callback events that can be generated + * by the DFD Client model. These events notify the application + * about the status of DFD operations, message responses, timeouts, + * and completion of various procedures. + * + * Application should handle these events to properly manage firmware + * distribution operations and provide user feedback. + */ +enum esp_ble_mesh_dfd_client_cb_evt { + /** DFD Client timeout event - operation timed out waiting for response */ + ESP_BLE_MESH_EVT_DFD_CLIENT_TIMEOUT, + /** DFD Client receive response event - received a response from DFD Server */ + ESP_BLE_MESH_EVT_DFD_CLIENT_RECV_RSP, + /** DFD Client send complete event - message transmission completed */ + ESP_BLE_MESH_ACT_DFD_CLIEND_SEND_COMP, + /** Maximum value for DFD Client callback event enumeration (bounds checking) */ + ESP_BLE_MESH_EVT_DFD_CLIENT_MAX, +}; + +/** + * @brief DFD Client get parameters union. + * + * This union contains different parameter structures for various DFD Client + * Get messages. The specific structure used depends on the type of + * get operation being performed. + * + * This allows type-safe access to message-specific parameters while + * using a single parameter type in the API. + */ +typedef union { + /** Parameters for DFD Receivers Get operation */ + esp_ble_mesh_dfd_receivers_get_t receivers_get; + /** Parameters for DFD Firmware Get operation */ + esp_ble_mesh_dfd_cli_dist_fw_get_t dist_fw_get; + /** Parameters for DFD Firmware Get By Index operation */ + esp_ble_mesh_dfd_cli_dist_fw_get_by_idx_t dist_fw_get_by_idx; +} esp_ble_mesh_dfd_client_get_param_t; + +/** + * @brief DFD Client set parameters union. + * + * This union contains different parameter structures for various DFD Client + * Set messages. The specific structure used depends on the type of + * set operation being performed. + * + * This allows type-safe access to message-specific parameters while + * using a single parameter type in the API. + */ +typedef union { + /** Parameters for DFD Receivers Add operation */ + esp_ble_mesh_dfd_cli_receivers_add_t receivers_add; + /** Parameters for DFD Start operation */ + esp_ble_mesh_dfd_cli_dist_start_t dist_start; + /** Parameters for DFD Upload Start operation */ + esp_ble_mesh_dfd_cli_dist_upload_start_t dist_upload_start; + /** Parameters for DFD Upload Start OOB operation */ + esp_ble_mesh_dfd_cli_dist_upload_oob_start_t dist_upload_oob_start; + /** Parameters for DFD Firmware Get operation */ + esp_ble_mesh_dfd_cli_dist_fw_get_t dist_fw_get; + /** Parameters for DFD Firmware Delete operation */ + esp_ble_mesh_dfd_cli_dist_fw_del_t dist_fw_del; +} esp_ble_mesh_dfd_client_set_param_t; + +/** + * @brief DFD Client common callback parameters union. + * + * This union contains different response structures for various DFD Client + * received messages. The specific structure used depends on the type of + * message that triggered the callback. + * + * This allows type-safe access to message-specific response data + * while using a single callback parameter type. + */ +union esp_ble_mesh_dfd_client_common_cb_param { + /** Response parameters for DFD Receivers Add/Delete operations */ + esp_ble_mesh_dfd_receiver_status_t receiver_status; + /** Response parameters for DFD Receivers Get operation */ + esp_ble_mesh_dfd_receiver_list_t receiver_list; + /** Response parameters for DFD Capabilities Get operation */ + esp_ble_mesh_dfd_dist_caps_t dist_caps; + /** Response parameters for DFD Get/Start operations */ + esp_ble_mesh_dfd_dist_status_t dist_status; + /** Response parameters for DFD Upload Get/Start operations */ + esp_ble_mesh_dfd_upload_status_t upload_status; + /** Response parameters for DFD Firmware Get operations */ + esp_ble_mesh_dfd_firmware_status_t firmware_status; +}; + +/** + * @brief DFD Client callback parameters structure. + * + * This structure contains the parameters passed to the DFD Client + * callback function for various events. It provides information about + * the operation status, error codes, and event-specific data. + * + * Applications should examine the err_code field first to determine + * whether the operation was successful, then check the appropriate + * field in the status_cb union for operation-specific data. + */ +struct esp_ble_mesh_dfd_client_cb_param { + /** Error code indicating success (0) or failure (negative) of the operation */ + int err_code; + /** Common client parameters including source address, destination, etc. */ + esp_ble_mesh_client_common_param_t *params; + /** Union containing event-specific response data */ + esp_ble_mesh_dfd_client_common_cb_param_t status_cb; +}; + +/** + * @brief Register DFD Client callback function. + * + * This function registers a callback function that will be called when + * DFD Client events occur, such as message timeouts, responses, + * or operation completions. + * + * @param callback Pointer to the callback function to register. + * + * @return ESP_OK on success, or error code otherwise. + * + * @note Only one callback can be registered at a time. Calling this + * function with a NULL callback will deregister the current callback. + */ +esp_err_t esp_ble_mesh_register_dfd_cli_callback(esp_ble_mesh_dfd_client_cb_t callback); + +/** + * @brief Send DFD Client Get message. + * + * This function sends a DFD Get message to retrieve information + * from the DFD Server. The specific type of information requested + * depends on the get_param structure provided. + * + * @param params Common client parameters including destination address, TTL, etc. + * @param get_param Union containing the specific get message parameters. + * + * @return ESP_OK on success, or error code otherwise. + * + * @note The response will be delivered through the registered callback + * function with the ESP_BLE_MESH_EVT_DFD_CLIENT_RECV_RSP event. + */ +esp_err_t esp_ble_mesh_dfd_cli_get(esp_ble_mesh_client_common_param_t *params, + esp_ble_mesh_dfd_client_get_param_t *get_param); + +/** + * @brief Send DFD Client Set message. + * + * This function sends a DFD Set message to perform various operations + * on the DFD Server, such as starting firmware distribution, + * uploading firmware, or managing the receiver list. + * + * @param params Common client parameters including destination address, TTL, etc. + * @param set_param Union containing the specific set message parameters. + * + * @return ESP_OK on success, or error code otherwise. + * + * @note The response will be delivered through the registered callback + * function with the ESP_BLE_MESH_EVT_DFD_CLIENT_RECV_RSP event. + */ +esp_err_t esp_ble_mesh_dfd_cli_set(esp_ble_mesh_client_common_param_t *params, + esp_ble_mesh_dfd_client_set_param_t *set_param); + +#endif /* CONFIG_BLE_MESH_DFD_CLI */ + +#if CONFIG_BLE_MESH_DFD_SRV +/** + * @brief Firmware Distribution status enumeration. + * + * This enumeration defines the status codes used in DFD operations. + * These status codes indicate success, failure, and error conditions + * for various firmware distribution operations. + */ +enum esp_ble_mesh_dfd_status { + /** The message was processed successfully. */ + ESP_BLE_MESH_DFD_SUCCESS, + + /** Insufficient resources on the node. */ + ESP_BLE_MESH_DFD_ERR_INSUFFICIENT_RESOURCES, + + /** The operation cannot be performed while the Server is in the current phase. */ + ESP_BLE_MESH_DFD_ERR_WRONG_PHASE, + + /** An internal error occurred on the node. */ + ESP_BLE_MESH_DFD_ERR_INTERNAL, + + /** The requested firmware image is not stored on the Distributor. */ + ESP_BLE_MESH_DFD_ERR_FW_NOT_FOUND, + + /** The AppKey identified by the AppKey Index is not known to the node. */ + ESP_BLE_MESH_DFD_ERR_INVALID_APPKEY_INDEX, + + /** There are no Target nodes in the Distribution Receivers List state. */ + ESP_BLE_MESH_DFD_ERR_RECEIVERS_LIST_EMPTY, + + /** Another firmware image distribution is in progress. */ + ESP_BLE_MESH_DFD_ERR_BUSY_WITH_DISTRIBUTION, + + /** Another upload is in progress. */ + ESP_BLE_MESH_DFD_ERR_BUSY_WITH_UPLOAD, + + /** The URI scheme name indicated by the Update URI is not supported. */ + ESP_BLE_MESH_DFD_ERR_URI_NOT_SUPPORTED, + + /** The format of the Update URI is invalid. */ + ESP_BLE_MESH_DFD_ERR_URI_MALFORMED, + + /** The URI is currently unreachable. */ + ESP_BLE_MESH_DFD_ERR_URI_UNREACHABLE, + + /** The Check Firmware OOB procedure did not find any new firmware. */ + ESP_BLE_MESH_DFD_ERR_NEW_FW_NOT_AVAILABLE, + + /** The suspension of the Distribute Firmware procedure failed. */ + ESP_BLE_MESH_DFD_ERR_SUSPEND_FAILED, +}; + +/** + * @brief Firmware distribution phases enumeration. + * + * This enumeration defines the different phases of a firmware distribution + * operation. The phases track the state of firmware distribution from + * idle through transfer, verification, application, and completion states. + */ +enum esp_ble_mesh_dfd_phase { + /** No firmware distribution is in progress. */ + ESP_BLE_MESH_DFD_PHASE_IDLE, + + /** Firmware distribution is in progress. */ + ESP_BLE_MESH_DFD_PHASE_TRANSFER_ACTIVE, + + /** The Transfer BLOB procedure has completed successfully. */ + ESP_BLE_MESH_DFD_PHASE_TRANSFER_SUCCESS, + + /** The Apply Firmware on Target Nodes procedure is being executed. */ + ESP_BLE_MESH_DFD_PHASE_APPLYING_UPDATE, + + /** The Distribute Firmware procedure has completed successfully. */ + ESP_BLE_MESH_DFD_PHASE_COMPLETED, + + /** The Distribute Firmware procedure has failed. */ + ESP_BLE_MESH_DFD_PHASE_FAILED, + + /** The Cancel Firmware Update procedure is being executed. */ + ESP_BLE_MESH_DFD_PHASE_CANCELING_UPDATE, + + /** The Transfer BLOB procedure is suspended. */ + ESP_BLE_MESH_DFD_PHASE_TRANSFER_SUSPENDED, +}; + +/** + * @brief Firmware upload phases enumeration. + * + * This enumeration defines the phases of firmware upload operations + * on the DFD Server. It tracks the state of firmware storage procedures + * including both in-band and out-of-band upload methods. + */ +enum esp_ble_mesh_dfd_upload_phase { + /** No firmware upload is in progress. */ + ESP_BLE_MESH_DFD_UPLOAD_PHASE_IDLE, + + /** The Store Firmware procedure is being executed. */ + ESP_BLE_MESH_DFD_UPLOAD_PHASE_TRANSFER_ACTIVE, + + /** The Store Firmware procedure or Store Firmware OOB procedure failed. */ + ESP_BLE_MESH_DFD_UPLOAD_PHASE_TRANSFER_ERROR, + + /** The Store Firmware procedure or the Store Firmware OOB procedure completed successfully. */ + ESP_BLE_MESH_DFD_UPLOAD_PHASE_TRANSFER_SUCCESS, +}; + +#if 0 +/** + * @brief Flash partition + * + * This structure represents a fixed-size partition on a flash device. + * Each partition contains one or more flash sectors. + */ +struct flash_area { + /** ID number */ + uint8_t fa_id; + uint16_t pad16; + /** Start offset from the beginning of the flash device */ + off_t fa_off; + /** Total size */ + size_t fa_size; + /** Backing flash device */ + const struct device *fa_dev; +#if CONFIG_FLASH_MAP_LABELS + /** Partition label if defined in DTS. Otherwise nullptr; */ + const char *fa_label; +#endif +}; +#endif + +/** Firmware Distribution Server callbacks: */ +struct esp_ble_mesh_dfd_srv_cb { + + /** @brief Slot receive callback. + * + * Called at the start of an upload procedure. The callback must fill + * @c io with a pointer to a writable BLOB stream for the Firmware Distribution + * Server to write the firmware image to. + * + * @param srv Firmware Distribution Server model instance. + * @param slot DFU image slot being received. + * @param io BLOB stream response pointer. + * + * @return 0 on success, or (negative) error code otherwise. + */ + int (*recv)(esp_ble_mesh_dfd_srv_t *srv, + const esp_ble_mesh_dfu_slot_t *slot, + const esp_ble_mesh_blob_io_t **io); + +#ifdef CONFIG_BLE_MESH_DFD_SRV_OOB_UPLOAD + /** @brief Firmware upload OOB start callback. + * + * Called at the start of an OOB firmware upload. The application must + * start a firmware check using an OOB mechanism, and then call + * bt_mesh_dfd_srv_oob_check_complete. Depending on the return + * value of this function, the application must then start storing the + * firmware image using an OOB mechanism, and call + * bt_mesh_dfd_srv_oob_store_complete. This callback is mandatory + * to support OOB uploads. + * + * @param srv Firmware Distribution Server model instance. + * @param slot Slot to be used for the upload. + * @param uri Pointer to buffer containing the URI used to + * check for new firmware. + * @param uri_len Length of the URI buffer. + * @param fwid Pointer to buffer containing the current + * firmware ID to be used when checking for + * availability of new firmware. + * @param fwid_len Length of the current firmware ID. Must be set + * to the length of the new firmware ID if it is + * available, or to 0 if new firmware is not + * available. + * + * @return ESP_BLE_MESH_DFD_SUCCESS on success, or error code otherwise. + */ + int (*start_oob_upload)(esp_ble_mesh_dfd_srv_t *srv, + const esp_ble_mesh_dfu_slot_t *slot, + const uint8_t *uri, uint8_t uri_len, + const uint8_t *fwid, uint16_t fwid_len); + + /** @brief Cancel store OOB callback + * + * Called when an OOB store is cancelled. The application must stop + * any ongoing OOB image transfer. This callback is mandatory to + * support OOB uploads. + * + * @param srv Firmware Distribution Server model instance. + * @param slot DFU image slot to cancel + */ + void (*cancel_oob_upload)(esp_ble_mesh_dfd_srv_t *srv, + const esp_ble_mesh_dfu_slot_t *slot); + + /** @brief Get the progress of an ongoing OOB store + * + * Called by the Firmware Distribution Server model when it needs to + * get the current progress of an ongoing OOB store from the + * application. This callback is mandatory to support OOB uploads. + * + * @param srv Firmware Distribution Server model instance. + * @param slot DFU image slot to get progress for. + * + * @return The current progress of the ongoing OOB store, in percent. + */ + uint8_t (*oob_progress_get)(esp_ble_mesh_dfd_srv_t *srv, + const esp_ble_mesh_dfu_slot_t *slot); +#endif /* CONFIG_BLE_MESH_DFD_SRV_OOB_UPLOAD */ + + /** @brief Slot delete callback. + * + * Called when the Firmware Distribution Server is about to delete a DFU image slot. + * All allocated data associated with the firmware slot should be + * deleted. + * + * @param srv Firmware Update Server instance. + * @param slot DFU image slot being deleted. + */ + void (*del)(esp_ble_mesh_dfd_srv_t *srv, + const esp_ble_mesh_dfu_slot_t *slot); + + /** @brief Slot send callback. + * + * Called at the start of a distribution procedure. The callback must + * fill @c io with a pointer to a readable BLOB stream for the Firmware + * Distribution Server to read the firmware image from. + * + * @param srv Firmware Distribution Server model instance. + * @param slot DFU image slot being sent. + * @param io BLOB stream response pointer. + * + * @return 0 on success, or (negative) error code otherwise. + */ + int (*send)(esp_ble_mesh_dfd_srv_t *srv, + const esp_ble_mesh_dfu_slot_t *slot, + const esp_ble_mesh_blob_io_t **io); + + /** @brief Phase change callback (Optional). + * + * Called whenever the phase of the Firmware Distribution Server changes. + * + * @param srv Firmware Distribution Server model instance. + * @param phase New Firmware Distribution phase. + */ + void (*phase)(esp_ble_mesh_dfd_srv_t *srv, esp_ble_mesh_dfd_phase_t phase); +}; + +/** Firmware Distribution Server instance. */ +struct esp_ble_mesh_dfd_srv { + const esp_ble_mesh_dfd_srv_cb_t *cb; + esp_ble_mesh_model_t *mod; + esp_ble_mesh_dfu_cli_t dfu; + esp_ble_mesh_dfu_target_t targets[CONFIG_BLE_MESH_DFD_SRV_TARGETS_MAX]; + esp_ble_mesh_blob_target_pull_t pull_ctxs[CONFIG_BLE_MESH_DFD_SRV_TARGETS_MAX]; + const esp_ble_mesh_blob_io_t *io; + uint16_t target_cnt; + uint16_t slot_idx; + bool apply; + esp_ble_mesh_dfd_phase_t phase; + esp_ble_mesh_blob_cli_inputs_t inputs; + + struct { + esp_ble_mesh_dfd_upload_phase_t phase; + esp_ble_mesh_dfu_slot_t *slot; +#if 0 + const struct flash_area *area; +#endif + esp_ble_mesh_blob_srv_t blob; +#ifdef CONFIG_BLE_MESH_DFD_SRV_OOB_UPLOAD + bool is_oob; + bool is_pending_oob_check; + struct { + uint8_t uri_len; + uint8_t uri[CONFIG_BLE_MESH_DFU_URI_MAXLEN]; + uint16_t current_fwid_len; + uint8_t current_fwid[CONFIG_BLE_MESH_DFU_FWID_MAXLEN]; + esp_ble_mesh_msg_ctx_t ctx; + } oob; +#endif /* CONFIG_BLE_MESH_DFD_SRV_OOB_UPLOAD */ + } upload; + +#ifdef CONFIG_BLE_MESH_DFD_SRV_OOB_UPLOAD + struct { + const uint8_t *schemes; + const uint8_t count; + } oob_schemes; +#endif /* CONFIG_BLE_MESH_DFD_SRV_OOB_UPLOAD */ +}; +#endif /* CONFIG_BLE_MESH_DFD_SRV */ + #ifdef __cplusplus } #endif 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 91f0ab9d83..d60df283f1 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 @@ -12,6 +12,9 @@ #include "esp_ble_mesh_dfu_model_api.h" #include "mesh_v1.1/dfu/dfu_cli.h" #include "mesh_v1.1/dfu/dfu_srv.h" +#include "mesh_v1.1/dfu/dfd.h" +#include "mesh_v1.1/dfu/dfd_cli.h" +#include "mesh_v1.1/dfu/dfd_srv.h" #if CONFIG_BLE_MESH_DFU_CLI /* Device Firmware Update Client model related functions */ @@ -59,15 +62,20 @@ void btc_ble_mesh_dfu_client_arg_deep_copy(btc_msg_t *msg, void *p_dest, void *p src->dfu_get.get->dfu_metadata_check.metadata->data, src->dfu_get.get->dfu_metadata_check.metadata->len); } else { + bt_mesh_free(dst->dfu_get.get); + bt_mesh_free(dst->dfu_get.params); BT_ERR("Out of memory for metadata"); return; } } else { + bt_mesh_free(dst->dfu_get.get); + bt_mesh_free(dst->dfu_get.params); BT_ERR("Metadata should be exists"); return; } } } else { + bt_mesh_free(dst->dfu_get.params); BT_ERR("%s, Out of memory, act %d", __func__, msg->act); } } @@ -92,12 +100,21 @@ void btc_ble_mesh_dfu_client_arg_deep_free(btc_msg_t *msg) switch (msg->act) { case BTC_BLE_MESH_ACT_DFU_CLIENT_GET_STATE: + if (arg->dfu_get.get) { + switch (arg->dfu_get.params->opcode) { + case ESP_BLE_MESH_DFU_OP_UPDATE_METADATA_CHECK: + if (arg->dfu_get.get->dfu_metadata_check.metadata) { + bt_mesh_free_buf(arg->dfu_get.get->dfu_metadata_check.metadata); + } + break; + default: + break; + } + bt_mesh_free(arg->dfu_get.get); + } if (arg->dfu_get.params) { bt_mesh_free(arg->dfu_get.params); } - if (arg->dfu_get.get) { - bt_mesh_free(arg->dfu_get.get); - } break; default: break; @@ -134,12 +151,15 @@ static void btc_ble_mesh_dfu_client_copy_req_data(btc_msg_t *msg, void *p_dest, length = p_src_data->recv.dfu_update_info_status.fw_info_list_cnt * sizeof(esp_ble_mesh_firmware_info_t); p_dest_data->recv.dfu_update_info_status.fw_info_list = bt_mesh_calloc(length); if (!p_dest_data->recv.dfu_update_info_status.fw_info_list) { + bt_mesh_free(p_dest_data->params); BT_ERR("%s, Out of memory, act %d", __func__, msg->act); return; } memcpy(p_dest_data->recv.dfu_update_info_status.fw_info_list, p_src_data->recv.dfu_update_info_status.fw_info_list, length); + } else { + p_dest_data->recv.dfu_update_info_status.fw_info_list = NULL; } break; default: @@ -165,7 +185,7 @@ static void btc_ble_mesh_dfu_client_free_req_data(btc_msg_t *msg) } if (msg->act >= ESP_BLE_MESH_DFU_CLIENT_EVT_MAX) { - BT_ERR("%s, Invalid event %d", msg->act); + BT_ERR("%s, Invalid event %d", __func, msg->act); return; } @@ -412,3 +432,785 @@ uint8_t btc_ble_mesh_dfu_srv_progress(const esp_ble_mesh_dfu_srv_t *srv) return bt_mesh_dfu_srv_progress((struct bt_mesh_dfu_srv *)srv); } #endif /* CONFIG_BLE_MESH_DFU_SRV */ + +#if CONFIG_BLE_MESH_DFD_CLI +static inline void btc_ble_mesh_dfd_client_cb_to_app(btc_ble_mesh_dfd_client_cb_evt_t event, + esp_ble_mesh_dfd_client_cb_param_t *param) +{ + esp_ble_mesh_dfd_client_cb_t btc_ble_mesh_cb = + (esp_ble_mesh_dfd_client_cb_t)btc_profile_cb_get(BTC_PID_DFD_CLIENT); + if (btc_ble_mesh_cb) { + btc_ble_mesh_cb(event, param); + } +} + +static inline bool dfd_client_param_need(uint32_t opcode) +{ + switch (opcode) { + case ESP_BLE_MESH_DFD_OP_RECEIVERS_GET: + case ESP_BLE_MESH_DFD_OP_FW_GET: + case ESP_BLE_MESH_DFD_OP_FW_GET_BY_INDEX: + case ESP_BLE_MESH_DFD_OP_RECEIVERS_ADD: + case ESP_BLE_MESH_DFD_OP_START: + case ESP_BLE_MESH_DFD_OP_UPLOAD_START: + case ESP_BLE_MESH_DFD_OP_UPLOAD_START_OOB: + case ESP_BLE_MESH_DFD_OP_FW_DELETE: + return true; + default: + break; + } + return false; +} + +static int btc_ble_mesh_dfd_client_get(esp_ble_mesh_client_common_param_t *params, + esp_ble_mesh_dfd_client_get_param_t *get) +{ + bt_mesh_client_common_param_t param = {0}; + + if (params == NULL) { + BT_ERR("%s:InvParam", __func__); + return -EINVAL; + } + + if (dfd_client_param_need(params->opcode) != (!!get)) { + BT_ERR("%s:InvParam", __func__); + return -EINVAL; + } + + switch(params->opcode) { + case ESP_BLE_MESH_DFD_OP_RECEIVERS_GET: + case ESP_BLE_MESH_DFD_OP_FW_GET: + case ESP_BLE_MESH_DFD_OP_FW_GET_BY_INDEX: + if (get == NULL) { + BT_ERR("%s:InvParam", __func__); + return -EINVAL; + } + break; + default: + break; + } + + btc_ble_mesh_set_client_common_param(params, ¶m, false); + + switch (params->opcode) { + case ESP_BLE_MESH_DFD_OP_RECEIVERS_GET: + return bt_mesh_dfd_cli_receivers_get(¶m, get->receivers_get.first_index, + get->receivers_get.entries_limit); + case ESP_BLE_MESH_DFD_OP_CAPABILITIES_GET: + return bt_mesh_dfd_cli_distribution_capabilities_get(¶m); + case ESP_BLE_MESH_DFD_OP_GET: + return bt_mesh_dfd_cli_distribution_get(¶m); + case ESP_BLE_MESH_DFD_OP_UPLOAD_GET: + return bt_mesh_dfd_cli_distribution_upload_get(¶m); + case ESP_BLE_MESH_DFD_OP_FW_GET: + return bt_mesh_dfd_cli_firmware_get(¶m, get->dist_fw_get.fwid); + case ESP_BLE_MESH_DFD_OP_FW_GET_BY_INDEX: + return bt_mesh_dfd_cli_firmware_get_by_index(¶m, get->dist_fw_get_by_idx.dist_fw_idx); + default: + BT_ERR("UknOpc:%04x", params->opcode); + return -EINVAL; + } +} + +static int btc_ble_mesh_dfd_client_set(esp_ble_mesh_client_common_param_t *params, + esp_ble_mesh_dfd_client_set_param_t *set) +{ + bt_mesh_client_common_param_t param = {0}; + + if (params == NULL) { + BT_ERR("%s:InvParam", __func__); + return -EINVAL; + } + + if (dfd_client_param_need(params->opcode) != (!!set)) { + BT_ERR("%s:InvParam", __func__); + return -EINVAL; + } + + btc_ble_mesh_set_client_common_param(params, ¶m, false); + + switch (params->opcode) { + case ESP_BLE_MESH_DFD_OP_RECEIVERS_ADD: + return bt_mesh_dfd_cli_receivers_add(¶m, (dfd_cli_receiver_entry_t *)set->receivers_add.receivers, set->receivers_add.receivers_cnt); + case ESP_BLE_MESH_DFD_OP_RECEIVERS_DELETE_ALL: + return bt_mesh_dfd_cli_receivers_delete_all(¶m); + case ESP_BLE_MESH_DFD_OP_START: + return bt_mesh_dfd_cli_distribution_start(¶m, (dfd_cli_dist_start_t *)&set->dist_start); + case ESP_BLE_MESH_DFD_OP_SUSPEND: + return bt_mesh_dfd_cli_distribution_suspend(¶m); + case ESP_BLE_MESH_DFD_OP_CANCEL: + return bt_mesh_dfd_cli_distribution_cancel(¶m); + case ESP_BLE_MESH_DFD_OP_APPLY: + return bt_mesh_dfd_cli_distribution_apply(¶m); + case ESP_BLE_MESH_DFD_OP_UPLOAD_START: + return bt_mesh_dfd_cli_distribution_upload_start(¶m, (dfd_cli_dist_upload_start_t *)&set->dist_upload_start); + case ESP_BLE_MESH_DFD_OP_UPLOAD_START_OOB: + return bt_mesh_dfd_cli_distribution_upload_oob_start(¶m, (dfd_cli_dist_upload_oob_start_t *)&set->dist_upload_oob_start); + case ESP_BLE_MESH_DFD_OP_UPLOAD_CANCEL: + return bt_mesh_dfd_cli_distribution_upload_oob_cancel(¶m); + case ESP_BLE_MESH_DFD_OP_FW_DELETE: + return bt_mesh_dfd_cli_firmware_get_delete(¶m, set->dist_fw_del.fwid); + case ESP_BLE_MESH_DFD_OP_FW_DELETE_ALL: + return bt_mesh_dfd_cli_firmware_delete_all(¶m); + default: + BT_ERR("UknOpc:%04x", params->opcode); + return -EINVAL; + } +} + +void btc_ble_mesh_dfd_client_cb_handler(btc_msg_t *msg) +{ + esp_ble_mesh_dfd_client_cb_param_t *arg = NULL; + + if (!msg) { + BT_ERR("%s, Invalid parameter", __func__); + return; + } + + arg = (esp_ble_mesh_dfd_client_cb_param_t *)msg->arg; + + if (msg->act < ESP_BLE_MESH_EVT_DFD_CLIENT_MAX) { + btc_ble_mesh_dfd_client_cb_to_app(msg->act, arg); + } else { + BT_ERR("%s, Unknown act %d", __func__, msg->act); + } + + btc_ble_mesh_dfd_client_rsp_deep_free(msg); +} + +void btc_ble_mesh_dfd_client_arg_deep_copy(btc_msg_t *msg, void *p_dest, void *p_src) +{ + btc_ble_mesh_dfd_client_args_t *dst = (btc_ble_mesh_dfd_client_args_t *)p_dest; + btc_ble_mesh_dfd_client_args_t *src = (btc_ble_mesh_dfd_client_args_t *)p_src; + + if (!msg || !dst || !src) { + BT_ERR("%s,InvParam", __func__); + return; + } + + switch (msg->act) { + case BTC_BLE_MESH_ACT_DFD_CLIENT_GET: + dst->dfd_client_get.params = + (esp_ble_mesh_client_common_param_t *)bt_mesh_calloc(sizeof(esp_ble_mesh_client_common_param_t)); + if (dst->dfd_client_get.params == NULL) { + BT_ERR("%s:%d,OutMem", __func__, __LINE__); + break; + } + memcpy(dst->dfd_client_get.params, src->dfd_client_get.params, sizeof(esp_ble_mesh_client_common_param_t)); + + if (src->dfd_client_get.get) { + dst->dfd_client_get.get = (esp_ble_mesh_dfd_client_get_param_t *)bt_mesh_calloc(sizeof(esp_ble_mesh_dfd_client_get_param_t)); + if (dst->dfd_client_get.get == NULL) { + bt_mesh_free(dst->dfd_client_get.params); + dst->dfd_client_get.params = NULL; + BT_ERR("%s:%d,OutMem", __func__, __LINE__); + break; + } + memcpy(dst->dfd_client_get.get, src->dfd_client_get.get, sizeof(esp_ble_mesh_dfd_client_get_param_t)); + } + + switch (dst->dfd_client_get.params->opcode) { + case ESP_BLE_MESH_DFD_OP_FW_GET: + if (src->dfd_client_get.get->dist_fw_get.fwid == NULL) { + BT_ERR("%s:%d,InvParam", __func__, __LINE__); + bt_mesh_free(dst->dfd_client_get.params); + dst->dfd_client_get.params = NULL; + bt_mesh_free(dst->dfd_client_get.get); + dst->dfd_client_get.get = NULL; + break; + } + + dst->dfd_client_get.get->dist_fw_get.fwid = + bt_mesh_alloc_buf(src->dfd_client_get.get->dist_fw_get.fwid->len); + if (dst->dfd_client_get.get->dist_fw_get.fwid == NULL) { + bt_mesh_free(dst->dfd_client_get.params); + dst->dfd_client_get.params = NULL; + bt_mesh_free(dst->dfd_client_get.get); + dst->dfd_client_get.get = NULL; + BT_ERR("%s:%d,OutMem", __func__, __LINE__); + break; + } + net_buf_simple_add_mem( + dst->dfd_client_get.get->dist_fw_get.fwid, + src->dfd_client_get.get->dist_fw_get.fwid->data, + src->dfd_client_get.get->dist_fw_get.fwid->len); + break; + default: + break; + } + break; + case BTC_BLE_MESH_ACT_DFD_CLIENT_SET: + dst->dfd_client_set.params = + (esp_ble_mesh_client_common_param_t *)bt_mesh_calloc(sizeof(esp_ble_mesh_client_common_param_t)); + if (dst->dfd_client_set.params == NULL) { + BT_ERR("%s:%d,OutMem", __func__, __LINE__); + break; + } + memcpy(dst->dfd_client_set.params, src->dfd_client_set.params, sizeof(esp_ble_mesh_client_common_param_t)); + + if (src->dfd_client_set.set) { + dst->dfd_client_set.set = (esp_ble_mesh_dfd_client_set_param_t *)bt_mesh_calloc(sizeof(esp_ble_mesh_dfd_client_set_param_t)); + if (dst->dfd_client_set.set == NULL) { + bt_mesh_free(dst->dfd_client_set.params); + dst->dfd_client_set.params = NULL; + BT_ERR("%s:%d,OutMem", __func__, __LINE__); + break; + } + memcpy(dst->dfd_client_set.set, src->dfd_client_set.set, sizeof(esp_ble_mesh_dfd_client_set_param_t)); + } + switch (dst->dfd_client_set.params->opcode) { + case ESP_BLE_MESH_DFD_OP_RECEIVERS_ADD: + if (src->dfd_client_set.set->receivers_add.receivers_cnt == 0) { + dst->dfd_client_set.set->receivers_add.receivers = NULL; + bt_mesh_free(dst->dfd_client_set.params); + dst->dfd_client_set.params = NULL; + bt_mesh_free(dst->dfd_client_set.set); + dst->dfd_client_set.set = NULL; + BT_ERR("%s:%d,InvParam", __func__, __LINE__); + break; + } + dst->dfd_client_set.set->receivers_add.receivers = + (esp_ble_mesh_dfd_cli_receiver_entry_t *)bt_mesh_calloc(dst->dfd_client_set.set->receivers_add.receivers_cnt * + sizeof(esp_ble_mesh_dfd_cli_receiver_entry_t)); + if (dst->dfd_client_set.set->receivers_add.receivers == NULL) { + bt_mesh_free(dst->dfd_client_set.params); + dst->dfd_client_set.params = NULL; + bt_mesh_free(dst->dfd_client_set.set); + dst->dfd_client_set.set = NULL; + BT_ERR("%s:%d,OutMem", __func__, __LINE__); + break; + } + memcpy(dst->dfd_client_set.set->receivers_add.receivers, src->dfd_client_set.set->receivers_add.receivers, + dst->dfd_client_set.set->receivers_add.receivers_cnt * sizeof(esp_ble_mesh_dfd_cli_receiver_entry_t)); + break; + case ESP_BLE_MESH_DFD_OP_UPLOAD_START: + if (src->dfd_client_set.set->dist_upload_start.fwid == NULL) { + dst->dfd_client_set.set->dist_upload_start.fwid = NULL; + bt_mesh_free(dst->dfd_client_set.params); + dst->dfd_client_set.params = NULL; + bt_mesh_free(dst->dfd_client_set.set); + dst->dfd_client_set.set = NULL; + BT_ERR("%s:%d,InvParam", __func__, __LINE__); + break; + } + + dst->dfd_client_set.set->dist_upload_start.fwid = + bt_mesh_alloc_buf(src->dfd_client_set.set->dist_upload_start.fwid->len); + if (dst->dfd_client_set.set->dist_upload_start.fwid == NULL) { + bt_mesh_free(dst->dfd_client_set.params); + dst->dfd_client_set.params = NULL; + bt_mesh_free(dst->dfd_client_set.set); + dst->dfd_client_set.set = NULL; + BT_ERR("%s:%d,OutMem", __func__, __LINE__); + break; + } + net_buf_simple_add_mem( + dst->dfd_client_set.set->dist_upload_start.fwid, + src->dfd_client_set.set->dist_upload_start.fwid->data, + src->dfd_client_set.set->dist_upload_start.fwid->len); + + if (src->dfd_client_set.set->dist_upload_start.fw_metadata->len == 0) { + dst->dfd_client_set.set->dist_upload_start.fw_metadata = NULL; + break; + } else { + dst->dfd_client_set.set->dist_upload_start.fw_metadata = + bt_mesh_alloc_buf(src->dfd_client_set.set->dist_upload_start.fw_metadata->len); + if (dst->dfd_client_set.set->dist_upload_start.fw_metadata == NULL) { + bt_mesh_free(dst->dfd_client_set.params); + dst->dfd_client_set.params = NULL; + bt_mesh_free(dst->dfd_client_set.set); + dst->dfd_client_set.set = NULL; + bt_mesh_free_buf(dst->dfd_client_set.set->dist_upload_start.fwid); + dst->dfd_client_set.set->dist_upload_start.fwid = NULL; + BT_ERR("%s:%d,OutMem", __func__, __LINE__); + break; + } + net_buf_simple_add_mem( + dst->dfd_client_set.set->dist_upload_start.fw_metadata, + src->dfd_client_set.set->dist_upload_start.fw_metadata->data, + src->dfd_client_set.set->dist_upload_start.fw_metadata->len); + } + break; + case ESP_BLE_MESH_DFD_OP_UPLOAD_START_OOB: + if (src->dfd_client_set.set->dist_upload_oob_start.url == NULL || + src->dfd_client_set.set->dist_upload_oob_start.fwid == NULL) { + bt_mesh_free(dst->dfd_client_set.params); + dst->dfd_client_set.params = NULL; + bt_mesh_free(dst->dfd_client_set.set); + dst->dfd_client_set.set = NULL; + BT_ERR("%s:%d,InvParam", __func__, __LINE__); + break; + } + dst->dfd_client_set.set->dist_upload_oob_start.url = + bt_mesh_alloc_buf(src->dfd_client_set.set->dist_upload_oob_start.url->len); + if (dst->dfd_client_set.set->dist_upload_oob_start.url == NULL) { + bt_mesh_free(dst->dfd_client_set.params); + dst->dfd_client_set.params = NULL; + bt_mesh_free(dst->dfd_client_set.set); + dst->dfd_client_set.set = NULL; + BT_ERR("%s:%d,OutMem", __func__, __LINE__); + break; + } + dst->dfd_client_set.set->dist_upload_oob_start.fwid = + bt_mesh_alloc_buf(src->dfd_client_set.set->dist_upload_oob_start.fwid->len); + if (dst->dfd_client_set.set->dist_upload_oob_start.fwid == NULL) { + bt_mesh_free(dst->dfd_client_set.params); + dst->dfd_client_set.params = NULL; + bt_mesh_free(dst->dfd_client_set.set); + dst->dfd_client_set.set = NULL; + bt_mesh_free_buf(dst->dfd_client_set.set->dist_upload_oob_start.url); + dst->dfd_client_set.set->dist_upload_oob_start.url = NULL; + BT_ERR("%s:%d,OutMem", __func__, __LINE__); + break; + } + net_buf_simple_add_mem( + dst->dfd_client_set.set->dist_upload_oob_start.url, + src->dfd_client_set.set->dist_upload_oob_start.url->data, + src->dfd_client_set.set->dist_upload_oob_start.url->len); + net_buf_simple_add_mem( + dst->dfd_client_set.set->dist_upload_oob_start.fwid, + src->dfd_client_set.set->dist_upload_oob_start.fwid->data, + src->dfd_client_set.set->dist_upload_oob_start.fwid->len); + break; + case ESP_BLE_MESH_DFD_OP_FW_DELETE: + if (src->dfd_client_set.set->dist_fw_del.fwid == NULL) { + bt_mesh_free(dst->dfd_client_set.params); + dst->dfd_client_set.params = NULL; + bt_mesh_free(dst->dfd_client_set.set); + dst->dfd_client_set.set = NULL; + BT_ERR("%s:%d,InvParam", __func__, __LINE__); + break; + } + dst->dfd_client_set.set->dist_fw_del.fwid = + bt_mesh_alloc_buf(src->dfd_client_set.set->dist_fw_del.fwid->len); + if (dst->dfd_client_set.set->dist_fw_del.fwid == NULL) { + bt_mesh_free(dst->dfd_client_set.params); + dst->dfd_client_set.params = NULL; + bt_mesh_free(dst->dfd_client_set.set); + dst->dfd_client_set.set = NULL; + BT_ERR("%s:%d,OutMem", __func__, __LINE__); + break; + } + net_buf_simple_add_mem( + dst->dfd_client_set.set->dist_fw_del.fwid, + src->dfd_client_set.set->dist_fw_del.fwid->data, + src->dfd_client_set.set->dist_fw_del.fwid->len); + break; + default: + break; + } + default: + BT_DBG("%s, Unknown act %d", __func__, msg->act); + break; + } +} + +void btc_ble_mesh_dfd_client_arg_deep_free(btc_msg_t *msg) +{ + btc_ble_mesh_dfd_client_args_t *arg = NULL; + + if (!msg) { + BT_ERR("%s, Invalid parameter", __func__); + return; + } + + if (msg->act >= ESP_BLE_MESH_ACT_DFD_CLIENT_MAX) { + BT_ERR("%s, Invalid event %d", __func__, msg->act); + return; + } + + arg = (btc_ble_mesh_dfd_client_args_t *)msg->arg; + + switch (msg->act) { + case ESP_BLE_MESH_ACT_DFD_CLIENT_GET: + if (arg->dfd_client_get.params == NULL) { + BT_ERR("%s:%d,InvParam", __func__, __LINE__); + break; + } + switch (arg->dfd_client_get.params->opcode) { + case ESP_BLE_MESH_DFD_OP_FW_GET: + if (arg->dfd_client_get.get->dist_fw_get.fwid == NULL) { + BT_ERR("%s:%d,InvParam", __func__, __LINE__); + break; + } + bt_mesh_free_buf(arg->dfd_client_get.get->dist_fw_get.fwid); + break; + default: + break; + } + if (arg->dfd_client_get.get) { + bt_mesh_free(arg->dfd_client_get.get); + } + bt_mesh_free(arg->dfd_client_get.params); + break; + case ESP_BLE_MESH_ACT_DFD_CLIENT_SET: + if (arg->dfd_client_set.params == NULL) { + BT_ERR("%s:%d,InvParam", __func__, __LINE__); + break; + } + switch (arg->dfd_client_set.params->opcode) { + case ESP_BLE_MESH_DFD_OP_RECEIVERS_ADD: + if (arg->dfd_client_set.set->receivers_add.receivers == NULL) { + BT_ERR("%s:%d,InvParam", __func__, __LINE__); + break; + } + bt_mesh_free(arg->dfd_client_set.set->receivers_add.receivers); + break; + case ESP_BLE_MESH_DFD_OP_UPLOAD_START: + if (arg->dfd_client_set.set->dist_upload_start.fwid == NULL) { + BT_ERR("%s:%d,InvParam", __func__, __LINE__); + break; + } + bt_mesh_free_buf(arg->dfd_client_set.set->dist_upload_start.fwid); + if (arg->dfd_client_set.set->dist_upload_start.fw_metadata) { + bt_mesh_free_buf(arg->dfd_client_set.set->dist_upload_start.fw_metadata); + } + break; + case ESP_BLE_MESH_DFD_OP_UPLOAD_START_OOB: + if (arg->dfd_client_set.set->dist_upload_oob_start.url == NULL || + arg->dfd_client_set.set->dist_upload_oob_start.fwid == NULL) { + BT_ERR("%s:%d,InvParam", __func__, __LINE__); + break; + } + bt_mesh_free_buf(arg->dfd_client_set.set->dist_upload_oob_start.url); + bt_mesh_free_buf(arg->dfd_client_set.set->dist_upload_oob_start.fwid); + break; + case ESP_BLE_MESH_DFD_OP_FW_DELETE: + if (arg->dfd_client_set.set->dist_fw_del.fwid == NULL) { + BT_ERR("%s:%d,InvParam", __func__, __LINE__); + break; + } + bt_mesh_free_buf(arg->dfd_client_set.set->dist_fw_del.fwid); + break; + } + if (arg->dfd_client_set.set) { + bt_mesh_free(arg->dfd_client_set.set); + } + bt_mesh_free(arg->dfd_client_set.params); + break; + default: + BT_WARN("Unprocessed event %d", msg->act); + break; + } + + return; +} + +void btc_ble_mesh_dfd_client_rsp_deep_copy(btc_msg_t *msg, void *p_dest, void *p_src) +{ + esp_ble_mesh_dfd_client_cb_param_t *dst =(esp_ble_mesh_dfd_client_cb_param_t *) p_dest; + esp_ble_mesh_dfd_client_cb_param_t *src =(esp_ble_mesh_dfd_client_cb_param_t *) p_src; + + if (!msg || !dst || !src) { + BT_ERR("%s, Invalid parameter", __func__); + return; + } + + dst->err_code = src->err_code; + + if (src->params) { + dst->params = bt_mesh_calloc(sizeof(esp_ble_mesh_client_common_param_t)); + if (dst->params == NULL) { + BT_ERR("%s:%d,OutOfMem", __func__, __LINE__); + return; + } + memcpy(dst->params, src->params, sizeof(esp_ble_mesh_client_common_param_t)); + } + + switch (msg->act) { + case ESP_BLE_MESH_EVT_DFD_CLIENT_RECV_RSP: + switch(dst->params->opcode) { + case BLE_MESH_DFD_OP_RECEIVERS_LIST: + dst->status_cb.receiver_list.first_index = src->status_cb.receiver_list.first_index; + dst->status_cb.receiver_list.entries_cnt = src->status_cb.receiver_list.entries_cnt; + if (dst->status_cb.receiver_list.entries_cnt) { + dst->status_cb.receiver_list.entries = bt_mesh_calloc(sizeof(esp_ble_mesh_dfd_target_node_entry_t) * dst->status_cb.receiver_list.entries_cnt); + if (dst->status_cb.receiver_list.entries == NULL) { + BT_ERR("%s:%d,OutOfMem", __func__, __LINE__); + return; + } + memcpy(dst->status_cb.receiver_list.entries, src->status_cb.receiver_list.entries, + sizeof(esp_ble_mesh_dfd_target_node_entry_t) * dst->status_cb.receiver_list.entries_cnt); + } else { + dst->status_cb.receiver_list.entries = NULL; + } + break; + case BLE_MESH_DFD_OP_CAPABILITIES_STATUS: + dst->status_cb.dist_caps.max_receiver_list_sz = src->status_cb.dist_caps.max_receiver_list_sz; + dst->status_cb.dist_caps.max_fw_list_sz = src->status_cb.dist_caps.max_fw_list_sz; + dst->status_cb.dist_caps.max_fw_sz = src->status_cb.dist_caps.max_fw_sz; + dst->status_cb.dist_caps.max_upload_space = src->status_cb.dist_caps.max_upload_space; + dst->status_cb.dist_caps.remaining_upload_space = src->status_cb.dist_caps.remaining_upload_space; + dst->status_cb.dist_caps.oob_retrieval_supported = src->status_cb.dist_caps.oob_retrieval_supported; + if (src->status_cb.dist_caps.supported_url_scheme_names) { + dst->status_cb.dist_caps.supported_url_scheme_names = + bt_mesh_alloc_buf(src->status_cb.dist_caps.supported_url_scheme_names->len); + if (dst->status_cb.dist_caps.supported_url_scheme_names == NULL) { + BT_ERR("%s:%d,OutOfMem", __func__, __LINE__); + return; + } + net_buf_simple_add_mem( + dst->status_cb.dist_caps.supported_url_scheme_names, + src->status_cb.dist_caps.supported_url_scheme_names->data, + src->status_cb.dist_caps.supported_url_scheme_names->len); + } else { + dst->status_cb.dist_caps.supported_url_scheme_names = NULL; + } + break; + case BLE_MESH_DFD_OP_UPLOAD_STATUS: + dst->status_cb.upload_status.status = src->status_cb.upload_status.status; + dst->status_cb.upload_status.upload_phase = src->status_cb.upload_status.upload_phase; + dst->status_cb.upload_status.upload_progress = src->status_cb.upload_status.upload_progress; + dst->status_cb.upload_status.upload_type = src->status_cb.upload_status.upload_type; + + if (dst->status_cb.upload_status.upload_progress == ESP_BLE_MESH_DFD_UPLOAD_PROGRESS_UNSET) { + dst->status_cb.upload_status.fwid = NULL; + return; + } + + if (dst->status_cb.upload_status.upload_type == ESP_BLE_MESH_DFD_UPLOAD_TYPE_INBAND) { + if (src->status_cb.upload_status.fwid) { + dst->status_cb.upload_status.fwid = bt_mesh_alloc_buf(src->status_cb.upload_status.fwid->len); + if (dst->status_cb.upload_status.fwid == NULL) { + BT_ERR("%s:%d,OutOfMem", __func__, __LINE__); + return; + } + net_buf_simple_add_mem( + dst->status_cb.upload_status.fwid, + src->status_cb.upload_status.fwid->data, + src->status_cb.upload_status.fwid->len); + } else { + BT_ERR("%s:%d,InvParam", __func__, __LINE__); + } + } else { + if(src->status_cb.upload_status.oob_fwid) { + dst->status_cb.upload_status.oob_fwid = bt_mesh_alloc_buf(src->status_cb.upload_status.oob_fwid->len); + if (dst->status_cb.upload_status.oob_fwid == NULL) { + BT_ERR("%s:%d,OutOfMem", __func__, __LINE__); + return; + } + net_buf_simple_add_mem( + dst->status_cb.upload_status.oob_fwid, + src->status_cb.upload_status.oob_fwid->data, + src->status_cb.upload_status.oob_fwid->len); + } else { + BT_ERR("%s:%d,InvParam", __func__, __LINE__); + } + } + break; + case BLE_MESH_DFD_OP_FW_STATUS: + dst->status_cb.firmware_status.status = src->status_cb.firmware_status.status; + dst->status_cb.firmware_status.entry_cnt = src->status_cb.firmware_status.entry_cnt; + dst->status_cb.firmware_status.fw_idx = src->status_cb.firmware_status.fw_idx; + if (src->status_cb.firmware_status.fwid) { + dst->status_cb.firmware_status.fwid = bt_mesh_alloc_buf(src->status_cb.firmware_status.fwid->len); + if (dst->status_cb.firmware_status.fwid == NULL) { + BT_ERR("%s:%d,OutOfMem", __func__, __LINE__); + return; + } + net_buf_simple_add_mem( + dst->status_cb.firmware_status.fwid, + src->status_cb.firmware_status.fwid->data, + src->status_cb.firmware_status.fwid->len); + } else { + dst->status_cb.firmware_status.fwid = NULL; + } + break; + case BLE_MESH_DFD_OP_RECEIVERS_STATUS: + dst->status_cb.receiver_status.status = src->status_cb.receiver_status.status; + dst->status_cb.receiver_status.receiver_list_cnt = src->status_cb.receiver_status.receiver_list_cnt; + break; + case BLE_MESH_DFD_OP_STATUS: + dst->status_cb.dist_status.status = src->status_cb.dist_status.status; + dst->status_cb.dist_status.dist_phase = src->status_cb.dist_status.dist_phase; + dst->status_cb.dist_status.multicast_address = src->status_cb.dist_status.multicast_address; + dst->status_cb.dist_status.appkey_idx = src->status_cb.dist_status.appkey_idx; + dst->status_cb.dist_status.ttl = src->status_cb.dist_status.ttl; + dst->status_cb.dist_status.timeout_base = src->status_cb.dist_status.timeout_base; + dst->status_cb.dist_status.trans_mode = src->status_cb.dist_status.trans_mode; + dst->status_cb.dist_status.update_policy = src->status_cb.dist_status.update_policy; + dst->status_cb.dist_status.firmware_image_index = src->status_cb.dist_status.firmware_image_index; + break; + default: + BT_ERR("Unknown opcode %04x", dst->params->opcode); + break; + + } + break; + case ESP_BLE_MESH_EVT_DFD_CLIENT_TIMEOUT: + break; + case ESP_BLE_MESH_ACT_DFD_CLIEND_SEND_COMP: + break; + default: + BT_ERR("Unknown event %d", msg->act); + } + +} + +void btc_ble_mesh_dfd_client_rsp_deep_free(btc_msg_t *msg) +{ + esp_ble_mesh_dfd_client_cb_param_t *arg = NULL; + + if (!msg) { + BT_ERR("%s:%d,InvParam", __func__, __LINE__); + return; + } + + arg = (esp_ble_mesh_dfd_client_cb_param_t *)(msg->arg); + + if (arg->params == NULL && + msg->act != ESP_BLE_MESH_ACT_DFD_CLIEND_SEND_COMP) { + BT_ERR("%s:%d,InvParam", __func__, __LINE__); + return; + } + + switch (msg->act) { + case ESP_BLE_MESH_EVT_DFD_CLIENT_RECV_RSP: + switch (arg->params->opcode) { + case BLE_MESH_DFD_OP_RECEIVERS_LIST: + if (arg->status_cb.receiver_list.entries) { + bt_mesh_free(arg->status_cb.receiver_list.entries); + arg->status_cb.receiver_list.entries = NULL; + } + break; + case BLE_MESH_DFD_OP_CAPABILITIES_STATUS: + if (arg->status_cb.dist_caps.supported_url_scheme_names) { + bt_mesh_free_buf(arg->status_cb.dist_caps.supported_url_scheme_names); + arg->status_cb.dist_caps.supported_url_scheme_names = NULL; + } + break; + case BLE_MESH_DFD_OP_UPLOAD_STATUS: + /** + * firmware_id and upload_oob_firmware_id are a union + * structure, so only one pointer needs to be released + */ + if (arg->status_cb.upload_status.fwid) { + bt_mesh_free_buf(arg->status_cb.upload_status.fwid); + arg->status_cb.upload_status.fwid = NULL; + } + break; + case BLE_MESH_DFD_OP_FW_STATUS: + if (arg->status_cb.firmware_status.fwid) { + bt_mesh_free_buf(arg->status_cb.firmware_status.fwid); + arg->status_cb.firmware_status.fwid = NULL; + } + break; + } + break; + default: + break; + } + + if (arg->params) { + bt_mesh_free(arg->params); + } +} + +static void btc_ble_mesh_dfd_client_callback(esp_ble_mesh_dfd_client_cb_param_t *cb_params, + uint8_t act) +{ + btc_msg_t msg = {0}; + + /* If corresponding callback is not registered, event will not be posted. */ + if (!btc_profile_cb_get(BTC_PID_DFD_CLIENT)) { + return; + } + + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_DFD_CLIENT; + msg.act = act; + + btc_transfer_context(&msg, cb_params, cb_params == NULL ? 0 : sizeof(esp_ble_mesh_dfd_client_cb_param_t), + btc_ble_mesh_dfd_client_rsp_deep_copy, btc_ble_mesh_dfd_client_rsp_deep_free); +} + +void bt_mesh_dfd_client_cb_evt_to_btc(btc_ble_mesh_dfd_client_cb_evt_t event, + struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + const uint8_t *val, size_t len) +{ + esp_ble_mesh_dfd_client_cb_param_t cb_params = {0}; + esp_ble_mesh_client_common_param_t params = {0}; + uint8_t act = 0U; + + if (!model || !ctx || len > sizeof(cb_params.status_cb)) { + BT_ERR("%s, Invalid parameter", __func__); + return; + } + + switch (event) { + case BTC_BLE_MESH_EVT_DFD_CLIENT_RECV_RSP: + act = ESP_BLE_MESH_EVT_DFD_CLIENT_RECV_RSP; + break; + case BTC_BLE_MESH_EVT_DFD_CLIENT_TIMEOUT: + act = ESP_BLE_MESH_EVT_DFD_CLIENT_TIMEOUT; + break; + default: + BT_ERR("Unknown event %d", event); + break; + } + + params.opcode = ctx->recv_op; + params.model = (esp_ble_mesh_model_t *)model; + btc_ble_mesh_msg_ctx_copy((struct bt_mesh_msg_ctx *)¶ms.ctx, ctx, false); + + cb_params.params = ¶ms; + cb_params.err_code = 0; + + if (val && len) { + memcpy(&cb_params.status_cb, val, len); + } + + btc_ble_mesh_dfd_client_callback(&cb_params, act); + return; +} + +void btc_ble_mesh_dfd_client_call_handler(btc_msg_t *msg) +{ + esp_ble_mesh_dfd_client_cb_param_t cb = {0}; + btc_ble_mesh_dfd_client_args_t *arg = NULL; + + if (!msg) { + BT_ERR("%s,InvParam", __func__); + return; + } + + arg = (btc_ble_mesh_dfd_client_args_t *)msg->arg; + + switch (msg->act) { + case BTC_BLE_MESH_ACT_DFD_CLIENT_GET: + cb.params = arg->dfd_client_get.params; + cb.err_code = btc_ble_mesh_dfd_client_get(arg->dfd_client_get.params, arg->dfd_client_get.get); + btc_ble_mesh_dfd_client_callback(&cb, ESP_BLE_MESH_ACT_DFD_CLIEND_SEND_COMP); + break; + case BTC_BLE_MESH_ACT_DFD_CLIENT_SET: + cb.params = arg->dfd_client_set.params; + cb.err_code = btc_ble_mesh_dfd_client_set(arg->dfd_client_set.params, arg->dfd_client_set.set); + btc_ble_mesh_dfd_client_callback(&cb, ESP_BLE_MESH_ACT_DFD_CLIEND_SEND_COMP); + break; + default: + break; + } + + btc_ble_mesh_dfd_client_arg_deep_free(msg); +} +#endif /* CONFIG_BLE_MESH_DFD_CLI */ + +#if CONFIG_BLE_MESH_DFD_SRV +#if CONFIG_BLE_MESH_DFD_SRV_OOB_UPLOAD +int btc_ble_mesh_dfd_srv_oob_check_complete(struct esp_ble_mesh_dfd_srv *srv, + const struct esp_ble_mesh_dfu_slot *slot, int status, + uint8_t *fwid, size_t fwid_len) +{ + bt_mesh_dfd_srv_oob_check_complete((struct bt_mesh_dfd_srv *)srv, (struct bt_mesh_dfu_slot *)slot, status, fwid, fwid_len); + return 0; +} +int btc_ble_mesh_dfd_srv_oob_store_complete(struct esp_ble_mesh_dfd_srv *srv, + const struct esp_ble_mesh_dfu_slot *slot, bool success, + size_t size, const uint8_t *metadata, size_t metadata_len) +{ + bt_mesh_dfd_srv_oob_store_complete((struct bt_mesh_dfd_srv *)srv, (struct bt_mesh_dfu_slot *)slot, success, size, metadata, metadata_len); + return 0; +} +#endif /* CONFIG_BLE_MESH_DFD_SRV_OOB_UPLOAD */ +#endif /* CONFIG_BLE_MESH_DFD_SRV */ diff --git a/components/bt/esp_ble_mesh/v1.1/btc/include/btc_ble_mesh_dfu_model.h b/components/bt/esp_ble_mesh/v1.1/btc/include/btc_ble_mesh_dfu_model.h index a74aeb5453..0111e86549 100644 --- a/components/bt/esp_ble_mesh/v1.1/btc/include/btc_ble_mesh_dfu_model.h +++ b/components/bt/esp_ble_mesh/v1.1/btc/include/btc_ble_mesh_dfu_model.h @@ -67,6 +67,55 @@ bool btc_ble_mesh_dfu_srv_is_busy(const esp_ble_mesh_dfu_srv_t *srv); uint8_t btc_ble_mesh_dfu_srv_progress(const esp_ble_mesh_dfu_srv_t *srv); #endif /* CONFIG_BLE_MESH_DFU_SRV */ +#if CONFIG_BLE_MESH_DFD_CLI +typedef enum { + BTC_BLE_MESH_ACT_DFD_CLIENT_GET, + BTC_BLE_MESH_ACT_DFD_CLIENT_SET, + BTC_BLE_MESH_ACT_DFD_CLIEND_SEND_COMP, + BTC_BLE_MESH_ACT_DFD_CLIENT_MAX, +} btc_ble_mesh_dfd_client_act_t; + +typedef enum { + BTC_BLE_MESH_EVT_DFD_CLIENT_TIMEOUT, + BTC_BLE_MESH_EVT_DFD_CLIENT_RECV_RSP, + BTC_BLE_MESH_EVT_DFD_CLIENT_MAX, +} btc_ble_mesh_dfd_client_cb_evt_t; + +typedef union { + struct { + esp_ble_mesh_client_common_param_t *params; + esp_ble_mesh_dfd_client_get_param_t *get; + } dfd_client_get; + struct { + esp_ble_mesh_client_common_param_t *params; + esp_ble_mesh_dfd_client_set_param_t *set; + } dfd_client_set; +} btc_ble_mesh_dfd_client_args_t; + +void btc_ble_mesh_dfd_client_cb_handler(btc_msg_t *msg); +void btc_ble_mesh_dfd_client_arg_deep_copy(btc_msg_t *msg, void *p_dest, void *p_src); +void btc_ble_mesh_dfd_client_arg_deep_free(btc_msg_t *msg); +void btc_ble_mesh_dfd_client_rsp_deep_copy(btc_msg_t *msg, void *p_dest, void *p_src); +void btc_ble_mesh_dfd_client_rsp_deep_free(btc_msg_t *msg); +void bt_mesh_dfd_client_cb_evt_to_btc(btc_ble_mesh_dfd_client_cb_evt_t event, + struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + const uint8_t *val, size_t len); +void btc_ble_mesh_dfd_client_call_handler(btc_msg_t *msg); + +#endif /* CONFIG_BLE_MESH_DFD_CLI */ + +#if CONFIG_BLE_MESH_DFD_SRV +#if CONFIG_BLE_MESH_DFD_SRV_OOB_UPLOAD +int btc_ble_mesh_dfd_srv_oob_check_complete(struct esp_ble_mesh_dfd_srv *srv, + const struct esp_ble_mesh_dfu_slot *slot, int status, + uint8_t *fwid, size_t fwid_len); +int btc_ble_mesh_dfd_srv_oob_store_complete(struct esp_ble_mesh_dfd_srv *srv, + const struct esp_ble_mesh_dfu_slot *slot, bool success, + size_t size, const uint8_t *metadata, size_t metadata_len); +#endif /* CONFIG_BLE_MESH_DFD_SRV_OOB_UPLOAD */ +#endif /* CONFIG_BLE_MESH_DFD_SRV */ + #ifdef __cplusplus } #endif diff --git a/components/bt/esp_ble_mesh/v1.1/dfu/dfd.h b/components/bt/esp_ble_mesh/v1.1/dfu/dfd.h deleted file mode 100644 index ee94be24e6..0000000000 --- a/components/bt/esp_ble_mesh/v1.1/dfu/dfd.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2020 Nordic Semiconductor ASA - * SPDX-FileContributor: 2025 Espressif Systems (Shanghai) CO LTD - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#include "access.h" - -/* @todo: adapt to dfd client */ -#if CONFIG_BLE_MESH_DFD_SRV -#define BT_MESH_DFD_OP_RECEIVERS_ADD BLE_MESH_MODEL_OP_2(0x83, 0x11) -#define BT_MESH_DFD_OP_RECEIVERS_DELETE_ALL BLE_MESH_MODEL_OP_2(0x83, 0x12) -#define BT_MESH_DFD_OP_RECEIVERS_STATUS BLE_MESH_MODEL_OP_2(0x83, 0x13) -#define BT_MESH_DFD_OP_RECEIVERS_GET BLE_MESH_MODEL_OP_2(0x83, 0x14) -#define BT_MESH_DFD_OP_RECEIVERS_LIST BLE_MESH_MODEL_OP_2(0x83, 0x15) -#define BT_MESH_DFD_OP_CAPABILITIES_GET BLE_MESH_MODEL_OP_2(0x83, 0x16) -#define BT_MESH_DFD_OP_CAPABILITIES_STATUS BLE_MESH_MODEL_OP_2(0x83, 0x17) -#define BT_MESH_DFD_OP_GET BLE_MESH_MODEL_OP_2(0x83, 0x18) -#define BT_MESH_DFD_OP_START BLE_MESH_MODEL_OP_2(0x83, 0x19) -#define BT_MESH_DFD_OP_SUSPEND BLE_MESH_MODEL_OP_2(0x83, 0x1a) -#define BT_MESH_DFD_OP_CANCEL BLE_MESH_MODEL_OP_2(0x83, 0x1b) -#define BT_MESH_DFD_OP_APPLY BLE_MESH_MODEL_OP_2(0x83, 0x1c) -#define BT_MESH_DFD_OP_STATUS BLE_MESH_MODEL_OP_2(0x83, 0x1d) -#define BT_MESH_DFD_OP_UPLOAD_GET BLE_MESH_MODEL_OP_2(0x83, 0x1e) -#define BT_MESH_DFD_OP_UPLOAD_START BLE_MESH_MODEL_OP_2(0x83, 0x1f) -#define BT_MESH_DFD_OP_UPLOAD_START_OOB BLE_MESH_MODEL_OP_2(0x83, 0x20) -#define BT_MESH_DFD_OP_UPLOAD_CANCEL BLE_MESH_MODEL_OP_2(0x83, 0x21) -#define BT_MESH_DFD_OP_UPLOAD_STATUS BLE_MESH_MODEL_OP_2(0x83, 0x22) -#define BT_MESH_DFD_OP_FW_GET BLE_MESH_MODEL_OP_2(0x83, 0x23) -#define BT_MESH_DFD_OP_FW_GET_BY_INDEX BLE_MESH_MODEL_OP_2(0x83, 0x24) -#define BT_MESH_DFD_OP_FW_DELETE BLE_MESH_MODEL_OP_2(0x83, 0x25) -#define BT_MESH_DFD_OP_FW_DELETE_ALL BLE_MESH_MODEL_OP_2(0x83, 0x26) -#define BT_MESH_DFD_OP_FW_STATUS BLE_MESH_MODEL_OP_2(0x83, 0x27) -#endif diff --git a/components/bt/esp_ble_mesh/v1.1/dfu/dfd_cli.c b/components/bt/esp_ble_mesh/v1.1/dfu/dfd_cli.c new file mode 100644 index 0000000000..19fce3b9a8 --- /dev/null +++ b/components/bt/esp_ble_mesh/v1.1/dfu/dfd_cli.c @@ -0,0 +1,699 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include "dfu_slot.h" +#include "dfu.h" +#include "net.h" +#include "transport.h" +#include "mesh_v1.1/dfu/dfd.h" +#include "mesh_v1.1/dfu/dfu_cli.h" +#include "mesh_v1.1/mbt/blob_srv.h" +#include "btc_ble_mesh_dfu_model.h" + +#include "mesh_v1.1/dfu/dfd_cli.h" + +#if CONFIG_BLE_MESH_DFD_CLI +static const bt_mesh_client_op_pair_t dfd_cli_pair[] = { + {BLE_MESH_DFD_OP_RECEIVERS_ADD, BLE_MESH_DFD_OP_RECEIVERS_STATUS}, + {BLE_MESH_DFD_OP_RECEIVERS_DELETE_ALL, BLE_MESH_DFD_OP_RECEIVERS_STATUS}, + {BLE_MESH_DFD_OP_RECEIVERS_GET, BLE_MESH_DFD_OP_RECEIVERS_LIST}, + {BLE_MESH_DFD_OP_CAPABILITIES_GET, BLE_MESH_DFD_OP_CAPABILITIES_STATUS}, + {BLE_MESH_DFD_OP_GET, BLE_MESH_DFD_OP_STATUS}, + {BLE_MESH_DFD_OP_START, BLE_MESH_DFD_OP_STATUS}, + {BLE_MESH_DFD_OP_SUSPEND, BLE_MESH_DFD_OP_STATUS}, + {BLE_MESH_DFD_OP_CANCEL, BLE_MESH_DFD_OP_STATUS}, + {BLE_MESH_DFD_OP_APPLY, BLE_MESH_DFD_OP_STATUS}, + {BLE_MESH_DFD_OP_UPLOAD_GET, BLE_MESH_DFD_OP_UPLOAD_STATUS}, + {BLE_MESH_DFD_OP_UPLOAD_START, BLE_MESH_DFD_OP_UPLOAD_STATUS}, + {BLE_MESH_DFD_OP_UPLOAD_START_OOB, BLE_MESH_DFD_OP_UPLOAD_STATUS}, + {BLE_MESH_DFD_OP_UPLOAD_CANCEL, BLE_MESH_DFD_OP_UPLOAD_STATUS}, + {BLE_MESH_DFD_OP_FW_GET, BLE_MESH_DFD_OP_FW_STATUS}, + {BLE_MESH_DFD_OP_FW_GET_BY_INDEX, BLE_MESH_DFD_OP_FW_STATUS}, + {BLE_MESH_DFD_OP_FW_DELETE, BLE_MESH_DFD_OP_FW_STATUS}, + {BLE_MESH_DFD_OP_FW_DELETE_ALL, BLE_MESH_DFD_OP_FW_STATUS}, +}; + +static bt_mesh_mutex_t dfd_client_lock; + +static void timeout_handler(struct k_work *work) +{ + struct k_delayed_work *timer = NULL; + bt_mesh_client_node_t *node = NULL; + + BT_WARN("DFDRspTmo"); + + bt_mesh_mutex_lock(&dfd_client_lock); + + timer = CONTAINER_OF(work, struct k_delayed_work, work); + + if (timer && !k_delayed_work_free(timer)) { + node = CONTAINER_OF(work, bt_mesh_client_node_t, timer.work); + if (node) { + bt_mesh_dfd_client_cb_evt_to_btc(BTC_BLE_MESH_EVT_DFD_CLIENT_TIMEOUT, node->model, &node->ctx, NULL, 0); + bt_mesh_client_free_node(node); + } + } + + bt_mesh_mutex_unlock(&dfd_client_lock); + + return; +} + +static void dfd_client_recv_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + void *status, size_t len) +{ + bt_mesh_client_node_t *node = NULL; + struct net_buf_simple buf = {0}; + btc_ble_mesh_dfd_client_cb_evt_t evt = 0; + + if (!model || !ctx) { + BT_ERR("%s,InvParam", __func__); + return; + } + + buf.data = (uint8_t *)status; + buf.len = (uint16_t)len; + + bt_mesh_mutex_lock(&dfd_client_lock); + + node = bt_mesh_is_client_recv_publish_msg(model, ctx, &buf, true); + if (!node) { + BT_DBG("UnexpDfdStus:0x%04x", ctx->recv_op); + } else { + switch (ctx->recv_op) { + case BLE_MESH_DFD_OP_RECEIVERS_STATUS: + case BLE_MESH_DFD_OP_CAPABILITIES_GET: + case BLE_MESH_DFD_OP_RECEIVERS_LIST: + case BLE_MESH_DFD_OP_CAPABILITIES_STATUS: + case BLE_MESH_DFD_OP_STATUS: + case BLE_MESH_DFD_OP_UPLOAD_STATUS: + case BLE_MESH_DFD_OP_FW_STATUS: + evt = BTC_BLE_MESH_EVT_DFD_CLIENT_RECV_RSP; + break; + default: + BT_ERR("UnkwnOpc:%04x", node->opcode); + break; + } + + if (!k_delayed_work_free(&node->timer)) { + bt_mesh_dfd_client_cb_evt_to_btc(evt, model, ctx, (const uint8_t *)status, len); + bt_mesh_client_free_node(node); + } + } + + bt_mesh_mutex_unlock(&dfd_client_lock); +} + +static void handle_receiver_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + dfd_status_t status = {0}; + + status.receiver_status.status = net_buf_simple_pull_u8(buf); + + if (status.receiver_status.status > BLE_MESH_DFD_ERR_SUSPEND_FAILED) { + BT_ERR("InvSrvStu:%d", status.receiver_status.status); + return; + } + + if (status.receiver_status.status != BLE_MESH_DFD_SUCCESS) { + BT_ERR("StusErr:%d", status); + return; + } + + status.receiver_status.receiver_list_cnt = net_buf_simple_pull_le16(buf); + + BT_DBG("RecvLstCnt:%d", status.receiver_status.receiver_list_cnt); + + dfd_client_recv_status(model, ctx, &status, sizeof(status.receiver_status)); +} + +static void handle_receiver_list(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + int i = 0; + uint32_t target_info = 0; + + dfd_status_t status = {0}; + status.receiver_list.entries_cnt = net_buf_simple_pull_le16(buf); + status.receiver_list.first_index = net_buf_simple_pull_le16(buf); + status.receiver_list.entries = bt_mesh_calloc(status.receiver_list.entries_cnt * sizeof(target_node_entry_t)); + for (i = 0; i < status.receiver_list.entries_cnt; i++) { + target_info = net_buf_simple_pull_le32(buf); + status.receiver_list.entries[i].addr = TARGET_ADDR(target_info); + status.receiver_list.entries[i].retrieved_update_phase = TARGET_UPDATE_PHASE(target_info); + status.receiver_list.entries[i].update_status = TARGET_UPDATE_STATUS(target_info); + status.receiver_list.entries[i].transfer_status = TARGET_TRANSFER_STATUS(target_info); + status.receiver_list.entries[i].transfer_progress = TARGET_TRANSFER_PROGRESS(target_info); + status.receiver_list.entries[i].update_fw_idx = net_buf_simple_pull_u8(buf); + } + dfd_client_recv_status(model, ctx, &status, sizeof(status.receiver_list)); + bt_mesh_free(status.receiver_list.entries); + return; +} + +static void handle_capabilities(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + dfd_status_t status = {0}; + struct net_buf_simple url_scheme_names = {0}; + + status.dist_caps.max_receiver_list_sz = net_buf_simple_pull_le16(buf); + status.dist_caps.max_fw_list_sz = net_buf_simple_pull_le16(buf); + status.dist_caps.max_fw_sz = net_buf_simple_pull_le32(buf); + status.dist_caps.max_upload_space = net_buf_simple_pull_le32(buf); + status.dist_caps.remaining_upload_space = net_buf_simple_pull_le32(buf); + status.dist_caps.oob_retrieval_supported = net_buf_simple_pull_le32(buf); + if (buf->len) { + status.dist_caps.supported_url_scheme_names = &url_scheme_names; + net_buf_simple_init_with_data(status.dist_caps.supported_url_scheme_names, buf->data, buf->len); + net_buf_simple_pull_mem(buf, buf->len); + } + dfd_client_recv_status(model, ctx, &status, sizeof(status.dist_caps)); + return; +} + +static void handle_dfd_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + dfd_status_t status = {0}; + uint8_t trans_mode_policy = 0; + + status.dist_status.status = net_buf_simple_pull_u8(buf); + status.dist_status.dist_phase = net_buf_simple_pull_u8(buf); + if (buf->len != 0 && buf->len != 10) { + BT_ERR("Invalid data"); + return; + } + status.dist_status.multicast_address = net_buf_simple_pull_le16(buf); + status.dist_status.appkey_idx = net_buf_simple_pull_le16(buf); + status.dist_status.ttl = net_buf_simple_pull_u8(buf); + status.dist_status.timeout_base = net_buf_simple_pull_le16(buf); + + trans_mode_policy = net_buf_simple_pull_u8(buf); + if ((trans_mode_policy & ~(BIT6 - 1)) != 0) { + BT_ERR("RFU should be zero"); + return; + } + status.dist_status.trans_mode = trans_mode_policy >> 6; + status.dist_status.update_policy = (trans_mode_policy >> 5) & 0x01; + status.dist_status.RFU = trans_mode_policy & 0x1f; + status.dist_status.fw_idx = net_buf_simple_pull_le16(buf); + dfd_client_recv_status(model, ctx, &status, sizeof(status.dist_status)); + return; +} + +static void handle_upload_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + dfd_status_t status = {0}; + uint8_t progress_type = 0; + struct net_buf_simple buf_cache = {0}; + status.upload_status.status = net_buf_simple_pull_u8(buf); + status.upload_status.upload_phase = net_buf_simple_pull_u8(buf); + + if (buf->len == 0) { + status.upload_status.upload_progress = UPLOAD_PROGRESS_UNSET; + dfd_client_recv_status(model, ctx, &status, sizeof(status.upload_status)); + return; + } + + progress_type = net_buf_simple_pull_u8(buf); + status.upload_status.upload_progress = progress_type>>1; + + if (status.upload_status.upload_progress >= UPLOAD_PROGRESS_UNSET) { + BT_ERR("Invalid upload progress"); + return; + } + + if (buf->len == 0) { + BT_ERR("InvFwID"); + return; + } + + status.upload_status.upload_type = progress_type & 0x01; + if (status.upload_status.upload_type == UPLOAD_IN_BAND) { + status.upload_status.fwid = &buf_cache; + net_buf_simple_init_with_data(status.upload_status.fwid, buf->data, buf->len); + net_buf_simple_pull_mem(buf, buf->len); + } else if (status.upload_status.upload_type == UPLOAD_OOB){ + status.upload_status.oob_fwid = &buf_cache; + net_buf_simple_init_with_data(status.upload_status.oob_fwid, buf->data, buf->len); + net_buf_simple_pull_mem(buf, buf->len); + } else { + BT_ERR("Invalid upload type:%d", status.upload_status.upload_type); + return; + } + + dfd_client_recv_status(model, ctx, &status, sizeof(status.upload_status)); + return; +} + +static void handle_fw_status(struct bt_mesh_model *model, + struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + dfd_status_t status = {0}; + struct net_buf_simple buf_cache = {0}; + status.firmware_status.status = net_buf_simple_pull_u8(buf); + status.firmware_status.entry_cnt = net_buf_simple_pull_le16(buf); + status.firmware_status.firmware_image_index = net_buf_simple_pull_le16(buf); + if (buf->len) { + status.firmware_status.fwid = &buf_cache; + net_buf_simple_init_with_data(status.firmware_status.fwid, buf->data, buf->len); + net_buf_simple_pull_mem(buf, buf->len); + } + + dfd_client_recv_status(model, ctx, &status, sizeof(status.firmware_status)); + return; +} + +const struct bt_mesh_model_op _bt_mesh_dfd_cli_op[] = { + { BLE_MESH_DFD_OP_RECEIVERS_STATUS, 3, handle_receiver_status }, + { BLE_MESH_DFD_OP_RECEIVERS_LIST, 4 , handle_receiver_list }, + { BLE_MESH_DFD_OP_CAPABILITIES_STATUS, 17, handle_capabilities }, + { BLE_MESH_DFD_OP_STATUS, 2, handle_dfd_status }, + { BLE_MESH_DFD_OP_UPLOAD_STATUS, 2, handle_upload_status }, + { BLE_MESH_DFD_OP_FW_STATUS, 5, handle_fw_status }, +}; + +int bt_mesh_dfd_cli_receivers_add(bt_mesh_client_common_param_t *param, dfd_cli_receiver_entry_t *receivers, uint16_t receivers_cnt) +{ + uint16_t msg_length = 2; + struct net_buf_simple *msg = NULL; + dfd_cli_receiver_entry_t *entry = NULL; + int err = 0; + + if (param == NULL) { + BT_ERR("Invalid param"); + return -EINVAL; + } + + BT_INFO("AddedValidCnt:%d", receivers_cnt); + msg_length += (receivers_cnt * 3); + + /* needs to confirm long or short mic */ + if (msg_length > BLE_MESH_MAX_PDU_LEN_WITH_SMIC) { + BT_ERR("Too much receivers added once time"); + return -EINVAL; + } + + msg = bt_mesh_alloc_buf(msg_length + BLE_MESH_MIC_SHORT); + if (!msg) { + BT_ERR("Failed to alloc buffer to send message"); + return -EINVAL; + } + + bt_mesh_model_msg_init(msg, BLE_MESH_DFD_OP_RECEIVERS_ADD); + + for (int i = 0; i < receivers_cnt; i++) { + entry = &receivers[i]; + if (BLE_MESH_ADDR_IS_UNICAST(entry->addr)) { + net_buf_simple_add_le16(msg, entry->addr); + net_buf_simple_add_u8(msg, entry->fw_idx); + BT_INFO("AddedUnicastAddr:0x%04x,FwIdx:%d", entry->addr, entry->fw_idx); + } + } + + err = bt_mesh_client_send_msg(param, msg, true, timeout_handler); + bt_mesh_free_buf(msg); + return err; +} + +int bt_mesh_dfd_cli_receivers_delete_all(bt_mesh_client_common_param_t *param) +{ + BLE_MESH_MODEL_BUF_DEFINE(msg, BLE_MESH_DFD_OP_RECEIVERS_DELETE_ALL, 0); + if (!param) { + BT_ERR("Invalid param"); + return -EINVAL; + } + bt_mesh_model_msg_init(&msg, BLE_MESH_DFD_OP_RECEIVERS_DELETE_ALL); + return bt_mesh_client_send_msg(param, &msg, true, timeout_handler); +} + +int bt_mesh_dfd_cli_receivers_get(bt_mesh_client_common_param_t *param, uint16_t first_index, uint16_t entries_limit) +{ + BLE_MESH_MODEL_BUF_DEFINE(msg, BLE_MESH_DFD_OP_RECEIVERS_GET, 4); + if (!param) { + BT_ERR("Invalid param"); + return -EINVAL; + } + + bt_mesh_model_msg_init(&msg, BLE_MESH_DFD_OP_RECEIVERS_GET); + net_buf_simple_add_le16(&msg, first_index); + net_buf_simple_add_le16(&msg, entries_limit); + + return bt_mesh_client_send_msg(param, &msg, true, timeout_handler); +} + +int bt_mesh_dfd_cli_distribution_capabilities_get(bt_mesh_client_common_param_t *param) +{ + BLE_MESH_MODEL_BUF_DEFINE(msg, BLE_MESH_DFD_OP_CAPABILITIES_GET, 0); + if (!param) { + BT_ERR("Invalid param"); + return -EINVAL; + } + bt_mesh_model_msg_init(&msg, BLE_MESH_DFD_OP_CAPABILITIES_GET); + + return bt_mesh_client_send_msg(param, &msg, true, timeout_handler); +} + +int bt_mesh_dfd_cli_distribution_get(bt_mesh_client_common_param_t *param) +{ + BLE_MESH_MODEL_BUF_DEFINE(msg, BLE_MESH_DFD_OP_GET, 0); + if (!param) { + BT_ERR("Invalid param"); + return -EINVAL; + } + bt_mesh_model_msg_init(&msg, BLE_MESH_DFD_OP_GET); + + return bt_mesh_client_send_msg(param, &msg, true, timeout_handler); +} + +int bt_mesh_dfd_cli_distribution_start(bt_mesh_client_common_param_t *param, + dfd_cli_dist_start_t *start) +{ + BLE_MESH_MODEL_BUF_DEFINE(msg, BLE_MESH_DFD_OP_START, 2 + 1 + 2 + 1 + 2 + 16); + if (!param || !start) { + BT_ERR("Invalid param"); + return -EINVAL; + } + + bt_mesh_model_msg_init(&msg, BLE_MESH_DFD_OP_START); + net_buf_simple_add_le16(&msg, start->app_idx); + net_buf_simple_add_u8(&msg, start->ttl); + net_buf_simple_add_le16(&msg, start->timeout_base); + net_buf_simple_add_u8(&msg, ((start->trans_mode) << 6) | ((start->update_policy) << 5)); + net_buf_simple_add_le16(&msg, start->fw_idx); + + if (start->is_va) { + net_buf_simple_add_mem(&msg, &(start->label_uuid), 16); + } else { + net_buf_simple_add_le16(&msg, start->group_addr); + } + + return bt_mesh_client_send_msg(param, &msg, true, timeout_handler); +} + +int bt_mesh_dfd_cli_distribution_suspend(bt_mesh_client_common_param_t *param) +{ + BLE_MESH_MODEL_BUF_DEFINE(msg, BLE_MESH_DFD_OP_SUSPEND, 0); + if (!param) { + BT_ERR("Invalid param"); + return -EINVAL; + } + bt_mesh_model_msg_init(&msg, BLE_MESH_DFD_OP_SUSPEND); + + return bt_mesh_client_send_msg(param, &msg, true, timeout_handler); +} + +int bt_mesh_dfd_cli_distribution_cancel(bt_mesh_client_common_param_t *param) +{ + BLE_MESH_MODEL_BUF_DEFINE(msg, BLE_MESH_DFD_OP_CANCEL, 0); + if (!param) { + BT_ERR("Invalid param"); + return -EINVAL; + } + bt_mesh_model_msg_init(&msg, BLE_MESH_DFD_OP_CANCEL); + + return bt_mesh_client_send_msg(param, &msg, true, timeout_handler); +} + +int bt_mesh_dfd_cli_distribution_apply(bt_mesh_client_common_param_t *param) +{ + BLE_MESH_MODEL_BUF_DEFINE(msg, BLE_MESH_DFD_OP_APPLY, 0); + if (!param) { + BT_ERR("Invalid param"); + return -EINVAL; + } + bt_mesh_model_msg_init(&msg, BLE_MESH_DFD_OP_APPLY); + + return bt_mesh_client_send_msg(param, &msg, true, timeout_handler); +} + +int bt_mesh_dfd_cli_distribution_upload_get(bt_mesh_client_common_param_t *param) +{ + BLE_MESH_MODEL_BUF_DEFINE(msg, BLE_MESH_DFD_OP_UPLOAD_GET, 0); + if (!param) { + BT_ERR("Invalid param"); + return -EINVAL; + } + bt_mesh_model_msg_init(&msg, BLE_MESH_DFD_OP_UPLOAD_GET); + + return bt_mesh_client_send_msg(param, &msg, true, timeout_handler); +} + +int bt_mesh_dfd_cli_distribution_upload_start(bt_mesh_client_common_param_t *param, + dfd_cli_dist_upload_start_t *start) +{ + struct net_buf_simple *msg = NULL; + uint16_t msg_length = 2; + int err = 0; + + if (!param || !start) { + BT_ERR("Invalid param"); + return -EINVAL; + } + + msg_length += (1 + 2 + 8 + 4 + 1 + start->fwid->len); + if (start->fw_metadata) { + msg_length += start->fw_metadata->len; + } + + + msg = bt_mesh_alloc_buf(msg_length + BLE_MESH_MIC_SHORT); + if (!msg) { + BT_ERR("Failed to alloc buffer to send message"); + return -EINVAL; + } + + bt_mesh_model_msg_init(msg, BLE_MESH_DFD_OP_UPLOAD_START); + net_buf_simple_add_u8(msg, start->ttl); + net_buf_simple_add_le16(msg, start->timeout_base); + net_buf_simple_add_le64(msg, start->blob_id); + net_buf_simple_add_le32(msg, start->fw_size); + if (start->fw_metadata) { + net_buf_simple_add_u8(msg, start->fw_metadata->len); + net_buf_simple_add_mem(msg, start->fw_metadata->data, start->fw_metadata->len); + } else { + net_buf_simple_add_u8(msg, 0); + } + net_buf_simple_add_mem(msg, start->fwid->data, start->fwid->len); + + err = bt_mesh_client_send_msg(param, msg, true, timeout_handler); + bt_mesh_free_buf(msg); + return err; +} + +int bt_mesh_dfd_cli_distribution_upload_oob_start(bt_mesh_client_common_param_t *param, + dfd_cli_dist_upload_oob_start_t *start) +{ + int err = 0; + struct net_buf_simple *msg = NULL; + uint16_t msg_length = 2; + + if (!param || !start) { + BT_ERR("Invalid param"); + return -1; + } + + if (!start->url) { + BT_ERR("Null url info"); + return -1; + } + + if (!start->fwid) { + BT_ERR("Invalid firmware id"); + return -1; + } + + msg_length += (1 + start->url->len + start->fwid->len); + + msg = bt_mesh_alloc_buf(msg_length + BLE_MESH_MIC_SHORT); + if (!msg) { + BT_ERR("Failed to alloc buffer to send message"); + return -EINVAL; + } + + bt_mesh_model_msg_init(msg, BLE_MESH_DFD_OP_UPLOAD_START_OOB); + net_buf_simple_add_u8(msg, start->url->len); + net_buf_simple_add_mem(msg, start->url->data, start->url->len); + net_buf_simple_add_mem(msg, start->fwid->data, start->fwid->len); + + err = bt_mesh_client_send_msg(param, msg, true, timeout_handler); + bt_mesh_free_buf(msg); + return err; +} + +int bt_mesh_dfd_cli_distribution_upload_oob_cancel(bt_mesh_client_common_param_t *param) +{ + BLE_MESH_MODEL_BUF_DEFINE(msg, BLE_MESH_DFD_OP_UPLOAD_CANCEL, 0); + bt_mesh_model_msg_init(&msg, BLE_MESH_DFD_OP_UPLOAD_CANCEL); + + return bt_mesh_client_send_msg(param, &msg, true, timeout_handler); +} + +int bt_mesh_dfd_cli_firmware_get(bt_mesh_client_common_param_t *param, struct net_buf_simple *fwid) +{ + struct net_buf_simple *msg = NULL; + uint16_t msg_length = 2; + int err; + + + if (!fwid) { + BT_ERR("NULL Firmware id"); + return -EINVAL; + } + + msg_length += fwid->len; + + msg = bt_mesh_alloc_buf(msg_length + BLE_MESH_MIC_SHORT); + if (!msg) { + BT_ERR("Failed to alloc buffer to send message"); + return -EINVAL; + } + + bt_mesh_model_msg_init(msg, BLE_MESH_DFD_OP_FW_GET); + net_buf_simple_add_mem(msg, fwid->data, fwid->len); + + err = bt_mesh_client_send_msg(param, msg, true, timeout_handler); + bt_mesh_free_buf(msg); + return err; +} + +int bt_mesh_dfd_cli_firmware_get_by_index(bt_mesh_client_common_param_t *param, uint16_t fw_id_index) +{ + BLE_MESH_MODEL_BUF_DEFINE(msg, BLE_MESH_DFD_OP_FW_GET_BY_INDEX, 2); + bt_mesh_model_msg_init(&msg, BLE_MESH_DFD_OP_FW_GET_BY_INDEX); + + net_buf_simple_add_le16(&msg, fw_id_index); + return bt_mesh_client_send_msg(param, &msg, true, timeout_handler); +} + +int bt_mesh_dfd_cli_firmware_get_delete(bt_mesh_client_common_param_t *param, struct net_buf_simple *fwid) +{ + struct net_buf_simple *msg = NULL; + uint16_t msg_length = 2; + int err; + + + if (!fwid) { + BT_ERR("NULL Firmware id"); + return -EINVAL; + } + + msg_length += fwid->len; + + msg = bt_mesh_alloc_buf(msg_length + BLE_MESH_MIC_SHORT); + if (!msg) { + BT_ERR("Failed to alloc buffer to send message"); + return -EINVAL; + } + + bt_mesh_model_msg_init(msg, BLE_MESH_DFD_OP_FW_DELETE); + net_buf_simple_add_mem(msg, fwid->data, fwid->len); + + err = bt_mesh_client_send_msg(param, msg, true, timeout_handler); + bt_mesh_free_buf(msg); + return err; +} + +int bt_mesh_dfd_cli_firmware_delete_all(bt_mesh_client_common_param_t *param) +{ + BLE_MESH_MODEL_BUF_DEFINE(msg, BLE_MESH_DFD_OP_FW_DELETE_ALL, 0); + bt_mesh_model_msg_init(&msg, BLE_MESH_DFD_OP_FW_DELETE_ALL); + + return bt_mesh_client_send_msg(param, &msg, true, timeout_handler); +} + +static int dfd_cli_init(struct bt_mesh_model *model) +{ + bt_mesh_dfd_client_t *client = NULL; + dfd_internal_data_t *internal = NULL; + + if (!model) { + BT_ERR("Invalid Device Firmware Distribution Client model"); + return -EINVAL; + } + + if (!bt_mesh_model_in_primary(model)) { + BT_ERR("Device Firmware Distribution Client only allowed in primary element"); + return -EINVAL; + } + + client = (bt_mesh_dfd_client_t *)model->user_data; + if (!client) { + BT_ERR("No Device Firmware Distribution Client context provided"); + return -EINVAL; + } + + if (client->internal_data) { + BT_WARN("%s, Already", __func__); + return -EALREADY; + } + + internal = bt_mesh_calloc(sizeof(dfd_internal_data_t)); + if (!internal) { + BT_ERR("%s, Out of memory", __func__); + return -ENOMEM; + } + + sys_slist_init(&internal->queue); + + client->model = model; + client->op_pair_size = ARRAY_SIZE(dfd_cli_pair); + client->op_pair = dfd_cli_pair; + client->internal_data = internal; + BT_INFO("Dfd client initialized"); + bt_mesh_mutex_create(&dfd_client_lock); + + return 0; +} + +#if CONFIG_BLE_MESH_DEINIT +static int dfd_cli_deinit(struct bt_mesh_model *model) +{ + bt_mesh_dfd_client_t *client = NULL; + + if (!model) { + BT_ERR("Invalid Device Firmware Distribution Client model"); + return -EINVAL; + } + + client = (bt_mesh_dfd_client_t *)model->user_data; + if (!client) { + BT_ERR("No Device Firmware Distribution Client context provided"); + return -EINVAL; + } + + if (client->internal_data) { + /* Remove items from the list */ + bt_mesh_client_clear_list(client->internal_data); + + /* Free the allocated internal data */ + bt_mesh_free(client->internal_data); + client->internal_data = NULL; + } + + bt_mesh_mutex_free(&dfd_client_lock); + + return 0; +} +#endif + +const struct bt_mesh_model_cb _bt_mesh_dfd_cli_cb = { + .init = dfd_cli_init, +#if CONFIG_BLE_MESH_DEINIT + .deinit = dfd_cli_deinit, +#endif /* CONFIG_BLE_MESH_DEINIT */ +}; +#endif diff --git a/components/bt/esp_ble_mesh/v1.1/dfu/dfd_srv.c b/components/bt/esp_ble_mesh/v1.1/dfu/dfd_srv.c index 7821b21b88..955419ef1f 100644 --- a/components/bt/esp_ble_mesh/v1.1/dfu/dfd_srv.c +++ b/components/bt/esp_ble_mesh/v1.1/dfu/dfd_srv.c @@ -6,7 +6,6 @@ */ #include #include "dfu_slot.h" -#include "dfd.h" #include "dfu.h" #include "dfd_srv_internal.h" #include "net.h" @@ -19,7 +18,7 @@ #if CONFIG_BLE_MESH_DFD_SRV #define DFD_UPLOAD_STATUS_MSG_MAXLEN (5 + CONFIG_BLE_MESH_DFU_FWID_MAXLEN) -_Static_assert((DFD_UPLOAD_STATUS_MSG_MAXLEN + BLE_MESH_MODEL_OP_LEN(BT_MESH_DFD_OP_UPLOAD_STATUS) + +_Static_assert((DFD_UPLOAD_STATUS_MSG_MAXLEN + BLE_MESH_MODEL_OP_LEN(BLE_MESH_DFD_OP_UPLOAD_STATUS) + BLE_MESH_MIC_SHORT) <= BLE_MESH_TX_SDU_MAX, "The Firmware Distribution Upload Status message does not fit into the maximum " "outgoing SDU size."); @@ -27,21 +26,21 @@ _Static_assert((DFD_UPLOAD_STATUS_MSG_MAXLEN + BLE_MESH_MODEL_OP_LEN(BT_MESH_DFD #define DFD_UPLOAD_START_MSG_MAXLEN (16 + CONFIG_BLE_MESH_DFU_FWID_MAXLEN + \ CONFIG_BLE_MESH_DFU_METADATA_MAXLEN) -_Static_assert((DFD_UPLOAD_START_MSG_MAXLEN + BLE_MESH_MODEL_OP_LEN(BT_MESH_DFD_OP_UPLOAD_START) + +_Static_assert((DFD_UPLOAD_START_MSG_MAXLEN + BLE_MESH_MODEL_OP_LEN(BLE_MESH_DFD_OP_UPLOAD_START) + BLE_MESH_MIC_SHORT) <= BLE_MESH_RX_SDU_MAX, "The Firmware Distribution Upload Start message does not fit into the maximum " "incoming SDU size."); #define DFD_RECEIVERS_LIST_MSG_MAXLEN (4 + CONFIG_BLE_MESH_DFD_SRV_TARGETS_MAX * 5) -_Static_assert((DFD_RECEIVERS_LIST_MSG_MAXLEN + BLE_MESH_MODEL_OP_LEN(BT_MESH_DFD_OP_RECEIVERS_LIST) + +_Static_assert((DFD_RECEIVERS_LIST_MSG_MAXLEN + BLE_MESH_MODEL_OP_LEN(BLE_MESH_DFD_OP_RECEIVERS_LIST) + BLE_MESH_MIC_SHORT) <= BLE_MESH_TX_SDU_MAX, "The Firmware Distribution Receivers List message does not fit into the maximum " "outgoing SDU size."); #define DFD_RECEIVERS_ADD_MSG_MAXLEN (CONFIG_BLE_MESH_DFD_SRV_TARGETS_MAX * 3) -_Static_assert((DFD_RECEIVERS_ADD_MSG_MAXLEN + BLE_MESH_MODEL_OP_LEN(BT_MESH_DFD_OP_RECEIVERS_ADD) + +_Static_assert((DFD_RECEIVERS_ADD_MSG_MAXLEN + BLE_MESH_MODEL_OP_LEN(BLE_MESH_DFD_OP_RECEIVERS_ADD) + BLE_MESH_MIC_SHORT) <= BLE_MESH_RX_SDU_MAX, "The Firmware Distribution Receivers Add message does not fit into the maximum " "incoming SDU size."); @@ -76,15 +75,15 @@ static struct bt_mesh_dfu_target *target_get(struct bt_mesh_dfd_srv *srv, static bool is_busy(const struct bt_mesh_dfd_srv *srv) { - return srv->phase == BT_MESH_DFD_PHASE_TRANSFER_ACTIVE || - srv->phase == BT_MESH_DFD_PHASE_TRANSFER_SUCCESS || - srv->phase == BT_MESH_DFD_PHASE_APPLYING_UPDATE; + return srv->phase == BLE_MESH_DFD_PHASE_TRANSFER_ACTIVE || + srv->phase == BLE_MESH_DFD_PHASE_TRANSFER_SUCCESS || + srv->phase == BLE_MESH_DFD_PHASE_APPLYING_UPDATE; } static bool upload_is_busy(const struct bt_mesh_dfd_srv *srv) { return bt_mesh_blob_srv_is_busy(&srv->upload.blob) || - srv->upload.phase == BT_MESH_DFD_UPLOAD_PHASE_TRANSFER_ACTIVE; + srv->upload.phase == BLE_MESH_DFD_UPLOAD_PHASE_TRANSFER_ACTIVE; } static int slot_del(struct bt_mesh_dfd_srv *srv, const struct bt_mesh_dfu_slot *slot) @@ -100,8 +99,8 @@ static void receivers_status_rsp(struct bt_mesh_dfd_srv *srv, struct bt_mesh_msg_ctx *ctx, enum bt_mesh_dfd_status status) { - BLE_MESH_MODEL_BUF_DEFINE(buf, BT_MESH_DFD_OP_RECEIVERS_STATUS, 3); - bt_mesh_model_msg_init(&buf, BT_MESH_DFD_OP_RECEIVERS_STATUS); + BLE_MESH_MODEL_BUF_DEFINE(buf, BLE_MESH_DFD_OP_RECEIVERS_STATUS, 3); + bt_mesh_model_msg_init(&buf, BLE_MESH_DFD_OP_RECEIVERS_STATUS); net_buf_simple_add_u8(&buf, status); net_buf_simple_add_le16(&buf, srv->target_cnt); @@ -112,7 +111,7 @@ static void receivers_status_rsp(struct bt_mesh_dfd_srv *srv, static int handle_receivers_add(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx, struct net_buf_simple *buf) { - enum bt_mesh_dfd_status status = BT_MESH_DFD_SUCCESS; + enum bt_mesh_dfd_status status = BLE_MESH_DFD_SUCCESS; struct bt_mesh_dfd_srv *srv = mod->user_data; if (buf->len % 3) { @@ -121,11 +120,11 @@ static int handle_receivers_add(const struct bt_mesh_model *mod, struct bt_mesh_ if (bt_mesh_dfu_cli_is_busy(&srv->dfu)) { receivers_status_rsp(srv, ctx, - BT_MESH_DFD_ERR_BUSY_WITH_DISTRIBUTION); + BLE_MESH_DFD_ERR_BUSY_WITH_DISTRIBUTION); return 0; } - while (buf->len >= 3 && status == BT_MESH_DFD_SUCCESS) { + while (buf->len >= 3 && status == BLE_MESH_DFD_SUCCESS) { uint8_t img_idx; uint16_t addr; @@ -166,9 +165,9 @@ static int handle_receivers_get(const struct bt_mesh_model *mod, struct bt_mesh_ /* Create a buffer that can fit the full target list, maxing out at TX_SDU_MAX: */ NET_BUF_SIMPLE_DEFINE( - rsp, BLE_MESH_MODEL_BUF_LEN(BT_MESH_DFD_OP_RECEIVERS_LIST, + rsp, BLE_MESH_MODEL_BUF_LEN(BLE_MESH_DFD_OP_RECEIVERS_LIST, DFD_RECEIVERS_LIST_MSG_MAXLEN)); - bt_mesh_model_msg_init(&rsp, BT_MESH_DFD_OP_RECEIVERS_LIST); + bt_mesh_model_msg_init(&rsp, BLE_MESH_DFD_OP_RECEIVERS_LIST); net_buf_simple_add_le16(&rsp, srv->target_cnt); net_buf_simple_add_le16(&rsp, first); @@ -208,8 +207,8 @@ static int handle_capabilities_get(const struct bt_mesh_model *mod, struct bt_me { size_t size = 0; - BLE_MESH_MODEL_BUF_DEFINE(rsp, BT_MESH_DFD_OP_CAPABILITIES_STATUS, 17); - bt_mesh_model_msg_init(&rsp, BT_MESH_DFD_OP_CAPABILITIES_STATUS); + BLE_MESH_MODEL_BUF_DEFINE(rsp, BLE_MESH_DFD_OP_CAPABILITIES_GET, 17); + bt_mesh_model_msg_init(&rsp, BLE_MESH_DFD_OP_CAPABILITIES_GET); net_buf_simple_add_le16(&rsp, CONFIG_BLE_MESH_DFD_SRV_TARGETS_MAX); net_buf_simple_add_le16(&rsp, CONFIG_BLE_MESH_DFU_SLOT_CNT); @@ -243,13 +242,13 @@ static int handle_capabilities_get(const struct bt_mesh_model *mod, struct bt_me static void status_rsp(struct bt_mesh_dfd_srv *srv, struct bt_mesh_msg_ctx *ctx, enum bt_mesh_dfd_status status) { - BLE_MESH_MODEL_BUF_DEFINE(rsp, BT_MESH_DFD_OP_STATUS, 12); - bt_mesh_model_msg_init(&rsp, BT_MESH_DFD_OP_STATUS); + BLE_MESH_MODEL_BUF_DEFINE(rsp, BLE_MESH_DFD_OP_STATUS, 12); + bt_mesh_model_msg_init(&rsp, BLE_MESH_DFD_OP_STATUS); net_buf_simple_add_u8(&rsp, status); net_buf_simple_add_u8(&rsp, srv->phase); - if (srv->phase == BT_MESH_DFD_PHASE_IDLE || !srv->dfu.xfer.slot) { + if (srv->phase == BLE_MESH_DFD_PHASE_IDLE || !srv->dfu.xfer.slot) { bt_mesh_model_send(srv->mod, ctx, &rsp, NULL, NULL); return; } @@ -270,7 +269,7 @@ static int handle_get(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *c { struct bt_mesh_dfd_srv *srv = mod->user_data; - status_rsp(srv, ctx, BT_MESH_DFD_SUCCESS); + status_rsp(srv, ctx, BLE_MESH_DFD_SUCCESS); return 0; } @@ -292,7 +291,7 @@ static int handle_start(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx if (buf->len == 16) { /* TODO: Virtual addresses not supported. */ - status_rsp(srv, ctx, BT_MESH_DFD_ERR_INTERNAL); + status_rsp(srv, ctx, BLE_MESH_DFD_ERR_INTERNAL); return 0; } @@ -343,14 +342,14 @@ static void upload_status_rsp_with_progress(struct bt_mesh_dfd_srv *srv, enum bt_mesh_dfd_status status, uint8_t progress) { - BLE_MESH_MODEL_BUF_DEFINE(rsp, BT_MESH_DFD_OP_UPLOAD_STATUS, + BLE_MESH_MODEL_BUF_DEFINE(rsp, BLE_MESH_DFD_OP_UPLOAD_STATUS, DFD_UPLOAD_STATUS_MSG_MAXLEN); - bt_mesh_model_msg_init(&rsp, BT_MESH_DFD_OP_UPLOAD_STATUS); + bt_mesh_model_msg_init(&rsp, BLE_MESH_DFD_OP_UPLOAD_STATUS); net_buf_simple_add_u8(&rsp, status); net_buf_simple_add_u8(&rsp, srv->upload.phase); - if (srv->upload.phase == BT_MESH_DFD_UPLOAD_PHASE_IDLE || + if (srv->upload.phase == BLE_MESH_DFD_UPLOAD_PHASE_IDLE || !srv->upload.slot) { bt_mesh_model_send(srv->mod, ctx, &rsp, NULL, NULL); return; @@ -395,7 +394,7 @@ static int handle_upload_get(const struct bt_mesh_model *mod, struct bt_mesh_msg { struct bt_mesh_dfd_srv *srv = mod->user_data; - upload_status_rsp(srv, ctx, BT_MESH_DFD_SUCCESS); + upload_status_rsp(srv, ctx, BLE_MESH_DFD_SUCCESS); return 0; } @@ -410,22 +409,22 @@ static inline int set_upload_fwid(struct bt_mesh_dfd_srv *srv, struct bt_mesh_ms case -EALREADY: /* Other server is in progress with this fwid */ bt_mesh_dfu_slot_release(srv->upload.slot); srv->upload.slot = NULL; - upload_status_rsp(srv, ctx, BT_MESH_DFD_ERR_INTERNAL); + upload_status_rsp(srv, ctx, BLE_MESH_DFD_ERR_INTERNAL); break; case -EEXIST: /* Img with this fwid already is in list */ - srv->upload.phase = BT_MESH_DFD_UPLOAD_PHASE_TRANSFER_SUCCESS; + srv->upload.phase = BLE_MESH_DFD_UPLOAD_PHASE_TRANSFER_SUCCESS; bt_mesh_dfu_slot_release(srv->upload.slot); err = bt_mesh_dfu_slot_get(fwid, fwid_len, &srv->upload.slot); if (!err) { - upload_status_rsp_with_progress(srv, ctx, BT_MESH_DFD_SUCCESS, 100); + upload_status_rsp_with_progress(srv, ctx, BLE_MESH_DFD_SUCCESS, 100); } else { srv->upload.slot = NULL; - upload_status_rsp(srv, ctx, BT_MESH_DFD_ERR_INTERNAL); + upload_status_rsp(srv, ctx, BLE_MESH_DFD_ERR_INTERNAL); } break; case 0: - srv->upload.phase = BT_MESH_DFD_UPLOAD_PHASE_TRANSFER_ACTIVE; + srv->upload.phase = BLE_MESH_DFD_UPLOAD_PHASE_TRANSFER_ACTIVE; break; case -EINVAL: /* Slot in wrong state. */ default: @@ -464,14 +463,14 @@ static int handle_upload_start(const struct bt_mesh_model *mod, struct bt_mesh_m if (size > CONFIG_BLE_MESH_DFD_SRV_SLOT_MAX_SIZE) { upload_status_rsp(srv, ctx, - BT_MESH_DFD_ERR_INSUFFICIENT_RESOURCES); + BLE_MESH_DFD_ERR_INSUFFICIENT_RESOURCES); return 0; } if (upload_is_busy(srv)) { if (!srv->upload.slot) { BT_WARN("Busy with no upload slot"); - upload_status_rsp(srv, ctx, BT_MESH_DFD_ERR_INTERNAL); + upload_status_rsp(srv, ctx, BLE_MESH_DFD_ERR_INTERNAL); return 0; } @@ -487,12 +486,12 @@ static int handle_upload_start(const struct bt_mesh_model *mod, struct bt_mesh_m #endif ) { BT_DBG("Duplicate upload start"); - upload_status_rsp(srv, ctx, BT_MESH_DFD_SUCCESS); + upload_status_rsp(srv, ctx, BLE_MESH_DFD_SUCCESS); return 0; } BT_WARN("Upload already in progress"); - upload_status_rsp(srv, ctx, BT_MESH_DFD_ERR_BUSY_WITH_UPLOAD); + upload_status_rsp(srv, ctx, BLE_MESH_DFD_ERR_BUSY_WITH_UPLOAD); return 0; } @@ -511,7 +510,7 @@ static int handle_upload_start(const struct bt_mesh_model *mod, struct bt_mesh_m if (!srv->upload.slot) { BT_WARN("No space for slot"); upload_status_rsp(srv, ctx, - BT_MESH_DFD_ERR_INSUFFICIENT_RESOURCES); + BLE_MESH_DFD_ERR_INSUFFICIENT_RESOURCES); return 0; } @@ -523,7 +522,7 @@ static int handle_upload_start(const struct bt_mesh_model *mod, struct bt_mesh_m err = bt_mesh_dfu_slot_info_set(srv->upload.slot, size, meta, meta_len); switch (err) { case -EFBIG: - upload_status_rsp(srv, ctx, BT_MESH_DFD_ERR_INTERNAL); + upload_status_rsp(srv, ctx, BLE_MESH_DFD_ERR_INTERNAL); break; case 0: break; @@ -536,7 +535,7 @@ static int handle_upload_start(const struct bt_mesh_model *mod, struct bt_mesh_m if (err || !srv->io) { BT_ERR("App rejected upload. err: %d io: %p", err, srv->io); bt_mesh_dfu_slot_release(srv->upload.slot); - upload_status_rsp(srv, ctx, BT_MESH_DFD_ERR_INTERNAL); + upload_status_rsp(srv, ctx, BLE_MESH_DFD_ERR_INTERNAL); return 0; } @@ -545,14 +544,14 @@ static int handle_upload_start(const struct bt_mesh_model *mod, struct bt_mesh_m if (err) { BT_ERR("BLOB Server rejected upload (err: %d)", err); bt_mesh_dfu_slot_release(srv->upload.slot); - upload_status_rsp(srv, ctx, BT_MESH_DFD_ERR_INTERNAL); + upload_status_rsp(srv, ctx, BLE_MESH_DFD_ERR_INTERNAL); return 0; } BT_DBG("%s", bt_hex(fwid, fwid_len)); - srv->upload.phase = BT_MESH_DFD_UPLOAD_PHASE_TRANSFER_ACTIVE; - upload_status_rsp(srv, ctx, BT_MESH_DFD_SUCCESS); + srv->upload.phase = BLE_MESH_DFD_UPLOAD_PHASE_TRANSFER_ACTIVE; + upload_status_rsp(srv, ctx, BLE_MESH_DFD_SUCCESS); return 0; } @@ -588,11 +587,11 @@ static int handle_upload_start_oob(const struct bt_mesh_model *mod, struct bt_me !memcmp(uri, srv->upload.oob.uri, uri_len) && !memcmp(fwid, srv->upload.oob.current_fwid, fwid_len)) { /* Same image, return SUCCESS for idempotency */ - upload_status_rsp(srv, ctx, BT_MESH_DFD_SUCCESS); + upload_status_rsp(srv, ctx, BLE_MESH_DFD_SUCCESS); return 0; } #endif - upload_status_rsp(srv, ctx, BT_MESH_DFD_ERR_BUSY_WITH_UPLOAD); + upload_status_rsp(srv, ctx, BLE_MESH_DFD_ERR_BUSY_WITH_UPLOAD); return 0; #ifdef CONFIG_BLE_MESH_DFD_SRV_OOB_UPLOAD } else if (srv->upload.is_oob && srv->upload.is_pending_oob_check) { @@ -604,14 +603,14 @@ static int handle_upload_start_oob(const struct bt_mesh_model *mod, struct bt_me #ifdef CONFIG_BLE_MESH_DFD_SRV_OOB_UPLOAD if (uri_len > CONFIG_BLE_MESH_DFU_URI_MAXLEN || fwid_len > CONFIG_BLE_MESH_DFU_FWID_MAXLEN) { - upload_status_rsp(srv, ctx, BT_MESH_DFD_ERR_INTERNAL); + upload_status_rsp(srv, ctx, BLE_MESH_DFD_ERR_INTERNAL); return 0; } struct bt_mesh_dfu_slot *slot = bt_mesh_dfu_slot_reserve(); if (slot == NULL) { - upload_status_rsp(srv, ctx, BT_MESH_DFD_ERR_INSUFFICIENT_RESOURCES); + upload_status_rsp(srv, ctx, BLE_MESH_DFD_ERR_INSUFFICIENT_RESOURCES); return 0; } @@ -635,14 +634,14 @@ static int handle_upload_start_oob(const struct bt_mesh_model *mod, struct bt_me srv->upload.oob.current_fwid, srv->upload.oob.current_fwid_len); - if (status != BT_MESH_DFD_SUCCESS) { + if (status != BLE_MESH_DFD_SUCCESS) { upload_status_rsp(srv, ctx, status); bt_mesh_dfu_slot_release(srv->upload.slot); } else { srv->upload.is_pending_oob_check = true; } #else - upload_status_rsp(srv, ctx, BT_MESH_DFD_ERR_URI_NOT_SUPPORTED); + upload_status_rsp(srv, ctx, BLE_MESH_DFD_ERR_URI_NOT_SUPPORTED); #endif /* CONFIG_BLE_MESH_DFD_SRV_OOB_UPLOAD */ return 0; @@ -653,7 +652,7 @@ static int handle_upload_cancel(const struct bt_mesh_model *mod, struct bt_mesh_ { struct bt_mesh_dfd_srv *srv = mod->user_data; - srv->upload.phase = BT_MESH_DFD_UPLOAD_PHASE_IDLE; + srv->upload.phase = BLE_MESH_DFD_UPLOAD_PHASE_IDLE; #ifdef CONFIG_BLE_MESH_DFD_SRV_OOB_UPLOAD if (srv->upload.is_oob) { srv->cb->cancel_oob_upload(srv, srv->upload.slot); @@ -662,7 +661,7 @@ static int handle_upload_cancel(const struct bt_mesh_model *mod, struct bt_mesh_ { (void)bt_mesh_blob_srv_cancel(&srv->upload.blob); } - upload_status_rsp(srv, ctx, BT_MESH_DFD_SUCCESS); + upload_status_rsp(srv, ctx, BLE_MESH_DFD_SUCCESS); return 0; } @@ -672,9 +671,9 @@ static void fw_status_rsp(struct bt_mesh_dfd_srv *srv, enum bt_mesh_dfd_status status, uint16_t idx, const uint8_t *fwid, size_t fwid_len) { - BLE_MESH_MODEL_BUF_DEFINE(rsp, BT_MESH_DFD_OP_FW_STATUS, + BLE_MESH_MODEL_BUF_DEFINE(rsp, BLE_MESH_DFD_OP_FW_STATUS, 7 + CONFIG_BLE_MESH_DFU_FWID_MAXLEN); - bt_mesh_model_msg_init(&rsp, BT_MESH_DFD_OP_FW_STATUS); + bt_mesh_model_msg_init(&rsp, BLE_MESH_DFD_OP_FW_STATUS); net_buf_simple_add_u8(&rsp, status); net_buf_simple_add_le16(&rsp, bt_mesh_dfu_slot_count()); @@ -701,10 +700,10 @@ static int handle_fw_get(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx idx = bt_mesh_dfu_slot_get(fwid, fwid_len, &slot); if (idx >= 0) { - fw_status_rsp(srv, ctx, BT_MESH_DFD_SUCCESS, idx, fwid, + fw_status_rsp(srv, ctx, BLE_MESH_DFD_SUCCESS, idx, fwid, fwid_len); } else { - fw_status_rsp(srv, ctx, BT_MESH_DFD_ERR_FW_NOT_FOUND, 0xffff, + fw_status_rsp(srv, ctx, BLE_MESH_DFD_ERR_FW_NOT_FOUND, 0xffff, fwid, fwid_len); } @@ -722,10 +721,10 @@ static int handle_fw_get_by_index(const struct bt_mesh_model *mod, struct bt_mes slot = bt_mesh_dfu_slot_at(idx); if (slot) { - fw_status_rsp(srv, ctx, BT_MESH_DFD_SUCCESS, idx, slot->fwid, + fw_status_rsp(srv, ctx, BLE_MESH_DFD_SUCCESS, idx, slot->fwid, slot->fwid_len); } else { - fw_status_rsp(srv, ctx, BT_MESH_DFD_ERR_FW_NOT_FOUND, idx, + fw_status_rsp(srv, ctx, BLE_MESH_DFD_ERR_FW_NOT_FOUND, idx, NULL, 0); } @@ -772,23 +771,23 @@ static int handle_fw_delete_all(const struct bt_mesh_model *mod, struct bt_mesh_ } const struct bt_mesh_model_op _bt_mesh_dfd_srv_op[] = { - { BT_MESH_DFD_OP_RECEIVERS_ADD, 3, (void *)handle_receivers_add }, - { BT_MESH_DFD_OP_RECEIVERS_DELETE_ALL, 0, (void *)handle_receivers_delete_all }, - { BT_MESH_DFD_OP_RECEIVERS_GET, 4, (void *)handle_receivers_get }, - { BT_MESH_DFD_OP_CAPABILITIES_GET, 0, (void *)handle_capabilities_get }, - { BT_MESH_DFD_OP_GET, 0, (void *)handle_get }, - { BT_MESH_DFD_OP_START, 10, (void *)handle_start }, - { BT_MESH_DFD_OP_SUSPEND, 0, (void *)handle_suspend }, - { BT_MESH_DFD_OP_CANCEL, 0, (void *)handle_cancel }, - { BT_MESH_DFD_OP_APPLY, 0, (void *)handle_apply }, - { BT_MESH_DFD_OP_UPLOAD_GET, 0, (void *)handle_upload_get }, - { BT_MESH_DFD_OP_UPLOAD_START, 16, (void *)handle_upload_start }, - { BT_MESH_DFD_OP_UPLOAD_START_OOB, 2, (void *)handle_upload_start_oob }, - { BT_MESH_DFD_OP_UPLOAD_CANCEL, 0, (void *)handle_upload_cancel }, - { BT_MESH_DFD_OP_FW_GET, 0, (void *)handle_fw_get }, - { BT_MESH_DFD_OP_FW_GET_BY_INDEX, 2, (void *)handle_fw_get_by_index }, - { BT_MESH_DFD_OP_FW_DELETE, 0, (void *)handle_fw_delete }, - { BT_MESH_DFD_OP_FW_DELETE_ALL, 0, (void *)handle_fw_delete_all }, + { BLE_MESH_DFD_OP_RECEIVERS_ADD, 3, (void *)handle_receivers_add }, + { BLE_MESH_DFD_OP_RECEIVERS_DELETE_ALL, 0, (void *)handle_receivers_delete_all }, + { BLE_MESH_DFD_OP_RECEIVERS_GET, 4, (void *)handle_receivers_get }, + { BLE_MESH_DFD_OP_CAPABILITIES_GET, 0, (void *)handle_capabilities_get }, + { BLE_MESH_DFD_OP_GET, 0, (void *)handle_get }, + { BLE_MESH_DFD_OP_START, 10, (void *)handle_start }, + { BLE_MESH_DFD_OP_SUSPEND, 0, (void *)handle_suspend }, + { BLE_MESH_DFD_OP_CANCEL, 0, (void *)handle_cancel }, + { BLE_MESH_DFD_OP_APPLY, 0, (void *)handle_apply }, + { BLE_MESH_DFD_OP_UPLOAD_GET, 0, (void *)handle_upload_get }, + { BLE_MESH_DFD_OP_UPLOAD_START, 16, (void *)handle_upload_start }, + { BLE_MESH_DFD_OP_UPLOAD_START_OOB, 2, (void *)handle_upload_start_oob }, + { BLE_MESH_DFD_OP_UPLOAD_CANCEL, 0, (void *)handle_upload_cancel }, + { BLE_MESH_DFD_OP_FW_GET, 0, (void *)handle_fw_get }, + { BLE_MESH_DFD_OP_FW_GET_BY_INDEX, 2, (void *)handle_fw_get_by_index }, + { BLE_MESH_DFD_OP_FW_DELETE, 0, (void *)handle_fw_delete }, + { BLE_MESH_DFD_OP_FW_DELETE_ALL, 0, (void *)handle_fw_delete_all }, BLE_MESH_MODEL_OP_END }; @@ -798,7 +797,7 @@ static void dfu_suspended(struct bt_mesh_dfu_cli *cli) struct bt_mesh_dfd_srv *srv = CONTAINER_OF(cli, struct bt_mesh_dfd_srv, dfu); - dfd_phase_set(srv, BT_MESH_DFD_PHASE_TRANSFER_SUSPENDED); + dfd_phase_set(srv, BLE_MESH_DFD_PHASE_TRANSFER_SUSPENDED); } static void dfu_ended(struct bt_mesh_dfu_cli *cli, @@ -810,31 +809,31 @@ static void dfu_ended(struct bt_mesh_dfu_cli *cli, BT_DBG("reason: %u, phase: %u, apply: %u", reason, srv->phase, srv->apply); - if (srv->phase == BT_MESH_DFD_PHASE_IDLE) { + if (srv->phase == BLE_MESH_DFD_PHASE_IDLE) { return; } - if (srv->phase == BT_MESH_DFD_PHASE_CANCELING_UPDATE) { - dfd_phase_set(srv, BT_MESH_DFD_PHASE_IDLE); + if (srv->phase == BLE_MESH_DFD_PHASE_CANCELING_UPDATE) { + dfd_phase_set(srv, BLE_MESH_DFD_PHASE_IDLE); return; } if (reason != BLE_MESH_DFU_SUCCESS) { - dfd_phase_set(srv, BT_MESH_DFD_PHASE_FAILED); + dfd_phase_set(srv, BLE_MESH_DFD_PHASE_FAILED); return; } if (!srv->apply) { - dfd_phase_set(srv, BT_MESH_DFD_PHASE_TRANSFER_SUCCESS); + dfd_phase_set(srv, BLE_MESH_DFD_PHASE_TRANSFER_SUCCESS); return; } - dfd_phase_set(srv, BT_MESH_DFD_PHASE_APPLYING_UPDATE); + dfd_phase_set(srv, BLE_MESH_DFD_PHASE_APPLYING_UPDATE); err = bt_mesh_dfu_cli_apply(cli); if (err) { BT_ERR("Apply failed: %d", err); - dfd_phase_set(srv, BT_MESH_DFD_PHASE_FAILED); + dfd_phase_set(srv, BLE_MESH_DFD_PHASE_FAILED); } } @@ -844,19 +843,19 @@ static void dfu_applied(struct bt_mesh_dfu_cli *cli) CONTAINER_OF(cli, struct bt_mesh_dfd_srv, dfu); int err; - if (srv->phase == BT_MESH_DFD_PHASE_CANCELING_UPDATE) { - dfd_phase_set(srv, BT_MESH_DFD_PHASE_FAILED); + if (srv->phase == BLE_MESH_DFD_PHASE_CANCELING_UPDATE) { + dfd_phase_set(srv, BLE_MESH_DFD_PHASE_FAILED); return; } - if (srv->phase != BT_MESH_DFD_PHASE_APPLYING_UPDATE) { + if (srv->phase != BLE_MESH_DFD_PHASE_APPLYING_UPDATE) { return; } err = bt_mesh_dfu_cli_confirm(cli); if (err) { BT_ERR("Confirm failed: %d", err); - dfd_phase_set(srv, BT_MESH_DFD_PHASE_FAILED); + dfd_phase_set(srv, BLE_MESH_DFD_PHASE_FAILED); } } @@ -865,12 +864,12 @@ static void dfu_confirmed(struct bt_mesh_dfu_cli *cli) struct bt_mesh_dfd_srv *srv = CONTAINER_OF(cli, struct bt_mesh_dfd_srv, dfu); - if (srv->phase != BT_MESH_DFD_PHASE_APPLYING_UPDATE && - srv->phase != BT_MESH_DFD_PHASE_CANCELING_UPDATE) { + if (srv->phase != BLE_MESH_DFD_PHASE_APPLYING_UPDATE && + srv->phase != BLE_MESH_DFD_PHASE_CANCELING_UPDATE) { return; } - dfd_phase_set(srv, BT_MESH_DFD_PHASE_COMPLETED); + dfd_phase_set(srv, BLE_MESH_DFD_PHASE_COMPLETED); } const struct bt_mesh_dfu_cli_cb _bt_mesh_dfd_srv_dfu_cb = { @@ -892,19 +891,19 @@ static void upload_end(struct bt_mesh_blob_srv *b, uint64_t id, bool success) struct bt_mesh_dfd_srv *srv = CONTAINER_OF(b, struct bt_mesh_dfd_srv, upload.blob); - if (srv->upload.phase != BT_MESH_DFD_UPLOAD_PHASE_TRANSFER_ACTIVE) { + if (srv->upload.phase != BLE_MESH_DFD_UPLOAD_PHASE_TRANSFER_ACTIVE) { return; } BT_DBG("%u", success); if (success && (bt_mesh_dfu_slot_commit(srv->upload.slot) == 0)) { - srv->upload.phase = BT_MESH_DFD_UPLOAD_PHASE_TRANSFER_SUCCESS; + srv->upload.phase = BLE_MESH_DFD_UPLOAD_PHASE_TRANSFER_SUCCESS; return; } /* Will delete slot when we start a new upload */ - srv->upload.phase = BT_MESH_DFD_UPLOAD_PHASE_TRANSFER_ERROR; + srv->upload.phase = BLE_MESH_DFD_UPLOAD_PHASE_TRANSFER_ERROR; } static void upload_timeout(struct bt_mesh_blob_srv *b) @@ -933,9 +932,9 @@ static void dfd_srv_reset(struct bt_mesh_model *mod) { struct bt_mesh_dfd_srv *srv = mod->user_data; - dfd_phase_set(srv, BT_MESH_DFD_PHASE_IDLE); + dfd_phase_set(srv, BLE_MESH_DFD_PHASE_IDLE); - srv->upload.phase = BT_MESH_DFD_UPLOAD_PHASE_IDLE; + srv->upload.phase = BLE_MESH_DFD_UPLOAD_PHASE_IDLE; sys_slist_init(&srv->inputs.targets); srv->target_cnt = 0; @@ -963,19 +962,19 @@ enum bt_mesh_dfd_status bt_mesh_dfd_srv_receiver_add(struct bt_mesh_dfd_srv *srv struct bt_mesh_blob_target_pull *p; if (!BLE_MESH_ADDR_IS_UNICAST(addr)) { - return BT_MESH_DFD_SUCCESS; + return BLE_MESH_DFD_SUCCESS; } t = target_get(srv, addr); if (t) { t->img_idx = img_idx; - return BT_MESH_DFD_SUCCESS; + return BLE_MESH_DFD_SUCCESS; } /* New target node, add it to the list */ if (srv->target_cnt == ARRAY_SIZE(srv->targets)) { - return BT_MESH_DFD_ERR_INSUFFICIENT_RESOURCES; + return BLE_MESH_DFD_ERR_INSUFFICIENT_RESOURCES; } t = &srv->targets[srv->target_cnt]; @@ -991,19 +990,19 @@ enum bt_mesh_dfd_status bt_mesh_dfd_srv_receiver_add(struct bt_mesh_dfd_srv *srv BT_DBG("Added receiver 0x%04x img: %u", t->blob.addr, t->img_idx); - return BT_MESH_DFD_SUCCESS; + return BLE_MESH_DFD_SUCCESS; } enum bt_mesh_dfd_status bt_mesh_dfd_srv_receivers_delete_all(struct bt_mesh_dfd_srv *srv) { if (bt_mesh_dfu_cli_is_busy(&srv->dfu)) { - return BT_MESH_DFD_ERR_BUSY_WITH_DISTRIBUTION; + return BLE_MESH_DFD_ERR_BUSY_WITH_DISTRIBUTION; } sys_slist_init(&srv->inputs.targets); srv->target_cnt = 0; - return BT_MESH_DFD_SUCCESS; + return BLE_MESH_DFD_SUCCESS; } enum bt_mesh_dfd_status bt_mesh_dfd_srv_start(struct bt_mesh_dfd_srv *srv, @@ -1013,17 +1012,17 @@ enum bt_mesh_dfd_status bt_mesh_dfd_srv_start(struct bt_mesh_dfd_srv *srv, struct bt_mesh_dfu_cli_xfer xfer = { 0 }; if (!srv->target_cnt) { - return BT_MESH_DFD_ERR_RECEIVERS_LIST_EMPTY; + return BLE_MESH_DFD_ERR_RECEIVERS_LIST_EMPTY; } if (!bt_mesh_app_key_get(params->app_idx)) { - return BT_MESH_DFD_ERR_INVALID_APPKEY_INDEX; + return BLE_MESH_DFD_ERR_INVALID_APPKEY_INDEX; } xfer.mode = params->xfer_mode; xfer.slot = bt_mesh_dfu_slot_at(params->slot_idx); if (!xfer.slot) { - return BT_MESH_DFD_ERR_FW_NOT_FOUND; + return BLE_MESH_DFD_ERR_FW_NOT_FOUND; } if (srv->inputs.app_idx == params->app_idx && @@ -1032,29 +1031,29 @@ enum bt_mesh_dfd_status bt_mesh_dfd_srv_start(struct bt_mesh_dfd_srv *srv, srv->dfu.xfer.blob.mode == xfer.mode && srv->apply == params->apply && srv->slot_idx == params->slot_idx) { if (is_busy(srv) || - srv->phase == BT_MESH_DFD_PHASE_COMPLETED) { + srv->phase == BLE_MESH_DFD_PHASE_COMPLETED) { BT_WARN("Already completed or in progress"); - return BT_MESH_DFD_SUCCESS; - } else if (srv->phase == BT_MESH_DFD_PHASE_TRANSFER_SUSPENDED) { + return BLE_MESH_DFD_SUCCESS; + } else if (srv->phase == BLE_MESH_DFD_PHASE_TRANSFER_SUSPENDED) { bt_mesh_dfu_cli_resume(&srv->dfu); - dfd_phase_set(srv, BT_MESH_DFD_PHASE_TRANSFER_ACTIVE); - return BT_MESH_DFD_SUCCESS; + dfd_phase_set(srv, BLE_MESH_DFD_PHASE_TRANSFER_ACTIVE); + return BLE_MESH_DFD_SUCCESS; } } else if (is_busy(srv) || - srv->phase == BT_MESH_DFD_PHASE_TRANSFER_SUSPENDED) { + srv->phase == BLE_MESH_DFD_PHASE_TRANSFER_SUSPENDED) { BT_WARN("Busy with distribution"); - return BT_MESH_DFD_ERR_BUSY_WITH_DISTRIBUTION; + return BLE_MESH_DFD_ERR_BUSY_WITH_DISTRIBUTION; } - if (srv->phase == BT_MESH_DFD_PHASE_CANCELING_UPDATE) { + if (srv->phase == BLE_MESH_DFD_PHASE_CANCELING_UPDATE) { BT_WARN("Canceling distribution"); - return BT_MESH_DFD_ERR_BUSY_WITH_DISTRIBUTION; + return BLE_MESH_DFD_ERR_BUSY_WITH_DISTRIBUTION; } srv->io = NULL; err = srv->cb->send(srv, xfer.slot, &srv->io); if (err || !srv->io) { - return BT_MESH_DFD_ERR_INTERNAL; + return BLE_MESH_DFD_ERR_INTERNAL; } sys_slist_init(&srv->inputs.targets); @@ -1083,35 +1082,35 @@ enum bt_mesh_dfd_status bt_mesh_dfd_srv_start(struct bt_mesh_dfd_srv *srv, /* DFD Server will always retrieve targets' capabilities before distributing a firmware.*/ xfer.blob_params = NULL; - dfd_phase_set(srv, BT_MESH_DFD_PHASE_TRANSFER_ACTIVE); + dfd_phase_set(srv, BLE_MESH_DFD_PHASE_TRANSFER_ACTIVE); err = bt_mesh_dfu_cli_send(&srv->dfu, &srv->inputs, srv->io, &xfer); if (err) { - dfd_phase_set(srv, BT_MESH_DFD_PHASE_IDLE); - return BT_MESH_DFD_ERR_INTERNAL; + dfd_phase_set(srv, BLE_MESH_DFD_PHASE_IDLE); + return BLE_MESH_DFD_ERR_INTERNAL; } - return BT_MESH_DFD_SUCCESS; + return BLE_MESH_DFD_SUCCESS; } enum bt_mesh_dfd_status bt_mesh_dfd_srv_suspend(struct bt_mesh_dfd_srv *srv) { int err; - if (srv->phase == BT_MESH_DFD_PHASE_TRANSFER_SUSPENDED) { - return BT_MESH_DFD_SUCCESS; + if (srv->phase == BLE_MESH_DFD_PHASE_TRANSFER_SUSPENDED) { + return BLE_MESH_DFD_SUCCESS; } - if (srv->phase != BT_MESH_DFD_PHASE_TRANSFER_ACTIVE) { - return BT_MESH_DFD_ERR_WRONG_PHASE; + if (srv->phase != BLE_MESH_DFD_PHASE_TRANSFER_ACTIVE) { + return BLE_MESH_DFD_ERR_WRONG_PHASE; } err = bt_mesh_dfu_cli_suspend(&srv->dfu); if (err) { - return BT_MESH_DFD_ERR_SUSPEND_FAILED; + return BLE_MESH_DFD_ERR_SUSPEND_FAILED; } - srv->phase = BT_MESH_DFD_PHASE_TRANSFER_SUSPENDED; - return BT_MESH_DFD_SUCCESS; + srv->phase = BLE_MESH_DFD_PHASE_TRANSFER_SUSPENDED; + return BLE_MESH_DFD_SUCCESS; } enum bt_mesh_dfd_status bt_mesh_dfd_srv_cancel(struct bt_mesh_dfd_srv *srv, @@ -1120,36 +1119,36 @@ enum bt_mesh_dfd_status bt_mesh_dfd_srv_cancel(struct bt_mesh_dfd_srv *srv, enum bt_mesh_dfd_phase prev_phase; int err; - if (srv->phase == BT_MESH_DFD_PHASE_CANCELING_UPDATE || - srv->phase == BT_MESH_DFD_PHASE_IDLE) { + if (srv->phase == BLE_MESH_DFD_PHASE_CANCELING_UPDATE || + srv->phase == BLE_MESH_DFD_PHASE_IDLE) { if (ctx != NULL) { - status_rsp(srv, ctx, BT_MESH_DFD_SUCCESS); + status_rsp(srv, ctx, BLE_MESH_DFD_SUCCESS); } - return BT_MESH_DFD_SUCCESS; + return BLE_MESH_DFD_SUCCESS; } - if (srv->phase == BT_MESH_DFD_PHASE_COMPLETED || - srv->phase == BT_MESH_DFD_PHASE_FAILED) { - dfd_phase_set(srv, BT_MESH_DFD_PHASE_IDLE); + if (srv->phase == BLE_MESH_DFD_PHASE_COMPLETED || + srv->phase == BLE_MESH_DFD_PHASE_FAILED) { + dfd_phase_set(srv, BLE_MESH_DFD_PHASE_IDLE); if (ctx != NULL) { - status_rsp(srv, ctx, BT_MESH_DFD_SUCCESS); + status_rsp(srv, ctx, BLE_MESH_DFD_SUCCESS); } - return BT_MESH_DFD_SUCCESS; + return BLE_MESH_DFD_SUCCESS; } /* Phase TRANSFER_ACTIVE, TRANSFER_SUSPENDED, TRANSFER_SUCCESS, APPLYING_UPDATE: */ prev_phase = srv->phase; - dfd_phase_set(srv, BT_MESH_DFD_PHASE_CANCELING_UPDATE); + dfd_phase_set(srv, BLE_MESH_DFD_PHASE_CANCELING_UPDATE); err = bt_mesh_dfu_cli_cancel(&srv->dfu, NULL); if (err) { if (ctx != NULL) { - status_rsp(srv, ctx, BT_MESH_DFD_ERR_INTERNAL); + status_rsp(srv, ctx, BLE_MESH_DFD_ERR_INTERNAL); } - return BT_MESH_DFD_ERR_INTERNAL; + return BLE_MESH_DFD_ERR_INTERNAL; } - if (prev_phase == BT_MESH_DFD_PHASE_APPLYING_UPDATE && ctx) { + if (prev_phase == BLE_MESH_DFD_PHASE_APPLYING_UPDATE && ctx) { /* Disable randomization for the Firmware Distribution State message to avoid * reordering when Firmware Distribution Server sends 2 messages in a row when * cancelling the update (see section 6.2.3.10 of MshDFUv1.0). @@ -1161,43 +1160,43 @@ enum bt_mesh_dfd_status bt_mesh_dfd_srv_cancel(struct bt_mesh_dfd_srv *srv, } if (ctx != NULL) { - status_rsp(srv, ctx, BT_MESH_DFD_SUCCESS); + status_rsp(srv, ctx, BLE_MESH_DFD_SUCCESS); } - if (prev_phase == BT_MESH_DFD_PHASE_APPLYING_UPDATE) { - dfd_phase_set(srv, BT_MESH_DFD_PHASE_IDLE); + if (prev_phase == BLE_MESH_DFD_PHASE_APPLYING_UPDATE) { + dfd_phase_set(srv, BLE_MESH_DFD_PHASE_IDLE); if (ctx != NULL) { - status_rsp(srv, ctx, BT_MESH_DFD_SUCCESS); + status_rsp(srv, ctx, BLE_MESH_DFD_SUCCESS); } } - return BT_MESH_DFD_SUCCESS; + return BLE_MESH_DFD_SUCCESS; } enum bt_mesh_dfd_status bt_mesh_dfd_srv_apply(struct bt_mesh_dfd_srv *srv) { int err; - if (srv->phase == BT_MESH_DFD_PHASE_IDLE || - srv->phase == BT_MESH_DFD_PHASE_CANCELING_UPDATE || - srv->phase == BT_MESH_DFD_PHASE_TRANSFER_ACTIVE || - srv->phase == BT_MESH_DFD_PHASE_TRANSFER_SUSPENDED || - srv->phase == BT_MESH_DFD_PHASE_FAILED) { - return BT_MESH_DFD_ERR_WRONG_PHASE; + if (srv->phase == BLE_MESH_DFD_PHASE_IDLE || + srv->phase == BLE_MESH_DFD_PHASE_CANCELING_UPDATE || + srv->phase == BLE_MESH_DFD_PHASE_TRANSFER_ACTIVE || + srv->phase == BLE_MESH_DFD_PHASE_TRANSFER_SUSPENDED || + srv->phase == BLE_MESH_DFD_PHASE_FAILED) { + return BLE_MESH_DFD_ERR_WRONG_PHASE; } - if (srv->phase == BT_MESH_DFD_PHASE_APPLYING_UPDATE || - srv->phase == BT_MESH_DFD_PHASE_COMPLETED) { - return BT_MESH_DFD_SUCCESS; + if (srv->phase == BLE_MESH_DFD_PHASE_APPLYING_UPDATE || + srv->phase == BLE_MESH_DFD_PHASE_COMPLETED) { + return BLE_MESH_DFD_SUCCESS; } err = bt_mesh_dfu_cli_apply(&srv->dfu); if (err) { - return BT_MESH_DFD_ERR_INTERNAL; + return BLE_MESH_DFD_ERR_INTERNAL; } - dfd_phase_set(srv, BT_MESH_DFD_PHASE_APPLYING_UPDATE); - return BT_MESH_DFD_SUCCESS; + dfd_phase_set(srv, BLE_MESH_DFD_PHASE_APPLYING_UPDATE); + return BLE_MESH_DFD_SUCCESS; } enum bt_mesh_dfd_status bt_mesh_dfd_srv_fw_delete(struct bt_mesh_dfd_srv *srv, size_t *fwid_len, @@ -1206,38 +1205,38 @@ enum bt_mesh_dfd_status bt_mesh_dfd_srv_fw_delete(struct bt_mesh_dfd_srv *srv, s struct bt_mesh_dfu_slot *slot; int idx, err; - if (srv->phase != BT_MESH_DFD_PHASE_IDLE) { + if (srv->phase != BLE_MESH_DFD_PHASE_IDLE) { *fwid = NULL; *fwid_len = 0; - return BT_MESH_DFD_ERR_BUSY_WITH_DISTRIBUTION; + return BLE_MESH_DFD_ERR_BUSY_WITH_DISTRIBUTION; } idx = bt_mesh_dfu_slot_get(*fwid, *fwid_len, &slot); if (idx < 0) { - return BT_MESH_DFD_SUCCESS; + return BLE_MESH_DFD_SUCCESS; } err = slot_del(srv, slot); if (err) { *fwid = NULL; *fwid_len = 0; - return BT_MESH_DFD_ERR_INTERNAL; + return BLE_MESH_DFD_ERR_INTERNAL; } else { - return BT_MESH_DFD_SUCCESS; + return BLE_MESH_DFD_SUCCESS; } } enum bt_mesh_dfd_status bt_mesh_dfd_srv_fw_delete_all(struct bt_mesh_dfd_srv *srv) { - if (srv->phase != BT_MESH_DFD_PHASE_IDLE) { - return BT_MESH_DFD_ERR_BUSY_WITH_DISTRIBUTION; + if (srv->phase != BLE_MESH_DFD_PHASE_IDLE) { + return BLE_MESH_DFD_ERR_BUSY_WITH_DISTRIBUTION; } bt_mesh_dfu_slot_foreach(slot_del_cb, srv); bt_mesh_dfu_slot_del_all(); - return BT_MESH_DFD_SUCCESS; + return BLE_MESH_DFD_SUCCESS; } #ifdef CONFIG_BLE_MESH_DFD_SRV_OOB_UPLOAD @@ -1248,7 +1247,7 @@ int bt_mesh_dfd_srv_oob_check_complete(struct bt_mesh_dfd_srv *srv, int err; if (slot != srv->upload.slot || !srv->upload.is_oob || - srv->upload.phase == BT_MESH_DFD_UPLOAD_PHASE_TRANSFER_ACTIVE || + srv->upload.phase == BLE_MESH_DFD_UPLOAD_PHASE_TRANSFER_ACTIVE || !srv->upload.is_pending_oob_check) { /* This should not happen, unless the application calls the function with a * "wrong" pointer or at a wrong time. @@ -1258,7 +1257,7 @@ int bt_mesh_dfd_srv_oob_check_complete(struct bt_mesh_dfd_srv *srv, srv->upload.is_pending_oob_check = false; - if (status != BT_MESH_DFD_SUCCESS) { + if (status != BLE_MESH_DFD_SUCCESS) { bt_mesh_dfu_slot_release(srv->upload.slot); upload_status_rsp(srv, &srv->upload.oob.ctx, status); return -ECANCELED; @@ -1270,7 +1269,7 @@ int bt_mesh_dfd_srv_oob_check_complete(struct bt_mesh_dfd_srv *srv, return err; } - upload_status_rsp(srv, &srv->upload.oob.ctx, BT_MESH_DFD_SUCCESS); + upload_status_rsp(srv, &srv->upload.oob.ctx, BLE_MESH_DFD_SUCCESS); return 0; } @@ -1280,7 +1279,7 @@ int bt_mesh_dfd_srv_oob_store_complete(struct bt_mesh_dfd_srv *srv, { int err = 0; - if (srv->upload.phase != BT_MESH_DFD_UPLOAD_PHASE_TRANSFER_ACTIVE || + if (srv->upload.phase != BLE_MESH_DFD_UPLOAD_PHASE_TRANSFER_ACTIVE || srv->upload.slot != slot || !srv->upload.is_oob) { return -EINVAL; } @@ -1299,11 +1298,11 @@ int bt_mesh_dfd_srv_oob_store_complete(struct bt_mesh_dfd_srv *srv, goto error; } - srv->upload.phase = BT_MESH_DFD_UPLOAD_PHASE_TRANSFER_SUCCESS; + srv->upload.phase = BLE_MESH_DFD_UPLOAD_PHASE_TRANSFER_SUCCESS; return 0; error: - srv->upload.phase = BT_MESH_DFD_UPLOAD_PHASE_TRANSFER_ERROR; + srv->upload.phase = BLE_MESH_DFD_UPLOAD_PHASE_TRANSFER_ERROR; bt_mesh_dfu_slot_release(srv->upload.slot); return err; } 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 8ce345831d..e5ff20c328 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 @@ -105,8 +105,10 @@ static void req_timeout_handler(struct k_work *work) } bt_mesh_dfu_cli_rm_req_from_list(req); + if (req->params) { + bt_mesh_free(req->params); + } req_free(req); - bt_mesh_r_mutex_unlock(&dfu_req_list.op_lock); } @@ -897,7 +899,7 @@ static int handle_status(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx } 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, - sizeof(struct bt_mesh_dfu_metadata_status)); + sizeof(struct bt_mesh_dfu_target_status)); bt_mesh_free(req->params); bt_mesh_dfu_cli_rm_req_from_list(req); k_delayed_work_cancel(&req->timer); diff --git a/components/bt/esp_ble_mesh/v1.1/include/mesh_v1.1/dfu/dfd.h b/components/bt/esp_ble_mesh/v1.1/include/mesh_v1.1/dfu/dfd.h index b91ee418e2..a9634ef220 100644 --- a/components/bt/esp_ble_mesh/v1.1/include/mesh_v1.1/dfu/dfd.h +++ b/components/bt/esp_ble_mesh/v1.1/include/mesh_v1.1/dfu/dfd.h @@ -12,6 +12,30 @@ extern "C" { #endif +#define BLE_MESH_DFD_OP_RECEIVERS_ADD BLE_MESH_MODEL_OP_2(0x83, 0x11) +#define BLE_MESH_DFD_OP_RECEIVERS_DELETE_ALL BLE_MESH_MODEL_OP_2(0x83, 0x12) +#define BLE_MESH_DFD_OP_RECEIVERS_STATUS BLE_MESH_MODEL_OP_2(0x83, 0x13) +#define BLE_MESH_DFD_OP_RECEIVERS_GET BLE_MESH_MODEL_OP_2(0x83, 0x14) +#define BLE_MESH_DFD_OP_RECEIVERS_LIST BLE_MESH_MODEL_OP_2(0x83, 0x15) +#define BLE_MESH_DFD_OP_CAPABILITIES_GET BLE_MESH_MODEL_OP_2(0x83, 0x16) +#define BLE_MESH_DFD_OP_CAPABILITIES_STATUS BLE_MESH_MODEL_OP_2(0x83, 0x17) +#define BLE_MESH_DFD_OP_GET BLE_MESH_MODEL_OP_2(0x83, 0x18) +#define BLE_MESH_DFD_OP_START BLE_MESH_MODEL_OP_2(0x83, 0x19) +#define BLE_MESH_DFD_OP_SUSPEND BLE_MESH_MODEL_OP_2(0x83, 0x1a) +#define BLE_MESH_DFD_OP_CANCEL BLE_MESH_MODEL_OP_2(0x83, 0x1b) +#define BLE_MESH_DFD_OP_APPLY BLE_MESH_MODEL_OP_2(0x83, 0x1c) +#define BLE_MESH_DFD_OP_STATUS BLE_MESH_MODEL_OP_2(0x83, 0x1d) +#define BLE_MESH_DFD_OP_UPLOAD_GET BLE_MESH_MODEL_OP_2(0x83, 0x1e) +#define BLE_MESH_DFD_OP_UPLOAD_START BLE_MESH_MODEL_OP_2(0x83, 0x1f) +#define BLE_MESH_DFD_OP_UPLOAD_START_OOB BLE_MESH_MODEL_OP_2(0x83, 0x20) +#define BLE_MESH_DFD_OP_UPLOAD_CANCEL BLE_MESH_MODEL_OP_2(0x83, 0x21) +#define BLE_MESH_DFD_OP_UPLOAD_STATUS BLE_MESH_MODEL_OP_2(0x83, 0x22) +#define BLE_MESH_DFD_OP_FW_GET BLE_MESH_MODEL_OP_2(0x83, 0x23) +#define BLE_MESH_DFD_OP_FW_GET_BY_INDEX BLE_MESH_MODEL_OP_2(0x83, 0x24) +#define BLE_MESH_DFD_OP_FW_DELETE BLE_MESH_MODEL_OP_2(0x83, 0x25) +#define BLE_MESH_DFD_OP_FW_DELETE_ALL BLE_MESH_MODEL_OP_2(0x83, 0x26) +#define BLE_MESH_DFD_OP_FW_STATUS BLE_MESH_MODEL_OP_2(0x83, 0x27) + /** * @defgroup bt_mesh_dfd Firmware Distribution models * @ingroup bt_mesh @@ -21,96 +45,96 @@ extern "C" { /** Firmware distribution status. */ enum bt_mesh_dfd_status { /** The message was processed successfully. */ - BT_MESH_DFD_SUCCESS, + BLE_MESH_DFD_SUCCESS, /** Insufficient resources on the node. */ - BT_MESH_DFD_ERR_INSUFFICIENT_RESOURCES, + BLE_MESH_DFD_ERR_INSUFFICIENT_RESOURCES, /** The operation cannot be performed while the Server is in the current * phase. */ - BT_MESH_DFD_ERR_WRONG_PHASE, + BLE_MESH_DFD_ERR_WRONG_PHASE, /** An internal error occurred on the node. */ - BT_MESH_DFD_ERR_INTERNAL, + BLE_MESH_DFD_ERR_INTERNAL, /** The requested firmware image is not stored on the Distributor. */ - BT_MESH_DFD_ERR_FW_NOT_FOUND, + BLE_MESH_DFD_ERR_FW_NOT_FOUND, /** The AppKey identified by the AppKey Index is not known to the node. */ - BT_MESH_DFD_ERR_INVALID_APPKEY_INDEX, + BLE_MESH_DFD_ERR_INVALID_APPKEY_INDEX, /** There are no Target nodes in the Distribution Receivers List * state. */ - BT_MESH_DFD_ERR_RECEIVERS_LIST_EMPTY, + BLE_MESH_DFD_ERR_RECEIVERS_LIST_EMPTY, /** Another firmware image distribution is in progress. */ - BT_MESH_DFD_ERR_BUSY_WITH_DISTRIBUTION, + BLE_MESH_DFD_ERR_BUSY_WITH_DISTRIBUTION, /** Another upload is in progress. */ - BT_MESH_DFD_ERR_BUSY_WITH_UPLOAD, + BLE_MESH_DFD_ERR_BUSY_WITH_UPLOAD, /** The URI scheme name indicated by the Update URI is not supported. */ - BT_MESH_DFD_ERR_URI_NOT_SUPPORTED, + BLE_MESH_DFD_ERR_URI_NOT_SUPPORTED, /** The format of the Update URI is invalid. */ - BT_MESH_DFD_ERR_URI_MALFORMED, + BLE_MESH_DFD_ERR_URI_MALFORMED, /** The URI is currently unreachable. */ - BT_MESH_DFD_ERR_URI_UNREACHABLE, + BLE_MESH_DFD_ERR_URI_UNREACHABLE, /** The Check Firmware OOB procedure did not find any new firmware. */ - BT_MESH_DFD_ERR_NEW_FW_NOT_AVAILABLE, + BLE_MESH_DFD_ERR_NEW_FW_NOT_AVAILABLE, /** The suspension of the Distribute Firmware procedure failed. */ - BT_MESH_DFD_ERR_SUSPEND_FAILED, + BLE_MESH_DFD_ERR_SUSPEND_FAILED, }; /** Firmware distribution phases. */ enum bt_mesh_dfd_phase { /** No firmware distribution is in progress. */ - BT_MESH_DFD_PHASE_IDLE, + BLE_MESH_DFD_PHASE_IDLE, /** Firmware distribution is in progress. */ - BT_MESH_DFD_PHASE_TRANSFER_ACTIVE, + BLE_MESH_DFD_PHASE_TRANSFER_ACTIVE, /** The Transfer BLOB procedure has completed successfully. */ - BT_MESH_DFD_PHASE_TRANSFER_SUCCESS, + BLE_MESH_DFD_PHASE_TRANSFER_SUCCESS, /** The Apply Firmware on Target Nodes procedure is being executed. */ - BT_MESH_DFD_PHASE_APPLYING_UPDATE, + BLE_MESH_DFD_PHASE_APPLYING_UPDATE, /** The Distribute Firmware procedure has completed successfully. */ - BT_MESH_DFD_PHASE_COMPLETED, + BLE_MESH_DFD_PHASE_COMPLETED, /** The Distribute Firmware procedure has failed. */ - BT_MESH_DFD_PHASE_FAILED, + BLE_MESH_DFD_PHASE_FAILED, /** The Cancel Firmware Update procedure is being executed. */ - BT_MESH_DFD_PHASE_CANCELING_UPDATE, + BLE_MESH_DFD_PHASE_CANCELING_UPDATE, /** The Transfer BLOB procedure is suspended. */ - BT_MESH_DFD_PHASE_TRANSFER_SUSPENDED, + BLE_MESH_DFD_PHASE_TRANSFER_SUSPENDED, }; /** Firmware upload phases. */ enum bt_mesh_dfd_upload_phase { /** No firmware upload is in progress. */ - BT_MESH_DFD_UPLOAD_PHASE_IDLE, + BLE_MESH_DFD_UPLOAD_PHASE_IDLE, /** The Store Firmware procedure is being executed. */ - BT_MESH_DFD_UPLOAD_PHASE_TRANSFER_ACTIVE, + BLE_MESH_DFD_UPLOAD_PHASE_TRANSFER_ACTIVE, /** The Store Firmware procedure or Store Firmware OOB procedure failed. */ - BT_MESH_DFD_UPLOAD_PHASE_TRANSFER_ERROR, + BLE_MESH_DFD_UPLOAD_PHASE_TRANSFER_ERROR, /** The Store Firmware procedure or the Store Firmware OOB procedure * completed successfully. */ - BT_MESH_DFD_UPLOAD_PHASE_TRANSFER_SUCCESS, + BLE_MESH_DFD_UPLOAD_PHASE_TRANSFER_SUCCESS, }; /** @} */ diff --git a/components/bt/esp_ble_mesh/v1.1/include/mesh_v1.1/dfu/dfd_cli.h b/components/bt/esp_ble_mesh/v1.1/include/mesh_v1.1/dfu/dfd_cli.h new file mode 100644 index 0000000000..2fa96b0373 --- /dev/null +++ b/components/bt/esp_ble_mesh/v1.1/include/mesh_v1.1/dfu/dfd_cli.h @@ -0,0 +1,472 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file + * @defgroup bt_mesh_dfd_cli Device Firmware Distribution Client model + * @ingroup bt_mesh_dfd + * @{ + * @brief API for the Device Firmware Distribution Client model + */ + +#ifndef _BLE_MESH_v11_DFD_CLI_H__ +#define _BLE_MESH_v11_DFD_CLI_H__ + +#include "mesh/access.h" +#include "mesh/client_common.h" +#include "mesh_v1.1/dfu/dfd.h" +#include "mesh_v1.1/dfu/dfu_cli.h" +#include "mesh_v1.1/mbt/blob_cli.h" +#include "mesh_v1.1/mbt/blob_srv.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** @brief Device Firmware Distribution Client model instance. */ +typedef bt_mesh_client_user_data_t bt_mesh_dfd_client_t; + +/** @brief DFD Client internal data structure. */ +typedef bt_mesh_client_internal_data_t dfd_internal_data_t; + +/*!< Maximum value of upload progress indicating progress is unset. */ +#define UPLOAD_PROGRESS_UNSET 101 + +/*!< In-band upload type. */ +#define UPLOAD_IN_BAND 0x00 + +/*!< Out-of-band upload type. */ +#define UPLOAD_OOB 0x01 + +/*!< Upload type is not set. */ +#define UPLOAD_TYPE_UNSET 0xFF + +/*!< Extract target address from target info bitfield. */ +#define TARGET_ADDR(target_info) (target_info >> 17) + +/*!< Extract target update phase from target info bitfield. */ +#define TARGET_UPDATE_PHASE(target_info) ((target_info >> 13) & 0x0f) + +/*!< Extract target update status from target info bitfield. */ +#define TARGET_UPDATE_STATUS(target_info) ((target_info >> 10) & 0x07) + +/*!< Extract target transfer status from target info bitfield. */ +#define TARGET_TRANSFER_STATUS(target_info) ((target_info >> 6) & 0x0f) + +/*!< Extract target transfer progress from target info bitfield. */ +#define TARGET_TRANSFER_PROGRESS(target_info) (target_info & 0x3f) + +/** + * @brief DFD Client receiver entry structure. + * + * This structure represents a single receiver node that will receive firmware + * distribution. Each receiver is identified by its unicast address and the + * firmware image index that should be distributed to it. + */ +typedef struct { + uint16_t addr; /*!< Unicast address of the receiver node. */ + uint8_t fw_idx; /*!< Index of the firmware image to distribute. */ +} dfd_cli_receiver_entry_t; + +/** + * @brief Target node entry information structure. + * + * This structure contains detailed information about a target node's + * firmware distribution status, including address, update phase, transfer + * progress, and other related information. + */ +typedef struct { + uint32_t addr:15; /*!< Unicast address of the target node. */ + uint32_t retrieved_update_phase:4; /*!< Current update phase of the target. */ + uint32_t update_status:3; /*!< Update status of the target node. */ + uint32_t transfer_status:4; /*!< Transfer status of the target. */ + uint32_t transfer_progress:6; /*!< Transfer progress percentage. */ + uint8_t update_fw_idx; /*!< Index of firmware being updated. */ +} target_node_entry_t; + +/** + * @brief DFD receiver status response structure. + * + * This structure contains the status response for receiver add/delete + * operations, including the operation result and current receiver list size. + */ +typedef struct { + uint8_t status; /*!< Status code of the operation (see @ref bt_mesh_dfd_status). */ + uint16_t receiver_list_cnt; /*!< Current number of receivers in the distribution list. */ +} dfd_cli_receiver_status_t; + +/** + * @brief DFD receiver list response structure. + * + * This structure contains the response to a receivers get request, + * providing a list of target nodes with their current status information. + */ +typedef struct { + uint16_t entries_cnt; /*!< Number of receiver entries in the list. */ + uint16_t first_index; /*!< Index of the first entry in the list. */ + target_node_entry_t *entries; /*!< Pointer to array of target node entries. */ +} dfd_cli_receiver_list_t; + +/** + * @brief DFD distribution capabilities structure. + * + * This structure contains the capabilities of the DFD Server, including + * storage limits, receiver limits, and supported features. + */ +typedef struct { + uint16_t max_receiver_list_sz; /*!< Maximum number of receivers supported. */ + uint16_t max_fw_list_sz; /*!< Maximum number of firmware images supported. */ + uint32_t max_fw_sz; /*!< Maximum firmware image size supported. */ + uint32_t max_upload_space; /*!< Total upload storage space available. */ + uint32_t remaining_upload_space; /*!< Remaining upload storage space. */ + uint8_t oob_retrieval_supported; /*!< OOB retrieval support flag. */ + struct net_buf_simple *supported_url_scheme_names; /*!< Supported URL scheme names. */ +} dfd_cli_dist_caps_t; + +/** + * @brief DFD distribution status structure. + * + * This structure contains the current status of an ongoing or completed + * firmware distribution operation, including phase, progress, and configuration. + */ +typedef struct { + uint8_t status; /*!< Distribution status code (see @ref bt_mesh_dfd_status). */ + uint8_t dist_phase; /*!< Current distribution phase (see @ref bt_mesh_dfd_phase). */ + uint16_t multicast_address; /*!< Multicast address used for distribution. */ + uint16_t appkey_idx; /*!< Application key index used for distribution. */ + uint8_t ttl; /*!< TTL value used for distribution messages. */ + uint16_t timeout_base; /*!< Base timeout value for distribution. */ + uint8_t trans_mode:2; /*!< Transfer mode (push or pull). */ + uint8_t update_policy:1; /*!< Update policy (single or all). */ + uint8_t RFU:5; /*!< Reserved for future use. */ + uint16_t fw_idx; /*!< Index of firmware being distributed. */ +} dfd_cli_dist_status_t; + +/** + * @brief DFD upload status structure. + * + * This structure contains the current status of a firmware upload operation, + * including phase, progress, and firmware identification. + */ +typedef struct { + uint8_t status; /*!< Upload operation status code (see @ref bt_mesh_dfd_status). */ + uint8_t upload_phase; /*!< Current upload phase (see @ref bt_mesh_dfd_upload_phase). */ + uint8_t upload_progress:7; /*!< Upload progress percentage (0-100). */ + uint8_t upload_type:1; /*!< Upload type (in-band or OOB). */ + union { + struct net_buf_simple *fwid; /*!< Firmware ID for in-band uploads. */ + struct net_buf_simple *oob_fwid; /*!< Firmware ID for OOB uploads. */ + }; +} dfd_cli_upload_status_t; + +/** + * @brief DFD firmware status structure. + * + * This structure contains the status response for firmware management + * operations, including information about firmware images. + */ +typedef struct { + uint8_t status; /*!< Firmware operation status code (see @ref bt_mesh_dfd_status). */ + uint16_t entry_cnt; /*!< Number of firmware entries. */ + uint16_t firmware_image_index; /*!< Index of the firmware image. */ + struct net_buf_simple *fwid; /*!< Firmware ID buffer. */ +} dfd_cli_firmware_status_t; + +/** + * @brief DFD distribution start parameters structure. + * + * This structure contains parameters for starting a firmware distribution + * operation, including target configuration and distribution settings. + */ +typedef struct { + uint8_t ttl; /*!< TTL value for distribution messages. */ + uint8_t trans_mode:2; /*!< Transfer mode (push or pull). */ + uint8_t update_policy:1; /*!< Update policy (single or all). */ + uint8_t RFU:5; /*!< Reserved for future use. */ + uint16_t app_idx; /*!< Application key index for distribution. */ + uint16_t timeout_base; /*!< Base timeout value for distribution. */ + uint16_t fw_idx; /*!< Index of firmware to distribute. */ + bool is_va; /*!< True if using virtual address, false for group address. */ + union { + uint16_t group_addr; /*!< Group address for distribution (if is_va is false). */ + uint8_t label_uuid[16]; /*!< Virtual label UUID for distribution (if is_va is true). */ + }; +} dfd_cli_dist_start_t; + +/** + * @brief DFD distribution upload start parameters structure. + * + * This structure contains parameters for starting an in-band firmware + * upload operation to the DFD Server. + */ +typedef struct { + uint8_t ttl; /*!< TTL value for upload messages. */ + uint16_t timeout_base; /*!< Base timeout value for upload. */ + uint32_t fw_size; /*!< Size of the firmware image in bytes. */ + uint64_t blob_id; /*!< BLOB ID for the firmware transfer. */ + struct net_buf_simple *fwid; /*!< Firmware ID buffer. */ + struct net_buf_simple *fw_metadata; /*!< Firmware metadata buffer. */ +} dfd_cli_dist_upload_start_t; + +/** + * @brief DFD distribution OOB upload start parameters structure. + * + * This structure contains parameters for starting an out-of-band firmware + * upload operation to the DFD Server. + */ +typedef struct { + struct net_buf_simple *fwid; /*!< Firmware ID buffer. */ + struct net_buf_simple *url; /*!< URL for retrieving the firmware. */ +} dfd_cli_dist_upload_oob_start_t; + +/** + * @brief DFD status response union. + * + * This union contains different status response structures for various DFD + * Client operations, allowing type-safe access to operation-specific responses. + */ +typedef union { + dfd_cli_receiver_status_t receiver_status; /*!< Receiver add/delete status. */ + dfd_cli_receiver_list_t receiver_list; /*!< Receiver list response. */ + dfd_cli_dist_caps_t dist_caps; /*!< Distribution capabilities. */ + dfd_cli_dist_status_t dist_status; /*!< Distribution status. */ + dfd_cli_upload_status_t upload_status; /*!< Upload status. */ + dfd_cli_firmware_status_t firmware_status; /*!< Firmware management status. */ +} dfd_status_t; + +/*!< DFD Client model operation handlers. */ +extern const struct bt_mesh_model_op _bt_mesh_dfd_cli_op[]; + +/*!< DFD Client model callbacks. */ +extern const struct bt_mesh_model_cb _bt_mesh_dfd_cli_cb; + +/** + * @brief Add receivers to the distribution list. + * + * Add multiple target nodes to the DFD Server's distribution receiver list. + * These receivers will be included in subsequent firmware distribution operations. + * + * @param[in] param Common client parameters for the operation. + * @param[in] receivers Pointer to array of receiver entries to add. + * @param[in] receivers_cnt Number of receiver entries in the array. + * + * @return 0 on success, or (negative) error code on failure. + */ +int bt_mesh_dfd_cli_receivers_add(bt_mesh_client_common_param_t *param, + dfd_cli_receiver_entry_t *receivers, + uint16_t receivers_cnt); + +/** + * @brief Delete all receivers from the distribution list. + * + * Remove all target nodes from the DFD Server's distribution receiver list. + * This operation will clear the entire receiver list. + * + * @param[in] param Common client parameters for the operation. + * + * @return 0 on success, or (negative) error code on failure. + */ +int bt_mesh_dfd_cli_receivers_delete_all(bt_mesh_client_common_param_t *param); + +/** + * @brief Get the distribution receiver list. + * + * Retrieve a portion of the DFD Server's distribution receiver list. + * Supports pagination to retrieve large receiver lists in chunks. + * + * @param[in] param Common client parameters for the operation. + * @param[in] first_index Index of the first receiver entry to retrieve. + * @param[in] entries_limit Maximum number of entries to retrieve. + * + * @return 0 on success, or (negative) error code on failure. + */ +int bt_mesh_dfd_cli_receivers_get(bt_mesh_client_common_param_t *param, + uint16_t first_index, + uint16_t entries_limit); + +/** + * @brief Get distribution capabilities from the DFD Server. + * + * Query the DFD Server's capabilities, including storage limits, + * supported features, and maximum receiver count. + * + * @param[in] param Common client parameters for the operation. + * + * @return 0 on success, or (negative) error code on failure. + */ +int bt_mesh_dfd_cli_distribution_capabilities_get(bt_mesh_client_common_param_t *param); +/** + * @brief Get current distribution status from the DFD Server. + * + * Query the current status of firmware distribution operations, + * including phase, progress, and configuration information. + * + * @param[in] param Common client parameters for the operation. + * + * @return 0 on success, or (negative) error code on failure. + */ +int bt_mesh_dfd_cli_distribution_get(bt_mesh_client_common_param_t *param); + +/** + * @brief Start firmware distribution to receivers. + * + * Initiate firmware distribution to all target nodes in the distribution + * receiver list using the specified configuration parameters. + * + * @param[in] param Common client parameters for the operation. + * @param[in] start Distribution start parameters and configuration. + * + * @return 0 on success, or (negative) error code on failure. + */ +int bt_mesh_dfd_cli_distribution_start(bt_mesh_client_common_param_t *param, + dfd_cli_dist_start_t *start); + +/** + * @brief Suspend ongoing firmware distribution. + * + * Suspend a currently active firmware distribution operation. + * The distribution can be resumed later if needed. + * + * @param[in] param Common client parameters for the operation. + * + * @return 0 on success, or (negative) error code on failure. + */ +int bt_mesh_dfd_cli_distribution_suspend(bt_mesh_client_common_param_t *param); + +/** + * @brief Cancel firmware distribution. + * + * Cancel an ongoing or suspended firmware distribution operation. + * This will terminate the distribution process. + * + * @param[in] param Common client parameters for the operation. + * + * @return 0 on success, or (negative) error code on failure. + */ +int bt_mesh_dfd_cli_distribution_cancel(bt_mesh_client_common_param_t *param); + +/** + * @brief Apply distributed firmware on target nodes. + * + * Apply the distributed firmware image on target nodes that have + * successfully received the firmware transfer. + * + * @param[in] param Common client parameters for the operation. + * + * @return 0 on success, or (negative) error code on failure. + */ +int bt_mesh_dfd_cli_distribution_apply(bt_mesh_client_common_param_t *param); +/** + * @brief Get firmware upload status from the DFD Server. + * + * Query the current status of firmware upload operations, including + * phase, progress, and transfer information. + * + * @param[in] param Common client parameters for the operation. + * + * @return 0 on success, or (negative) error code on failure. + */ +int bt_mesh_dfd_cli_distribution_upload_get(bt_mesh_client_common_param_t *param); + +/** + * @brief Start in-band firmware upload to the DFD Server. + * + * Initiate an in-band firmware upload using BLOB transfer protocol. + * The firmware image will be transferred directly through the mesh network. + * + * @param[in] param Common client parameters for the operation. + * @param[in] start Upload start parameters including firmware details. + * + * @return 0 on success, or (negative) error code on failure. + */ +int bt_mesh_dfd_cli_distribution_upload_start(bt_mesh_client_common_param_t *param, + dfd_cli_dist_upload_start_t *start); + +/** + * @brief Start out-of-band firmware upload to the DFD Server. + * + * Initiate an out-of-band firmware upload where the DFD Server + * retrieves the firmware from the specified URL. + * + * @param[in] param Common client parameters for the operation. + * @param[in] start OOB upload start parameters including URL and firmware ID. + * + * @return 0 on success, or (negative) error code on failure. + */ +int bt_mesh_dfd_cli_distribution_upload_oob_start(bt_mesh_client_common_param_t *param, + dfd_cli_dist_upload_oob_start_t *start); + +/** + * @brief Cancel out-of-band firmware upload. + * + * Cancel an ongoing out-of-band firmware upload operation. + * + * @param[in] param Common client parameters for the operation. + * + * @return 0 on success, or (negative) error code on failure. + */ +int bt_mesh_dfd_cli_distribution_upload_oob_cancel(bt_mesh_client_common_param_t *param); +/** + * @brief Get firmware information by firmware ID. + * + * Query detailed information about a specific firmware image + * stored on the DFD Server using its firmware ID. + * + * @param[in] param Common client parameters for the operation. + * @param[in] fwid Firmware ID to query information for. + * + * @return 0 on success, or (negative) error code on failure. + */ +int bt_mesh_dfd_cli_firmware_get(bt_mesh_client_common_param_t *param, + struct net_buf_simple *fwid); + +/** + * @brief Get firmware information by index. + * + * Query detailed information about a firmware image stored on the + * DFD Server using its index in the firmware list. + * + * @param[in] param Common client parameters for the operation. + * @param[in] fw_id_index Index of the firmware image to query. + * + * @return 0 on success, or (negative) error code on failure. + */ +int bt_mesh_dfd_cli_firmware_get_by_index(bt_mesh_client_common_param_t *param, + uint16_t fw_id_index); + +/** + * @brief Delete specific firmware image by firmware ID. + * + * Delete a specific firmware image from the DFD Server's storage + * using its firmware ID. + * + * @param[in] param Common client parameters for the operation. + * @param[in] fwid Firmware ID of the image to delete. + * + * @return 0 on success, or (negative) error code on failure. + */ +int bt_mesh_dfd_cli_firmware_get_delete(bt_mesh_client_common_param_t *param, + struct net_buf_simple *fwid); + +/** + * @brief Delete all firmware images from the DFD Server. + * + * Delete all firmware images stored on the DFD Server. + * This operation will clear the entire firmware storage. + * + * @param[in] param Common client parameters for the operation. + * + * @return 0 on success, or (negative) error code on failure. + */ +int bt_mesh_dfd_cli_firmware_delete_all(bt_mesh_client_common_param_t *param); + +#ifdef __cplusplus +} +#endif + +/** @} */ + +#endif /* _BLE_MESH_v11_DFD_CLI_H__ */ diff --git a/components/bt/esp_ble_mesh/v1.1/include/mesh_v1.1/dfu/dfd_srv.h b/components/bt/esp_ble_mesh/v1.1/include/mesh_v1.1/dfu/dfd_srv.h index fd0223af1c..b494ce775d 100644 --- a/components/bt/esp_ble_mesh/v1.1/include/mesh_v1.1/dfu/dfd_srv.h +++ b/components/bt/esp_ble_mesh/v1.1/include/mesh_v1.1/dfu/dfd_srv.h @@ -42,15 +42,14 @@ struct bt_mesh_dfd_srv; #ifdef CONFIG_BLE_MESH_DFD_SRV_OOB_UPLOAD /** + * @brief Initialization parameters for the @ref bt_mesh_dfd_srv with OOB + * upload support. * - * @brief Initialization parameters for the @ref bt_mesh_dfd_srv with OOB - * upload support. - * - * @param[in] _cb Pointer to a @ref bt_mesh_dfd_srv_cb instance. - * @param[in] _oob_schemes Array of OOB schemes supported by the server, - * each scheme being a code point from the - * Bluetooth SIG Assigned Numbers document. - * @param[in] _oob_schemes_count Number of schemes in @c _oob_schemes. + * @param[in] _cb Pointer to a @ref bt_mesh_dfd_srv_cb instance. + * @param[in] _oob_schemes Array of OOB schemes supported by the server, + * each scheme being a code point from the + * Bluetooth SIG Assigned Numbers document. + * @param[in] _oob_schemes_count Number of schemes in @c _oob_schemes. */ #define BT_MESH_DFD_SRV_OOB_INIT(_cb, _oob_schemes, _oob_schemes_count) \ { \ @@ -67,10 +66,9 @@ struct bt_mesh_dfd_srv; #endif /* CONFIG_BLE_MESH_DFD_SRV_OOB_UPLOAD */ /** + * @brief Initialization parameters for the @ref bt_mesh_dfd_srv. * - * @brief Initialization parameters for the @ref bt_mesh_dfd_srv. - * - * @param[in] _cb Pointer to a @ref bt_mesh_dfd_srv_cb instance. + * @param[in] _cb Pointer to a @ref bt_mesh_dfd_srv_cb instance. */ #define BT_MESH_DFD_SRV_INIT(_cb) \ { \ @@ -82,10 +80,9 @@ struct bt_mesh_dfd_srv; } /** + * @brief Firmware Distribution Server model Composition Data entry. * - * @brief Firmware Distribution Server model Composition Data entry. - * - * @param _srv Pointer to a @ref bt_mesh_dfd_srv instance. + * @param _srv Pointer to a @ref bt_mesh_dfd_srv instance. */ #define BT_MESH_MODEL_DFD_SRV(_srv) \ BT_MESH_MODEL_DFU_CLI(&(_srv)->dfu), \ @@ -98,15 +95,15 @@ struct bt_mesh_dfd_srv_cb { /** @brief Slot receive callback. * - * Called at the start of an upload procedure. The callback must fill - * @c io with a pointer to a writable BLOB stream for the Firmware Distribution - * Server to write the firmware image to. + * Called at the start of an upload procedure. The callback must fill + * @c io with a pointer to a writable BLOB stream for the Firmware Distribution + * Server to write the firmware image to. * - * @param srv Firmware Distribution Server model instance. - * @param slot DFU image slot being received. - * @param io BLOB stream response pointer. + * @param srv Firmware Distribution Server model instance. + * @param slot DFU image slot being received. + * @param io BLOB stream response pointer. * - * @return 0 on success, or (negative) error code otherwise. + * @return 0 on success, or (negative) error code otherwise. */ int (*recv)(struct bt_mesh_dfd_srv *srv, const struct bt_mesh_dfu_slot *slot, @@ -136,7 +133,7 @@ struct bt_mesh_dfd_srv_cb { * available, or to 0 if new firmware is not * available. * - * @return BT_MESH_DFD_SUCCESS on success, or error code otherwise. + * @return BLE_MESH_DFD_SUCCESS on success, or error code otherwise. */ int (*start_oob_upload)(struct bt_mesh_dfd_srv *srv, const struct bt_mesh_dfu_slot *slot, @@ -255,11 +252,11 @@ struct bt_mesh_dfd_srv { * callback has completed or failed. The @p status param should be set to one of the following * values: * - * * @c BT_MESH_DFD_SUCCESS if the check was successful and a new firmware ID was found. - * * @c BT_MESH_DFD_ERR_URI_MALFORMED if the URI is not formatted correctly. - * * @c BT_MESH_DFD_ERR_URI_NOT_SUPPORTED if the URI scheme is not supported by the node. - * * @c BT_MESH_DFD_ERR_URI_UNREACHABLE if the URI can't be reached. - * * @c BT_MESH_DFD_ERR_NEW_FW_NOT_AVAILABLE if the check completes successfully but no new + * * @c BLE_MESH_DFD_SUCCESS if the check was successful and a new firmware ID was found. + * * @c BLE_MESH_DFD_ERR_URI_MALFORMED if the URI is not formatted correctly. + * * @c BLE_MESH_DFD_ERR_URI_NOT_SUPPORTED if the URI scheme is not supported by the node. + * * @c BLE_MESH_DFD_ERR_URI_UNREACHABLE if the URI can't be reached. + * * @c BLE_MESH_DFD_ERR_NEW_FW_NOT_AVAILABLE if the check completes successfully but no new * firmware is available. * * If this function returns 0, the application should then download the firmware to the diff --git a/docs/en/api-guides/esp-ble-mesh/ble-mesh-feature-list.rst b/docs/en/api-guides/esp-ble-mesh/ble-mesh-feature-list.rst index b83b6059ff..94e7cccfe6 100644 --- a/docs/en/api-guides/esp-ble-mesh/ble-mesh-feature-list.rst +++ b/docs/en/api-guides/esp-ble-mesh/ble-mesh-feature-list.rst @@ -135,11 +135,11 @@ Mesh Models * Device Firmware Update Client models * Firmware Update Client model (Preview) - * Firmware Distribution Client model (coming soon) + * Firmware Distribution Client model (Preview) + * Device Firmware Update Server models * Firmware Update Server model (Preview) - * Firmware Distribution Server model (coming soon) - + * Firmware Distribution Server model (Preview) Mesh Examples """"""""""""" diff --git a/docs/zh_CN/api-guides/esp-ble-mesh/ble-mesh-feature-list.rst b/docs/zh_CN/api-guides/esp-ble-mesh/ble-mesh-feature-list.rst index edac2c0b2c..a383f17c3d 100644 --- a/docs/zh_CN/api-guides/esp-ble-mesh/ble-mesh-feature-list.rst +++ b/docs/zh_CN/api-guides/esp-ble-mesh/ble-mesh-feature-list.rst @@ -132,6 +132,14 @@ Mesh 模型 * 灯光 LC 服务器 * 灯光 LC 设置服务器 +* 设备固件更新客户端模型 + * 固件更新客户端模型(预览) + * 固件分发客户端模型(预览) + +* 设备固件更新服务器模型 + * 固件更新服务器模型(预览) + * 固件分发服务器模型(预览) + Mesh 示例 """""""""""