diff --git a/components/bt/CMakeLists.txt b/components/bt/CMakeLists.txt index c412ca6146..a215bd4e8e 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) @@ -622,9 +625,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" @@ -632,6 +639,14 @@ 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/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) diff --git a/components/bt/common/btc/core/btc_task.c b/components/bt/common/btc/core/btc_task.c index 41c0b4368e..9299015591 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,12 @@ 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_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 0436558242..5d17120180 100644 --- a/components/bt/common/btc/include/btc/btc_task.h +++ b/components/bt/common/btc/include/btc/btc_task.h @@ -106,6 +106,12 @@ 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_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/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) diff --git a/components/bt/esp_ble_mesh/Kconfig.in b/components/bt/esp_ble_mesh/Kconfig.in index 3f8f0a37d7..e6c19f0e45 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,259 @@ 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 + + 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 7d4b0ed170..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 @@ -1982,9 +1982,14 @@ 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 +#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 72d1774d5e..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 @@ -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,31 @@ 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 */ +#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) { @@ -2205,9 +2230,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 +2245,61 @@ 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 */ +#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/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 0804bca4ed..2f70e64eea 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); @@ -331,7 +331,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) { BT_DBG("ModelElem, ElemIdx %u", mod->elem_idx); @@ -605,6 +605,25 @@ 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) { BT_DBG("ElemCount %u", comp_0->elem_count); @@ -612,7 +631,7 @@ 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; @@ -702,7 +721,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); @@ -871,7 +890,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); @@ -1043,7 +1062,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) @@ -1136,7 +1155,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 61bc34d8db..b48274eade 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 @@ -2937,3 +2939,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..84110f8ade --- /dev/null +++ b/components/bt/esp_ble_mesh/v1.1/api/models/esp_ble_mesh_dfu_model_api.c @@ -0,0 +1,249 @@ +/* + * 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 */ + +#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/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..6ef42d3dd8 --- /dev/null +++ b/components/bt/esp_ble_mesh/v1.1/api/models/include/esp_ble_mesh_dfu_model_api.h @@ -0,0 +1,1988 @@ +/* + * 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) + +/* 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 +/** + * @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 `esp_ble_mesh_model_pub_t`. + * @param cli_data Pointer to the unique `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) + + +/** + * @brief Initialize a DFU Client model instance. + * + * @note This macro initializes the DFU Client model and sets callback handlers. + * + * @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 +/** + * @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 `esp_ble_mesh_model_pub_t`. + * @param srv_data Pointer to the unique `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) + + +/** + * @brief Initialize a DFU Server model instance. + * + * @note This macro initializes the DFU Server model and sets callback handlers + * and image information. + * + * @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 */ + +#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; + +#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 */ + +#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. */ + 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 received 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 */ + +#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 + +#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..d60df283f1 --- /dev/null +++ b/components/bt/esp_ble_mesh/v1.1/btc/btc_ble_mesh_dfu_model.c @@ -0,0 +1,1216 @@ +/* + * 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" +#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 */ + +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_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); + } + } + 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.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); + } + 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_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: + 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", __func, 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 */ + +#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/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..0111e86549 --- /dev/null +++ b/components/bt/esp_ble_mesh/v1.1/btc/include/btc_ble_mesh_dfu_model.h @@ -0,0 +1,123 @@ +/* + * 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 */ + +#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 + +#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_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 new file mode 100644 index 0000000000..955419ef1f --- /dev/null +++ b/components/bt/esp_ble_mesh/v1.1/dfu/dfd_srv.c @@ -0,0 +1,1310 @@ +/* + * 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 "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" + +#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(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."); + +#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(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(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(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."); + +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 == 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 == BLE_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) +{ + 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); + + 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 = BLE_MESH_DFD_SUCCESS; + struct bt_mesh_dfd_srv *srv = mod->user_data; + + if (buf->len % 3) { + return -EINVAL; + } + + if (bt_mesh_dfu_cli_is_busy(&srv->dfu)) { + receivers_status_rsp(srv, ctx, + BLE_MESH_DFD_ERR_BUSY_WITH_DISTRIBUTION); + return 0; + } + + while (buf->len >= 3 && status == BLE_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->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->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, BLE_MESH_MODEL_BUF_LEN(BLE_MESH_DFD_OP_RECEIVERS_LIST, + DFD_RECEIVERS_LIST_MSG_MAXLEN)); + 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); + + 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 + 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); + } + + 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 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) +{ + size_t size = 0; + + 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); + 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_BLE_MESH_DFD_SRV_SLOT_SPACE); + + net_buf_simple_add_le32(&rsp, CONFIG_BLE_MESH_DFD_SRV_SLOT_SPACE - size); + +#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 +#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) +{ + 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 == BLE_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->user_data; + + status_rsp(srv, ctx, BLE_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->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, BLE_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->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->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->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) +{ + BLE_MESH_MODEL_BUF_DEFINE(rsp, BLE_MESH_DFD_OP_UPLOAD_STATUS, + DFD_UPLOAD_STATUS_MSG_MAXLEN); + 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 == BLE_MESH_DFD_UPLOAD_PHASE_IDLE || + !srv->upload.slot) { + bt_mesh_model_send(srv->mod, ctx, &rsp, NULL, NULL); + return; + } + +#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); + } + + 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_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); + } + + 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->user_data; + + upload_status_rsp(srv, ctx, BLE_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, BLE_MESH_DFD_ERR_INTERNAL); + break; + case -EEXIST: /* Img with this fwid already is in list */ + 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, BLE_MESH_DFD_SUCCESS, 100); + } else { + srv->upload.slot = NULL; + upload_status_rsp(srv, ctx, BLE_MESH_DFD_ERR_INTERNAL); + } + break; + case 0: + srv->upload.phase = BLE_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->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); + + BT_DBG("Upload Start: size: %d, fwid: %s, metadata: %s", size, bt_hex(fwid, fwid_len), + bt_hex(meta, meta_len)); + + if (size > CONFIG_BLE_MESH_DFD_SRV_SLOT_MAX_SIZE) { + upload_status_rsp(srv, ctx, + 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, BLE_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_BLE_MESH_DFD_SRV_OOB_UPLOAD + && !srv->upload.is_oob +#endif + ) { + BT_DBG("Duplicate upload start"); + upload_status_rsp(srv, ctx, BLE_MESH_DFD_SUCCESS); + return 0; + } + + BT_WARN("Upload already in progress"); + upload_status_rsp(srv, ctx, BLE_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_BLE_MESH_DFD_SRV_OOB_UPLOAD + srv->upload.is_oob = false; +#endif + srv->upload.slot = bt_mesh_dfu_slot_reserve(); + + if (!srv->upload.slot) { + BT_WARN("No space for slot"); + upload_status_rsp(srv, ctx, + BLE_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, BLE_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) { + 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, BLE_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, BLE_MESH_DFD_ERR_INTERNAL); + return 0; + } + + BT_DBG("%s", bt_hex(fwid, fwid_len)); + + srv->upload.phase = BLE_MESH_DFD_UPLOAD_PHASE_TRANSFER_ACTIVE; + upload_status_rsp(srv, ctx, BLE_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->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); + + 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_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, BLE_MESH_DFD_SUCCESS); + return 0; + } +#endif + 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) { + /* Ignore the request if we didn't confirm the previous one. */ + return 0; +#endif + } + +#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, 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, BLE_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 != 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, BLE_MESH_DFD_ERR_URI_NOT_SUPPORTED); +#endif /* CONFIG_BLE_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->user_data; + + 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); + } else +#endif + { + (void)bt_mesh_blob_srv_cancel(&srv->upload.blob); + } + upload_status_rsp(srv, ctx, BLE_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) +{ + 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, BLE_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->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, BLE_MESH_DFD_SUCCESS, idx, fwid, + fwid_len); + } else { + fw_status_rsp(srv, ctx, BLE_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->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, BLE_MESH_DFD_SUCCESS, idx, slot->fwid, + slot->fwid_len); + } else { + fw_status_rsp(srv, ctx, BLE_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->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 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 bt_mesh_dfd_srv *srv = mod->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[] = { + { 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 +}; + +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, BLE_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; + + BT_DBG("reason: %u, phase: %u, apply: %u", reason, srv->phase, srv->apply); + + if (srv->phase == BLE_MESH_DFD_PHASE_IDLE) { + return; + } + + 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, BLE_MESH_DFD_PHASE_FAILED); + return; + } + + if (!srv->apply) { + dfd_phase_set(srv, BLE_MESH_DFD_PHASE_TRANSFER_SUCCESS); + return; + } + + 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, BLE_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 == BLE_MESH_DFD_PHASE_CANCELING_UPDATE) { + dfd_phase_set(srv, BLE_MESH_DFD_PHASE_FAILED); + return; + } + + 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, BLE_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 != BLE_MESH_DFD_PHASE_APPLYING_UPDATE && + srv->phase != BLE_MESH_DFD_PHASE_CANCELING_UPDATE) { + return; + } + + dfd_phase_set(srv, BLE_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) +{ + 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); + + 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 = BLE_MESH_DFD_UPLOAD_PHASE_TRANSFER_SUCCESS; + return; + } + + /* Will delete slot when we start a new upload */ + srv->upload.phase = BLE_MESH_DFD_UPLOAD_PHASE_TRANSFER_ERROR; +} + +static void upload_timeout(struct bt_mesh_blob_srv *b) +{ + BT_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(struct bt_mesh_model *mod) +{ + struct bt_mesh_dfd_srv *srv = mod->user_data; + + srv->mod = mod; + + return 0; +} + +static void dfd_srv_reset(struct bt_mesh_model *mod) +{ + struct bt_mesh_dfd_srv *srv = mod->user_data; + + dfd_phase_set(srv, BLE_MESH_DFD_PHASE_IDLE); + + srv->upload.phase = BLE_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(); +} + +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, +#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) +{ + struct bt_mesh_dfu_target *t; + struct bt_mesh_blob_target_pull *p; + + if (!BLE_MESH_ADDR_IS_UNICAST(addr)) { + return BLE_MESH_DFD_SUCCESS; + } + + t = target_get(srv, addr); + if (t) { + t->img_idx = img_idx; + return BLE_MESH_DFD_SUCCESS; + } + + /* New target node, add it to the list */ + + if (srv->target_cnt == ARRAY_SIZE(srv->targets)) { + return BLE_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; + + BT_DBG("Added receiver 0x%04x img: %u", t->blob.addr, + t->img_idx); + + 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 BLE_MESH_DFD_ERR_BUSY_WITH_DISTRIBUTION; + } + + sys_slist_init(&srv->inputs.targets); + srv->target_cnt = 0; + + return BLE_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 BLE_MESH_DFD_ERR_RECEIVERS_LIST_EMPTY; + } + + if (!bt_mesh_app_key_get(params->app_idx)) { + 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 BLE_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 == BLE_MESH_DFD_PHASE_COMPLETED) { + BT_WARN("Already completed or in progress"); + 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, BLE_MESH_DFD_PHASE_TRANSFER_ACTIVE); + return BLE_MESH_DFD_SUCCESS; + } + } else if (is_busy(srv) || + srv->phase == BLE_MESH_DFD_PHASE_TRANSFER_SUSPENDED) { + BT_WARN("Busy with distribution"); + return BLE_MESH_DFD_ERR_BUSY_WITH_DISTRIBUTION; + } + + if (srv->phase == BLE_MESH_DFD_PHASE_CANCELING_UPDATE) { + BT_WARN("Canceling 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 BLE_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; + + 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_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, BLE_MESH_DFD_PHASE_IDLE); + return BLE_MESH_DFD_ERR_INTERNAL; + } + + 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 == BLE_MESH_DFD_PHASE_TRANSFER_SUSPENDED) { + return BLE_MESH_DFD_SUCCESS; + } + + 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 BLE_MESH_DFD_ERR_SUSPEND_FAILED; + } + + 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, + struct bt_mesh_msg_ctx *ctx) +{ + enum bt_mesh_dfd_phase prev_phase; + int err; + + if (srv->phase == BLE_MESH_DFD_PHASE_CANCELING_UPDATE || + srv->phase == BLE_MESH_DFD_PHASE_IDLE) { + if (ctx != NULL) { + status_rsp(srv, ctx, BLE_MESH_DFD_SUCCESS); + } + return BLE_MESH_DFD_SUCCESS; + } + + 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, BLE_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, BLE_MESH_DFD_PHASE_CANCELING_UPDATE); + err = bt_mesh_dfu_cli_cancel(&srv->dfu, NULL); + if (err) { + if (ctx != NULL) { + status_rsp(srv, ctx, BLE_MESH_DFD_ERR_INTERNAL); + } + return BLE_MESH_DFD_ERR_INTERNAL; + } + + 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). + */ + /* + * Changed by Espressif: Access Message random delay is not currently supported in IDF. + */ + // ctx->rnd_delay = false; + } + + if (ctx != NULL) { + status_rsp(srv, ctx, BLE_MESH_DFD_SUCCESS); + } + + 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, BLE_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 == 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 == 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 BLE_MESH_DFD_ERR_INTERNAL; + } + + 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, + const uint8_t **fwid) +{ + struct bt_mesh_dfu_slot *slot; + int idx, err; + + if (srv->phase != BLE_MESH_DFD_PHASE_IDLE) { + *fwid = NULL; + *fwid_len = 0; + return BLE_MESH_DFD_ERR_BUSY_WITH_DISTRIBUTION; + } + + idx = bt_mesh_dfu_slot_get(*fwid, *fwid_len, &slot); + if (idx < 0) { + return BLE_MESH_DFD_SUCCESS; + } + + err = slot_del(srv, slot); + if (err) { + *fwid = NULL; + *fwid_len = 0; + return BLE_MESH_DFD_ERR_INTERNAL; + } else { + 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 != 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 BLE_MESH_DFD_SUCCESS; +} + +#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) +{ + int err; + + if (slot != srv->upload.slot || !srv->upload.is_oob || + 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. + */ + return -EINVAL; + } + + srv->upload.is_pending_oob_check = false; + + 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; + } + + err = set_upload_fwid(srv, &srv->upload.oob.ctx, fwid, fwid_len); + + if (err) { + return err; + } + + upload_status_rsp(srv, &srv->upload.oob.ctx, BLE_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 != BLE_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 = BLE_MESH_DFD_UPLOAD_PHASE_TRANSFER_SUCCESS; + return 0; + +error: + srv->upload.phase = BLE_MESH_DFD_UPLOAD_PHASE_TRANSFER_ERROR; + bt_mesh_dfu_slot_release(srv->upload.slot); + return err; +} +#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 new file mode 100644 index 0000000000..403b8b3d17 --- /dev/null +++ b/components/bt/esp_ble_mesh/v1.1/dfu/dfd_srv_internal.h @@ -0,0 +1,49 @@ +/* + * SPDX-FileCopyrightText: 2020 Nordic Semiconductor ASA + * SPDX-FileContributor: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef _BLE_MESH_v11_DFD_SRV_INTERNAL_H__ +#define _BLE_MESH_v11_DFD_SRV_INTERNAL_H__ + +#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; +}; + +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 +#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 new file mode 100644 index 0000000000..17a2059803 --- /dev/null +++ b/components/bt/esp_ble_mesh/v1.1/dfu/dfu.h @@ -0,0 +1,47 @@ +/* + * SPDX-FileCopyrightText: 2020 Nordic Semiconductor ASA + * SPDX-FileContributor: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include "mesh/buf.h" + +#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 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 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_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}; + + 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]; +} +#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 new file mode 100644 index 0000000000..e5ff20c328 --- /dev/null +++ b/components/bt/esp_ble_mesh/v1.1/dfu/dfu_cli.c @@ -0,0 +1,1513 @@ +/* + * SPDX-FileCopyrightText: 2020 Nordic Semiconductor ASA + * SPDX-FileContributor: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "access.h" +#include "dfu.h" +#include "btc_ble_mesh_dfu_model.h" +#include "blob.h" +#include "mesh/slist.h" +#include "mesh/mutex.h" +#include "mesh_v1.1/dfu/dfu.h" +#include "mesh_v1.1/dfu/dfu_cli.h" + +#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 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) + +_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."); + +_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, + 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), +}; + +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) +{ + struct bt_mesh_dfu_target *target; + + TARGETS_FOR_EACH(cli, target) { + if (addr == target->blob.addr) { + return target; + } + } + + 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); + if (req->params) { + bt_mesh_free(req->params); + } + 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) +{ + target->status = 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; + } + + if (cli->cb && cli->cb->lost_target) { + cli->cb->lost_target(cli, target); + } +} + +static void dfu_complete(struct bt_mesh_dfu_cli *cli) +{ + BT_DBG(""); + + if (cli->cb && cli->cb->ended) { + cli->cb->ended(cli, BLE_MESH_DFU_SUCCESS); + } +} + +static void dfu_applied(struct bt_mesh_dfu_cli *cli) +{ + BT_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) +{ + BT_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, + uint32_t opcode, struct bt_mesh_msg_ctx *ctx, + void *params) +{ + if (!cli->req || + cli->req->type != REQ_NONE) { + return -EBUSY; + } + + cli->req->params = params; + cli->req->type = type; + cli->req->dfu_cli = cli; + cli->req->opcode = opcode; + + 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 = ESP_OK; + bt_mesh_dfu_cli_req_t *req = cli->req; + + 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; + + TARGETS_FOR_EACH(cli, target) { + if (target->status == BLE_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, 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; + + /* 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) { + 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_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 == 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, BLE_MESH_DFU_ERR_INTERNAL); +} + +static void blob_suspended(struct bt_mesh_blob_cli *b) +{ + struct bt_mesh_dfu_cli *cli = DFU_CLI(b); + + BT_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); + bt_mesh_dfu_cli_req_t *req = 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 (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) { + BT_ERR("Blob failed in invalid state %u", cli->xfer.state); + return; + } + + 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, +}; + +/******************************************************************************* + * 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) { + BT_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) +{ + 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); +} + +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); + } + + 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); + + (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); + + 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); +} + +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); + + 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); +} + +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); + + 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); +} + +/******************************************************************************* + * 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; + } + } + + BT_DBG(""); + + cli->op = BLE_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 == 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; + + 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; + + BT_DBG(""); + + if (!targets_active(cli)) { + dfu_failed(cli, BLE_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) { + 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); + + if (!targets_active(cli)) { + dfu_failed(cli, BLE_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 + }; + + BT_DBG(""); + + 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); + + 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 + }; + + BT_DBG(""); + + cli->xfer.state = STATE_APPLY; + cli->op = BLE_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, BLE_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 BLE_MESH_DFU_ITER_CONTINUE; + } + + 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 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, + }; + + BT_DBG(""); + + 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); +} + +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 != 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 == BLE_MESH_DFU_EFFECT_UNPROV) { + if (!target->blob.acked) { + success = true; + continue; + } + + 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 (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 + }; + + BT_DBG(""); + + cli->op = BLE_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, BLE_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->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; + + req = bt_mesh_dfu_req_find_by_addr(ctx->addr); + + // 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->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 = 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_target_status)); + bt_mesh_free(req->params); + bt_mesh_dfu_cli_rm_req_from_list(req); + k_delayed_work_cancel(&req->timer); + req_free(req); + } + + if (cli->op != BLE_MESH_DFU_OP_UPDATE_STATUS) { + return 0; + } + + target = target_get(cli, ctx->addr); + if (!target) { + BT_WARN("Unknown target 0x%04x", ctx->addr); + return -ENOENT; + } + + BT_DBG("%u phase: %u, cur state: %u", status, phase, cli->xfer.state); + + target->phase = phase; + + 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 (status != BLE_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) { + 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; + } + + target->img_idx = net_buf_simple_pull_u8(buf); + + BT_DBG("Target 0x%04x receiving transfer", ctx->addr); + } else if (buf->len) { + return -EINVAL; + } + + 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; + } + + 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 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; + + req = bt_mesh_dfu_req_find_by_addr(ctx->addr); + if (!req) { + return 0; + } + + if (req->type == INTERNAL_REQ_IMG) { + goto confirm_procedure; + } + + 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; + } + + img_cnt = net_buf_simple_pull_u8(buf); + if (img_cnt < req->img_cnt) { + req->img_cnt = img_cnt; + } + + idx = net_buf_simple_pull_u8(buf); + if (idx >= img_cnt) { + BT_WARN("Invalid idx %u", idx); + return -ENOENT; + } + + BT_DBG("Image list from 0x%04x from index %u", ctx->addr, idx); + + 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; + + img.fwid_len = net_buf_simple_pull_u8(buf); + if (buf->len < img.fwid_len + 1) { + BT_WARN("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) { + BT_WARN("Invalid format: uri"); + return -EINVAL; + } + + BT_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_BLE_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 = 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); + } + + return 0; + } + + idx++; + } + + 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); + } + } + + 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) +{ + 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); + + req = bt_mesh_dfu_req_find_by_addr(ctx->addr); + if (!req) { + BT_WARN("Unexcept metadata status form 0x%04x", ctx->addr); + return 0; + } + + rsp = req->params; + + 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; + } + + 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[] = { + {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(struct bt_mesh_model *mod) +{ + struct bt_mesh_dfu_cli *cli = mod->user_data; + + if (mod->elem_idx != 0) { + BT_ERR("DFU update client must be instantiated on first elem"); + return -EINVAL; + } + + cli->mod = mod; + + cli->blob.cb = &_bt_mesh_dfu_cli_blob_handlers; + + bt_mesh_dfu_cli_req_list_init(); + + return 0; +} + +static void dfu_cli_reset(struct bt_mesh_model *mod) +{ + struct bt_mesh_dfu_cli *cli = mod->user_data; + + 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, +#if CONFIG_BLE_MESH_DEINIT + .deinit = dfu_cli_deinit, +#endif +}; + +/******************************************************************************* + * 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) { + bt_mesh_rand(&cli->xfer.blob.id, sizeof(cli->xfer.blob.id)); + } 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 = BLE_MESH_DFU_SUCCESS; + target->phase = BLE_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 = BLE_MESH_DFU_SUCCESS; + target->phase = BLE_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) +{ + 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); + + 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); + + 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) +{ + bt_mesh_dfu_cli_req_t *req = NULL; + + if (cli->xfer.state != STATE_APPLIED) { + return -EBUSY; + } + + 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_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 first_idx, uint8_t max_count) +{ + int err; + + if (bt_mesh_dfu_req_find_by_addr(ctx->addr)) { + BT_WARN("Exists waiting request"); + return -EBUSY; + } + + cli->req = req_alloc(); + if (!cli->req) { + return -ENOMEM; + } + + err = req_setup(cli, REQ_IMG, BLE_MESH_DFU_OP_UPDATE_INFO_GET, ctx, cb_data); + if (err) { + req_free(cli->req); + return err; + } + + cli->req->img_cb = cb; + cli->req->ttl = ctx->send_ttl; + cli->req->img_cnt = max_count; + + 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; + } + + err = req_wait(cli, K_MSEC(dfu_cli_timeout)); + + 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, + struct net_buf_simple *metadata) +{ + int err; + bt_mesh_dfu_cli_req_t *req = NULL; + + 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; + } + + 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); + + if (metadata) { + net_buf_simple_add_mem(&buf, metadata->data, 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) +{ + int err; + bt_mesh_dfu_cli_req_t *req = NULL; + + 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; + } + + 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; + } + + 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); + + 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; +} +#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 new file mode 100644 index 0000000000..9c966c5e38 --- /dev/null +++ b/components/bt/esp_ble_mesh/v1.1/dfu/dfu_metadata.c @@ -0,0 +1,105 @@ +/* + * SPDX-FileCopyrightText: 2020 Nordic Semiconductor ASA + * SPDX-FileContributor: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#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) +{ + 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 & 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->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 & BLE_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}; + + /* 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); + + return 0; +} + +int bt_mesh_dfu_metadata_comp_hash_local_get(uint8_t *key, uint32_t *hash) +{ + NET_BUF_SIMPLE_DEFINE(buf, BLE_MESH_TX_SDU_MAX); + int 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; +} +#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 new file mode 100644 index 0000000000..76f3a87e81 --- /dev/null +++ b/components/bt/esp_ble_mesh/v1.1/dfu/dfu_slot.c @@ -0,0 +1,444 @@ +/* + * SPDX-FileCopyrightText: 2020 Nordic Semiconductor ASA + * SPDX-FileContributor: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "dfu_slot.h" +#include +#include +#include + +#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 "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_BLE_MESH_DFU_SLOT_CNT]; + +static uint32_t slot_index; + +inline int settings_name_next(const char *name, const char **next) +{ + int rc = 0; + + 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; +} + +#if CONFIG_BLE_MESH_SETTINGS +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; +} +#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; + + 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) +{ + int err = 0; +#if CONFIG_BLE_MESH_SETTINGS + uint16_t idx = ARRAY_INDEX(slots, slot_to_store); + char buf[SLOT_ENTRY_BUFLEN]; + + 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 = 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 = bt_mesh_save_core_settings(slot_entry_encode(idx, buf, PROP_METADATA), + slot_to_store->slot.metadata, slot_to_store->slot.metadata_len); + +#endif + return err; +} + +static void slot_erase(struct slot *slot_to_erase) +{ +#if CONFIG_BLE_MESH_SETTINGS + uint16_t idx = ARRAY_INDEX(slots, slot_to_erase); + char buf[SLOT_ENTRY_BUFLEN]; + + 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; + + 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) { + BT_WARN("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; + + BT_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_BLE_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_BLE_MESH_DFU_FWID_MAXLEN) { + return -EFBIG; + } + + if (slot->idx == 0) { + return -EINVAL; + } + + if (is_slot_committed(slot)) { + return -EEXIST; + } + + 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); + + 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) { + BT_WARN("Store failed (err: %d)", err); + return err; + } + + sys_slist_append(&list, &slot->n); + + 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); + + 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); + + BT_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 != BLE_MESH_DFU_ITER_CONTINUE) { + break; + } + } + + return cnt; +} + +#if 0 +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; +} +#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 new file mode 100644 index 0000000000..7509ad24a6 --- /dev/null +++ b/components/bt/esp_ble_mesh/v1.1/dfu/dfu_slot.h @@ -0,0 +1,137 @@ +/* + * SPDX-FileCopyrightText: 2020 Nordic Semiconductor ASA + * SPDX-FileContributor: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "mesh_v1.1/dfu/dfu.h" +#if CONFIG_BLE_MESH_DFU_SLOTS +/** @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_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); + +/** @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 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 BLE_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); +#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 new file mode 100644 index 0000000000..b36746a822 --- /dev/null +++ b/components/bt/esp_ble_mesh/v1.1/dfu/dfu_srv.c @@ -0,0 +1,631 @@ +/* + * SPDX-FileCopyrightText: 2020 Nordic Semiconductor ASA + * SPDX-FileContributor: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "mesh_v1.1/dfu/dfu.h" +#include "mesh_v1.1/dfu/dfu_srv.h" + +#include "dfu.h" +#include "blob.h" +#include "access.h" + +#if CONFIG_BLE_MESH_SETTINGS +#include "settings.h" +#endif + +#if CONFIG_BLE_MESH_DFU_SRV +#define UPDATE_IDX_NONE 0xff + +_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."); + +_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) +{ +#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) +{ +#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; + } + + 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 = BLE_MESH_DFU_EFFECT_NONE; + + if (idx >= srv->img_count) { + return BLE_MESH_DFU_ERR_FW_IDX; + } + + if (!srv->cb->check) { + return BLE_MESH_DFU_SUCCESS; + } + + if (srv->cb->check(srv, &srv->imgs[idx], buf, effect)) { + *effect = BLE_MESH_DFU_EFFECT_NONE; + return BLE_MESH_DFU_ERR_METADATA; + } + + return BLE_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 = BLE_MESH_DFU_PHASE_VERIFY_OK; + BT_WARN("Apply response failed, wait for retry (err %d)", err); + return; + } + + BT_DBG(""); + + 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); + + 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); + } +} + +static void verify(struct bt_mesh_dfu_srv *srv) +{ + srv->update.phase = BLE_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 == 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 bt_mesh_dfu_srv *srv = mod->user_data; + uint8_t idx, limit; + + 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); + + BT_DBG("from %u (limit: %u)", idx, limit); + + 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; + + 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) + 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); + + 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 != BLE_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->user_data; + enum bt_mesh_dfu_status status; + enum bt_mesh_dfu_effect effect; + uint8_t idx; + + 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); + + BT_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 != BLE_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) +{ + 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))); + + 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; + } + + 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->user_data; + + BT_DBG(""); + + update_status_rsp(srv, ctx, BLE_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->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); + + BT_DBG("%u ttl: %u extra time: %u", idx, ttl, timeout_base); + + 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 = BLE_MESH_DFU_ERR_WRONG_PHASE; + } else { + status = BLE_MESH_DFU_SUCCESS; + srv->update.ttl = ttl; + srv->blob.state.xfer.id = blob_id; + } + + 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 != BLE_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 = 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 = BLE_MESH_DFU_ERR_RESOURCES; + goto rsp; + } + + if (err == -EBUSY) { + status = BLE_MESH_DFU_ERR_TEMPORARILY_UNAVAILABLE; + 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 = BLE_MESH_DFU_ERR_BLOB_XFER_BUSY; + goto rsp; + } + + 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); + + 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->user_data; + + if (srv->update.idx == UPDATE_IDX_NONE) { + goto rsp; + } + + BT_DBG(""); + + bt_mesh_blob_srv_cancel(&srv->blob); + srv->update.phase = BLE_MESH_DFU_PHASE_IDLE; + xfer_failed(srv); + +rsp: + update_status_rsp(srv, ctx, BLE_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->user_data; + static const struct bt_mesh_send_cb send_cb = { + .start = apply_rsp_sending, + .end = apply_rsp_sent, + }; + + BT_DBG(""); + + 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 != 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 = BLE_MESH_DFU_PHASE_APPLYING; + update_status_rsp(srv, ctx, BLE_MESH_DFU_SUCCESS, &send_cb); + + return 0; +} + +const struct bt_mesh_model_op _bt_mesh_dfu_srv_op[] = { + { 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(struct bt_mesh_model *mod) +{ + struct bt_mesh_dfu_srv *srv = mod->user_data; + + 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) { + BT_ERR("Invalid DFU Server initialization"); + return -EINVAL; + } + + 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) +{ + struct bt_mesh_dfu_srv *srv = mod->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; + } + + 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; +} + +static void dfu_srv_reset(struct bt_mesh_model *mod) +{ + struct bt_mesh_dfu_srv *srv = mod->user_data; + + 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, +#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); + + 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); + + BT_DBG("success: %u", success); + + if (!success) { + srv->update.phase = BLE_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 != 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); +} + +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 != BLE_MESH_DFU_PHASE_VERIFY) { + BT_WARN("Wrong state"); + return; + } + + BT_DBG(""); + + 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 != BLE_MESH_DFU_PHASE_VERIFY) { + BT_WARN("Wrong state"); + return; + } + + BT_DBG(""); + + 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 == BLE_MESH_DFU_PHASE_IDLE) { + BT_WARN("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 != BLE_MESH_DFU_PHASE_APPLYING) { + BT_WARN("Wrong state"); + return; + } + + BT_DBG(""); + + 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 != 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 (srv->update.phase == BLE_MESH_DFU_PHASE_TRANSFER_ACTIVE) { + return bt_mesh_blob_srv_progress(&srv->blob); + } + + 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 new file mode 100644 index 0000000000..a9634ef220 --- /dev/null +++ b/components/bt/esp_ble_mesh/v1.1/include/mesh_v1.1/dfu/dfd.h @@ -0,0 +1,146 @@ +/* + * SPDX-FileCopyrightText: 2020 Nordic Semiconductor ASA + * SPDX-FileContributor: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef _BLE_MESH_v11_DFD_H__ +#define _BLE_MESH_v11_DFD_H__ + +#ifdef __cplusplus +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 + * @{ + */ + +/** Firmware distribution status. */ +enum bt_mesh_dfd_status { + /** The message was processed successfully. */ + BLE_MESH_DFD_SUCCESS, + + /** Insufficient resources on the node. */ + BLE_MESH_DFD_ERR_INSUFFICIENT_RESOURCES, + + /** The operation cannot be performed while the Server is in the current + * phase. + */ + BLE_MESH_DFD_ERR_WRONG_PHASE, + + /** An internal error occurred on the node. */ + BLE_MESH_DFD_ERR_INTERNAL, + + /** The requested firmware image is not stored on the Distributor. */ + BLE_MESH_DFD_ERR_FW_NOT_FOUND, + + /** The AppKey identified by the AppKey Index is not known to the node. + */ + BLE_MESH_DFD_ERR_INVALID_APPKEY_INDEX, + + /** There are no Target nodes in the Distribution Receivers List + * state. + */ + BLE_MESH_DFD_ERR_RECEIVERS_LIST_EMPTY, + + /** Another firmware image distribution is in progress. */ + BLE_MESH_DFD_ERR_BUSY_WITH_DISTRIBUTION, + + /** Another upload is in progress. */ + BLE_MESH_DFD_ERR_BUSY_WITH_UPLOAD, + + /** The URI scheme name indicated by the Update URI is not supported. */ + BLE_MESH_DFD_ERR_URI_NOT_SUPPORTED, + + /** The format of the Update URI is invalid. */ + BLE_MESH_DFD_ERR_URI_MALFORMED, + + /** The URI is currently unreachable. */ + BLE_MESH_DFD_ERR_URI_UNREACHABLE, + + /** The Check Firmware OOB procedure did not find any new firmware. */ + BLE_MESH_DFD_ERR_NEW_FW_NOT_AVAILABLE, + + /** The suspension of the Distribute Firmware procedure failed. */ + BLE_MESH_DFD_ERR_SUSPEND_FAILED, +}; + +/** Firmware distribution phases. */ +enum bt_mesh_dfd_phase { + /** No firmware distribution is in progress. */ + BLE_MESH_DFD_PHASE_IDLE, + + /** Firmware distribution is in progress. */ + BLE_MESH_DFD_PHASE_TRANSFER_ACTIVE, + + /** The Transfer BLOB procedure has completed successfully. */ + BLE_MESH_DFD_PHASE_TRANSFER_SUCCESS, + + /** The Apply Firmware on Target Nodes procedure is being executed. */ + BLE_MESH_DFD_PHASE_APPLYING_UPDATE, + + /** The Distribute Firmware procedure has completed successfully. */ + BLE_MESH_DFD_PHASE_COMPLETED, + + /** The Distribute Firmware procedure has failed. */ + BLE_MESH_DFD_PHASE_FAILED, + + /** The Cancel Firmware Update procedure is being executed. */ + BLE_MESH_DFD_PHASE_CANCELING_UPDATE, + + /** The Transfer BLOB procedure is suspended. */ + BLE_MESH_DFD_PHASE_TRANSFER_SUSPENDED, +}; + +/** Firmware upload phases. */ +enum bt_mesh_dfd_upload_phase { + /** No firmware upload is in progress. */ + BLE_MESH_DFD_UPLOAD_PHASE_IDLE, + + /** The Store Firmware procedure is being executed. */ + BLE_MESH_DFD_UPLOAD_PHASE_TRANSFER_ACTIVE, + + /** The Store Firmware procedure or Store Firmware OOB procedure failed. + */ + BLE_MESH_DFD_UPLOAD_PHASE_TRANSFER_ERROR, + + /** The Store Firmware procedure or the Store Firmware OOB procedure + * completed successfully. + */ + BLE_MESH_DFD_UPLOAD_PHASE_TRANSFER_SUCCESS, +}; + +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif /* _BLE_MESH_v11_DFD_H__ */ 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 new file mode 100644 index 0000000000..b494ce775d --- /dev/null +++ b/components/bt/esp_ble_mesh/v1.1/include/mesh_v1.1/dfu/dfd_srv.h @@ -0,0 +1,311 @@ +/* + * SPDX-FileCopyrightText: 2020 Nordic Semiconductor ASA + * SPDX-FileContributor: 2025 Espressif Systems (Shanghai) CO LTD + * + * 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 _BLE_MESH_v11_DFD_SRV_H__ +#define _BLE_MESH_v11_DFD_SRV_H__ + +#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_BLE_MESH_DFD_SRV_TARGETS_MAX +#define CONFIG_BLE_MESH_DFD_SRV_TARGETS_MAX 0 +#endif + +#ifndef CONFIG_BLE_MESH_DFD_SRV_SLOT_MAX_SIZE +#define CONFIG_BLE_MESH_DFD_SRV_SLOT_MAX_SIZE 0 +#endif + +#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_BLE_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 = 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 */ + +/** + * @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 = BLE_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), \ + 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); + +#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 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, + 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_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 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_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_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_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_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 + * callback has completed or failed. The @p status param should be set to one of the following + * values: + * + * * @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 + * 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_BLE_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 /* _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 new file mode 100644 index 0000000000..073aae901b --- /dev/null +++ b/components/bt/esp_ble_mesh/v1.1/include/mesh_v1.1/dfu/dfu.h @@ -0,0 +1,173 @@ +/* + * SPDX-FileCopyrightText: 2020 Nordic Semiconductor ASA + * SPDX-FileContributor: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef _BLE_MESH_v11_DFU_H__ +#define _BLE_MESH_v11_DFU_H__ + +#include + +#include "mesh/types.h" +#include "mesh/utils.h" + +#include "mesh_v1.1/mbt/blob.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @defgroup bt_mesh_dfu Bluetooth Mesh Device Firmware Update + * @ingroup bt_mesh + * @{ + */ + +#ifndef CONFIG_BLE_MESH_DFU_FWID_MAXLEN +#define CONFIG_BLE_MESH_DFU_FWID_MAXLEN 0 +#endif + +#ifndef CONFIG_BLE_MESH_DFU_METADATA_MAXLEN +#define CONFIG_BLE_MESH_DFU_METADATA_MAXLEN 0 +#endif + +#ifndef CONFIG_BLE_MESH_DFU_URI_MAXLEN +#define CONFIG_BLE_MESH_DFU_URI_MAXLEN 0 +#endif + +#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. */ + BLE_MESH_DFU_PHASE_IDLE, + + /** The Transfer BLOB procedure failed. */ + BLE_MESH_DFU_PHASE_TRANSFER_ERR, + + /** The Receive Firmware procedure is being executed. */ + BLE_MESH_DFU_PHASE_TRANSFER_ACTIVE, + + /** The Verify Firmware procedure is being executed. */ + BLE_MESH_DFU_PHASE_VERIFY, + + /** The Verify Firmware procedure completed successfully. */ + BLE_MESH_DFU_PHASE_VERIFY_OK, + + /** The Verify Firmware procedure failed. */ + BLE_MESH_DFU_PHASE_VERIFY_FAIL, + + /** The Apply New Firmware procedure is being executed. */ + BLE_MESH_DFU_PHASE_APPLYING, + + /** Firmware transfer has been canceled. */ + BLE_MESH_DFU_PHASE_TRANSFER_CANCELED, + + /** Firmware applying succeeded. */ + BLE_MESH_DFU_PHASE_APPLY_SUCCESS, + + /** Firmware applying failed. */ + BLE_MESH_DFU_PHASE_APPLY_FAIL, + + /** 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. */ + BLE_MESH_DFU_SUCCESS, + + /** Insufficient resources on the node */ + BLE_MESH_DFU_ERR_RESOURCES, + + /** 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. */ + BLE_MESH_DFU_ERR_INTERNAL, + + /** The message contains a firmware index value that is not expected. */ + BLE_MESH_DFU_ERR_FW_IDX, + + /** The metadata check failed. */ + BLE_MESH_DFU_ERR_METADATA, + + /** The Server cannot start a firmware update. */ + BLE_MESH_DFU_ERR_TEMPORARILY_UNAVAILABLE, + + /** 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. */ + BLE_MESH_DFU_EFFECT_NONE, + + /** 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. + */ + BLE_MESH_DFU_EFFECT_COMP_CHANGE, + + /** Node will be unprovisioned after the update. */ + BLE_MESH_DFU_EFFECT_UNPROV, +}; + +/** Action for DFU iteration callbacks. */ +enum bt_mesh_dfu_iter { + /** Stop iterating. */ + BLE_MESH_DFU_ITER_STOP, + + /** Continue iterating. */ + BLE_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_BLE_MESH_DFU_FWID_MAXLEN]; + /** Metadata. */ + uint8_t metadata[CONFIG_BLE_MESH_DFU_METADATA_MAXLEN]; +}; + +/** @} */ + +#ifdef __cplusplus +} +#endif + +#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 new file mode 100644 index 0000000000..531e359994 --- /dev/null +++ b/components/bt/esp_ble_mesh/v1.1/include/mesh_v1.1/dfu/dfu_cli.h @@ -0,0 +1,430 @@ +/* + * SPDX-FileCopyrightText: 2020 Nordic Semiconductor ASA + * SPDX-FileContributor: 2025 Espressif Systems (Shanghai) CO LTD + * + * 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 _BLE_MESH_v11_DFU_CLI_H__ +#define _BLE_MESH_v11_DFU_CLI_H__ + +#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" { +#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 BLE_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(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; +}; + +/** 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. + * 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; + + /** 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 BLE_MESH_DFU_ITER_STOP Stop iterating through the image list and + * return from @ref bt_mesh_dfu_cli_imgs_get. + * @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); + +/** 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); +}; + +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 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; + + /* runtime state */ + + 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; + + 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; +}; + +/** 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 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. + * + * @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 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 first_idx, 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 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, + struct net_buf_simple *metadata); + +/** @brief Get the status of a Target node. + * + * @param cli Firmware Update Client model instance. + * @param ctx Message context. + * + * @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); + +/** @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 /* _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 new file mode 100644 index 0000000000..baa4049a2b --- /dev/null +++ b/components/bt/esp_ble_mesh/v1.1/include/mesh_v1.1/dfu/dfu_metadata.h @@ -0,0 +1,113 @@ +/* + * SPDX-FileCopyrightText: 2020 Nordic Semiconductor ASA + * SPDX-FileContributor: 2025 Espressif Systems (Shanghai) CO LTD + * + * 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 _BLE_MESH_v11_DFU_METADATA_H__ +#define _BLE_MESH_v11_DFU_METADATA_H__ + +#include + +#include + +#include "mesh/types.h" +#include "mesh/utils.h" + +#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. */ + 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; +}; + +/** @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 /* _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 new file mode 100644 index 0000000000..dc134fe07e --- /dev/null +++ b/components/bt/esp_ble_mesh/v1.1/include/mesh_v1.1/dfu/dfu_srv.h @@ -0,0 +1,264 @@ +/* + * SPDX-FileCopyrightText: 2020 Nordic Semiconductor ASA + * SPDX-FileContributor: 2025 Espressif Systems (Shanghai) CO LTD + * + * 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 _BLE_MESH_v11_DFU_SRV_H__ +#define _BLE_MESH_v11_DFU_SRV_H__ + +#include "mesh/access.h" +#include "mesh_v1.1/mbt/blob_srv.h" + +#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 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, \ + } + +/** + * + * @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), \ + 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 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 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; + + /* 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 /* _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 new file mode 100644 index 0000000000..29199360a4 --- /dev/null +++ b/components/bt/esp_ble_mesh/v1.1/include/mesh_v1.1/mbt/blob.h @@ -0,0 +1,272 @@ +/* + * SPDX-FileCopyrightText: 2020 Nordic Semiconductor ASA + * SPDX-FileContributor: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef _BLE_MESH_v11_BLOB_H__ +#define _BLE_MESH_v11_BLOB_H__ + +#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" { +#endif + +/** + * @defgroup bt_mesh_blob Bluetooth Mesh BLOB model API + * @ingroup bt_mesh + * @{ + */ + +#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, +}; + +/** 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_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; +}; + +/** 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 /* _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 new file mode 100644 index 0000000000..59b2169370 --- /dev/null +++ b/components/bt/esp_ble_mesh/v1.1/include/mesh_v1.1/mbt/blob_cli.h @@ -0,0 +1,467 @@ +/* + * SPDX-FileCopyrightText: 2020 Nordic Semiconductor ASA + * SPDX-FileContributor: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef _BLE_MESH_v11_BLOB_CLI_H_ +#define _BLE_MESH_v11_BLOB_CLI_H_ + +#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" { +#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) \ + 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; + + /** 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; + + /** 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 + * 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 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); + +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 + * 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 /* _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 new file mode 100644 index 0000000000..c2a6e48765 --- /dev/null +++ b/components/bt/esp_ble_mesh/v1.1/include/mesh_v1.1/mbt/blob_srv.h @@ -0,0 +1,229 @@ +/* + * SPDX-FileCopyrightText: 2020 Nordic Semiconductor ASA + * SPDX-FileContributor: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef _BLE_MESH_v11_BLOB_SRV_H_ +#define _BLE_MESH_v11_BLOB_SRV_H_ + +#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" { +#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_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 + +/** + * + * @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) \ + 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 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. */ + 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; +}; + +/** @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 /* _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 new file mode 100644 index 0000000000..c539219d9e --- /dev/null +++ b/components/bt/esp_ble_mesh/v1.1/mbt/blob.h @@ -0,0 +1,164 @@ +/* + * SPDX-FileCopyrightText: 2020 Nordic Semiconductor ASA + * SPDX-FileContributor: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#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 \ + (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_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_BLE_MESH_RX_BLOB_CHUNK_SIZE +#endif + +#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_BLE_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_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_BLE_MESH_BLOB_BLOCK_SIZE_MAX) + +#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)) +#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)); +} + +#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 + * 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); +#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 new file mode 100644 index 0000000000..9132829a23 --- /dev/null +++ b/components/bt/esp_ble_mesh/v1.1/mbt/blob_cli.c @@ -0,0 +1,1693 @@ +/* + * SPDX-FileCopyrightText: 2020 Nordic Semiconductor ASA + * SPDX-FileContributor: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include + +#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" + +#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) + +/* 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 == BLE_MESH_ADDR_UNASSIGNED || \ + (cli)->tx.ctx.force_unicast) + +_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."); + +_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."); + +_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; +}; + +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_BLE_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; + } + } + + 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) +{ + 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); + } +} + +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)); + } + } + + 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; + + 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; + + BT_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) +{ + BT_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; + + BT_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) { + BT_DBG("Transfer timed out."); + + if (!cli->tx.ctx.optional) { + drop_remaining_targets(cli); + } + } + + broadcast_complete(cli); + return; + } + + BT_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) { + BT_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) { + BT_ERR("BLOB cli busy"); + return; + } + + 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); + + BT_DBG("%u targets", cli->tx.pending); + + cli->tx.target = NULL; + if (!next_target(cli, &cli->tx.target)) { + BT_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; + } + + BT_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((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; +} + +static void send_start(uint16_t duration, int err, void *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; + + 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) +{ + 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); +} + +static void xfer_start_tx(struct bt_mesh_blob_cli *cli, uint16_t dst) +{ + 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); +} + +static void xfer_get_tx(struct bt_mesh_blob_cli *cli, uint16_t dst) +{ + 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); +} + +static void xfer_cancel_tx(struct bt_mesh_blob_cli *cli, uint16_t dst) +{ + 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); +} + +static void block_start_tx(struct bt_mesh_blob_cli *cli, uint16_t dst) +{ + 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); +} + +static void chunk_tx(struct bt_mesh_blob_cli *cli, uint16_t dst) +{ + struct bt_mesh_blob_chunk chunk; + int err; + + BT_DBG("Chunk will send to 0x%04x", dst); + + /* 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); + + 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); + + 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) +{ + 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); +} + +/************************************************************************************************** + * 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; + } + + BT_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; + } + + 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); +} + +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. + */ + + BT_DBG("A Chunk sent finished"); + + 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; + + BT_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; + } + + 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_retry_timer(cli); +} + +static void block_check_end(struct bt_mesh_blob_cli *cli) +{ + BT_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; + } + + 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->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, + }; + + BT_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) +{ + BT_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, + }; + + BT_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, + }; + + BT_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) { + BT_WARN("Invalid state %u", cli->state); + return; + } + + BT_WARN("0x%04x: block: %u status: %u", target->addr, block->block.number, block->status); + + 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) { + 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 (cli->xfer->mode == BT_MESH_BLOB_XFER_MODE_PULL) { + memset(target->pull->missing, 0, sizeof(target->pull->missing)); + update_missing_chunks(cli); + } + + 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)); + + BT_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->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); + } + + 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; + } + + 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) { + 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); + } + + 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->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) { + BT_WARN("Unexpected encoded block report in push mode"); + return -EINVAL; + } + + BT_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) { + BT_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->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); + + 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; + } + + len = buf->len; + memcpy(status.block.missing, net_buf_simple_pull_mem(buf, len), + len); + + 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) { + BT_ERR("Invalid encoding"); + return -EINVAL; + } + + BT_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->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; + } + + 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; + } + + 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, 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(struct bt_mesh_model *mod) +{ + struct bt_mesh_blob_cli *cli = mod->user_data; + + cli->mod = mod; + + 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; +} + +static void blob_cli_reset(struct bt_mesh_model *mod) +{ + struct bt_mesh_blob_cli *cli = mod->user_data; + + 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, +#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) +{ + 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_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)) { + BT_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)) { + 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) { + BT_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_BLE_MESH_BLOB_CHUNK_COUNT_MAX) { + BT_ERR("Too many chunks"); + return -EINVAL; + } + + if (!targets_reset(cli)) { + BT_ERR("No valid targets"); + return -ENODEV; + } + + 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); +} + +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) { + 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; +} + +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) { + 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; + } + } + + if (!targets_reset(cli)) { + BT_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)) { + BT_WARN("BLOB xfer already cancelled"); + return; + } + + 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; + } + + 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) +{ + 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; +} + +#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 new file mode 100644 index 0000000000..3540a6abea --- /dev/null +++ b/components/bt/esp_ble_mesh/v1.1/mbt/blob_srv.c @@ -0,0 +1,1073 @@ +/* + * SPDX-FileCopyrightText: 2020 Nordic Semiconductor ASA + * SPDX-FileContributor: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#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" + +#if CONFIG_BLE_MESH_SETTINGS +#include "settings.h" +#endif + +#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_BLE_MESH_BLOB_REPORT_TIMEOUT) + +_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."); + +_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."); + +_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."); + +_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)); +} + +static inline uint32_t max_chunk_size(const struct bt_mesh_blob_srv *srv) +{ + 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_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; + + 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 CONFIG_BLE_MESH_SETTINGS + /* 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); +#endif +} + +static void erase_state(struct bt_mesh_blob_srv *srv) +{ +#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; + } + + 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_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))); + } +} + +static int pull_req_max(const struct bt_mesh_blob_srv *srv) +{ + int count = CONFIG_BLE_MESH_BLOB_SRV_PULL_REQ_COUNT; + +#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_BLE_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; + + BT_DBG(""); + + 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); + } +} + +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; + + BT_DBG("rx BLOB Timeout Timer: %i", k_work_delayable_is_pending(&srv->rx_timeout)); + + 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); + + 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; + 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 = 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); + } +} + +static void suspend(struct bt_mesh_blob_srv *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) +{ + BT_DBG("%s", __func__); + + 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); + + 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); + } +} + +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); + + BT_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) +{ + 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); + + 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; + + 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->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; + } + + 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); + + 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)); + + 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); + + 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; + } + + (void)bt_mesh_model_send(srv->mod, ctx, &buf, NULL, NULL); +} + +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->user_data; + + 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); + } + + 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->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); + + 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) { + 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; + BT_WARN("Uninitialized"); + goto rsp; + } + +#if 0 /* For case DFU/SR/FD/BV-59-C, workaround */ + srv->state.xfer.id = id; +#endif + + 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_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; + } + + if (srv->phase == BT_MESH_BLOB_XFER_PHASE_SUSPENDED) { + resume(srv); + store_state(srv); + } else { + BT_DBG("Duplicate"); + } + + status = BT_MESH_BLOB_SUCCESS; + goto rsp; + } + + if (size > CONFIG_BLE_MESH_BLOB_SIZE_MAX) { + BT_WARN("Too large"); + status = BT_MESH_BLOB_ERR_BLOB_TOO_LARGE; + goto rsp; + } + + 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; + } + + 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) { + BT_WARN("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++) { + bt_mesh_atomic_set_bit(srv->state.blocks, i); + } + + 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; + } + + 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); + + 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->user_data; + uint64_t id; + + id = net_buf_simple_pull_le64(buf); + + BT_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->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; + } + + BT_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->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))) { + 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); + + 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) { + BT_INFO("Waiting nxt chunk to resume blob srv"); + } + + phase_set(srv, BT_MESH_BLOB_XFER_PHASE_WAITING_FOR_CHUNK); + blob_chunk_missing_set_all(&srv->block); + + 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->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(struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + 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; + + 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 (expected_size == 0) { + expected_size = srv->state.xfer.chunk_size; + } + + if (chunk.size != expected_size) { + BT_ERR("Unexpected size: %u != %u", expected_size, chunk.size); + return -EINVAL; + } + + /** + * 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); + } + + BT_INFO("%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)) { + BT_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); + } + + bt_mesh_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->user_data; + + BT_DBG(""); + + 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; + } + + (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, 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(struct bt_mesh_model *mod) +{ + struct bt_mesh_blob_srv *srv = mod->user_data; + + 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; +} + +__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) +{ + 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; + } + + 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) { + 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 == 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); + + BT_DBG("Recovered transfer from 0x%04x (%llu)", srv->state.cli, + srv->state.xfer.id); + } + + return 0; +} + +__attribute__((unused)) +static int blob_srv_start(const struct bt_mesh_model *mod) +{ + 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->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) { + 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; +} + +static void blob_srv_reset(struct bt_mesh_model *mod) +{ + 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); +} + +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, +#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) +{ + 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 (!bt_mesh_atomic_test_bit(srv->state.blocks, i)) { + received++; + } + } + + 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..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 @@ -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 (Preview) + +* Device Firmware Update Server models + * Firmware Update Server model (Preview) + * Firmware Distribution Server model (Preview) + 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 + 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 示例 """""""""""